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'); 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": "@angular/core@file:../../dist/packages-dist/core":
version "7.2.0" version "8.0.0-beta.13"
dependencies: dependencies:
tslib "^1.9.0" tslib "^1.9.0"
"@angular/language-service@file:../../dist/packages-dist/language-service": "@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": "@types/node@file:../../node_modules/@types/node":
version "10.9.4" version "10.9.4"
@ -61,16 +61,16 @@ inherits@2:
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
jasmine-core@~3.1.0: jasmine-core@~3.3.0:
version "3.1.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.1.0.tgz#a4785e135d5df65024dfc9224953df585bd2766c" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.3.0.tgz#dea1cdc634bc93c7e0d4ad27185df30fa971b10e"
integrity sha1-pHheE11d9lAk38kiSVPfWFvSdmw= integrity sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==
"jasmine@file:../../node_modules/jasmine": "jasmine@file:../../node_modules/jasmine":
version "3.1.0" version "3.3.1"
dependencies: dependencies:
glob "^7.0.6" glob "^7.0.6"
jasmine-core "~3.1.0" jasmine-core "~3.3.0"
minimatch@^3.0.4: minimatch@^3.0.4:
version "3.0.4" version "3.0.4"
@ -97,7 +97,7 @@ tslib@^1.9.0:
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
"typescript@file:../../node_modules/typescript": "typescript@file:../../node_modules/typescript":
version "3.2.2" version "3.4.2"
wrappy@1: wrappy@1:
version "1.0.2" 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 * as tss from 'typescript/lib/tsserverlibrary'; // used as type only
import {createLanguageService} from './language_service'; import {createLanguageService} from './language_service';
import {Completion, Diagnostic, DiagnosticMessageChain} from './types'; import {Completion, Diagnostic, DiagnosticMessageChain, Location} from './types';
import {TypeScriptServiceHost} from './typescript_host'; import {TypeScriptServiceHost} from './typescript_host';
const projectHostMap = new WeakMap<tss.server.Project, TypeScriptServiceHost>(); const projectHostMap = new WeakMap<tss.server.Project, TypeScriptServiceHost>();
@ -156,36 +156,61 @@ export function create(info: tss.server.PluginCreateInfo): ts.LanguageService {
return base; return base;
}; };
proxy.getDefinitionAtPosition = function( proxy.getDefinitionAtPosition = function(fileName: string, position: number):
fileName: string, position: number): ReadonlyArray<ts.DefinitionInfo> { ReadonlyArray<ts.DefinitionInfo>|
let base = oldLS.getDefinitionAtPosition(fileName, position); undefined {
if (base && base.length) { const base = oldLS.getDefinitionAtPosition(fileName, position);
return base; 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', () => { proxy.getDefinitionAndBoundSpan = function(fileName: string, position: number):
const ours = ls.getDefinitionAt(fileName, position); ts.DefinitionInfoAndBoundSpan |
let combined; undefined {
const base = oldLS.getDefinitionAndBoundSpan(fileName, position);
if (ours && ours.length) { if (base && base.definitions && base.definitions.length) {
combined = base && base.concat([]) || []; return base;
for (const loc of ours) { }
combined.push({ const ours = ls.getDefinitionAt(fileName, position);
fileName: loc.fileName, if (ours && ours.length) {
textSpan: {start: loc.span.start, length: loc.span.end - loc.span.start}, return {
name: '', definitions: ours.map((loc: Location) => {
// TODO: remove any and fix type error. return {
kind: 'definition' as any, fileName: loc.fileName,
containerName: loc.fileName, textSpan: {
containerKind: 'file' as any, start: loc.span.start,
}); length: loc.span.end - loc.span.start,
} },
} else { name: '',
combined = base; kind: ts.ScriptElementKind.unknown,
} containerName: loc.fileName,
return combined; containerKind: ts.ScriptElementKind.unknown,
}) || []; };
}; }),
textSpan: {
start: ours[0].span.start,
length: ours[0].span.end - ours[0].span.start,
},
};
}
};
return proxy; return proxy;
} }