Skip to content

Commit c2f5ac4

Browse files
author
Andy
authored
Merge pull request #13566 from Microsoft/find_all_refs_primitive
Support find-all-references for type keywords
2 parents 0a535f0 + 36a9f67 commit c2f5ac4

File tree

8 files changed

+168
-127
lines changed

8 files changed

+168
-127
lines changed

src/services/documentHighlights.ts

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,11 @@ namespace ts.DocumentHighlights {
1717
}
1818

1919
function getSemanticDocumentHighlights(node: Node, typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
20-
if (node.kind === SyntaxKind.Identifier ||
21-
node.kind === SyntaxKind.ThisKeyword ||
22-
node.kind === SyntaxKind.ThisType ||
23-
node.kind === SyntaxKind.SuperKeyword ||
24-
node.kind === SyntaxKind.StringLiteral ||
25-
isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
26-
27-
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false, /*implementations*/false);
28-
return convertReferencedSymbols(referencedSymbols);
29-
}
30-
31-
return undefined;
20+
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/false, /*findInComments*/false, /*implementations*/false);
21+
return referencedSymbols && convertReferencedSymbols(referencedSymbols);
3222
}
3323

3424
function convertReferencedSymbols(referencedSymbols: ReferencedSymbol[]): DocumentHighlights[] {
35-
if (!referencedSymbols) {
36-
return undefined;
37-
}
38-
3925
const fileNameToDocumentHighlights = createMap<DocumentHighlights>();
4026
const result: DocumentHighlights[] = [];
4127
for (const referencedSymbol of referencedSymbols) {

src/services/findAllReferences.ts

Lines changed: 105 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,16 @@
11
/* @internal */
22
namespace ts.FindAllReferences {
3-
export function findReferencedSymbols(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] {
3+
export function findReferencedSymbols(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], sourceFile: SourceFile, position: number, findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] | undefined {
44
const node = getTouchingPropertyName(sourceFile, position, /*includeJsDocComment*/ true);
5-
if (node === sourceFile) {
6-
return undefined;
7-
}
8-
9-
switch (node.kind) {
10-
case SyntaxKind.NumericLiteral:
11-
if (!isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
12-
break;
13-
}
14-
// Fallthrough
15-
case SyntaxKind.Identifier:
16-
case SyntaxKind.ThisKeyword:
17-
// case SyntaxKind.SuperKeyword: TODO:GH#9268
18-
case SyntaxKind.ConstructorKeyword:
19-
case SyntaxKind.StringLiteral:
20-
return getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFiles, findInStrings, findInComments, /*implementations*/false);
21-
}
22-
return undefined;
5+
return getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFiles, findInStrings, findInComments, /*implementations*/false);
236
}
247

25-
export function getReferencedSymbolsForNode(typeChecker: TypeChecker, cancellationToken: CancellationToken, node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean, implementations: boolean): ReferencedSymbol[] {
8+
export function getReferencedSymbolsForNode(typeChecker: TypeChecker, cancellationToken: CancellationToken, node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean, implementations: boolean): ReferencedSymbol[] | undefined {
269
if (!implementations) {
10+
if (isTypeKeyword(node.kind)) {
11+
return getAllReferencesForKeyword(sourceFiles, node.kind, cancellationToken);
12+
}
13+
2714
// Labels
2815
if (isLabelName(node)) {
2916
if (isJumpStatementTarget(node)) {
@@ -68,8 +55,6 @@ namespace ts.FindAllReferences {
6855
return undefined;
6956
}
7057

71-
let result: ReferencedSymbol[];
72-
7358
// Compute the meaning from the location and the symbol it references
7459
const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), declarations);
7560

@@ -84,6 +69,7 @@ namespace ts.FindAllReferences {
8469
// Maps from a symbol ID to the ReferencedSymbol entry in 'result'.
8570
const symbolToIndex: number[] = [];
8671

72+
let result: ReferencedSymbol[];
8773
if (scope) {
8874
result = [];
8975
getReferencesInNode(scope, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex, implementations, typeChecker, cancellationToken);
@@ -92,10 +78,7 @@ namespace ts.FindAllReferences {
9278
const internedName = getInternedName(symbol, node);
9379
for (const sourceFile of sourceFiles) {
9480
cancellationToken.throwIfCancellationRequested();
95-
96-
const nameTable = getNameTable(sourceFile);
97-
98-
if (nameTable.get(internedName) !== undefined) {
81+
if (sourceFileHasName(sourceFile, internedName)) {
9982
result = result || [];
10083
getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex, implementations, typeChecker, cancellationToken);
10184
}
@@ -105,9 +88,13 @@ namespace ts.FindAllReferences {
10588
return result;
10689
}
10790

91+
function sourceFileHasName(sourceFile: SourceFile, name: string): boolean {
92+
return getNameTable(sourceFile).get(name) !== undefined;
93+
}
94+
10895
function getDefinition(symbol: Symbol, node: Node, typeChecker: TypeChecker): ReferencedSymbolDefinitionInfo {
109-
const info = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, node.getSourceFile(), getContainerNode(node), node);
110-
const name = map(info.displayParts, p => p.text).join("");
96+
const { displayParts, symbolKind } = SymbolDisplay.getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker, symbol, node.getSourceFile(), getContainerNode(node), node);
97+
const name = displayParts.map(p => p.text).join("");
11198
const declarations = symbol.declarations;
11299
if (!declarations || declarations.length === 0) {
113100
return undefined;
@@ -117,10 +104,10 @@ namespace ts.FindAllReferences {
117104
containerKind: "",
118105
containerName: "",
119106
name,
120-
kind: info.symbolKind,
107+
kind: symbolKind,
121108
fileName: declarations[0].getSourceFile().fileName,
122109
textSpan: createTextSpan(declarations[0].getStart(), 0),
123-
displayParts: info.displayParts
110+
displayParts
124111
};
125112
}
126113

@@ -351,6 +338,43 @@ namespace ts.FindAllReferences {
351338
}
352339
}
353340

341+
function getAllReferencesForKeyword(sourceFiles: SourceFile[], keywordKind: ts.SyntaxKind, cancellationToken: CancellationToken): ReferencedSymbol[] {
342+
const name = tokenToString(keywordKind);
343+
const definition: ReferencedSymbolDefinitionInfo = {
344+
containerKind: "",
345+
containerName: "",
346+
fileName: "",
347+
kind: ScriptElementKind.keyword,
348+
name,
349+
textSpan: createTextSpan(0, 1),
350+
displayParts: [{ text: name, kind: ScriptElementKind.keyword }]
351+
}
352+
353+
const references: ReferenceEntry[] = [];
354+
for (const sourceFile of sourceFiles) {
355+
cancellationToken.throwIfCancellationRequested();
356+
addReferencesForKeywordInFile(sourceFile, keywordKind, name, cancellationToken, references);
357+
}
358+
359+
return [{ definition, references }];
360+
}
361+
362+
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, cancellationToken: CancellationToken, references: Push<ReferenceEntry>): void {
363+
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
364+
for (const position of possiblePositions) {
365+
cancellationToken.throwIfCancellationRequested();
366+
const referenceLocation = getTouchingPropertyName(sourceFile, position);
367+
if (referenceLocation.kind === kind) {
368+
references.push({
369+
textSpan: createTextSpanFromNode(referenceLocation),
370+
fileName: sourceFile.fileName,
371+
isWriteAccess: false,
372+
isDefinition: false,
373+
});
374+
}
375+
}
376+
}
377+
354378
/** Search within node "container" for references for a search value, where the search value is defined as a
355379
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
356380
* searchLocation: a node where the search value
@@ -375,67 +399,64 @@ namespace ts.FindAllReferences {
375399

376400
const parents = getParentSymbolsOfPropertyAccess();
377401
const inheritsFromCache: Map<boolean> = createMap<boolean>();
402+
// Build the set of symbols to search for, initially it has only the current symbol
403+
const searchSymbols = populateSearchSymbolSet(searchSymbol, searchLocation, typeChecker, implementations);
378404

379-
if (possiblePositions.length) {
380-
// Build the set of symbols to search for, initially it has only the current symbol
381-
const searchSymbols = populateSearchSymbolSet(searchSymbol, searchLocation, typeChecker, implementations);
382-
383-
forEach(possiblePositions, position => {
384-
cancellationToken.throwIfCancellationRequested();
405+
for (const position of possiblePositions) {
406+
cancellationToken.throwIfCancellationRequested();
385407

386-
const referenceLocation = getTouchingPropertyName(sourceFile, position);
387-
if (!isValidReferencePosition(referenceLocation, searchText)) {
388-
// This wasn't the start of a token. Check to see if it might be a
389-
// match in a comment or string if that's what the caller is asking
390-
// for.
391-
if (!implementations && ((findInStrings && isInString(sourceFile, position)) ||
392-
(findInComments && isInNonReferenceComment(sourceFile, position)))) {
393-
394-
// In the case where we're looking inside comments/strings, we don't have
395-
// an actual definition. So just use 'undefined' here. Features like
396-
// 'Rename' won't care (as they ignore the definitions), and features like
397-
// 'FindReferences' will just filter out these results.
398-
result.push({
399-
definition: undefined,
400-
references: [{
401-
fileName: sourceFile.fileName,
402-
textSpan: createTextSpan(position, searchText.length),
403-
isWriteAccess: false,
404-
isDefinition: false
405-
}]
406-
});
407-
}
408-
return;
408+
const referenceLocation = getTouchingPropertyName(sourceFile, position);
409+
if (!isValidReferencePosition(referenceLocation, searchText)) {
410+
// This wasn't the start of a token. Check to see if it might be a
411+
// match in a comment or string if that's what the caller is asking
412+
// for.
413+
if (!implementations && ((findInStrings && isInString(sourceFile, position)) ||
414+
(findInComments && isInNonReferenceComment(sourceFile, position)))) {
415+
416+
// In the case where we're looking inside comments/strings, we don't have
417+
// an actual definition. So just use 'undefined' here. Features like
418+
// 'Rename' won't care (as they ignore the definitions), and features like
419+
// 'FindReferences' will just filter out these results.
420+
result.push({
421+
definition: undefined,
422+
references: [{
423+
fileName: sourceFile.fileName,
424+
textSpan: createTextSpan(position, searchText.length),
425+
isWriteAccess: false,
426+
isDefinition: false
427+
}]
428+
});
409429
}
430+
continue;
431+
}
410432

411-
if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) {
412-
return;
413-
}
433+
if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) {
434+
continue;
435+
}
414436

415-
const referenceSymbol = typeChecker.getSymbolAtLocation(referenceLocation);
416-
if (referenceSymbol) {
417-
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
418-
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
419-
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation,
420-
/*searchLocationIsConstructor*/ searchLocation.kind === SyntaxKind.ConstructorKeyword, parents, inheritsFromCache, typeChecker);
437+
const referenceSymbol = typeChecker.getSymbolAtLocation(referenceLocation);
438+
if (referenceSymbol) {
439+
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
440+
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
441+
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation,
442+
/*searchLocationIsConstructor*/ searchLocation.kind === SyntaxKind.ConstructorKeyword, parents, inheritsFromCache, typeChecker);
421443

422-
if (relatedSymbol) {
423-
addReferenceToRelatedSymbol(referenceLocation, relatedSymbol);
424-
}
425-
/* Because in short-hand property assignment, an identifier which stored as name of the short-hand property assignment
426-
* has two meaning : property name and property value. Therefore when we do findAllReference at the position where
427-
* an identifier is declared, the language service should return the position of the variable declaration as well as
428-
* the position in short-hand property assignment excluding property accessing. However, if we do findAllReference at the
429-
* position of property accessing, the referenceEntry of such position will be handled in the first case.
430-
*/
431-
else if (!(referenceSymbol.flags & SymbolFlags.Transient) && searchSymbols.indexOf(shorthandValueSymbol) >= 0) {
432-
addReferenceToRelatedSymbol(referenceSymbolDeclaration.name, shorthandValueSymbol);
433-
}
434-
else if (searchLocation.kind === SyntaxKind.ConstructorKeyword) {
435-
findAdditionalConstructorReferences(referenceSymbol, referenceLocation);
436-
}
444+
if (relatedSymbol) {
445+
addReferenceToRelatedSymbol(referenceLocation, relatedSymbol);
437446
}
438-
});
447+
/* Because in short-hand property assignment, an identifier which stored as name of the short-hand property assignment
448+
* has two meaning : property name and property value. Therefore when we do findAllReference at the position where
449+
* an identifier is declared, the language service should return the position of the variable declaration as well as
450+
* the position in short-hand property assignment excluding property accessing. However, if we do findAllReference at the
451+
* position of property accessing, the referenceEntry of such position will be handled in the first case.
452+
*/
453+
else if (!(referenceSymbol.flags & SymbolFlags.Transient) && searchSymbols.indexOf(shorthandValueSymbol) >= 0) {
454+
addReferenceToRelatedSymbol(referenceSymbolDeclaration.name, shorthandValueSymbol);
455+
}
456+
else if (searchLocation.kind === SyntaxKind.ConstructorKeyword) {
457+
findAdditionalConstructorReferences(referenceSymbol, referenceLocation);
458+
}
459+
}
439460
}
440461
return;
441462

src/services/services.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1959,11 +1959,9 @@ namespace ts {
19591959

19601960
function walk(node: Node) {
19611961
switch (node.kind) {
1962-
case SyntaxKind.Identifier: {
1963-
const text = (<Identifier>node).text;
1964-
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
1962+
case SyntaxKind.Identifier:
1963+
setNameTable((<Identifier>node).text, node);
19651964
break;
1966-
}
19671965
case SyntaxKind.StringLiteral:
19681966
case SyntaxKind.NumericLiteral:
19691967
// We want to store any numbers/strings if they were a name that could be
@@ -1974,9 +1972,7 @@ namespace ts {
19741972
node.parent.kind === SyntaxKind.ExternalModuleReference ||
19751973
isArgumentOfElementAccessExpression(node) ||
19761974
isLiteralComputedPropertyDeclarationName(node)) {
1977-
1978-
const text = (<LiteralExpression>node).text;
1979-
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
1975+
setNameTable((<LiteralExpression>node).text, node);
19801976
}
19811977
break;
19821978
default:
@@ -1988,6 +1984,10 @@ namespace ts {
19881984
}
19891985
}
19901986
}
1987+
1988+
function setNameTable(text: string, node: ts.Node): void {
1989+
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
1990+
}
19911991
}
19921992

19931993
function isArgumentOfElementAccessExpression(node: Node) {

src/services/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -495,7 +495,7 @@ namespace ts {
495495

496496
export interface SymbolDisplayPart {
497497
text: string;
498-
kind: string;
498+
kind: string; // A ScriptElementKind
499499
}
500500

501501
export interface QuickInfo {

0 commit comments

Comments
 (0)