Skip to content

Commit 2d232c2

Browse files
author
Andy Hanson
committed
Support find-all-references for type keywords
1 parent 4759ade commit 2d232c2

10 files changed

+191
-131
lines changed

src/harness/fourslash.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1668,7 +1668,7 @@ namespace FourSlash {
16681668
this.goToPosition(len);
16691669
}
16701670

1671-
private goToRangeStart({fileName, start}: Range) {
1671+
public goToRangeStart({fileName, start}: Range) {
16721672
this.openFile(fileName);
16731673
this.goToPosition(start);
16741674
}
@@ -3082,6 +3082,10 @@ namespace FourSlashInterface {
30823082
this.state.goToMarker(name);
30833083
}
30843084

3085+
public rangeStart(range: FourSlash.Range) {
3086+
this.state.goToRangeStart(range);
3087+
}
3088+
30853089
public bof() {
30863090
this.state.goToBOF();
30873091
}

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: 107 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,45 @@ 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+
if (sourceFileHasName(sourceFile, name)) {
357+
addReferencesForKeywordInFile(sourceFile, keywordKind, name, cancellationToken, references);
358+
}
359+
}
360+
361+
return [{ definition, references }];
362+
}
363+
364+
function addReferencesForKeywordInFile(sourceFile: SourceFile, kind: SyntaxKind, searchText: string, cancellationToken: CancellationToken, references: Push<ReferenceEntry>): void {
365+
const possiblePositions = getPossibleSymbolReferencePositions(sourceFile, searchText, sourceFile.getStart(), sourceFile.getEnd(), cancellationToken);
366+
for (const position of possiblePositions) {
367+
cancellationToken.throwIfCancellationRequested();
368+
const referenceLocation = getTouchingPropertyName(sourceFile, position);
369+
if (referenceLocation.kind === kind) {
370+
references.push({
371+
textSpan: createTextSpanFromNode(referenceLocation),
372+
fileName: sourceFile.fileName,
373+
isWriteAccess: false,
374+
isDefinition: false,
375+
});
376+
}
377+
}
378+
}
379+
354380
/** Search within node "container" for references for a search value, where the search value is defined as a
355381
* tuple of(searchSymbol, searchText, searchLocation, and searchMeaning).
356382
* searchLocation: a node where the search value
@@ -375,67 +401,64 @@ namespace ts.FindAllReferences {
375401

376402
const parents = getParentSymbolsOfPropertyAccess();
377403
const inheritsFromCache: Map<boolean> = createMap<boolean>();
404+
// Build the set of symbols to search for, initially it has only the current symbol
405+
const searchSymbols = populateSearchSymbolSet(searchSymbol, searchLocation, typeChecker, implementations);
378406

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();
407+
for (const position of possiblePositions) {
408+
cancellationToken.throwIfCancellationRequested();
385409

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;
410+
const referenceLocation = getTouchingPropertyName(sourceFile, position);
411+
if (!isValidReferencePosition(referenceLocation, searchText)) {
412+
// This wasn't the start of a token. Check to see if it might be a
413+
// match in a comment or string if that's what the caller is asking
414+
// for.
415+
if (!implementations && ((findInStrings && isInString(sourceFile, position)) ||
416+
(findInComments && isInNonReferenceComment(sourceFile, position)))) {
417+
418+
// In the case where we're looking inside comments/strings, we don't have
419+
// an actual definition. So just use 'undefined' here. Features like
420+
// 'Rename' won't care (as they ignore the definitions), and features like
421+
// 'FindReferences' will just filter out these results.
422+
result.push({
423+
definition: undefined,
424+
references: [{
425+
fileName: sourceFile.fileName,
426+
textSpan: createTextSpan(position, searchText.length),
427+
isWriteAccess: false,
428+
isDefinition: false
429+
}]
430+
});
409431
}
432+
continue;
433+
}
410434

411-
if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) {
412-
return;
413-
}
435+
if (!(getMeaningFromLocation(referenceLocation) & searchMeaning)) {
436+
continue;
437+
}
414438

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);
439+
const referenceSymbol = typeChecker.getSymbolAtLocation(referenceLocation);
440+
if (referenceSymbol) {
441+
const referenceSymbolDeclaration = referenceSymbol.valueDeclaration;
442+
const shorthandValueSymbol = typeChecker.getShorthandAssignmentValueSymbol(referenceSymbolDeclaration);
443+
const relatedSymbol = getRelatedSymbol(searchSymbols, referenceSymbol, referenceLocation,
444+
/*searchLocationIsConstructor*/ searchLocation.kind === SyntaxKind.ConstructorKeyword, parents, inheritsFromCache, typeChecker);
421445

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-
}
446+
if (relatedSymbol) {
447+
addReferenceToRelatedSymbol(referenceLocation, relatedSymbol);
437448
}
438-
});
449+
/* Because in short-hand property assignment, an identifier which stored as name of the short-hand property assignment
450+
* has two meaning : property name and property value. Therefore when we do findAllReference at the position where
451+
* an identifier is declared, the language service should return the position of the variable declaration as well as
452+
* the position in short-hand property assignment excluding property accessing. However, if we do findAllReference at the
453+
* position of property accessing, the referenceEntry of such position will be handled in the first case.
454+
*/
455+
else if (!(referenceSymbol.flags & SymbolFlags.Transient) && searchSymbols.indexOf(shorthandValueSymbol) >= 0) {
456+
addReferenceToRelatedSymbol(referenceSymbolDeclaration.name, shorthandValueSymbol);
457+
}
458+
else if (searchLocation.kind === SyntaxKind.ConstructorKeyword) {
459+
findAdditionalConstructorReferences(referenceSymbol, referenceLocation);
460+
}
461+
}
439462
}
440463
return;
441464

src/services/services.ts

Lines changed: 20 additions & 11 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,20 +1972,31 @@ 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;
1982-
default:
1978+
case SyntaxKind.TypeOperator:
1979+
setNameTable(tokenToString((node as ts.TypeOperatorNode).operator), node);
19831980
forEachChild(node, walk);
1984-
if (node.jsDoc) {
1985-
for (const jsDoc of node.jsDoc) {
1986-
forEachChild(jsDoc, walk);
1981+
break;
1982+
default:
1983+
if (isTypeKeyword(node.kind)) {
1984+
setNameTable(tokenToString(node.kind), node);
1985+
}
1986+
else {
1987+
forEachChild(node, walk);
1988+
if (node.jsDoc) {
1989+
for (const jsDoc of node.jsDoc) {
1990+
forEachChild(jsDoc, walk);
1991+
}
19871992
}
19881993
}
19891994
}
19901995
}
1996+
1997+
function setNameTable(text: string, node: ts.Node): void {
1998+
nameTable.set(text, nameTable.get(text) === undefined ? node.pos : -1);
1999+
}
19912000
}
19922001

19932002
function isArgumentOfElementAccessExpression(node: Node) {

0 commit comments

Comments
 (0)