@@ -53,95 +53,48 @@ namespace ts.DocumentHighlights {
5353 return [ { fileName : sourceFile . fileName , highlightSpans } ] ;
5454 }
5555
56- // returns true if 'node' is defined and has a matching 'kind'.
57- function hasKind ( node : Node , kind : SyntaxKind ) {
58- return node !== undefined && node . kind === kind ;
59- }
60-
61- // Null-propagating 'parent' function.
62- function parent ( node : Node ) : Node {
63- return node && node . parent ;
64- }
65-
66- function getHighlightSpans ( node : Node , sourceFile : SourceFile ) : HighlightSpan [ ] {
67- if ( ! node ) {
68- return undefined ;
69- }
70-
56+ function getHighlightSpans ( node : Node , sourceFile : SourceFile ) : HighlightSpan [ ] | undefined {
7157 switch ( node . kind ) {
7258 case SyntaxKind . IfKeyword :
7359 case SyntaxKind . ElseKeyword :
74- if ( hasKind ( node . parent , SyntaxKind . IfStatement ) ) {
75- return getIfElseOccurrences ( < IfStatement > node . parent , sourceFile ) ;
76- }
77- break ;
60+ return isIfStatement ( node . parent ) ? getIfElseOccurrences ( node . parent , sourceFile ) : undefined ;
7861 case SyntaxKind . ReturnKeyword :
79- if ( hasKind ( node . parent , SyntaxKind . ReturnStatement ) ) {
80- return highlightSpans ( getReturnOccurrences ( < ReturnStatement > node . parent ) ) ;
81- }
82- break ;
62+ return useParent ( node . parent , isReturnStatement , getReturnOccurrences ) ;
8363 case SyntaxKind . ThrowKeyword :
84- if ( hasKind ( node . parent , SyntaxKind . ThrowStatement ) ) {
85- return highlightSpans ( getThrowOccurrences ( < ThrowStatement > node . parent ) ) ;
86- }
87- break ;
64+ return useParent ( node . parent , isThrowStatement , getThrowOccurrences ) ;
8865 case SyntaxKind . TryKeyword :
8966 case SyntaxKind . CatchKeyword :
9067 case SyntaxKind . FinallyKeyword :
91- const tryStatement = node . kind === SyntaxKind . CatchKeyword ? parent ( parent ( node ) ) : parent ( node ) ;
92- if ( hasKind ( tryStatement , SyntaxKind . TryStatement ) ) {
93- return highlightSpans ( getTryCatchFinallyOccurrences ( < TryStatement > tryStatement , sourceFile ) ) ;
94- }
95- break ;
68+ const tryStatement = node . kind === SyntaxKind . CatchKeyword ? node . parent . parent : node . parent ;
69+ return useParent ( tryStatement , isTryStatement , getTryCatchFinallyOccurrences ) ;
9670 case SyntaxKind . SwitchKeyword :
97- if ( hasKind ( node . parent , SyntaxKind . SwitchStatement ) ) {
98- return highlightSpans ( getSwitchCaseDefaultOccurrences ( < SwitchStatement > node . parent ) ) ;
99- }
100- break ;
71+ return useParent ( node . parent , isSwitchStatement , getSwitchCaseDefaultOccurrences ) ;
10172 case SyntaxKind . CaseKeyword :
10273 case SyntaxKind . DefaultKeyword :
103- if ( hasKind ( parent ( parent ( parent ( node ) ) ) , SyntaxKind . SwitchStatement ) ) {
104- return highlightSpans ( getSwitchCaseDefaultOccurrences ( < SwitchStatement > node . parent . parent . parent ) ) ;
105- }
106- break ;
74+ return useParent ( node . parent . parent . parent , isSwitchStatement , getSwitchCaseDefaultOccurrences ) ;
10775 case SyntaxKind . BreakKeyword :
10876 case SyntaxKind . ContinueKeyword :
109- if ( hasKind ( node . parent , SyntaxKind . BreakStatement ) || hasKind ( node . parent , SyntaxKind . ContinueStatement ) ) {
110- return highlightSpans ( getBreakOrContinueStatementOccurrences ( < BreakOrContinueStatement > node . parent ) ) ;
111- }
112- break ;
77+ return useParent ( node . parent , isBreakOrContinueStatement , getBreakOrContinueStatementOccurrences ) ;
11378 case SyntaxKind . ForKeyword :
114- if ( hasKind ( node . parent , SyntaxKind . ForStatement ) ||
115- hasKind ( node . parent , SyntaxKind . ForInStatement ) ||
116- hasKind ( node . parent , SyntaxKind . ForOfStatement ) ) {
117- return highlightSpans ( getLoopBreakContinueOccurrences ( < IterationStatement > node . parent ) ) ;
118- }
119- break ;
12079 case SyntaxKind . WhileKeyword :
12180 case SyntaxKind . DoKeyword :
122- if ( hasKind ( node . parent , SyntaxKind . WhileStatement ) || hasKind ( node . parent , SyntaxKind . DoStatement ) ) {
123- return highlightSpans ( getLoopBreakContinueOccurrences ( < IterationStatement > node . parent ) ) ;
124- }
125- break ;
81+ return useParent ( node . parent , ( n ) : n is IterationStatement => isIterationStatement ( n , /*lookInLabeledStatements*/ true ) , getLoopBreakContinueOccurrences ) ;
12682 case SyntaxKind . ConstructorKeyword :
127- if ( hasKind ( node . parent , SyntaxKind . Constructor ) ) {
128- return highlightSpans ( getConstructorOccurrences ( < ConstructorDeclaration > node . parent ) ) ;
129- }
130- break ;
83+ return useParent ( node . parent , isConstructorDeclaration , getConstructorOccurrences ) ;
13184 case SyntaxKind . GetKeyword :
13285 case SyntaxKind . SetKeyword :
133- if ( hasKind ( node . parent , SyntaxKind . GetAccessor ) || hasKind ( node . parent , SyntaxKind . SetAccessor ) ) {
134- return highlightSpans ( getGetAndSetOccurrences ( < AccessorDeclaration > node . parent ) ) ;
135- }
136- break ;
86+ return useParent ( node . parent , isAccessor , getGetAndSetOccurrences ) ;
13787 default :
138- if ( isModifierKind ( node . kind ) && node . parent &&
139- ( isDeclaration ( node . parent ) || node . parent . kind === SyntaxKind . VariableStatement ) ) {
140- return highlightSpans ( getModifierOccurrences ( node . kind , node . parent ) ) ;
141- }
88+ return isModifierKind ( node . kind ) && ( isDeclaration ( node . parent ) || isVariableStatement ( node . parent ) )
89+ ? highlightSpans ( getModifierOccurrences ( node . kind , node . parent ) )
90+ : undefined ;
14291 }
14392
144- function highlightSpans ( nodes : Node [ ] ) : HighlightSpan [ ] {
93+ function useParent < T extends Node > ( node : Node , nodeTest : ( node : Node ) => node is T , getNodes : ( node : T , sourceFile : SourceFile ) => Node [ ] | undefined ) : HighlightSpan [ ] | undefined {
94+ return nodeTest ( node ) ? highlightSpans ( getNodes ( node , sourceFile ) ) : undefined ;
95+ }
96+
97+ function highlightSpans ( nodes : Node [ ] | undefined ) : HighlightSpan [ ] | undefined {
14598 return nodes && nodes . map ( node => getHighlightSpanForNode ( node , sourceFile ) ) ;
14699 }
147100 }
@@ -156,23 +109,21 @@ namespace ts.DocumentHighlights {
156109 return statementAccumulator ;
157110
158111 function aggregate ( node : Node ) : void {
159- if ( node . kind === SyntaxKind . ThrowStatement ) {
160- statementAccumulator . push ( < ThrowStatement > node ) ;
112+ if ( isThrowStatement ( node ) ) {
113+ statementAccumulator . push ( node ) ;
161114 }
162- else if ( node . kind === SyntaxKind . TryStatement ) {
163- const tryStatement = < TryStatement > node ;
164-
165- if ( tryStatement . catchClause ) {
166- aggregate ( tryStatement . catchClause ) ;
115+ else if ( isTryStatement ( node ) ) {
116+ if ( node . catchClause ) {
117+ aggregate ( node . catchClause ) ;
167118 }
168119 else {
169120 // Exceptions thrown within a try block lacking a catch clause
170121 // are "owned" in the current context.
171- aggregate ( tryStatement . tryBlock ) ;
122+ aggregate ( node . tryBlock ) ;
172123 }
173124
174- if ( tryStatement . finallyBlock ) {
175- aggregate ( tryStatement . finallyBlock ) ;
125+ if ( node . finallyBlock ) {
126+ aggregate ( node . finallyBlock ) ;
176127 }
177128 }
178129 // Do not cross function boundaries.
@@ -236,32 +187,25 @@ namespace ts.DocumentHighlights {
236187 }
237188
238189 function getBreakOrContinueOwner ( statement : BreakOrContinueStatement ) : Node {
239- for ( let node = statement . parent ; node ; node = node . parent ) {
190+ return findAncestor ( statement , node => {
240191 switch ( node . kind ) {
241192 case SyntaxKind . SwitchStatement :
242193 if ( statement . kind === SyntaxKind . ContinueStatement ) {
243- continue ;
194+ return false ;
244195 }
245196 // falls through
246197 case SyntaxKind . ForStatement :
247198 case SyntaxKind . ForInStatement :
248199 case SyntaxKind . ForOfStatement :
249200 case SyntaxKind . WhileStatement :
250201 case SyntaxKind . DoStatement :
251- if ( ! statement . label || isLabeledBy ( node , statement . label . text ) ) {
252- return node ;
253- }
254- break ;
202+ return ! statement . label || isLabeledBy ( node , statement . label . text ) ;
255203 default :
256204 // Don't cross function boundaries.
257- if ( isFunctionLike ( node ) ) {
258- return undefined ;
259- }
260- break ;
205+ // TODO: GH#20090
206+ return ( isFunctionLike ( node ) && "quit" ) as false | "quit" ;
261207 }
262- }
263-
264- return undefined ;
208+ } ) ;
265209 }
266210
267211 function getModifierOccurrences ( modifier : SyntaxKind , declaration : Node ) : Node [ ] {
@@ -494,16 +438,14 @@ namespace ts.DocumentHighlights {
494438 return keywords ;
495439 }
496440
497- function getReturnOccurrences ( returnStatement : ReturnStatement ) : Node [ ] {
441+ function getReturnOccurrences ( returnStatement : ReturnStatement ) : Node [ ] | undefined {
498442 const func = < FunctionLikeDeclaration > getContainingFunction ( returnStatement ) ;
499-
500- // If we didn't find a containing function with a block body, bail out.
501- if ( ! ( func && hasKind ( func . body , SyntaxKind . Block ) ) ) {
443+ if ( ! func ) {
502444 return undefined ;
503445 }
504446
505447 const keywords : Node [ ] = [ ] ;
506- forEachReturnStatement ( < Block > func . body , returnStatement => {
448+ forEachReturnStatement ( cast ( func . body , isBlock ) , returnStatement => {
507449 pushKeywordIf ( keywords , returnStatement . getFirstToken ( ) , SyntaxKind . ReturnKeyword ) ;
508450 } ) ;
509451
@@ -516,32 +458,7 @@ namespace ts.DocumentHighlights {
516458 }
517459
518460 function getIfElseOccurrences ( ifStatement : IfStatement , sourceFile : SourceFile ) : HighlightSpan [ ] {
519- const keywords : Node [ ] = [ ] ;
520-
521- // Traverse upwards through all parent if-statements linked by their else-branches.
522- while ( hasKind ( ifStatement . parent , SyntaxKind . IfStatement ) && ( < IfStatement > ifStatement . parent ) . elseStatement === ifStatement ) {
523- ifStatement = < IfStatement > ifStatement . parent ;
524- }
525-
526- // Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
527- while ( ifStatement ) {
528- const children = ifStatement . getChildren ( ) ;
529- pushKeywordIf ( keywords , children [ 0 ] , SyntaxKind . IfKeyword ) ;
530-
531- // Generally the 'else' keyword is second-to-last, so we traverse backwards.
532- for ( let i = children . length - 1 ; i >= 0 ; i -- ) {
533- if ( pushKeywordIf ( keywords , children [ i ] , SyntaxKind . ElseKeyword ) ) {
534- break ;
535- }
536- }
537-
538- if ( ! hasKind ( ifStatement . elseStatement , SyntaxKind . IfStatement ) ) {
539- break ;
540- }
541-
542- ifStatement = < IfStatement > ifStatement . elseStatement ;
543- }
544-
461+ const keywords = getIfElseKeywords ( ifStatement , sourceFile ) ;
545462 const result : HighlightSpan [ ] = [ ] ;
546463
547464 // We'd like to highlight else/ifs together if they are only separated by whitespace
@@ -551,17 +468,17 @@ namespace ts.DocumentHighlights {
551468 const elseKeyword = keywords [ i ] ;
552469 const ifKeyword = keywords [ i + 1 ] ; // this *should* always be an 'if' keyword.
553470
554- let shouldCombindElseAndIf = true ;
471+ let shouldCombineElseAndIf = true ;
555472
556473 // Avoid recalculating getStart() by iterating backwards.
557- for ( let j = ifKeyword . getStart ( ) - 1 ; j >= elseKeyword . end ; j -- ) {
474+ for ( let j = ifKeyword . getStart ( sourceFile ) - 1 ; j >= elseKeyword . end ; j -- ) {
558475 if ( ! isWhiteSpaceSingleLine ( sourceFile . text . charCodeAt ( j ) ) ) {
559- shouldCombindElseAndIf = false ;
476+ shouldCombineElseAndIf = false ;
560477 break ;
561478 }
562479 }
563480
564- if ( shouldCombindElseAndIf ) {
481+ if ( shouldCombineElseAndIf ) {
565482 result . push ( {
566483 fileName : sourceFile . fileName ,
567484 textSpan : createTextSpanFromBounds ( elseKeyword . getStart ( ) , ifKeyword . end ) ,
@@ -579,6 +496,36 @@ namespace ts.DocumentHighlights {
579496 return result ;
580497 }
581498
499+ function getIfElseKeywords ( ifStatement : IfStatement , sourceFile : SourceFile ) : Node [ ] {
500+ const keywords : Node [ ] = [ ] ;
501+
502+ // Traverse upwards through all parent if-statements linked by their else-branches.
503+ while ( isIfStatement ( ifStatement . parent ) && ifStatement . parent . elseStatement === ifStatement ) {
504+ ifStatement = ifStatement . parent ;
505+ }
506+
507+ // Now traverse back down through the else branches, aggregating if/else keywords of if-statements.
508+ while ( true ) {
509+ const children = ifStatement . getChildren ( sourceFile ) ;
510+ pushKeywordIf ( keywords , children [ 0 ] , SyntaxKind . IfKeyword ) ;
511+
512+ // Generally the 'else' keyword is second-to-last, so we traverse backwards.
513+ for ( let i = children . length - 1 ; i >= 0 ; i -- ) {
514+ if ( pushKeywordIf ( keywords , children [ i ] , SyntaxKind . ElseKeyword ) ) {
515+ break ;
516+ }
517+ }
518+
519+ if ( ! ifStatement . elseStatement || ! isIfStatement ( ifStatement . elseStatement ) ) {
520+ break ;
521+ }
522+
523+ ifStatement = ifStatement . elseStatement ;
524+ }
525+
526+ return keywords ;
527+ }
528+
582529 /**
583530 * Whether or not a 'node' is preceded by a label of the given string.
584531 * Note: 'node' cannot be a SourceFile.
0 commit comments