Skip to content

Commit 5953426

Browse files
authored
abstract code in FindReferences from concrete way of creating result reference entry to seamlessly reuse the same code for gotoImplementation (microsoft#14637)
1 parent 7ead44f commit 5953426

File tree

9 files changed

+172
-86
lines changed

9 files changed

+172
-86
lines changed

src/harness/fourslash.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1859,17 +1859,41 @@ namespace FourSlash {
18591859

18601860
const unsatisfiedRanges: Range[] = [];
18611861

1862+
const delayedErrors: string[] = [];
18621863
for (const range of ranges) {
18631864
const length = range.end - range.start;
18641865
const matchingImpl = ts.find(implementations, impl =>
18651866
range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length);
18661867
if (matchingImpl) {
1868+
if (range.marker && range.marker.data) {
1869+
const expected = <{ displayParts?: ts.SymbolDisplayPart[], parts: string[], kind?: string }>range.marker.data;
1870+
if (expected.displayParts) {
1871+
if (!ts.arrayIsEqualTo(expected.displayParts, matchingImpl.displayParts, displayPartIsEqualTo)) {
1872+
delayedErrors.push(`Mismatched display parts: expected ${JSON.stringify(expected.displayParts)}, actual ${JSON.stringify(matchingImpl.displayParts)}`);
1873+
}
1874+
}
1875+
else if (expected.parts) {
1876+
const actualParts = matchingImpl.displayParts.map(p => p.text);
1877+
if (!ts.arrayIsEqualTo(expected.parts, actualParts)) {
1878+
delayedErrors.push(`Mismatched non-tagged display parts: expected ${JSON.stringify(expected.parts)}, actual ${JSON.stringify(actualParts)}`);
1879+
}
1880+
}
1881+
if (expected.kind !== undefined) {
1882+
if (expected.kind !== matchingImpl.kind) {
1883+
delayedErrors.push(`Mismatched kind: expected ${JSON.stringify(expected.kind)}, actual ${JSON.stringify(matchingImpl.kind)}`);
1884+
}
1885+
}
1886+
}
1887+
18671888
matchingImpl.matched = true;
18681889
}
18691890
else {
18701891
unsatisfiedRanges.push(range);
18711892
}
18721893
}
1894+
if (delayedErrors.length) {
1895+
this.raiseError(delayedErrors.join("\n"));
1896+
}
18731897

18741898
const unmatchedImplementations = implementations.filter(impl => !impl.matched);
18751899
if (unmatchedImplementations.length || unsatisfiedRanges.length) {
@@ -1894,6 +1918,10 @@ namespace FourSlash {
18941918
function implementationsAreEqual(a: ImplementationLocationInformation, b: ImplementationLocationInformation) {
18951919
return a.fileName === b.fileName && TestState.textSpansEqual(a.textSpan, b.textSpan);
18961920
}
1921+
1922+
function displayPartIsEqualTo(a: ts.SymbolDisplayPart, b: ts.SymbolDisplayPart): boolean {
1923+
return a.kind === b.kind && a.text === b.text;
1924+
}
18971925
}
18981926

18991927
public getMarkers(): Marker[] {

src/server/client.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,9 @@ namespace ts.server {
382382
const end = this.lineOffsetToPosition(fileName, entry.end);
383383
return {
384384
fileName,
385-
textSpan: ts.createTextSpanFromBounds(start, end)
385+
textSpan: ts.createTextSpanFromBounds(start, end),
386+
kind: ScriptElementKind.unknown,
387+
displayParts: []
386388
};
387389
});
388390
}

src/services/documentHighlights.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ namespace ts.DocumentHighlights {
1717
}
1818

1919
function getSemanticDocumentHighlights(node: Node, typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFilesToSearch: SourceFile[]): DocumentHighlights[] {
20-
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch);
20+
const context = new FindAllReferences.DefaultFindReferencesContext(typeChecker, cancellationToken);
21+
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(context, node, sourceFilesToSearch);
2122
return referencedSymbols && convertReferencedSymbols(referencedSymbols);
2223
}
2324

src/services/findAllReferences.ts

Lines changed: 115 additions & 66 deletions
Large diffs are not rendered by default.

src/services/goToImplementation.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
/* @internal */
22
namespace ts.GoToImplementation {
33
export function getImplementationAtPosition(typeChecker: TypeChecker, cancellationToken: CancellationToken, sourceFiles: SourceFile[], node: Node): ImplementationLocation[] {
4+
const context = new FindAllReferences.FindImplementationsContext(typeChecker, cancellationToken);
45
// If invoked directly on a shorthand property assignment, then return
56
// the declaration of the symbol being assigned (not the symbol being assigned to).
67
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
7-
const result: ReferenceEntry[] = [];
8-
FindAllReferences.getReferenceEntriesForShorthandPropertyAssignment(node, typeChecker, result);
8+
const result: ImplementationLocation[] = [];
9+
FindAllReferences.getReferenceEntriesForShorthandPropertyAssignment(node, context, result);
910
return result.length > 0 ? result : undefined;
1011
}
1112
else if (node.kind === SyntaxKind.SuperKeyword || isSuperProperty(node.parent)) {
1213
// References to and accesses on the super keyword only have one possible implementation, so no
1314
// need to "Find all References"
1415
const symbol = typeChecker.getSymbolAtLocation(node);
15-
return symbol.valueDeclaration && [FindAllReferences.getReferenceEntryFromNode(symbol.valueDeclaration)];
16+
return symbol.valueDeclaration && [context.getReferenceEntryFromNode(symbol.valueDeclaration)];
1617
}
1718
else {
1819
// Perform "Find all References" and retrieve only those that are implementations
19-
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken,
20+
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(context,
2021
node, sourceFiles, /*findInStrings*/false, /*findInComments*/false, /*isForRename*/false, /*implementations*/true);
21-
const result = flatMap(referencedSymbols, symbol =>
22-
map(symbol.references, ({ textSpan, fileName }) => ({ textSpan, fileName })));
22+
const result = flatMap(referencedSymbols, symbol => symbol.references);
2323

2424
return result && result.length > 0 ? result : undefined;
2525
}

src/services/types.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -354,22 +354,23 @@ namespace ts {
354354
caretOffset: number;
355355
}
356356

357-
export interface RenameLocation {
357+
export interface DocumentSpan {
358358
textSpan: TextSpan;
359359
fileName: string;
360360
}
361361

362-
export interface ReferenceEntry {
363-
textSpan: TextSpan;
364-
fileName: string;
362+
export interface RenameLocation extends DocumentSpan {
363+
}
364+
365+
export interface ReferenceEntry extends DocumentSpan {
365366
isWriteAccess: boolean;
366367
isDefinition: boolean;
367368
isInString?: true;
368369
}
369370

370-
export interface ImplementationLocation {
371-
textSpan: TextSpan;
372-
fileName: string;
371+
export interface ImplementationLocation extends DocumentSpan {
372+
kind: string;
373+
displayParts: SymbolDisplayPart[];
373374
}
374375

375376
export interface DocumentHighlights {
@@ -478,9 +479,12 @@ namespace ts {
478479
displayParts: SymbolDisplayPart[];
479480
}
480481

481-
export interface ReferencedSymbol {
482+
export interface ReferencedSymbolOf<T extends DocumentSpan> {
482483
definition: ReferencedSymbolDefinitionInfo;
483-
references: ReferenceEntry[];
484+
references: T[];
485+
}
486+
487+
export interface ReferencedSymbol extends ReferencedSymbolOf<ReferenceEntry> {
484488
}
485489

486490
export enum SymbolDisplayPartKind {

tests/cases/fourslash/goToImplementationClassMethod_00.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// Should handle calls made on members declared in a class
44

55
//// class Bar {
6-
//// [|hello() {}|]
6+
//// [|{|"parts": ["(","method",")"," ","Bar",".","hello","(",")",":"," ","void"], "kind": "method"|}hello() {}|]
77
//// }
88
////
99
//// new Bar().hel/*reference*/lo;

tests/cases/fourslash/goToImplementationInterface_00.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
////
99
//// interface Baz extends Foo {}
1010
////
11-
//// var bar: Foo = [|{ hello: helloImpl /**0*/ }|];
11+
//// var bar: Foo = [|{|"parts": ["(","object literal",")"], "kind": "interface"|}{ hello: helloImpl /**0*/ }|];
1212
//// var baz: Foo[] = [|[{ hello: helloImpl /**4*/ }]|];
1313
////
1414
//// function helloImpl () {}
1515
////
16-
//// function whatever(x: Foo = [|{ hello() {/**1*/} }|] ) {
16+
//// function whatever(x: Foo = [|{|"parts": ["(","object literal",")"], "kind": "interface"|}{ hello() {/**1*/} }|] ) {
1717
//// }
1818
////
1919
//// class Bar {

tests/cases/fourslash/goToImplementationInterface_07.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
//// let x7: (new() => Foo) = [|class { hello () { /**constructorType*/} }|];
2020
//// let x8: Foo[] = [|[{ hello () { /**arrayType*/} }]|];
2121
//// let x9: { y: Foo } = [|{ y: { hello () { /**typeLiteral*/} } }|];
22+
//// let x10 = [|{|"parts": ["(","anonymous local class",")"], "kind": "local class"|}class implements Foo { hello() {} }|]
23+
//// let x11 = [|{|"parts": ["(","local class",")"," ","C"], "kind": "local class"|}class C implements Foo { hello() {} }|]
2224
////
2325
//// // Should not do anything for type predicates
2426
//// function isFoo(a: any): a is Foo {

0 commit comments

Comments
 (0)