2016-11-22 12:10:23 -05:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
import * as ts from 'typescript';
|
|
|
|
|
|
|
|
import {createLanguageService} from './language_service';
|
2017-01-06 23:43:17 -05:00
|
|
|
import {Completion, Diagnostic, LanguageService, LanguageServiceHost} from './types';
|
2016-11-22 12:10:23 -05:00
|
|
|
import {TypeScriptServiceHost} from './typescript_host';
|
|
|
|
|
2017-01-06 23:43:17 -05:00
|
|
|
export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageService {
|
|
|
|
// Create the proxy
|
|
|
|
const proxy: ts.LanguageService = Object.create(null);
|
|
|
|
const oldLS: ts.LanguageService = info.languageService;
|
|
|
|
for (const k in oldLS) {
|
|
|
|
(<any>proxy)[k] = function() { return (oldLS as any)[k].apply(oldLS, arguments); };
|
|
|
|
}
|
2016-11-22 12:10:23 -05:00
|
|
|
|
2017-01-06 23:43:17 -05:00
|
|
|
function completionToEntry(c: Completion): ts.CompletionEntry {
|
|
|
|
return {kind: c.kind, name: c.name, sortText: c.sort, kindModifiers: ''};
|
|
|
|
}
|
2016-11-22 12:10:23 -05:00
|
|
|
|
2017-01-06 23:43:17 -05:00
|
|
|
function diagnosticToDiagnostic(d: Diagnostic, file: ts.SourceFile): ts.Diagnostic {
|
|
|
|
return {
|
|
|
|
file,
|
|
|
|
start: d.span.start,
|
|
|
|
length: d.span.end - d.span.start,
|
|
|
|
messageText: d.message,
|
|
|
|
category: ts.DiagnosticCategory.Error,
|
|
|
|
code: 0
|
|
|
|
};
|
|
|
|
}
|
2016-11-22 12:10:23 -05:00
|
|
|
|
2017-01-06 23:43:17 -05:00
|
|
|
function tryOperation(attempting: string, callback: () => void) {
|
|
|
|
try {
|
|
|
|
callback();
|
|
|
|
} catch (e) {
|
|
|
|
info.project.projectService.logger.info(`Failed to ${attempting}: ${e.toString()}`);
|
|
|
|
info.project.projectService.logger.info(`Stack trace: ${e.stack}`);
|
|
|
|
}
|
2016-11-22 12:10:23 -05:00
|
|
|
}
|
|
|
|
|
2017-01-06 23:43:17 -05:00
|
|
|
const serviceHost = new TypeScriptServiceHost(info.languageServiceHost, info.languageService);
|
2017-03-24 12:57:32 -04:00
|
|
|
const ls = createLanguageService(serviceHost as any);
|
2017-01-06 23:43:17 -05:00
|
|
|
serviceHost.setSite(ls);
|
|
|
|
|
|
|
|
proxy.getCompletionsAtPosition = function(fileName: string, position: number) {
|
|
|
|
let base = oldLS.getCompletionsAtPosition(fileName, position);
|
|
|
|
tryOperation('get completions', () => {
|
|
|
|
const results = ls.getCompletionsAt(fileName, position);
|
|
|
|
if (results && results.length) {
|
|
|
|
if (base === undefined) {
|
2017-01-24 12:05:34 -05:00
|
|
|
base = {
|
|
|
|
isGlobalCompletion: false,
|
|
|
|
isMemberCompletion: false,
|
|
|
|
isNewIdentifierLocation: false,
|
|
|
|
entries: []
|
|
|
|
};
|
2017-01-06 23:43:17 -05:00
|
|
|
}
|
|
|
|
for (const entry of results) {
|
|
|
|
base.entries.push(completionToEntry(entry));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return base;
|
|
|
|
};
|
|
|
|
|
|
|
|
proxy.getQuickInfoAtPosition = function(fileName: string, position: number): ts.QuickInfo {
|
|
|
|
let base = oldLS.getQuickInfoAtPosition(fileName, position);
|
2017-04-14 19:04:59 -04:00
|
|
|
// TODO(vicb): the tags property has been removed in TS 2.2
|
|
|
|
const tags = (<any>base).tags;
|
2017-01-06 23:43:17 -05:00
|
|
|
tryOperation('get quick info', () => {
|
|
|
|
const ours = ls.getHoverAt(fileName, position);
|
|
|
|
if (ours) {
|
|
|
|
const displayParts: typeof base.displayParts = [];
|
|
|
|
for (const part of ours.text) {
|
2017-03-24 12:57:32 -04:00
|
|
|
displayParts.push({kind: part.language !, text: part.text});
|
2017-01-06 23:43:17 -05:00
|
|
|
}
|
2017-04-14 19:04:59 -04:00
|
|
|
base = <any>{
|
2017-01-06 23:43:17 -05:00
|
|
|
displayParts,
|
|
|
|
documentation: [],
|
|
|
|
kind: 'angular',
|
|
|
|
kindModifiers: 'what does this do?',
|
2017-01-24 12:05:34 -05:00
|
|
|
textSpan: {start: ours.span.start, length: ours.span.end - ours.span.start},
|
2017-01-06 23:43:17 -05:00
|
|
|
};
|
2017-04-14 19:04:59 -04:00
|
|
|
if (tags) {
|
|
|
|
(<any>base).tags = tags;
|
|
|
|
}
|
2016-11-22 12:10:23 -05:00
|
|
|
}
|
2017-01-06 23:43:17 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
return base;
|
|
|
|
};
|
|
|
|
|
|
|
|
proxy.getSemanticDiagnostics = function(fileName: string) {
|
|
|
|
let base = oldLS.getSemanticDiagnostics(fileName);
|
|
|
|
if (base === undefined) {
|
|
|
|
base = [];
|
2016-11-22 12:10:23 -05:00
|
|
|
}
|
2017-01-06 23:43:17 -05:00
|
|
|
tryOperation('get diagnostics', () => {
|
|
|
|
info.project.projectService.logger.info(`Computing Angular semantic diagnostics...`);
|
|
|
|
const ours = ls.getDiagnostics(fileName);
|
|
|
|
if (ours && ours.length) {
|
|
|
|
const file = oldLS.getProgram().getSourceFile(fileName);
|
|
|
|
base.push.apply(base, ours.map(d => diagnosticToDiagnostic(d, file)));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return base;
|
|
|
|
};
|
2016-11-22 12:10:23 -05:00
|
|
|
|
2017-01-06 23:43:17 -05:00
|
|
|
proxy.getDefinitionAtPosition = function(
|
|
|
|
fileName: string, position: number): ts.DefinitionInfo[] {
|
|
|
|
let base = oldLS.getDefinitionAtPosition(fileName, position);
|
|
|
|
if (base && base.length) {
|
|
|
|
return base;
|
2016-11-22 12:10:23 -05:00
|
|
|
}
|
2017-01-06 23:43:17 -05:00
|
|
|
|
|
|
|
tryOperation('get definition', () => {
|
|
|
|
const ours = ls.getDefinitionAt(fileName, position);
|
|
|
|
if (ours && ours.length) {
|
|
|
|
base = base || [];
|
|
|
|
for (const loc of ours) {
|
|
|
|
base.push({
|
|
|
|
fileName: loc.fileName,
|
|
|
|
textSpan: {start: loc.span.start, length: loc.span.end - loc.span.start},
|
|
|
|
name: '',
|
|
|
|
kind: 'definition',
|
|
|
|
containerName: loc.fileName,
|
|
|
|
containerKind: 'file'
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return base;
|
|
|
|
};
|
|
|
|
|
|
|
|
return proxy;
|
|
|
|
}
|