feat(language-service): Implement `definitionAndBoundSpan` (#30125)

This PR adds the implementation for `definitionAndBoundSpan` because
it's now preferred over `definition`. vscode would send this new request
for `Go to definition`. As part of this PR the implementation for
`definition` is refactored and simplified. Goldens for both methods are
checked in.

PR Close #30125
This commit is contained in:
Keen Yee Liau 2019-04-25 11:11:33 -07:00 committed by Andrew Kushnir
parent 67012509a8
commit f4916730b5
4 changed files with 117 additions and 39 deletions

View File

@ -0,0 +1,32 @@
{
"seq": 0,
"type": "response",
"command": "definitionAndBoundSpan",
"request_seq": 2,
"success": true,
"body": {
"definitions": [
{
"file": "${PWD}/project/app/app.component.ts",
"start": {
"line": 7,
"offset": 30
},
"end": {
"line": 7,
"offset": 47
}
}
],
"textSpan": {
"start": {
"line": 7,
"offset": 30
},
"end": {
"line": 7,
"offset": 47
}
}
}
}

View File

@ -139,4 +139,25 @@ describe('Angular Language Service', () => {
});
expect(resp2).toMatchGolden('definition.json');
});
it('should perform definitionAndBoundSpan', async () => {
client.sendRequest('open', {
file: `${PWD}/project/app/app.component.ts`,
});
const resp1 = await client.sendRequest('reload', {
file: `${PWD}/project/app/app.component.ts`,
tmpFile: `${PWD}/project/app/app.component.ts`,
}) as any;
expect(resp1.command).toBe('reload');
expect(resp1.success).toBe(true);
const resp2 = await client.sendRequest('definitionAndBoundSpan', {
file: `${PWD}/project/app/app.component.ts`,
line: 5,
offset: 28,
});
expect(resp2).toMatchGolden('definitionAndBoundSpan.json');
});
});

View File

@ -3,12 +3,12 @@
"@angular/core@file:../../dist/packages-dist/core":
version "7.2.0"
version "8.0.0-beta.13"
dependencies:
tslib "^1.9.0"
"@angular/language-service@file:../../dist/packages-dist/language-service":
version "7.2.0"
version "8.0.0-beta.13"
"@types/node@file:../../node_modules/@types/node":
version "10.9.4"
@ -61,16 +61,16 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
jasmine-core@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.1.0.tgz#a4785e135d5df65024dfc9224953df585bd2766c"
integrity sha1-pHheE11d9lAk38kiSVPfWFvSdmw=
jasmine-core@~3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.3.0.tgz#dea1cdc634bc93c7e0d4ad27185df30fa971b10e"
integrity sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==
"jasmine@file:../../node_modules/jasmine":
version "3.1.0"
version "3.3.1"
dependencies:
glob "^7.0.6"
jasmine-core "~3.1.0"
jasmine-core "~3.3.0"
minimatch@^3.0.4:
version "3.0.4"
@ -97,7 +97,7 @@ tslib@^1.9.0:
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
"typescript@file:../../node_modules/typescript":
version "3.2.2"
version "3.4.2"
wrappy@1:
version "1.0.2"

View File

@ -10,7 +10,7 @@ import * as ts from 'typescript'; // used as value, passed in by tsserver at run
import * as tss from 'typescript/lib/tsserverlibrary'; // used as type only
import {createLanguageService} from './language_service';
import {Completion, Diagnostic, DiagnosticMessageChain} from './types';
import {Completion, Diagnostic, DiagnosticMessageChain, Location} from './types';
import {TypeScriptServiceHost} from './typescript_host';
const projectHostMap = new WeakMap<tss.server.Project, TypeScriptServiceHost>();
@ -156,36 +156,61 @@ export function create(info: tss.server.PluginCreateInfo): ts.LanguageService {
return base;
};
proxy.getDefinitionAtPosition = function(
fileName: string, position: number): ReadonlyArray<ts.DefinitionInfo> {
let base = oldLS.getDefinitionAtPosition(fileName, position);
if (base && base.length) {
return base;
}
proxy.getDefinitionAtPosition = function(fileName: string, position: number):
ReadonlyArray<ts.DefinitionInfo>|
undefined {
const base = oldLS.getDefinitionAtPosition(fileName, position);
if (base && base.length) {
return base;
}
const ours = ls.getDefinitionAt(fileName, position);
if (ours && ours.length) {
return ours.map((loc: Location) => {
return {
fileName: loc.fileName,
textSpan: {
start: loc.span.start,
length: loc.span.end - loc.span.start,
},
name: '',
kind: ts.ScriptElementKind.unknown,
containerName: loc.fileName,
containerKind: ts.ScriptElementKind.unknown,
};
});
}
};
return tryOperation('get definition', () => {
const ours = ls.getDefinitionAt(fileName, position);
let combined;
if (ours && ours.length) {
combined = base && base.concat([]) || [];
for (const loc of ours) {
combined.push({
fileName: loc.fileName,
textSpan: {start: loc.span.start, length: loc.span.end - loc.span.start},
name: '',
// TODO: remove any and fix type error.
kind: 'definition' as any,
containerName: loc.fileName,
containerKind: 'file' as any,
});
}
} else {
combined = base;
}
return combined;
}) || [];
};
proxy.getDefinitionAndBoundSpan = function(fileName: string, position: number):
ts.DefinitionInfoAndBoundSpan |
undefined {
const base = oldLS.getDefinitionAndBoundSpan(fileName, position);
if (base && base.definitions && base.definitions.length) {
return base;
}
const ours = ls.getDefinitionAt(fileName, position);
if (ours && ours.length) {
return {
definitions: ours.map((loc: Location) => {
return {
fileName: loc.fileName,
textSpan: {
start: loc.span.start,
length: loc.span.end - loc.span.start,
},
name: '',
kind: ts.ScriptElementKind.unknown,
containerName: loc.fileName,
containerKind: ts.ScriptElementKind.unknown,
};
}),
textSpan: {
start: ours[0].span.start,
length: ours[0].span.end - ours[0].span.start,
},
};
}
};
return proxy;
}