Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 50d243e

Browse filesBrowse files
authored
Merge pull request #10482 from Microsoft/go_to_implementation_pr
Go to Implementation
2 parents 98a3fc5 + 29d85cd commit 50d243e
Copy full SHA for 50d243e

67 files changed

+1,609-38Lines changed: 1609 additions & 38 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎Jakefile.js‎

Copy file name to clipboardExpand all lines: Jakefile.js
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ var servicesSources = [
129129
"documentRegistry.ts",
130130
"findAllReferences.ts",
131131
"goToDefinition.ts",
132+
"goToImplementation.ts",
132133
"jsDoc.ts",
133134
"jsTyping.ts",
134135
"navigateTo.ts",
@@ -789,7 +790,7 @@ function cleanTestDirs() {
789790

790791
// used to pass data from jake command line directly to run.js
791792
function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount, stackTraceLimit) {
792-
var testConfigContents = JSON.stringify({
793+
var testConfigContents = JSON.stringify({
793794
test: tests ? [tests] : undefined,
794795
light: light,
795796
workerCount: workerCount,
Collapse file

‎src/harness/fourslash.ts‎

Copy file name to clipboardExpand all lines: src/harness/fourslash.ts
+103Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ namespace FourSlash {
8888
marker?: Marker;
8989
}
9090

91+
interface ImplementationLocationInformation extends ts.ImplementationLocation {
92+
matched?: boolean;
93+
}
94+
9195
export interface TextSpan {
9296
start: number;
9397
end: number;
@@ -1699,6 +1703,17 @@ namespace FourSlash {
16991703
assertFn(actualCount, expectedCount, this.messageAtLastKnownMarker("Type definitions Count"));
17001704
}
17011705

1706+
public verifyImplementationListIsEmpty(negative: boolean) {
1707+
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
1708+
1709+
if (negative) {
1710+
assert.isTrue(implementations && implementations.length > 0, "Expected at least one implementation but got 0");
1711+
}
1712+
else {
1713+
assert.isUndefined(implementations, "Expected implementation list to be empty but implementations returned");
1714+
}
1715+
}
1716+
17021717
public verifyGoToDefinitionName(expectedName: string, expectedContainerName: string) {
17031718
const definitions = this.languageService.getDefinitionAtPosition(this.activeFile.fileName, this.currentCaretPosition);
17041719
const actualDefinitionName = definitions && definitions.length ? definitions[0].name : "";
@@ -1707,6 +1722,82 @@ namespace FourSlash {
17071722
assert.equal(actualDefinitionContainerName, expectedContainerName, this.messageAtLastKnownMarker("Definition Info Container Name"));
17081723
}
17091724

1725+
public goToImplementation() {
1726+
const implementations = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
1727+
if (!implementations || !implementations.length) {
1728+
this.raiseError("goToImplementation failed - expected to find at least one implementation location but got 0");
1729+
}
1730+
if (implementations.length > 1) {
1731+
this.raiseError(`goToImplementation failed - more than 1 implementation returned (${implementations.length})`);
1732+
}
1733+
1734+
const implementation = implementations[0];
1735+
this.openFile(implementation.fileName);
1736+
this.currentCaretPosition = implementation.textSpan.start;
1737+
}
1738+
1739+
public verifyRangesInImplementationList(markerName: string) {
1740+
this.goToMarker(markerName);
1741+
const implementations: ImplementationLocationInformation[] = this.languageService.getImplementationAtPosition(this.activeFile.fileName, this.currentCaretPosition);
1742+
if (!implementations || !implementations.length) {
1743+
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one implementation location but got 0");
1744+
}
1745+
1746+
for (let i = 0; i < implementations.length; i++) {
1747+
for (let j = 0; j < implementations.length; j++) {
1748+
if (i !== j && implementationsAreEqual(implementations[i], implementations[j])) {
1749+
const { textSpan, fileName } = implementations[i];
1750+
const end = textSpan.start + textSpan.length;
1751+
this.raiseError(`Duplicate implementations returned for range (${textSpan.start}, ${end}) in ${fileName}`);
1752+
}
1753+
}
1754+
}
1755+
1756+
const ranges = this.getRanges();
1757+
1758+
if (!ranges || !ranges.length) {
1759+
this.raiseError("verifyRangesInImplementationList failed - expected to find at least one range in test source");
1760+
}
1761+
1762+
const unsatisfiedRanges: Range[] = [];
1763+
1764+
for (const range of ranges) {
1765+
const length = range.end - range.start;
1766+
const matchingImpl = ts.find(implementations, impl =>
1767+
range.fileName === impl.fileName && range.start === impl.textSpan.start && length === impl.textSpan.length);
1768+
if (matchingImpl) {
1769+
matchingImpl.matched = true;
1770+
}
1771+
else {
1772+
unsatisfiedRanges.push(range);
1773+
}
1774+
}
1775+
1776+
const unmatchedImplementations = implementations.filter(impl => !impl.matched);
1777+
if (unmatchedImplementations.length || unsatisfiedRanges.length) {
1778+
let error = "Not all ranges or implementations are satisfied";
1779+
if (unsatisfiedRanges.length) {
1780+
error += "\nUnsatisfied ranges:";
1781+
for (const range of unsatisfiedRanges) {
1782+
error += `\n (${range.start}, ${range.end}) in ${range.fileName}: ${this.rangeText(range)}`;
1783+
}
1784+
}
1785+
1786+
if (unmatchedImplementations.length) {
1787+
error += "\nUnmatched implementations:";
1788+
for (const impl of unmatchedImplementations) {
1789+
const end = impl.textSpan.start + impl.textSpan.length;
1790+
error += `\n (${impl.textSpan.start}, ${end}) in ${impl.fileName}: ${this.getFileContent(impl.fileName).slice(impl.textSpan.start, end)}`;
1791+
}
1792+
}
1793+
this.raiseError(error);
1794+
}
1795+
1796+
function implementationsAreEqual(a: ImplementationLocationInformation, b: ImplementationLocationInformation) {
1797+
return a.fileName === b.fileName && TestState.textSpansEqual(a.textSpan, b.textSpan);
1798+
}
1799+
}
1800+
17101801
public getMarkers(): Marker[] {
17111802
// Return a copy of the list
17121803
return this.testData.markers.slice(0);
@@ -2885,6 +2976,10 @@ namespace FourSlashInterface {
28852976
this.state.goToTypeDefinition(definitionIndex);
28862977
}
28872978

2979+
public implementation() {
2980+
this.state.goToImplementation();
2981+
}
2982+
28882983
public position(position: number, fileIndex?: number): void;
28892984
public position(position: number, fileName?: string): void;
28902985
public position(position: number, fileNameOrIndex?: any): void {
@@ -2985,6 +3080,10 @@ namespace FourSlashInterface {
29853080
this.state.verifyTypeDefinitionsCount(this.negative, expectedCount);
29863081
}
29873082

3083+
public implementationListIsEmpty() {
3084+
this.state.verifyImplementationListIsEmpty(this.negative);
3085+
}
3086+
29883087
public isValidBraceCompletionAtPosition(openingBrace: string) {
29893088
this.state.verifyBraceCompletionAtPosition(this.negative, openingBrace);
29903089
}
@@ -3253,6 +3352,10 @@ namespace FourSlashInterface {
32533352
public ProjectInfo(expected: string[]) {
32543353
this.state.verifyProjectInfo(expected);
32553354
}
3355+
3356+
public allRangesAppearInImplementationList(markerName: string) {
3357+
this.state.verifyRangesInImplementationList(markerName);
3358+
}
32563359
}
32573360

32583361
export class Edit {
Collapse file

‎src/harness/harnessLanguageService.ts‎

Copy file name to clipboardExpand all lines: src/harness/harnessLanguageService.ts
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,6 +435,9 @@ namespace Harness.LanguageService {
435435
getTypeDefinitionAtPosition(fileName: string, position: number): ts.DefinitionInfo[] {
436436
return unwrapJSONCallResult(this.shim.getTypeDefinitionAtPosition(fileName, position));
437437
}
438+
getImplementationAtPosition(fileName: string, position: number): ts.ImplementationLocation[] {
439+
return unwrapJSONCallResult(this.shim.getImplementationAtPosition(fileName, position));
440+
}
438441
getReferencesAtPosition(fileName: string, position: number): ts.ReferenceEntry[] {
439442
return unwrapJSONCallResult(this.shim.getReferencesAtPosition(fileName, position));
440443
}
Collapse file

‎src/server/client.ts‎

Copy file name to clipboardExpand all lines: src/server/client.ts
+22Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,28 @@ namespace ts.server {
368368
});
369369
}
370370

371+
getImplementationAtPosition(fileName: string, position: number): ImplementationLocation[] {
372+
const lineOffset = this.positionToOneBasedLineOffset(fileName, position);
373+
const args: protocol.FileLocationRequestArgs = {
374+
file: fileName,
375+
line: lineOffset.line,
376+
offset: lineOffset.offset,
377+
};
378+
379+
const request = this.processRequest<protocol.ImplementationRequest>(CommandNames.Implementation, args);
380+
const response = this.processResponse<protocol.ImplementationResponse>(request);
381+
382+
return response.body.map(entry => {
383+
const fileName = entry.file;
384+
const start = this.lineOffsetToPosition(fileName, entry.start);
385+
const end = this.lineOffsetToPosition(fileName, entry.end);
386+
return {
387+
fileName,
388+
textSpan: ts.createTextSpanFromBounds(start, end)
389+
};
390+
});
391+
}
392+
371393
findReferences(fileName: string, position: number): ReferencedSymbol[] {
372394
// Not yet implemented.
373395
return [];
Collapse file

‎src/server/protocol.d.ts‎

Copy file name to clipboardExpand all lines: src/server/protocol.d.ts
+15Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,14 @@ declare namespace ts.server.protocol {
193193
export interface TypeDefinitionRequest extends FileLocationRequest {
194194
}
195195

196+
/**
197+
* Go to implementation request; value of command field is
198+
* "implementation". Return response giving the file locations that
199+
* implement the symbol found in file at location line, col.
200+
*/
201+
export interface ImplementationRequest extends FileLocationRequest {
202+
}
203+
196204
/**
197205
* Location in source code expressed as (one-based) line and character offset.
198206
*/
@@ -240,6 +248,13 @@ declare namespace ts.server.protocol {
240248
body?: FileSpan[];
241249
}
242250

251+
/**
252+
* Implementation response message. Gives text range for implementations.
253+
*/
254+
export interface ImplementationResponse extends Response {
255+
body?: FileSpan[];
256+
}
257+
243258
/**
244259
* Get occurrences request; value of command field is
245260
* "occurrences". Return response giving spans that are relevant
Collapse file

‎src/server/session.ts‎

Copy file name to clipboardExpand all lines: src/server/session.ts
+27Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ namespace ts.server {
111111
export const Formatonkey = "formatonkey";
112112
export const Geterr = "geterr";
113113
export const GeterrForProject = "geterrForProject";
114+
export const Implementation = "implementation";
114115
export const SemanticDiagnosticsSync = "semanticDiagnosticsSync";
115116
export const SyntacticDiagnosticsSync = "syntacticDiagnosticsSync";
116117
export const NavBar = "navbar";
@@ -363,6 +364,28 @@ namespace ts.server {
363364
}));
364365
}
365366

367+
private getImplementation(line: number, offset: number, fileName: string): protocol.FileSpan[] {
368+
const file = ts.normalizePath(fileName);
369+
const project = this.projectService.getProjectForFile(file);
370+
if (!project || project.languageServiceDiabled) {
371+
throw Errors.NoProject;
372+
}
373+
374+
const compilerService = project.compilerService;
375+
const implementations = compilerService.languageService.getImplementationAtPosition(file,
376+
compilerService.host.lineOffsetToPosition(file, line, offset));
377+
378+
if (!implementations) {
379+
return undefined;
380+
}
381+
382+
return implementations.map(impl => ({
383+
file: impl.fileName,
384+
start: compilerService.host.positionToLineOffset(impl.fileName, impl.textSpan.start),
385+
end: compilerService.host.positionToLineOffset(impl.fileName, ts.textSpanEnd(impl.textSpan))
386+
}));
387+
}
388+
366389
private getOccurrences(line: number, offset: number, fileName: string): protocol.OccurrencesResponseItem[] {
367390
fileName = ts.normalizePath(fileName);
368391
const project = this.projectService.getProjectForFile(fileName);
@@ -1090,6 +1113,10 @@ namespace ts.server {
10901113
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
10911114
return { response: this.getTypeDefinition(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
10921115
},
1116+
[CommandNames.Implementation]: (request: protocol.Request) => {
1117+
const implArgs = <protocol.FileLocationRequestArgs>request.arguments;
1118+
return { response: this.getImplementation(implArgs.line, implArgs.offset, implArgs.file), responseRequired: true };
1119+
},
10931120
[CommandNames.References]: (request: protocol.Request) => {
10941121
const defArgs = <protocol.FileLocationRequestArgs>request.arguments;
10951122
return { response: this.getReferences(defArgs.line, defArgs.offset, defArgs.file), responseRequired: true };
Collapse file

‎src/services/documentHighlights.ts‎

Copy file name to clipboardExpand all lines: src/services/documentHighlights.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace ts.DocumentHighlights {
2727
node.kind === SyntaxKind.StringLiteral ||
2828
isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
2929

30-
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false);
30+
const referencedSymbols = FindAllReferences.getReferencedSymbolsForNode(typeChecker, cancellationToken, node, sourceFilesToSearch, /*findInStrings*/ false, /*findInComments*/ false, /*implementations*/false);
3131
return convertReferencedSymbols(referencedSymbols);
3232

3333
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.