@@ -76,21 +76,26 @@ namespace ts.DocumentHighlights {
76
76
case SyntaxKind . DoKeyword :
77
77
return useParent ( node . parent , ( n ) : n is IterationStatement => isIterationStatement ( n , /*lookInLabeledStatements*/ true ) , getLoopBreakContinueOccurrences ) ;
78
78
case SyntaxKind . ConstructorKeyword :
79
- return useParent ( node . parent , isConstructorDeclaration , getConstructorOccurrences ) ;
79
+ return getFromAllDeclarations ( isConstructorDeclaration , [ SyntaxKind . ConstructorKeyword ] ) ;
80
80
case SyntaxKind . GetKeyword :
81
81
case SyntaxKind . SetKeyword :
82
- return useParent ( node . parent , isAccessor , getGetAndSetOccurrences ) ;
82
+ return getFromAllDeclarations ( isAccessor , [ SyntaxKind . GetKeyword , SyntaxKind . SetKeyword ] ) ;
83
83
default :
84
84
return isModifierKind ( node . kind ) && ( isDeclaration ( node . parent ) || isVariableStatement ( node . parent ) )
85
85
? highlightSpans ( getModifierOccurrences ( node . kind , node . parent ) )
86
86
: undefined ;
87
87
}
88
88
89
- function useParent < T extends Node > ( node : Node , nodeTest : ( node : Node ) => node is T , getNodes : ( node : T , sourceFile : SourceFile ) => Node [ ] | undefined ) : HighlightSpan [ ] | undefined {
89
+ function getFromAllDeclarations < T extends Node > ( nodeTest : ( node : Node ) => node is T , keywords : ReadonlyArray < SyntaxKind > ) : HighlightSpan [ ] | undefined {
90
+ return useParent ( node . parent , nodeTest , decl => mapDefined ( decl . symbol . declarations , d =>
91
+ nodeTest ( d ) ? find ( d . getChildren ( sourceFile ) , c => contains ( keywords , c . kind ) ) : undefined ) ) ;
92
+ }
93
+
94
+ function useParent < T extends Node > ( node : Node , nodeTest : ( node : Node ) => node is T , getNodes : ( node : T , sourceFile : SourceFile ) => ReadonlyArray < Node > | undefined ) : HighlightSpan [ ] | undefined {
90
95
return nodeTest ( node ) ? highlightSpans ( getNodes ( node , sourceFile ) ) : undefined ;
91
96
}
92
97
93
- function highlightSpans ( nodes : Node [ ] | undefined ) : HighlightSpan [ ] | undefined {
98
+ function highlightSpans ( nodes : ReadonlyArray < Node > | undefined ) : HighlightSpan [ ] | undefined {
94
99
return nodes && nodes . map ( node => getHighlightSpanForNode ( node , sourceFile ) ) ;
95
100
}
96
101
}
@@ -99,34 +104,18 @@ namespace ts.DocumentHighlights {
99
104
* Aggregates all throw-statements within this node *without* crossing
100
105
* into function boundaries and try-blocks with catch-clauses.
101
106
*/
102
- function aggregateOwnedThrowStatements ( node : Node ) : ThrowStatement [ ] {
103
- const statementAccumulator : ThrowStatement [ ] = [ ] ;
104
- aggregate ( node ) ;
105
- return statementAccumulator ;
106
-
107
- function aggregate ( node : Node ) : void {
108
- if ( isThrowStatement ( node ) ) {
109
- statementAccumulator . push ( node ) ;
110
- }
111
- else if ( isTryStatement ( node ) ) {
112
- if ( node . catchClause ) {
113
- aggregate ( node . catchClause ) ;
114
- }
115
- else {
116
- // Exceptions thrown within a try block lacking a catch clause
117
- // are "owned" in the current context.
118
- aggregate ( node . tryBlock ) ;
119
- }
120
-
121
- if ( node . finallyBlock ) {
122
- aggregate ( node . finallyBlock ) ;
123
- }
124
- }
125
- // Do not cross function boundaries.
126
- else if ( ! isFunctionLike ( node ) ) {
127
- forEachChild ( node , aggregate ) ;
128
- }
107
+ function aggregateOwnedThrowStatements ( node : Node ) : ReadonlyArray < ThrowStatement > | undefined {
108
+ if ( isThrowStatement ( node ) ) {
109
+ return [ node ] ;
110
+ }
111
+ else if ( isTryStatement ( node ) ) {
112
+ // Exceptions thrown within a try block lacking a catch clause are "owned" in the current context.
113
+ return concatenate (
114
+ node . catchClause ? aggregateOwnedThrowStatements ( node . catchClause ) : node . tryBlock && aggregateOwnedThrowStatements ( node . tryBlock ) ,
115
+ aggregateOwnedThrowStatements ( node . finallyBlock ) ) ;
129
116
}
117
+ // Do not cross function boundaries.
118
+ return isFunctionLike ( node ) ? undefined : flatMapChildren ( node , aggregateOwnedThrowStatements ) ;
130
119
}
131
120
132
121
/**
@@ -146,12 +135,8 @@ namespace ts.DocumentHighlights {
146
135
147
136
// A throw-statement is only owned by a try-statement if the try-statement has
148
137
// a catch clause, and if the throw-statement occurs within the try block.
149
- if ( parent . kind === SyntaxKind . TryStatement ) {
150
- const tryStatement = < TryStatement > parent ;
151
-
152
- if ( tryStatement . tryBlock === child && tryStatement . catchClause ) {
153
- return child ;
154
- }
138
+ if ( isTryStatement ( parent ) && parent . tryBlock === child && parent . catchClause ) {
139
+ return child ;
155
140
}
156
141
157
142
child = parent ;
@@ -160,20 +145,19 @@ namespace ts.DocumentHighlights {
160
145
return undefined ;
161
146
}
162
147
163
- function aggregateAllBreakAndContinueStatements ( node : Node ) : BreakOrContinueStatement [ ] {
164
- const statementAccumulator : BreakOrContinueStatement [ ] = [ ] ;
165
- aggregate ( node ) ;
166
- return statementAccumulator ;
148
+ function aggregateAllBreakAndContinueStatements ( node : Node ) : ReadonlyArray < BreakOrContinueStatement > | undefined {
149
+ return isBreakOrContinueStatement ( node ) ? [ node ] : isFunctionLike ( node ) ? undefined : flatMapChildren ( node , aggregateAllBreakAndContinueStatements ) ;
150
+ }
167
151
168
- function aggregate ( node : Node ) : void {
169
- if ( node . kind === SyntaxKind . BreakStatement || node . kind === SyntaxKind . ContinueStatement ) {
170
- statementAccumulator . push ( < BreakOrContinueStatement > node ) ;
152
+ function flatMapChildren < T > ( node : Node , cb : ( child : Node ) => ReadonlyArray < T > | T | undefined ) : ReadonlyArray < T > {
153
+ const result : T [ ] = [ ] ;
154
+ node . forEachChild ( child => {
155
+ const value = cb ( child ) ;
156
+ if ( value !== undefined ) {
157
+ result . push ( ...toArray ( value ) ) ;
171
158
}
172
- // Do not cross function boundaries.
173
- else if ( ! isFunctionLike ( node ) ) {
174
- forEachChild ( node , aggregate ) ;
175
- }
176
- }
159
+ } ) ;
160
+ return result ;
177
161
}
178
162
179
163
function ownsBreakOrContinueStatement ( owner : Node , statement : BreakOrContinueStatement ) : boolean {
@@ -195,7 +179,7 @@ namespace ts.DocumentHighlights {
195
179
case SyntaxKind . ForOfStatement :
196
180
case SyntaxKind . WhileStatement :
197
181
case SyntaxKind . DoStatement :
198
- return ! statement . label || isLabeledBy ( node , statement . label . text ) ;
182
+ return ! statement . label || isLabeledBy ( node , statement . label . escapedText ) ;
199
183
default :
200
184
// Don't cross function boundaries.
201
185
// TODO: GH#20090
@@ -285,7 +269,7 @@ namespace ts.DocumentHighlights {
285
269
}
286
270
}
287
271
288
- function pushKeywordIf ( keywordList : Node [ ] , token : Node , ...expected : SyntaxKind [ ] ) : boolean {
272
+ function pushKeywordIf ( keywordList : Push < Node > , token : Node , ...expected : SyntaxKind [ ] ) : boolean {
289
273
if ( token && contains ( expected , token . kind ) ) {
290
274
keywordList . push ( token ) ;
291
275
return true ;
@@ -294,37 +278,6 @@ namespace ts.DocumentHighlights {
294
278
return false ;
295
279
}
296
280
297
- function getGetAndSetOccurrences ( accessorDeclaration : AccessorDeclaration ) : Node [ ] {
298
- const keywords : Node [ ] = [ ] ;
299
-
300
- tryPushAccessorKeyword ( accessorDeclaration . symbol , SyntaxKind . GetAccessor ) ;
301
- tryPushAccessorKeyword ( accessorDeclaration . symbol , SyntaxKind . SetAccessor ) ;
302
-
303
- return keywords ;
304
-
305
- function tryPushAccessorKeyword ( accessorSymbol : Symbol , accessorKind : SyntaxKind ) : void {
306
- const accessor = getDeclarationOfKind ( accessorSymbol , accessorKind ) ;
307
-
308
- if ( accessor ) {
309
- forEach ( accessor . getChildren ( ) , child => pushKeywordIf ( keywords , child , SyntaxKind . GetKeyword , SyntaxKind . SetKeyword ) ) ;
310
- }
311
- }
312
- }
313
-
314
- function getConstructorOccurrences ( constructorDeclaration : ConstructorDeclaration ) : Node [ ] {
315
- const declarations = constructorDeclaration . symbol . getDeclarations ( ) ;
316
-
317
- const keywords : Node [ ] = [ ] ;
318
-
319
- forEach ( declarations , declaration => {
320
- forEach ( declaration . getChildren ( ) , token => {
321
- return pushKeywordIf ( keywords , token , SyntaxKind . ConstructorKeyword ) ;
322
- } ) ;
323
- } ) ;
324
-
325
- return keywords ;
326
- }
327
-
328
281
function getLoopBreakContinueOccurrences ( loopNode : IterationStatement ) : Node [ ] {
329
282
const keywords : Node [ ] = [ ] ;
330
283
@@ -341,9 +294,7 @@ namespace ts.DocumentHighlights {
341
294
}
342
295
}
343
296
344
- const breaksAndContinues = aggregateAllBreakAndContinueStatements ( loopNode . statement ) ;
345
-
346
- forEach ( breaksAndContinues , statement => {
297
+ forEach ( aggregateAllBreakAndContinueStatements ( loopNode . statement ) , statement => {
347
298
if ( ownsBreakOrContinueStatement ( loopNode , statement ) ) {
348
299
pushKeywordIf ( keywords , statement . getFirstToken ( ) , SyntaxKind . BreakKeyword , SyntaxKind . ContinueKeyword ) ;
349
300
}
@@ -381,9 +332,7 @@ namespace ts.DocumentHighlights {
381
332
forEach ( switchStatement . caseBlock . clauses , clause => {
382
333
pushKeywordIf ( keywords , clause . getFirstToken ( ) , SyntaxKind . CaseKeyword , SyntaxKind . DefaultKeyword ) ;
383
334
384
- const breaksAndContinues = aggregateAllBreakAndContinueStatements ( clause ) ;
385
-
386
- forEach ( breaksAndContinues , statement => {
335
+ forEach ( aggregateAllBreakAndContinueStatements ( clause ) , statement => {
387
336
if ( ownsBreakOrContinueStatement ( switchStatement , statement ) ) {
388
337
pushKeywordIf ( keywords , statement . getFirstToken ( ) , SyntaxKind . BreakKeyword ) ;
389
338
}
@@ -410,7 +359,7 @@ namespace ts.DocumentHighlights {
410
359
return keywords ;
411
360
}
412
361
413
- function getThrowOccurrences ( throwStatement : ThrowStatement ) : Node [ ] {
362
+ function getThrowOccurrences ( throwStatement : ThrowStatement , sourceFile : SourceFile ) : Node [ ] {
414
363
const owner = getThrowStatementOwner ( throwStatement ) ;
415
364
416
365
if ( ! owner ) {
@@ -420,34 +369,34 @@ namespace ts.DocumentHighlights {
420
369
const keywords : Node [ ] = [ ] ;
421
370
422
371
forEach ( aggregateOwnedThrowStatements ( owner ) , throwStatement => {
423
- pushKeywordIf ( keywords , throwStatement . getFirstToken ( ) , SyntaxKind . ThrowKeyword ) ;
372
+ keywords . push ( findChildOfKind ( throwStatement , SyntaxKind . ThrowKeyword , sourceFile ) ! ) ;
424
373
} ) ;
425
374
426
375
// If the "owner" is a function, then we equate 'return' and 'throw' statements in their
427
376
// ability to "jump out" of the function, and include occurrences for both.
428
377
if ( isFunctionBlock ( owner ) ) {
429
378
forEachReturnStatement ( < Block > owner , returnStatement => {
430
- pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
379
+ keywords . push ( findChildOfKind ( returnStatement , SyntaxKind . ReturnKeyword , sourceFile ) ! ) ;
431
380
} ) ;
432
381
}
433
382
434
383
return keywords ;
435
384
}
436
385
437
- function getReturnOccurrences ( returnStatement : ReturnStatement ) : Node [ ] | undefined {
386
+ function getReturnOccurrences ( returnStatement : ReturnStatement , sourceFile : SourceFile ) : Node [ ] | undefined {
438
387
const func = < FunctionLikeDeclaration > getContainingFunction ( returnStatement ) ;
439
388
if ( ! func ) {
440
389
return undefined ;
441
390
}
442
391
443
392
const keywords : Node [ ] = [ ] ;
444
393
forEachReturnStatement ( cast ( func . body , isBlock ) , returnStatement => {
445
- pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
394
+ keywords . push ( findChildOfKind ( returnStatement , SyntaxKind . ReturnKeyword , sourceFile ) ! ) ;
446
395
} ) ;
447
396
448
397
// Include 'throw' statements that do not occur within a try block.
449
398
forEach ( aggregateOwnedThrowStatements ( func . body ) , throwStatement => {
450
- pushKeywordIf ( keywords , throwStatement . getFirstToken ( ) , SyntaxKind . ThrowKeyword ) ;
399
+ keywords . push ( findChildOfKind ( throwStatement , SyntaxKind . ThrowKeyword , sourceFile ) ! ) ;
451
400
} ) ;
452
401
453
402
return keywords ;
@@ -526,13 +475,7 @@ namespace ts.DocumentHighlights {
526
475
* Whether or not a 'node' is preceded by a label of the given string.
527
476
* Note: 'node' cannot be a SourceFile.
528
477
*/
529
- function isLabeledBy ( node : Node , labelName : string ) {
530
- for ( let owner = node . parent ; owner . kind === SyntaxKind . LabeledStatement ; owner = owner . parent ) {
531
- if ( ( < LabeledStatement > owner ) . label . escapedText === labelName ) {
532
- return true ;
533
- }
534
- }
535
-
536
- return false ;
478
+ function isLabeledBy ( node : Node , labelName : __String ) : boolean {
479
+ return ! ! findAncestor ( node . parent , owner => ! isLabeledStatement ( owner ) ? "quit" : owner . label . escapedText === labelName ) ;
537
480
}
538
481
}
0 commit comments