@@ -36,24 +36,7 @@ namespace ts.formatting {
36
36
37
37
const enclosingCommentRange = getRangeOfEnclosingComment ( sourceFile , position , /*onlyMultiLine*/ true , precedingToken || null ) ; // tslint:disable-line:no-null-keyword
38
38
if ( enclosingCommentRange ) {
39
- const previousLine = getLineAndCharacterOfPosition ( sourceFile , position ) . line - 1 ;
40
- const commentStartLine = getLineAndCharacterOfPosition ( sourceFile , enclosingCommentRange . pos ) . line ;
41
-
42
- Debug . assert ( commentStartLine >= 0 ) ;
43
-
44
- if ( previousLine <= commentStartLine ) {
45
- return findFirstNonWhitespaceColumn ( getStartPositionOfLine ( commentStartLine , sourceFile ) , position , sourceFile , options ) ;
46
- }
47
-
48
- const startPostionOfLine = getStartPositionOfLine ( previousLine , sourceFile ) ;
49
- const { column, character } = findFirstNonWhitespaceCharacterAndColumn ( startPostionOfLine , position , sourceFile , options ) ;
50
-
51
- if ( column === 0 ) {
52
- return column ;
53
- }
54
-
55
- const firstNonWhitespaceCharacterCode = sourceFile . text . charCodeAt ( startPostionOfLine + character ) ;
56
- return firstNonWhitespaceCharacterCode === CharacterCodes . asterisk ? column - 1 : column ;
39
+ return getCommentIndent ( sourceFile , position , options , enclosingCommentRange ) ;
57
40
}
58
41
59
42
if ( ! precedingToken ) {
@@ -72,20 +55,7 @@ namespace ts.formatting {
72
55
// for block indentation, we should look for a line which contains something that's not
73
56
// whitespace.
74
57
if ( options . indentStyle === IndentStyle . Block ) {
75
-
76
- // move backwards until we find a line with a non-whitespace character,
77
- // then find the first non-whitespace character for that line.
78
- let current = position ;
79
- while ( current > 0 ) {
80
- const char = sourceFile . text . charCodeAt ( current ) ;
81
- if ( ! isWhiteSpaceLike ( char ) ) {
82
- break ;
83
- }
84
- current -- ;
85
- }
86
-
87
- const lineStart = ts . getLineStartPositionForPosition ( current , sourceFile ) ;
88
- return SmartIndenter . findFirstNonWhitespaceColumn ( lineStart , current , sourceFile , options ) ;
58
+ return getBlockIndent ( sourceFile , position , options ) ;
89
59
}
90
60
91
61
if ( precedingToken . kind === SyntaxKind . CommaToken && precedingToken . parent . kind !== SyntaxKind . BinaryExpression ) {
@@ -96,26 +66,60 @@ namespace ts.formatting {
96
66
}
97
67
}
98
68
69
+ return getSmartIndent ( sourceFile , position , precedingToken , lineAtPosition , assumeNewLineBeforeCloseBrace , options ) ;
70
+ }
71
+
72
+ function getCommentIndent ( sourceFile : SourceFile , position : number , options : EditorSettings , enclosingCommentRange : CommentRange ) : number {
73
+ const previousLine = getLineAndCharacterOfPosition ( sourceFile , position ) . line - 1 ;
74
+ const commentStartLine = getLineAndCharacterOfPosition ( sourceFile , enclosingCommentRange . pos ) . line ;
75
+
76
+ Debug . assert ( commentStartLine >= 0 ) ;
77
+
78
+ if ( previousLine <= commentStartLine ) {
79
+ return findFirstNonWhitespaceColumn ( getStartPositionOfLine ( commentStartLine , sourceFile ) , position , sourceFile , options ) ;
80
+ }
81
+
82
+ const startPostionOfLine = getStartPositionOfLine ( previousLine , sourceFile ) ;
83
+ const { column, character } = findFirstNonWhitespaceCharacterAndColumn ( startPostionOfLine , position , sourceFile , options ) ;
84
+
85
+ if ( column === 0 ) {
86
+ return column ;
87
+ }
88
+
89
+ const firstNonWhitespaceCharacterCode = sourceFile . text . charCodeAt ( startPostionOfLine + character ) ;
90
+ return firstNonWhitespaceCharacterCode === CharacterCodes . asterisk ? column - 1 : column ;
91
+ }
92
+
93
+ function getBlockIndent ( sourceFile : SourceFile , position : number , options : EditorSettings ) : number {
94
+ // move backwards until we find a line with a non-whitespace character,
95
+ // then find the first non-whitespace character for that line.
96
+ let current = position ;
97
+ while ( current > 0 ) {
98
+ const char = sourceFile . text . charCodeAt ( current ) ;
99
+ if ( ! isWhiteSpaceLike ( char ) ) {
100
+ break ;
101
+ }
102
+ current -- ;
103
+ }
104
+
105
+ const lineStart = ts . getLineStartPositionForPosition ( current , sourceFile ) ;
106
+ return findFirstNonWhitespaceColumn ( lineStart , current , sourceFile , options ) ;
107
+ }
108
+
109
+ function getSmartIndent ( sourceFile : SourceFile , position : number , precedingToken : Node , lineAtPosition : number , assumeNewLineBeforeCloseBrace : boolean , options : EditorSettings ) : number {
99
110
// try to find node that can contribute to indentation and includes 'position' starting from 'precedingToken'
100
111
// if such node is found - compute initial indentation for 'position' inside this node
101
- let previous : Node ;
112
+ let previous : Node | undefined ;
102
113
let current = precedingToken ;
103
- let currentStart : LineAndCharacter ;
104
- let indentationDelta : number ;
105
-
106
114
while ( current ) {
107
- if ( positionBelongsToNode ( current , position , sourceFile ) && shouldIndentChildNode ( current , previous ) ) {
108
- currentStart = getStartLineAndCharacterForNode ( current , sourceFile ) ;
109
-
115
+ if ( positionBelongsToNode ( current , position , sourceFile ) && shouldIndentChildNode ( current , previous , /*isNextChild*/ true ) ) {
116
+ const currentStart = getStartLineAndCharacterForNode ( current , sourceFile ) ;
110
117
const nextTokenKind = nextTokenIsCurlyBraceOnSameLineAsCursor ( precedingToken , current , lineAtPosition , sourceFile ) ;
111
- if ( nextTokenKind !== NextTokenKind . Unknown ) {
118
+ const indentationDelta = nextTokenKind !== NextTokenKind . Unknown
112
119
// handle cases when codefix is about to be inserted before the close brace
113
- indentationDelta = assumeNewLineBeforeCloseBrace && nextTokenKind === NextTokenKind . CloseBrace ? options . indentSize : 0 ;
114
- }
115
- else {
116
- indentationDelta = lineAtPosition !== currentStart . line ? options . indentSize : 0 ;
117
- }
118
- break ;
120
+ ? assumeNewLineBeforeCloseBrace && nextTokenKind === NextTokenKind . CloseBrace ? options . indentSize : 0
121
+ : lineAtPosition !== currentStart . line ? options . indentSize : 0 ;
122
+ return getIndentationForNodeWorker ( current , currentStart , /*ignoreActualIndentationRange*/ undefined , indentationDelta , sourceFile , /*isNextChild*/ true , options ) ;
119
123
}
120
124
121
125
// check if current node is a list item - if yes, take indentation from it
@@ -131,18 +135,13 @@ namespace ts.formatting {
131
135
previous = current ;
132
136
current = current . parent ;
133
137
}
134
-
135
- if ( ! current ) {
136
- // no parent was found - return the base indentation of the SourceFile
137
- return getBaseIndentation ( options ) ;
138
- }
139
-
140
- return getIndentationForNodeWorker ( current , currentStart , /*ignoreActualIndentationRange*/ undefined , indentationDelta , sourceFile , options ) ;
138
+ // no parent was found - return the base indentation of the SourceFile
139
+ return getBaseIndentation ( options ) ;
141
140
}
142
141
143
142
export function getIndentationForNode ( n : Node , ignoreActualIndentationRange : TextRange , sourceFile : SourceFile , options : EditorSettings ) : number {
144
143
const start = sourceFile . getLineAndCharacterOfPosition ( n . getStart ( sourceFile ) ) ;
145
- return getIndentationForNodeWorker ( n , start , ignoreActualIndentationRange , /*indentationDelta*/ 0 , sourceFile , options ) ;
144
+ return getIndentationForNodeWorker ( n , start , ignoreActualIndentationRange , /*indentationDelta*/ 0 , sourceFile , /*isNextChild*/ false , options ) ;
146
145
}
147
146
148
147
export function getBaseIndentation ( options : EditorSettings ) {
@@ -155,11 +154,9 @@ namespace ts.formatting {
155
154
ignoreActualIndentationRange : TextRange ,
156
155
indentationDelta : number ,
157
156
sourceFile : SourceFile ,
157
+ isNextChild : boolean ,
158
158
options : EditorSettings ) : number {
159
-
160
- let parent : Node = current . parent ;
161
- let containingListOrParentStart : LineAndCharacter ;
162
-
159
+ let parent = current . parent ! ;
163
160
// Walk up the tree and collect indentation for parent-child node pairs. Indentation is not added if
164
161
// * parent and child nodes start on the same line, or
165
162
// * parent is an IfStatement and child starts on the same line as an 'else clause'.
@@ -178,7 +175,7 @@ namespace ts.formatting {
178
175
}
179
176
}
180
177
181
- containingListOrParentStart = getContainingListOrParentStart ( parent , current , sourceFile ) ;
178
+ const containingListOrParentStart = getContainingListOrParentStart ( parent , current , sourceFile ) ;
182
179
const parentAndChildShareLine =
183
180
containingListOrParentStart . line === currentStart . line ||
184
181
childStartsOnTheSameLineWithElseInIfStatement ( parent , current , currentStart . line , sourceFile ) ;
@@ -196,7 +193,7 @@ namespace ts.formatting {
196
193
}
197
194
198
195
// increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line
199
- if ( shouldIndentChildNode ( parent , current ) && ! parentAndChildShareLine ) {
196
+ if ( shouldIndentChildNode ( parent , current , isNextChild ) && ! parentAndChildShareLine ) {
200
197
indentationDelta += options . indentSize ;
201
198
}
202
199
@@ -214,7 +211,7 @@ namespace ts.formatting {
214
211
215
212
current = parent ;
216
213
parent = current . parent ;
217
- currentStart = useTrueStart ? sourceFile . getLineAndCharacterOfPosition ( current . getStart ( ) ) : containingListOrParentStart ;
214
+ currentStart = useTrueStart ? sourceFile . getLineAndCharacterOfPosition ( current . getStart ( sourceFile ) ) : containingListOrParentStart ;
218
215
}
219
216
220
217
return indentationDelta + getBaseIndentation ( options ) ;
@@ -533,8 +530,7 @@ namespace ts.formatting {
533
530
return false ;
534
531
}
535
532
536
- /* @internal */
537
- export function nodeWillIndentChild ( parent : TextRangeWithKind , child : TextRangeWithKind , indentByDefault : boolean ) {
533
+ export function nodeWillIndentChild ( parent : TextRangeWithKind , child : TextRangeWithKind | undefined , indentByDefault : boolean ) : boolean {
538
534
const childKind = child ? child . kind : SyntaxKind . Unknown ;
539
535
switch ( parent . kind ) {
540
536
case SyntaxKind . DoStatement :
@@ -555,19 +551,52 @@ namespace ts.formatting {
555
551
return childKind !== SyntaxKind . NamedExports ;
556
552
case SyntaxKind . ImportDeclaration :
557
553
return childKind !== SyntaxKind . ImportClause ||
558
- ( ( < ImportClause > child ) . namedBindings && ( < ImportClause > child ) . namedBindings . kind !== SyntaxKind . NamedImports ) ;
554
+ ( ! ! ( < ImportClause > child ) . namedBindings && ( < ImportClause > child ) . namedBindings . kind !== SyntaxKind . NamedImports ) ;
559
555
case SyntaxKind . JsxElement :
560
556
return childKind !== SyntaxKind . JsxClosingElement ;
561
557
}
562
558
// No explicit rule for given nodes so the result will follow the default value argument
563
559
return indentByDefault ;
564
560
}
565
561
566
- /*
567
- Function returns true when the parent node should indent the given child by an explicit rule
568
- */
569
- export function shouldIndentChildNode ( parent : TextRangeWithKind , child ?: TextRangeWithKind ) : boolean {
570
- return nodeContentIsAlwaysIndented ( parent . kind ) || nodeWillIndentChild ( parent , child , /*indentByDefault*/ false ) ;
562
+ function isControlFlowEndingStatement ( kind : SyntaxKind , parent : TextRangeWithKind ) : boolean {
563
+ switch ( kind ) {
564
+ case SyntaxKind . ReturnStatement :
565
+ case SyntaxKind . ThrowStatement :
566
+ switch ( parent . kind ) {
567
+ case SyntaxKind . Block :
568
+ const grandParent = ( parent as Node ) . parent ;
569
+ switch ( grandParent && grandParent . kind ) {
570
+ case SyntaxKind . FunctionDeclaration :
571
+ case SyntaxKind . FunctionExpression :
572
+ // We may want to write inner functions after this.
573
+ return false ;
574
+ default :
575
+ return true ;
576
+ }
577
+ case SyntaxKind . CaseClause :
578
+ case SyntaxKind . DefaultClause :
579
+ case SyntaxKind . SourceFile :
580
+ case SyntaxKind . ModuleBlock :
581
+ return true ;
582
+ default :
583
+ throw Debug . fail ( ) ;
584
+ }
585
+ case SyntaxKind . ContinueStatement :
586
+ case SyntaxKind . BreakStatement :
587
+ return true ;
588
+ default :
589
+ return false ;
590
+ }
591
+ }
592
+
593
+ /**
594
+ * True when the parent node should indent the given child by an explicit rule.
595
+ * @param isNextChild If true, we are judging indent of a hypothetical child *after* this one, not the current child.
596
+ */
597
+ export function shouldIndentChildNode ( parent : TextRangeWithKind , child ?: TextRangeWithKind , isNextChild = false ) : boolean {
598
+ return ( nodeContentIsAlwaysIndented ( parent . kind ) || nodeWillIndentChild ( parent , child , /*indentByDefault*/ false ) )
599
+ && ! ( isNextChild && child && isControlFlowEndingStatement ( child . kind , parent ) ) ;
571
600
}
572
601
}
573
602
}
0 commit comments