@@ -35,17 +35,17 @@ module ts.formatting {
35
35
var previous : Node ;
36
36
var current = precedingToken ;
37
37
var currentStart : LineAndCharacter ;
38
- var indentation : number ;
38
+ var indentationDelta : number ;
39
39
40
40
while ( current ) {
41
41
if ( positionBelongsToNode ( current , position , sourceFile ) && nodeContentIsIndented ( current , previous ) ) {
42
42
currentStart = getStartLineAndCharacterForNode ( current , sourceFile ) ;
43
43
44
- if ( discardInitialIndentationIfNextTokenIsOpenOrCloseBrace ( precedingToken , current , lineAtPosition , sourceFile ) ) {
45
- indentation = 0 ;
44
+ if ( nextTokenIsCurlyBraceOnSameLineAsCursor ( precedingToken , current , lineAtPosition , sourceFile ) ) {
45
+ indentationDelta = 0 ;
46
46
}
47
47
else {
48
- indentation = lineAtPosition !== currentStart . line ? options . indentSpaces : 0 ;
48
+ indentationDelta = lineAtPosition !== currentStart . line ? options . indentSpaces : 0 ;
49
49
}
50
50
51
51
break ;
@@ -73,11 +73,10 @@ module ts.formatting {
73
73
// walk upwards and collect indentations for pairs of parent-child nodes
74
74
// indentation is not added if parent and child nodes start on the same line or if parent is IfStatement and child starts on the same line with 'else clause'
75
75
while ( parent ) {
76
-
77
76
// check if current node is a list item - if yes, take indentation from it
78
77
var actualIndentation = getActualIndentationForListItem ( current , sourceFile , options ) ;
79
78
if ( actualIndentation !== - 1 ) {
80
- return actualIndentation + indentation ;
79
+ return actualIndentation + indentationDelta ;
81
80
}
82
81
83
82
parentStart = sourceFile . getLineAndCharacterFromPosition ( parent . getStart ( sourceFile ) ) ;
@@ -88,69 +87,44 @@ module ts.formatting {
88
87
// try to fetch actual indentation for current node from source text
89
88
var actualIndentation = getActualIndentationForNode ( current , parent , currentStart , parentAndChildShareLine , sourceFile , options ) ;
90
89
if ( actualIndentation !== - 1 ) {
91
- return actualIndentation + indentation ;
90
+ return actualIndentation + indentationDelta ;
92
91
}
93
92
94
93
// increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line
95
- var increaseIndentation = nodeContentIsIndented ( parent , current ) && ! parentAndChildShareLine ;
96
-
97
- if ( increaseIndentation ) {
98
- indentation += options . indentSpaces ;
94
+ if ( nodeContentIsIndented ( parent , current ) && ! parentAndChildShareLine ) {
95
+ indentationDelta += options . indentSpaces ;
99
96
}
100
97
101
98
current = parent ;
102
99
currentStart = parentStart ;
103
100
parent = current . parent ;
104
101
}
105
102
106
- return indentation ;
107
- }
108
-
109
- function isStatement ( n : Node ) : boolean {
110
- switch ( n . kind ) {
111
- case SyntaxKind . BreakStatement :
112
- case SyntaxKind . ContinueStatement :
113
- case SyntaxKind . DebuggerStatement :
114
- case SyntaxKind . DoStatement :
115
- case SyntaxKind . ExpressionStatement :
116
- case SyntaxKind . EmptyStatement :
117
- case SyntaxKind . ForInStatement :
118
- case SyntaxKind . ForStatement :
119
- case SyntaxKind . IfStatement :
120
- case SyntaxKind . LabelledStatement :
121
- case SyntaxKind . ReturnStatement :
122
- case SyntaxKind . SwitchStatement :
123
- case SyntaxKind . ThrowKeyword :
124
- case SyntaxKind . TryStatement :
125
- case SyntaxKind . VariableStatement :
126
- case SyntaxKind . WhileStatement :
127
- case SyntaxKind . WithStatement :
128
- return true ;
129
- default :
130
- return false ;
131
- }
103
+ return indentationDelta ;
132
104
}
133
105
134
106
/*
135
107
* Function returns -1 if indentation cannot be determined
136
108
*/
137
109
function getActualIndentationForListItemBeforeComma ( commaToken : Node , sourceFile : SourceFile , options : TypeScript . FormattingOptions ) : number {
138
110
// previous token is comma that separates items in list - find the previous item and try to derive indentation from it
139
- var precedingListItem = findPrecedingListItem ( commaToken ) ;
140
- var precedingListItemStartLineAndChar = sourceFile . getLineAndCharacterFromPosition ( precedingListItem . getStart ( sourceFile ) ) ;
141
- var listStart = getStartLineAndCharacterForNode ( precedingListItem . parent , sourceFile ) ;
142
-
143
- if ( precedingListItemStartLineAndChar . line !== listStart . line ) {
144
- return findColumnForFirstNonWhitespaceCharacterInLine ( precedingListItemStartLineAndChar , sourceFile , options ) ;
145
- }
146
-
147
- return - 1 ;
111
+ var itemInfo = findPrecedingListItem ( commaToken ) ;
112
+ return deriveActualIndentationFromList ( itemInfo . list . getChildren ( ) , itemInfo . listItemIndex , sourceFile , options ) ;
148
113
}
149
114
150
115
/*
151
116
* Function returns -1 if actual indentation for node should not be used (i.e because node is nested expression)
152
117
*/
153
- function getActualIndentationForNode ( current : Node , parent : Node , currentLineAndChar : LineAndCharacter , parentAndChildShareLine : boolean , sourceFile : SourceFile , options : TypeScript . FormattingOptions ) : number {
118
+ function getActualIndentationForNode ( current : Node ,
119
+ parent : Node ,
120
+ currentLineAndChar : LineAndCharacter ,
121
+ parentAndChildShareLine : boolean ,
122
+ sourceFile : SourceFile ,
123
+ options : TypeScript . FormattingOptions ) : number {
124
+
125
+ // actual indentation is used for statements\declarations if one of cases below is true:
126
+ // - parent is SourceFile - by default immediate children of SourceFile are not indented except when user indents them manually
127
+ // - parent and child are not on the same line
154
128
var useActualIndentation =
155
129
( isDeclaration ( current ) || isStatement ( current ) ) &&
156
130
( parent . kind === SyntaxKind . SourceFile || ! parentAndChildShareLine ) ;
@@ -162,7 +136,7 @@ module ts.formatting {
162
136
return findColumnForFirstNonWhitespaceCharacterInLine ( currentLineAndChar , sourceFile , options ) ;
163
137
}
164
138
165
- function discardInitialIndentationIfNextTokenIsOpenOrCloseBrace ( precedingToken : Node , current : Node , lineAtPosition : number , sourceFile : SourceFile ) : boolean {
139
+ function nextTokenIsCurlyBraceOnSameLineAsCursor ( precedingToken : Node , current : Node , lineAtPosition : number , sourceFile : SourceFile ) : boolean {
166
140
var nextToken = findNextToken ( precedingToken , current ) ;
167
141
if ( ! nextToken ) {
168
142
return false ;
@@ -193,7 +167,7 @@ module ts.formatting {
193
167
return sourceFile . getLineAndCharacterFromPosition ( n . getStart ( sourceFile ) ) ;
194
168
}
195
169
196
- function findPrecedingListItem ( commaToken : Node ) : Node {
170
+ function findPrecedingListItem ( commaToken : Node ) : { listItemIndex : number ; list : Node } {
197
171
// CommaToken node is synthetic and thus will be stored in SyntaxList, however parent of the CommaToken points to the container of the SyntaxList skipping the list.
198
172
// In order to find the preceding list item we first need to locate SyntaxList itself and then search for the position of CommaToken
199
173
var syntaxList = forEach ( commaToken . parent . getChildren ( ) , c => {
@@ -208,7 +182,10 @@ module ts.formatting {
208
182
var commaIndex = indexOf ( children , commaToken ) ;
209
183
Debug . assert ( commaIndex !== - 1 && commaIndex !== 0 ) ;
210
184
211
- return children [ commaIndex - 1 ] ;
185
+ return {
186
+ listItemIndex : commaIndex - 1 ,
187
+ list : syntaxList
188
+ } ;
212
189
}
213
190
214
191
function positionBelongsToNode ( candidate : Node , position : number , sourceFile : SourceFile ) : boolean {
@@ -228,6 +205,10 @@ module ts.formatting {
228
205
function getActualIndentationForListItem ( node : Node , sourceFile : SourceFile , options : TypeScript . FormattingOptions ) : number {
229
206
if ( node . parent ) {
230
207
switch ( node . parent . kind ) {
208
+ case SyntaxKind . TypeReference :
209
+ if ( ( < TypeReferenceNode > node . parent ) . typeArguments ) {
210
+ return getActualIndentationFromList ( ( < TypeReferenceNode > node . parent ) . typeArguments ) ;
211
+ }
231
212
case SyntaxKind . ObjectLiteral :
232
213
return getActualIndentationFromList ( ( < ObjectLiteral > node . parent ) . properties ) ;
233
214
case SyntaxKind . TypeLiteral :
@@ -243,40 +224,47 @@ module ts.formatting {
243
224
if ( ( < SignatureDeclaration > node . parent ) . typeParameters && node . end < ( < SignatureDeclaration > node . parent ) . typeParameters . end ) {
244
225
return getActualIndentationFromList ( ( < SignatureDeclaration > node . parent ) . typeParameters ) ;
245
226
}
246
- else {
247
- return getActualIndentationFromList ( ( < SignatureDeclaration > node . parent ) . parameters ) ;
248
- }
227
+
228
+ return getActualIndentationFromList ( ( < SignatureDeclaration > node . parent ) . parameters ) ;
249
229
case SyntaxKind . NewExpression :
250
230
case SyntaxKind . CallExpression :
251
231
if ( ( < CallExpression > node . parent ) . typeArguments && node . end < ( < CallExpression > node . parent ) . typeArguments . end ) {
252
232
return getActualIndentationFromList ( ( < CallExpression > node . parent ) . typeArguments ) ;
253
233
}
254
- else {
255
- return getActualIndentationFromList ( ( < CallExpression > node . parent ) . arguments ) ;
256
- }
257
-
258
- break ;
234
+
235
+ return getActualIndentationFromList ( ( < CallExpression > node . parent ) . arguments ) ;
259
236
}
260
237
}
261
238
262
239
return - 1 ;
263
240
264
241
function getActualIndentationFromList ( list : Node [ ] ) : number {
265
242
var index = indexOf ( list , node ) ;
266
- if ( index !== - 1 ) {
267
- // walk toward the start of the list starting from current node and check if if line is the same for all items.
268
- // if line for item [i - 1] differs from the line for item [i] - find column of the first non-whitespace character on the line of item [i]
269
- var lineAndCharacter = getStartLineAndCharacterForNode ( node , sourceFile ) ; ;
270
- for ( var i = index - 1 ; i >= 0 ; -- i ) {
271
- var prevLineAndCharacter = getStartLineAndCharacterForNode ( list [ i ] , sourceFile ) ;
272
- if ( lineAndCharacter . line !== prevLineAndCharacter . line ) {
273
- return findColumnForFirstNonWhitespaceCharacterInLine ( lineAndCharacter , sourceFile , options ) ;
274
- }
275
- lineAndCharacter = prevLineAndCharacter ;
276
- }
243
+ return index !== - 1 ? deriveActualIndentationFromList ( list , index , sourceFile , options ) : - 1 ;
244
+ }
245
+ }
246
+
247
+
248
+ function deriveActualIndentationFromList ( list : Node [ ] , index : number , sourceFile : SourceFile , options : TypeScript . FormattingOptions ) : number {
249
+ Debug . assert ( index >= 0 && index < list . length ) ;
250
+ var node = list [ index ] ;
251
+
252
+ // walk toward the start of the list starting from current node and check if the line is the same for all items.
253
+ // if end line for item [i - 1] differs from the start line for item [i] - find column of the first non-whitespace character on the line of item [i]
254
+ var lineAndCharacter = getStartLineAndCharacterForNode ( node , sourceFile ) ;
255
+ for ( var i = index - 1 ; i >= 0 ; -- i ) {
256
+ if ( list [ i ] . kind === SyntaxKind . CommaToken ) {
257
+ continue ;
277
258
}
278
- return - 1 ;
259
+ // skip list items that ends on the same line with the current list element
260
+ var prevEndLine = sourceFile . getLineAndCharacterFromPosition ( list [ i ] . end ) . line ;
261
+ if ( prevEndLine !== lineAndCharacter . line ) {
262
+ return findColumnForFirstNonWhitespaceCharacterInLine ( lineAndCharacter , sourceFile , options ) ;
263
+ }
264
+
265
+ lineAndCharacter = getStartLineAndCharacterForNode ( list [ i ] , sourceFile ) ;
279
266
}
267
+ return - 1 ;
280
268
}
281
269
282
270
function findColumnForFirstNonWhitespaceCharacterInLine ( lineAndCharacter : LineAndCharacter , sourceFile : SourceFile , options : TypeScript . FormattingOptions ) : number {
@@ -317,10 +305,12 @@ module ts.formatting {
317
305
// previous token ends exactly at the beginning of child
318
306
( child . pos === previousToken . end ) ;
319
307
320
- if ( shouldDiveInChildNode && isCandidateNode ( child ) ) {
308
+ if ( shouldDiveInChildNode && nodeHasTokens ( child ) ) {
321
309
return find ( child ) ;
322
310
}
323
311
}
312
+
313
+ return undefined ;
324
314
}
325
315
}
326
316
@@ -335,12 +325,12 @@ module ts.formatting {
335
325
var children = n . getChildren ( ) ;
336
326
if ( diveIntoLastChild ) {
337
327
var candidate = findLastChildNodeCandidate ( children , /*exclusiveStartPosition*/ children . length ) ;
338
- return candidate && find ( candidate , diveIntoLastChild ) ;
328
+ return candidate && find ( candidate , /* diveIntoLastChild*/ true ) ;
339
329
}
340
330
341
331
for ( var i = 0 , len = children . length ; i < len ; ++ i ) {
342
332
var child = children [ i ] ;
343
- if ( isCandidateNode ( child ) ) {
333
+ if ( nodeHasTokens ( child ) ) {
344
334
if ( position < child . end ) {
345
335
if ( child . getStart ( sourceFile ) >= position ) {
346
336
// actual start of the node is past the position - previous token should be at the end of previous child
@@ -366,7 +356,7 @@ module ts.formatting {
366
356
/// finds last node that is considered as candidate for search (isCandidate(node) === true) starting from 'exclusiveStartPosition'
367
357
function findLastChildNodeCandidate ( children : Node [ ] , exclusiveStartPosition : number ) : Node {
368
358
for ( var i = exclusiveStartPosition - 1 ; i >= 0 ; -- i ) {
369
- if ( isCandidateNode ( children [ i ] ) ) {
359
+ if ( nodeHasTokens ( children [ i ] ) ) {
370
360
return children [ i ] ;
371
361
}
372
362
}
@@ -376,9 +366,9 @@ module ts.formatting {
376
366
/*
377
367
* Checks if node is something that can contain tokens (except EOF) - filters out EOF tokens, Missing\Omitted expressions, empty SyntaxLists and expression statements that wrap any of listed nodes.
378
368
*/
379
- function isCandidateNode ( n : Node ) : boolean {
369
+ function nodeHasTokens ( n : Node ) : boolean {
380
370
if ( n . kind === SyntaxKind . ExpressionStatement ) {
381
- return isCandidateNode ( ( < ExpressionStatement > n ) . expression ) ;
371
+ return nodeHasTokens ( ( < ExpressionStatement > n ) . expression ) ;
382
372
}
383
373
384
374
if ( n . kind === SyntaxKind . EndOfFileToken || n . kind === SyntaxKind . OmittedExpression || n . kind === SyntaxKind . Missing ) {
@@ -404,7 +394,10 @@ module ts.formatting {
404
394
return false ;
405
395
case SyntaxKind . FunctionDeclaration :
406
396
case SyntaxKind . Method :
407
- case SyntaxKind . FunctionExpression :
397
+ case SyntaxKind . FunctionExpression :
398
+ case SyntaxKind . GetAccessor :
399
+ case SyntaxKind . SetAccessor :
400
+ case SyntaxKind . Constructor :
408
401
// FunctionBlock should take care of indentation
409
402
return false ;
410
403
case SyntaxKind . DoStatement :
@@ -477,6 +470,7 @@ module ts.formatting {
477
470
case SyntaxKind . ParenExpression :
478
471
case SyntaxKind . CallSignature :
479
472
case SyntaxKind . CallExpression :
473
+ case SyntaxKind . ConstructSignature :
480
474
return nodeEndsWith ( n , SyntaxKind . CloseParenToken , sourceFile ) ;
481
475
case SyntaxKind . FunctionDeclaration :
482
476
case SyntaxKind . FunctionExpression :
0 commit comments