@@ -76,21 +76,26 @@ namespace ts.DocumentHighlights {
7676 case SyntaxKind . DoKeyword :
7777 return useParent ( node . parent , ( n ) : n is IterationStatement => isIterationStatement ( n , /*lookInLabeledStatements*/ true ) , getLoopBreakContinueOccurrences ) ;
7878 case SyntaxKind . ConstructorKeyword :
79- return useParent ( node . parent , isConstructorDeclaration , getConstructorOccurrences ) ;
79+ return getFromAllDeclarations ( isConstructorDeclaration , [ SyntaxKind . ConstructorKeyword ] ) ;
8080 case SyntaxKind . GetKeyword :
8181 case SyntaxKind . SetKeyword :
82- return useParent ( node . parent , isAccessor , getGetAndSetOccurrences ) ;
82+ return getFromAllDeclarations ( isAccessor , [ SyntaxKind . GetKeyword , SyntaxKind . SetKeyword ] ) ;
8383 default :
8484 return isModifierKind ( node . kind ) && ( isDeclaration ( node . parent ) || isVariableStatement ( node . parent ) )
8585 ? highlightSpans ( getModifierOccurrences ( node . kind , node . parent ) )
8686 : undefined ;
8787 }
8888
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 {
9095 return nodeTest ( node ) ? highlightSpans ( getNodes ( node , sourceFile ) ) : undefined ;
9196 }
9297
93- function highlightSpans ( nodes : Node [ ] | undefined ) : HighlightSpan [ ] | undefined {
98+ function highlightSpans ( nodes : ReadonlyArray < Node > | undefined ) : HighlightSpan [ ] | undefined {
9499 return nodes && nodes . map ( node => getHighlightSpanForNode ( node , sourceFile ) ) ;
95100 }
96101 }
@@ -99,34 +104,18 @@ namespace ts.DocumentHighlights {
99104 * Aggregates all throw-statements within this node *without* crossing
100105 * into function boundaries and try-blocks with catch-clauses.
101106 */
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 ) ) ;
129116 }
117+ // Do not cross function boundaries.
118+ return isFunctionLike ( node ) ? undefined : flatMapChildren ( node , aggregateOwnedThrowStatements ) ;
130119 }
131120
132121 /**
@@ -146,12 +135,8 @@ namespace ts.DocumentHighlights {
146135
147136 // A throw-statement is only owned by a try-statement if the try-statement has
148137 // 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 ;
155140 }
156141
157142 child = parent ;
@@ -160,20 +145,19 @@ namespace ts.DocumentHighlights {
160145 return undefined ;
161146 }
162147
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+ }
167151
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 ) ) ;
171158 }
172- // Do not cross function boundaries.
173- else if ( ! isFunctionLike ( node ) ) {
174- forEachChild ( node , aggregate ) ;
175- }
176- }
159+ } ) ;
160+ return result ;
177161 }
178162
179163 function ownsBreakOrContinueStatement ( owner : Node , statement : BreakOrContinueStatement ) : boolean {
@@ -195,7 +179,7 @@ namespace ts.DocumentHighlights {
195179 case SyntaxKind . ForOfStatement :
196180 case SyntaxKind . WhileStatement :
197181 case SyntaxKind . DoStatement :
198- return ! statement . label || isLabeledBy ( node , statement . label . text ) ;
182+ return ! statement . label || isLabeledBy ( node , statement . label . escapedText ) ;
199183 default :
200184 // Don't cross function boundaries.
201185 // TODO: GH#20090
@@ -285,7 +269,7 @@ namespace ts.DocumentHighlights {
285269 }
286270 }
287271
288- function pushKeywordIf ( keywordList : Node [ ] , token : Node , ...expected : SyntaxKind [ ] ) : boolean {
272+ function pushKeywordIf ( keywordList : Push < Node > , token : Node , ...expected : SyntaxKind [ ] ) : boolean {
289273 if ( token && contains ( expected , token . kind ) ) {
290274 keywordList . push ( token ) ;
291275 return true ;
@@ -294,37 +278,6 @@ namespace ts.DocumentHighlights {
294278 return false ;
295279 }
296280
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-
328281 function getLoopBreakContinueOccurrences ( loopNode : IterationStatement ) : Node [ ] {
329282 const keywords : Node [ ] = [ ] ;
330283
@@ -341,9 +294,7 @@ namespace ts.DocumentHighlights {
341294 }
342295 }
343296
344- const breaksAndContinues = aggregateAllBreakAndContinueStatements ( loopNode . statement ) ;
345-
346- forEach ( breaksAndContinues , statement => {
297+ forEach ( aggregateAllBreakAndContinueStatements ( loopNode . statement ) , statement => {
347298 if ( ownsBreakOrContinueStatement ( loopNode , statement ) ) {
348299 pushKeywordIf ( keywords , statement . getFirstToken ( ) , SyntaxKind . BreakKeyword , SyntaxKind . ContinueKeyword ) ;
349300 }
@@ -381,9 +332,7 @@ namespace ts.DocumentHighlights {
381332 forEach ( switchStatement . caseBlock . clauses , clause => {
382333 pushKeywordIf ( keywords , clause . getFirstToken ( ) , SyntaxKind . CaseKeyword , SyntaxKind . DefaultKeyword ) ;
383334
384- const breaksAndContinues = aggregateAllBreakAndContinueStatements ( clause ) ;
385-
386- forEach ( breaksAndContinues , statement => {
335+ forEach ( aggregateAllBreakAndContinueStatements ( clause ) , statement => {
387336 if ( ownsBreakOrContinueStatement ( switchStatement , statement ) ) {
388337 pushKeywordIf ( keywords , statement . getFirstToken ( ) , SyntaxKind . BreakKeyword ) ;
389338 }
@@ -410,7 +359,7 @@ namespace ts.DocumentHighlights {
410359 return keywords ;
411360 }
412361
413- function getThrowOccurrences ( throwStatement : ThrowStatement ) : Node [ ] {
362+ function getThrowOccurrences ( throwStatement : ThrowStatement , sourceFile : SourceFile ) : Node [ ] {
414363 const owner = getThrowStatementOwner ( throwStatement ) ;
415364
416365 if ( ! owner ) {
@@ -420,34 +369,34 @@ namespace ts.DocumentHighlights {
420369 const keywords : Node [ ] = [ ] ;
421370
422371 forEach ( aggregateOwnedThrowStatements ( owner ) , throwStatement => {
423- pushKeywordIf ( keywords , throwStatement . getFirstToken ( ) , SyntaxKind . ThrowKeyword ) ;
372+ keywords . push ( findChildOfKind ( throwStatement , SyntaxKind . ThrowKeyword , sourceFile ) ! ) ;
424373 } ) ;
425374
426375 // If the "owner" is a function, then we equate 'return' and 'throw' statements in their
427376 // ability to "jump out" of the function, and include occurrences for both.
428377 if ( isFunctionBlock ( owner ) ) {
429378 forEachReturnStatement ( < Block > owner , returnStatement => {
430- pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
379+ keywords . push ( findChildOfKind ( returnStatement , SyntaxKind . ReturnKeyword , sourceFile ) ! ) ;
431380 } ) ;
432381 }
433382
434383 return keywords ;
435384 }
436385
437- function getReturnOccurrences ( returnStatement : ReturnStatement ) : Node [ ] | undefined {
386+ function getReturnOccurrences ( returnStatement : ReturnStatement , sourceFile : SourceFile ) : Node [ ] | undefined {
438387 const func = < FunctionLikeDeclaration > getContainingFunction ( returnStatement ) ;
439388 if ( ! func ) {
440389 return undefined ;
441390 }
442391
443392 const keywords : Node [ ] = [ ] ;
444393 forEachReturnStatement ( cast ( func . body , isBlock ) , returnStatement => {
445- pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
394+ keywords . push ( findChildOfKind ( returnStatement , SyntaxKind . ReturnKeyword , sourceFile ) ! ) ;
446395 } ) ;
447396
448397 // Include 'throw' statements that do not occur within a try block.
449398 forEach ( aggregateOwnedThrowStatements ( func . body ) , throwStatement => {
450- pushKeywordIf ( keywords , throwStatement . getFirstToken ( ) , SyntaxKind . ThrowKeyword ) ;
399+ keywords . push ( findChildOfKind ( throwStatement , SyntaxKind . ThrowKeyword , sourceFile ) ! ) ;
451400 } ) ;
452401
453402 return keywords ;
@@ -526,13 +475,7 @@ namespace ts.DocumentHighlights {
526475 * Whether or not a 'node' is preceded by a label of the given string.
527476 * Note: 'node' cannot be a SourceFile.
528477 */
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 ) ;
537480 }
538481}
0 commit comments