feat(language-service): provide external file list to TypeScript (#16417)
Also ensures that it only calls base language service for files that it contains. PR Close #16417
This commit is contained in:
parent
a4de214e2b
commit
f4b771a0c6
|
@ -12,7 +12,7 @@
|
||||||
* Entry point for all public APIs of the language service package.
|
* Entry point for all public APIs of the language service package.
|
||||||
*/
|
*/
|
||||||
export {createLanguageService} from './src/language_service';
|
export {createLanguageService} from './src/language_service';
|
||||||
export {create} from './src/ts_plugin';
|
export * from './src/ts_plugin';
|
||||||
export {Completion, Completions, Declaration, Declarations, Definition, Diagnostic, Diagnostics, Hover, HoverTextSection, LanguageService, LanguageServiceHost, Location, Span, TemplateSource, TemplateSources} from './src/types';
|
export {Completion, Completions, Declaration, Declarations, Definition, Diagnostic, Diagnostics, Hover, HoverTextSection, LanguageService, LanguageServiceHost, Location, Span, TemplateSource, TemplateSources} from './src/types';
|
||||||
export {TypeScriptServiceHost, createLanguageServiceFromTypescript} from './src/typescript_host';
|
export {TypeScriptServiceHost, createLanguageServiceFromTypescript} from './src/typescript_host';
|
||||||
export {VERSION} from './src/version';
|
export {VERSION} from './src/version';
|
||||||
|
|
|
@ -12,10 +12,155 @@ import {createLanguageService} from './language_service';
|
||||||
import {Completion, Diagnostic, LanguageService, LanguageServiceHost} from './types';
|
import {Completion, Diagnostic, LanguageService, LanguageServiceHost} from './types';
|
||||||
import {TypeScriptServiceHost} from './typescript_host';
|
import {TypeScriptServiceHost} from './typescript_host';
|
||||||
|
|
||||||
|
const projectHostMap = new WeakMap<any, TypeScriptServiceHost>();
|
||||||
|
|
||||||
|
export function getExternalFiles(project: any): string[]|undefined {
|
||||||
|
const host = projectHostMap.get(project);
|
||||||
|
if (host) {
|
||||||
|
return host.getTemplateReferences();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const angularOnlyResults = process.argv.indexOf('--angularOnlyResults') >= 0;
|
||||||
|
|
||||||
|
function angularOnlyFilter(ls: ts.LanguageService): ts.LanguageService {
|
||||||
|
return {
|
||||||
|
cleanupSemanticCache: () => ls.cleanupSemanticCache(),
|
||||||
|
getSyntacticDiagnostics: fileName => <ts.Diagnostic[]>[],
|
||||||
|
getSemanticDiagnostics: fileName => <ts.Diagnostic[]>[],
|
||||||
|
getCompilerOptionsDiagnostics: () => <ts.Diagnostic[]>[],
|
||||||
|
getSyntacticClassifications: (fileName, span) => [],
|
||||||
|
getSemanticClassifications: (fileName, span) => [],
|
||||||
|
getEncodedSyntacticClassifications: (fileName, span) => <ts.Classifications><any>{undefined},
|
||||||
|
getEncodedSemanticClassifications: (fileName, span) => <ts.Classifications><any>undefined,
|
||||||
|
getCompletionsAtPosition: (fileName, position) => <ts.CompletionInfo><any>undefined,
|
||||||
|
getCompletionEntryDetails: (fileName, position, entryName) =>
|
||||||
|
<ts.CompletionEntryDetails><any>undefined,
|
||||||
|
getCompletionEntrySymbol: (fileName, position, entryName) => <ts.Symbol><any>undefined,
|
||||||
|
getQuickInfoAtPosition: (fileName, position) => <ts.QuickInfo><any>undefined,
|
||||||
|
getNameOrDottedNameSpan: (fileName, startPos, endPos) => <ts.TextSpan><any>undefined,
|
||||||
|
getBreakpointStatementAtPosition: (fileName, position) => <ts.TextSpan><any>undefined,
|
||||||
|
getSignatureHelpItems: (fileName, position) => <ts.SignatureHelpItems><any>undefined,
|
||||||
|
getRenameInfo: (fileName, position) => <ts.RenameInfo><any>undefined,
|
||||||
|
findRenameLocations: (fileName, position, findInStrings, findInComments) =>
|
||||||
|
<ts.RenameLocation[]>[],
|
||||||
|
getDefinitionAtPosition: (fileName, position) => <ts.DefinitionInfo[]>[],
|
||||||
|
getTypeDefinitionAtPosition: (fileName, position) => <ts.DefinitionInfo[]>[],
|
||||||
|
getImplementationAtPosition: (fileName, position) => <ts.ImplementationLocation[]>[],
|
||||||
|
getReferencesAtPosition: (fileName: string, position: number) => <ts.ReferenceEntry[]>[],
|
||||||
|
findReferences: (fileName: string, position: number) => <ts.ReferencedSymbol[]>[],
|
||||||
|
getDocumentHighlights: (fileName, position, filesToSearch) => <ts.DocumentHighlights[]>[],
|
||||||
|
/** @deprecated */
|
||||||
|
getOccurrencesAtPosition: (fileName, position) => <ts.ReferenceEntry[]>[],
|
||||||
|
getNavigateToItems: searchValue => <ts.NavigateToItem[]>[],
|
||||||
|
getNavigationBarItems: fileName => <ts.NavigationBarItem[]>[],
|
||||||
|
getNavigationTree: fileName => <ts.NavigationTree><any>undefined,
|
||||||
|
getOutliningSpans: fileName => <ts.OutliningSpan[]>[],
|
||||||
|
getTodoComments: (fileName, descriptors) => <ts.TodoComment[]>[],
|
||||||
|
getBraceMatchingAtPosition: (fileName, position) => <ts.TextSpan[]>[],
|
||||||
|
getIndentationAtPosition: (fileName, position, options) => <number><any>undefined,
|
||||||
|
getFormattingEditsForRange: (fileName, start, end, options) => <ts.TextChange[]>[],
|
||||||
|
getFormattingEditsForDocument: (fileName, options) => <ts.TextChange[]>[],
|
||||||
|
getFormattingEditsAfterKeystroke: (fileName, position, key, options) => <ts.TextChange[]>[],
|
||||||
|
getDocCommentTemplateAtPosition: (fileName, position) => <ts.TextInsertion><any>undefined,
|
||||||
|
isValidBraceCompletionAtPosition: (fileName, position, openingBrace) => <boolean><any>undefined,
|
||||||
|
getCodeFixesAtPosition: (fileName, start, end, errorCodes) => <ts.CodeAction[]>[],
|
||||||
|
getEmitOutput: fileName => <ts.EmitOutput><any>undefined,
|
||||||
|
getProgram: () => ls.getProgram(),
|
||||||
|
dispose: () => ls.dispose()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageService {
|
export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageService {
|
||||||
// Create the proxy
|
// Create the proxy
|
||||||
const proxy: ts.LanguageService = Object.create(null);
|
const proxy: ts.LanguageService = Object.create(null);
|
||||||
const oldLS: ts.LanguageService = info.languageService;
|
let oldLS: ts.LanguageService = info.languageService;
|
||||||
|
|
||||||
|
if (angularOnlyResults) {
|
||||||
|
oldLS = angularOnlyFilter(oldLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryCall<T>(fileName: string | undefined, callback: () => T): T {
|
||||||
|
if (fileName && !oldLS.getProgram().getSourceFile(fileName)) {
|
||||||
|
return undefined as any as T;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return callback();
|
||||||
|
} catch (e) {
|
||||||
|
return undefined as any as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFilenameCall<T>(m: (fileName: string) => T): (fileName: string) => T {
|
||||||
|
return fileName => tryCall(fileName, () => <T>(m.call(ls, fileName)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFilenameOneCall<T, P>(m: (fileName: string, p: P) => T): (filename: string, p: P) =>
|
||||||
|
T {
|
||||||
|
return (fileName, p) => tryCall(fileName, () => <T>(m.call(ls, fileName, p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFilenameTwoCall<T, P1, P2>(m: (fileName: string, p1: P1, p2: P2) => T): (
|
||||||
|
filename: string, p1: P1, p2: P2) => T {
|
||||||
|
return (fileName, p1, p2) => tryCall(fileName, () => <T>(m.call(ls, fileName, p1, p2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryFilenameThreeCall<T, P1, P2, P3>(m: (fileName: string, p1: P1, p2: P2, p3: P3) => T):
|
||||||
|
(filename: string, p1: P1, p2: P2, p3: P3) => T {
|
||||||
|
return (fileName, p1, p2, p3) => tryCall(fileName, () => <T>(m.call(ls, fileName, p1, p2, p3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function typescriptOnly(ls: ts.LanguageService): ts.LanguageService {
|
||||||
|
return {
|
||||||
|
cleanupSemanticCache: () => ls.cleanupSemanticCache(),
|
||||||
|
getSyntacticDiagnostics: tryFilenameCall(ls.getSyntacticDiagnostics),
|
||||||
|
getSemanticDiagnostics: tryFilenameCall(ls.getSemanticDiagnostics),
|
||||||
|
getCompilerOptionsDiagnostics: () => ls.getCompilerOptionsDiagnostics(),
|
||||||
|
getSyntacticClassifications: tryFilenameOneCall(ls.getSemanticClassifications),
|
||||||
|
getSemanticClassifications: tryFilenameOneCall(ls.getSemanticClassifications),
|
||||||
|
getEncodedSyntacticClassifications: tryFilenameOneCall(ls.getEncodedSyntacticClassifications),
|
||||||
|
getEncodedSemanticClassifications: tryFilenameOneCall(ls.getEncodedSemanticClassifications),
|
||||||
|
getCompletionsAtPosition: tryFilenameOneCall(ls.getCompletionsAtPosition),
|
||||||
|
getCompletionEntryDetails: tryFilenameTwoCall(ls.getCompletionEntryDetails),
|
||||||
|
getCompletionEntrySymbol: tryFilenameTwoCall(ls.getCompletionEntrySymbol),
|
||||||
|
getQuickInfoAtPosition: tryFilenameOneCall(ls.getQuickInfoAtPosition),
|
||||||
|
getNameOrDottedNameSpan: tryFilenameTwoCall(ls.getNameOrDottedNameSpan),
|
||||||
|
getBreakpointStatementAtPosition: tryFilenameOneCall(ls.getBreakpointStatementAtPosition),
|
||||||
|
getSignatureHelpItems: tryFilenameOneCall(ls.getSignatureHelpItems),
|
||||||
|
getRenameInfo: tryFilenameOneCall(ls.getRenameInfo),
|
||||||
|
findRenameLocations: tryFilenameThreeCall(ls.findRenameLocations),
|
||||||
|
getDefinitionAtPosition: tryFilenameOneCall(ls.getDefinitionAtPosition),
|
||||||
|
getTypeDefinitionAtPosition: tryFilenameOneCall(ls.getTypeDefinitionAtPosition),
|
||||||
|
getImplementationAtPosition: tryFilenameOneCall(ls.getImplementationAtPosition),
|
||||||
|
getReferencesAtPosition: tryFilenameOneCall(ls.getReferencesAtPosition),
|
||||||
|
findReferences: tryFilenameOneCall(ls.findReferences),
|
||||||
|
getDocumentHighlights: tryFilenameTwoCall(ls.getDocumentHighlights),
|
||||||
|
/** @deprecated */
|
||||||
|
getOccurrencesAtPosition: tryFilenameOneCall(ls.getOccurrencesAtPosition),
|
||||||
|
getNavigateToItems:
|
||||||
|
(searchValue, maxResultCount, fileName, excludeDtsFiles) => tryCall(
|
||||||
|
fileName,
|
||||||
|
() => ls.getNavigateToItems(searchValue, maxResultCount, fileName, excludeDtsFiles)),
|
||||||
|
getNavigationBarItems: tryFilenameCall(ls.getNavigationBarItems),
|
||||||
|
getNavigationTree: tryFilenameCall(ls.getNavigationTree),
|
||||||
|
getOutliningSpans: tryFilenameCall(ls.getOutliningSpans),
|
||||||
|
getTodoComments: tryFilenameOneCall(ls.getTodoComments),
|
||||||
|
getBraceMatchingAtPosition: tryFilenameOneCall(ls.getBraceMatchingAtPosition),
|
||||||
|
getIndentationAtPosition: tryFilenameTwoCall(ls.getIndentationAtPosition),
|
||||||
|
getFormattingEditsForRange: tryFilenameThreeCall(ls.getFormattingEditsForRange),
|
||||||
|
getFormattingEditsForDocument: tryFilenameOneCall(ls.getFormattingEditsForDocument),
|
||||||
|
getFormattingEditsAfterKeystroke: tryFilenameThreeCall(ls.getFormattingEditsAfterKeystroke),
|
||||||
|
getDocCommentTemplateAtPosition: tryFilenameOneCall(ls.getDocCommentTemplateAtPosition),
|
||||||
|
isValidBraceCompletionAtPosition: tryFilenameTwoCall(ls.isValidBraceCompletionAtPosition),
|
||||||
|
getCodeFixesAtPosition: tryFilenameThreeCall(ls.getCodeFixesAtPosition),
|
||||||
|
getEmitOutput: tryFilenameCall(ls.getEmitOutput),
|
||||||
|
getProgram: () => ls.getProgram(),
|
||||||
|
dispose: () => ls.dispose()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
oldLS = typescriptOnly(oldLS);
|
||||||
|
|
||||||
for (const k in oldLS) {
|
for (const k in oldLS) {
|
||||||
(<any>proxy)[k] = function() { return (oldLS as any)[k].apply(oldLS, arguments); };
|
(<any>proxy)[k] = function() { return (oldLS as any)[k].apply(oldLS, arguments); };
|
||||||
}
|
}
|
||||||
|
@ -35,21 +180,28 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function tryOperation(attempting: string, callback: () => void) {
|
function tryOperation<T>(attempting: string, callback: () => T): T|null {
|
||||||
try {
|
try {
|
||||||
callback();
|
return callback();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
info.project.projectService.logger.info(`Failed to ${attempting}: ${e.toString()}`);
|
info.project.projectService.logger.info(`Failed to ${attempting}: ${e.toString()}`);
|
||||||
info.project.projectService.logger.info(`Stack trace: ${e.stack}`);
|
info.project.projectService.logger.info(`Stack trace: ${e.stack}`);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, info.languageService);
|
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, info.languageService);
|
||||||
const ls = createLanguageService(serviceHost as any);
|
const ls = createLanguageService(serviceHost as any);
|
||||||
serviceHost.setSite(ls);
|
serviceHost.setSite(ls);
|
||||||
|
projectHostMap.set(info.project, serviceHost);
|
||||||
|
|
||||||
proxy.getCompletionsAtPosition = function(fileName: string, position: number) {
|
proxy.getCompletionsAtPosition = function(fileName: string, position: number) {
|
||||||
let base = oldLS.getCompletionsAtPosition(fileName, position);
|
let base = oldLS.getCompletionsAtPosition(fileName, position) || {
|
||||||
|
isGlobalCompletion: false,
|
||||||
|
isMemberCompletion: false,
|
||||||
|
isNewIdentifierLocation: false,
|
||||||
|
entries: []
|
||||||
|
};
|
||||||
tryOperation('get completions', () => {
|
tryOperation('get completions', () => {
|
||||||
const results = ls.getCompletionsAt(fileName, position);
|
const results = ls.getCompletionsAt(fileName, position);
|
||||||
if (results && results.length) {
|
if (results && results.length) {
|
||||||
|
@ -72,14 +224,14 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||||
proxy.getQuickInfoAtPosition = function(fileName: string, position: number): ts.QuickInfo {
|
proxy.getQuickInfoAtPosition = function(fileName: string, position: number): ts.QuickInfo {
|
||||||
let base = oldLS.getQuickInfoAtPosition(fileName, position);
|
let base = oldLS.getQuickInfoAtPosition(fileName, position);
|
||||||
// TODO(vicb): the tags property has been removed in TS 2.2
|
// TODO(vicb): the tags property has been removed in TS 2.2
|
||||||
const tags = (<any>base).tags;
|
|
||||||
tryOperation('get quick info', () => {
|
tryOperation('get quick info', () => {
|
||||||
const ours = ls.getHoverAt(fileName, position);
|
const ours = ls.getHoverAt(fileName, position);
|
||||||
if (ours) {
|
if (ours) {
|
||||||
const displayParts: typeof base.displayParts = [];
|
const displayParts: ts.SymbolDisplayPart[] = [];
|
||||||
for (const part of ours.text) {
|
for (const part of ours.text) {
|
||||||
displayParts.push({kind: part.language !, text: part.text});
|
displayParts.push({kind: part.language !, text: part.text});
|
||||||
}
|
}
|
||||||
|
const tags = base && (<any>base).tags;
|
||||||
base = <any>{
|
base = <any>{
|
||||||
displayParts,
|
displayParts,
|
||||||
documentation: [],
|
documentation: [],
|
||||||
|
@ -97,10 +249,8 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||||
};
|
};
|
||||||
|
|
||||||
proxy.getSemanticDiagnostics = function(fileName: string) {
|
proxy.getSemanticDiagnostics = function(fileName: string) {
|
||||||
let base = oldLS.getSemanticDiagnostics(fileName);
|
let result = oldLS.getSemanticDiagnostics(fileName);
|
||||||
if (base === undefined) {
|
const base = result || [];
|
||||||
base = [];
|
|
||||||
}
|
|
||||||
tryOperation('get diagnostics', () => {
|
tryOperation('get diagnostics', () => {
|
||||||
info.project.projectService.logger.info(`Computing Angular semantic diagnostics...`);
|
info.project.projectService.logger.info(`Computing Angular semantic diagnostics...`);
|
||||||
const ours = ls.getDiagnostics(fileName);
|
const ours = ls.getDiagnostics(fileName);
|
||||||
|
@ -120,7 +270,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
tryOperation('get definition', () => {
|
return tryOperation('get definition', () => {
|
||||||
const ours = ls.getDefinitionAt(fileName, position);
|
const ours = ls.getDefinitionAt(fileName, position);
|
||||||
if (ours && ours.length) {
|
if (ours && ours.length) {
|
||||||
base = base || [];
|
base = base || [];
|
||||||
|
@ -135,8 +285,8 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return base;
|
return base;
|
||||||
|
}) || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
return proxy;
|
return proxy;
|
||||||
|
|
Loading…
Reference in New Issue