Skip to content

Commit c03ee9d

Browse files
author
Andy
authored
Use helper functions more in documentHighlights (microsoft#21336)
1 parent 3ef1b56 commit c03ee9d

File tree

1 file changed

+45
-102
lines changed

1 file changed

+45
-102
lines changed

src/services/documentHighlights.ts

Lines changed: 45 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)