fix(language-service): Make Definition and QuickInfo compatible with TS LS (#31972)
Now that the Angular LS is a proper tsserver plugin, it does not make sense for it to maintain its own language service API. This is part one of the effort to remove our custom LanguageService interface. This interface is cumbersome because we have to do two transformations: ng def -> ts def -> lsp definition The TS LS interface is more comprehensive, so this allows the Angular LS to return more information. PR Close #31972
This commit is contained in:

committed by
Alex Rickabaugh

parent
e906a4f0d8
commit
a8e2ee1343
@ -9,9 +9,8 @@
|
||||
import * as ts from 'typescript'; // used as value, passed in by tsserver at runtime
|
||||
import * as tss from 'typescript/lib/tsserverlibrary'; // used as type only
|
||||
|
||||
import {ngLocationToTsDefinitionInfo} from './definitions';
|
||||
import {createLanguageService} from './language_service';
|
||||
import {Completion, Diagnostic, DiagnosticMessageChain, Location} from './types';
|
||||
import {Completion, Diagnostic, DiagnosticMessageChain} from './types';
|
||||
import {TypeScriptServiceHost} from './typescript_host';
|
||||
|
||||
const projectHostMap = new WeakMap<tss.server.Project, TypeScriptServiceHost>();
|
||||
@ -76,13 +75,13 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
|
||||
// This effectively disables native TS features and is meant for internal
|
||||
// use only.
|
||||
const angularOnly = config ? config.angularOnly === true : false;
|
||||
const proxy: tss.LanguageService = Object.assign({}, tsLS);
|
||||
const ngLSHost = new TypeScriptServiceHost(tsLSHost, tsLS);
|
||||
const ngLS = createLanguageService(ngLSHost);
|
||||
projectHostMap.set(project, ngLSHost);
|
||||
|
||||
proxy.getCompletionsAtPosition = function(
|
||||
fileName: string, position: number, options: tss.GetCompletionsAtPositionOptions|undefined) {
|
||||
function getCompletionsAtPosition(
|
||||
fileName: string, position: number,
|
||||
options: tss.GetCompletionsAtPositionOptions | undefined) {
|
||||
if (!angularOnly) {
|
||||
const results = tsLS.getCompletionsAtPosition(fileName, position, options);
|
||||
if (results && results.entries.length) {
|
||||
@ -100,39 +99,20 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
|
||||
isNewIdentifierLocation: false,
|
||||
entries: results.map(completionToEntry),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
proxy.getQuickInfoAtPosition = function(fileName: string, position: number): tss.QuickInfo |
|
||||
undefined {
|
||||
if (!angularOnly) {
|
||||
const result = tsLS.getQuickInfoAtPosition(fileName, position);
|
||||
if (result) {
|
||||
// If TS could answer the query, then return results immediately.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const result = ngLS.getHoverAt(fileName, position);
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
return {
|
||||
// TODO(kyliau): Provide more useful info for kind and kindModifiers
|
||||
kind: ts.ScriptElementKind.unknown,
|
||||
kindModifiers: ts.ScriptElementKindModifier.none,
|
||||
textSpan: {
|
||||
start: result.span.start,
|
||||
length: result.span.end - result.span.start,
|
||||
},
|
||||
displayParts: result.text.map((part) => {
|
||||
return {
|
||||
text: part.text,
|
||||
kind: part.language || 'angular',
|
||||
};
|
||||
}),
|
||||
};
|
||||
};
|
||||
function getQuickInfoAtPosition(fileName: string, position: number): tss.QuickInfo|undefined {
|
||||
if (!angularOnly) {
|
||||
const result = tsLS.getQuickInfoAtPosition(fileName, position);
|
||||
if (result) {
|
||||
// If TS could answer the query, then return results immediately.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return ngLS.getHoverAt(fileName, position);
|
||||
}
|
||||
|
||||
proxy.getSemanticDiagnostics = function(fileName: string): tss.Diagnostic[] {
|
||||
function getSemanticDiagnostics(fileName: string): tss.Diagnostic[] {
|
||||
const results: tss.Diagnostic[] = [];
|
||||
if (!angularOnly) {
|
||||
const tsResults = tsLS.getSemanticDiagnostics(fileName);
|
||||
@ -146,48 +126,43 @@ export function create(info: tss.server.PluginCreateInfo): tss.LanguageService {
|
||||
const sourceFile = fileName.endsWith('.ts') ? ngLSHost.getSourceFile(fileName) : undefined;
|
||||
results.push(...ngResults.map(d => diagnosticToDiagnostic(d, sourceFile)));
|
||||
return results;
|
||||
};
|
||||
}
|
||||
|
||||
proxy.getDefinitionAtPosition = function(fileName: string, position: number):
|
||||
ReadonlyArray<tss.DefinitionInfo>|
|
||||
undefined {
|
||||
if (!angularOnly) {
|
||||
const results = tsLS.getDefinitionAtPosition(fileName, position);
|
||||
if (results) {
|
||||
// If TS could answer the query, then return results immediately.
|
||||
return results;
|
||||
}
|
||||
}
|
||||
const results = ngLS.getDefinitionAt(fileName, position);
|
||||
if (!results) {
|
||||
return;
|
||||
}
|
||||
return results.map(ngLocationToTsDefinitionInfo);
|
||||
};
|
||||
function getDefinitionAtPosition(
|
||||
fileName: string, position: number): ReadonlyArray<tss.DefinitionInfo>|undefined {
|
||||
if (!angularOnly) {
|
||||
const results = tsLS.getDefinitionAtPosition(fileName, position);
|
||||
if (results) {
|
||||
// If TS could answer the query, then return results immediately.
|
||||
return results;
|
||||
}
|
||||
}
|
||||
const result = ngLS.getDefinitionAt(fileName, position);
|
||||
if (!result || !result.definitions || !result.definitions.length) {
|
||||
return;
|
||||
}
|
||||
return result.definitions;
|
||||
}
|
||||
|
||||
proxy.getDefinitionAndBoundSpan = function(fileName: string, position: number):
|
||||
tss.DefinitionInfoAndBoundSpan |
|
||||
undefined {
|
||||
if (!angularOnly) {
|
||||
const result = tsLS.getDefinitionAndBoundSpan(fileName, position);
|
||||
if (result) {
|
||||
// If TS could answer the query, then return results immediately.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
const results = ngLS.getDefinitionAt(fileName, position);
|
||||
if (!results || !results.length) {
|
||||
return;
|
||||
}
|
||||
const {span} = results[0];
|
||||
return {
|
||||
definitions: results.map(ngLocationToTsDefinitionInfo),
|
||||
textSpan: {
|
||||
start: span.start,
|
||||
length: span.end - span.start,
|
||||
},
|
||||
};
|
||||
};
|
||||
function getDefinitionAndBoundSpan(
|
||||
fileName: string, position: number): tss.DefinitionInfoAndBoundSpan|undefined {
|
||||
if (!angularOnly) {
|
||||
const result = tsLS.getDefinitionAndBoundSpan(fileName, position);
|
||||
if (result) {
|
||||
// If TS could answer the query, then return results immediately.
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return ngLS.getDefinitionAt(fileName, position);
|
||||
}
|
||||
|
||||
const proxy: tss.LanguageService = Object.assign(
|
||||
// First clone the original TS language service
|
||||
{}, tsLS,
|
||||
// Then override the methods supported by Angular language service
|
||||
{
|
||||
getCompletionsAtPosition, getQuickInfoAtPosition, getSemanticDiagnostics,
|
||||
getDefinitionAtPosition, getDefinitionAndBoundSpan,
|
||||
});
|
||||
return proxy;
|
||||
}
|
||||
|
Reference in New Issue
Block a user