Switch LanguageService from a class to a function
This commit is contained in:
parent
b4d5b984ed
commit
52688af148
|
@ -381,46 +381,37 @@ module TypeScript.Services {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LanguageService implements ILanguageService {
|
export function createLanguageService(host: ILanguageServiceHost, documentRegistry: IDocumentRegistry) :ILanguageService{
|
||||||
private logger: TypeScript.ILogger;
|
var logger: TypeScript.ILogger = host;
|
||||||
private _syntaxTreeCache: SyntaxTreeCache;
|
var _syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host);
|
||||||
private formattingRulesProvider: Formatting.RulesProvider;
|
var formattingRulesProvider: Formatting.RulesProvider;
|
||||||
|
var hostCache: HostCache; // A cache of all the information about the files on the host side.
|
||||||
// A cache of all the information about the files on the host side.
|
var program: ts.Program;
|
||||||
private hostCache: HostCache = null;
|
var typeChecker: ts.TypeChecker;
|
||||||
private program: ts.Program;
|
var useCaseSensitivefilenames = false;
|
||||||
private typeChecker: ts.TypeChecker;
|
var documentsByName: ts.Map<Document> = {};
|
||||||
private useCaseSensitivefilenames = false;
|
var documentRegistry = documentRegistry;
|
||||||
private documentsByName: ts.Map<Document> = {};
|
var cancellationToken = new CancellationToken(host.getCancellationToken());
|
||||||
private documentRegistry: IDocumentRegistry
|
var activeCompletionSession: CompletionSession;
|
||||||
private cancellationToken: CancellationToken;
|
|
||||||
private activeCompletionSession: CompletionSession;
|
|
||||||
|
|
||||||
constructor(public host: ILanguageServiceHost, documentRegistry: IDocumentRegistry) {
|
|
||||||
this.logger = this.host;
|
|
||||||
this.cancellationToken = new CancellationToken(this.host.getCancellationToken());
|
|
||||||
this.documentRegistry = documentRegistry;
|
|
||||||
this._syntaxTreeCache = new SyntaxTreeCache(this.host);
|
|
||||||
|
|
||||||
// Check if the localized messages json is set, otherwise query the host for it
|
// Check if the localized messages json is set, otherwise query the host for it
|
||||||
if (!TypeScript.LocalizedDiagnosticMessages) {
|
if (!TypeScript.LocalizedDiagnosticMessages) {
|
||||||
TypeScript.LocalizedDiagnosticMessages = this.host.getLocalizedDiagnosticMessages();
|
TypeScript.LocalizedDiagnosticMessages = host.getLocalizedDiagnosticMessages();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createCompilerHost(): ts.CompilerHost {
|
function createCompilerHost(): ts.CompilerHost {
|
||||||
return {
|
return {
|
||||||
getSourceFile: (filename, languageVersion) => {
|
getSourceFile: (filename, languageVersion) => {
|
||||||
var document = this.documentsByName[filename];
|
var document = documentsByName[filename];
|
||||||
|
|
||||||
Debug.assert(!!document, "document can not be undefined");
|
Debug.assert(!!document, "document can not be undefined");
|
||||||
|
|
||||||
return document.sourceFile();
|
return document.sourceFile();
|
||||||
},
|
},
|
||||||
getCancellationToken: () => this.cancellationToken,
|
getCancellationToken: () => cancellationToken,
|
||||||
getCanonicalFileName: (filename) => this.useCaseSensitivefilenames ? filename : filename.toLowerCase(),
|
getCanonicalFileName: (filename) => useCaseSensitivefilenames ? filename : filename.toLowerCase(),
|
||||||
useCaseSensitiveFileNames: () => this.useCaseSensitivefilenames,
|
useCaseSensitiveFileNames: () => useCaseSensitivefilenames,
|
||||||
getNewLine: ()=> "\n",
|
getNewLine: () => "\n",
|
||||||
// Need something that doesn't depend on sys.ts here
|
// Need something that doesn't depend on sys.ts here
|
||||||
getDefaultLibFilename: (): string => {
|
getDefaultLibFilename: (): string => {
|
||||||
throw Error("TOD:: getDefaultLibfilename");
|
throw Error("TOD:: getDefaultLibfilename");
|
||||||
|
@ -434,20 +425,20 @@ module TypeScript.Services {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronizeHostData(): void {
|
function synchronizeHostData(): void {
|
||||||
// Reset the cache at start of every refresh
|
// Reset the cache at start of every refresh
|
||||||
this.hostCache = new HostCache(this.host);
|
hostCache = new HostCache(host);
|
||||||
|
|
||||||
var compilationSettings = this.hostCache.compilationSettings();
|
var compilationSettings = hostCache.compilationSettings();
|
||||||
|
|
||||||
// TODO: check if we need to create a new compiler to start with
|
// TODO: check if we need to create a new compiler to start with
|
||||||
// 1. files are identical
|
// 1. files are identical
|
||||||
// 2. compilation settings are identical
|
// 2. compilation settings are identical
|
||||||
|
|
||||||
// Now, remove any files from the compiler that are no longer in the host.
|
// Now, remove any files from the compiler that are no longer in the host.
|
||||||
var oldProgram = this.program;
|
var oldProgram = program;
|
||||||
if (oldProgram) {
|
if (oldProgram) {
|
||||||
var oldSettings = this.program.getCompilerOptions();
|
var oldSettings = program.getCompilerOptions();
|
||||||
|
|
||||||
// If the language version changed, then that affects what types of things we parse. So
|
// If the language version changed, then that affects what types of things we parse. So
|
||||||
// we have to dump all syntax trees.
|
// we have to dump all syntax trees.
|
||||||
|
@ -456,14 +447,14 @@ module TypeScript.Services {
|
||||||
|
|
||||||
var changesInCompilationSettingsAffectSyntax =
|
var changesInCompilationSettingsAffectSyntax =
|
||||||
oldSettings && compilationSettings && !compareDataObjects(oldSettings, compilationSettings) && settingsChangeAffectsSyntax;
|
oldSettings && compilationSettings && !compareDataObjects(oldSettings, compilationSettings) && settingsChangeAffectsSyntax;
|
||||||
var oldSourceFiles = this.program.getSourceFiles();
|
var oldSourceFiles = program.getSourceFiles();
|
||||||
|
|
||||||
for (var i = 0, n = oldSourceFiles.length; i < n; i++) {
|
for (var i = 0, n = oldSourceFiles.length; i < n; i++) {
|
||||||
this.cancellationToken.throwIfCancellationRequested();
|
cancellationToken.throwIfCancellationRequested();
|
||||||
var filename = oldSourceFiles[i].filename;
|
var filename = oldSourceFiles[i].filename;
|
||||||
if (!this.hostCache.contains(filename) || changesInCompilationSettingsAffectSyntax) {
|
if (!hostCache.contains(filename) || changesInCompilationSettingsAffectSyntax) {
|
||||||
this.documentRegistry.releaseDocument(filename, oldSettings);
|
documentRegistry.releaseDocument(filename, oldSettings);
|
||||||
delete this.documentsByName[filename];
|
delete documentsByName[filename];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -471,16 +462,16 @@ module TypeScript.Services {
|
||||||
// Now, for every file the host knows about, either add the file (if the compiler
|
// Now, for every file the host knows about, either add the file (if the compiler
|
||||||
// doesn't know about it.). Or notify the compiler about any changes (if it does
|
// doesn't know about it.). Or notify the compiler about any changes (if it does
|
||||||
// know about it.)
|
// know about it.)
|
||||||
var hostfilenames = this.hostCache.getfilenames();
|
var hostfilenames = hostCache.getfilenames();
|
||||||
|
|
||||||
for (var i = 0, n = hostfilenames.length; i < n; i++) {
|
for (var i = 0, n = hostfilenames.length; i < n; i++) {
|
||||||
var filename = hostfilenames[i];
|
var filename = hostfilenames[i];
|
||||||
|
|
||||||
var version = this.hostCache.getVersion(filename);
|
var version = hostCache.getVersion(filename);
|
||||||
var isOpen = this.hostCache.isOpen(filename);
|
var isOpen = hostCache.isOpen(filename);
|
||||||
var scriptSnapshot = this.hostCache.getScriptSnapshot(filename);
|
var scriptSnapshot = hostCache.getScriptSnapshot(filename);
|
||||||
|
|
||||||
var document: Document = this.documentsByName[filename];
|
var document: Document = documentsByName[filename];
|
||||||
if (document) {
|
if (document) {
|
||||||
//
|
//
|
||||||
// If the document is the same, assume no update
|
// If the document is the same, assume no update
|
||||||
|
@ -497,64 +488,53 @@ module TypeScript.Services {
|
||||||
// new text buffer).
|
// new text buffer).
|
||||||
var textChangeRange: TextChangeRange = null;
|
var textChangeRange: TextChangeRange = null;
|
||||||
if (document.isOpen && isOpen) {
|
if (document.isOpen && isOpen) {
|
||||||
textChangeRange = this.hostCache.getScriptTextChangeRangeSinceVersion(filename, document.version);
|
textChangeRange = hostCache.getScriptTextChangeRangeSinceVersion(filename, document.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
document = this.documentRegistry.updateDocument(document, filename, compilationSettings, scriptSnapshot, version, isOpen, textChangeRange);
|
document = documentRegistry.updateDocument(document, filename, compilationSettings, scriptSnapshot, version, isOpen, textChangeRange);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
document = this.documentRegistry.acquireDocument(filename, compilationSettings, scriptSnapshot, this.hostCache.getByteOrderMark(filename), version, isOpen, []);
|
document = documentRegistry.acquireDocument(filename, compilationSettings, scriptSnapshot, hostCache.getByteOrderMark(filename), version, isOpen, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remeber the new document
|
// Remeber the new document
|
||||||
this.documentsByName[filename] = document;
|
documentsByName[filename] = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now create a new compiler
|
// Now create a new compiler
|
||||||
this.program = ts.createProgram(hostfilenames, compilationSettings, this.createCompilerHost());
|
program = ts.createProgram(hostfilenames, compilationSettings, createCompilerHost());
|
||||||
this.typeChecker = this.program.getTypeChecker();
|
typeChecker = program.getTypeChecker();
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
function dispose(): void {
|
||||||
if (this.program) {
|
if (program) {
|
||||||
ts.forEach(this.program.getSourceFiles(),
|
ts.forEach(program.getSourceFiles(),
|
||||||
(f) => this.documentRegistry.releaseDocument(f.filename, this.program.getCompilerOptions()));
|
(f) => documentRegistry.releaseDocument(f.filename, program.getCompilerOptions()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
/// Diagnostics
|
||||||
// No-op. Only kept around for compatability with the interface we shipped.
|
function getSyntacticDiagnostics(filename: string) {
|
||||||
}
|
synchronizeHostData();
|
||||||
cleanupSemanticCache() { }
|
return program.getDiagnostics(program.getSourceFile(filename));
|
||||||
|
|
||||||
getSyntacticDiagnostics(filename: string) {
|
|
||||||
this.synchronizeHostData();
|
|
||||||
return this.program.getDiagnostics(this.program.getSourceFile(filename));
|
|
||||||
}
|
|
||||||
getSemanticDiagnostics(filename: string) {
|
|
||||||
this.synchronizeHostData();
|
|
||||||
return this.typeChecker.getDiagnostics(this.program.getSourceFile(filename));
|
|
||||||
}
|
|
||||||
getCompilerOptionsDiagnostics() {
|
|
||||||
this.synchronizeHostData();
|
|
||||||
return this.program.getGlobalDiagnostics();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCompletionEntriesFromSymbols(symbols: ts.Symbol[], session:CompletionSession): void {
|
function getSemanticDiagnostics(filename: string) {
|
||||||
ts.forEach(symbols, (symbol) => {
|
synchronizeHostData();
|
||||||
var entry = this.createCompletionEntry(symbol);
|
return typeChecker.getDiagnostics(program.getSourceFile(filename));
|
||||||
if (entry) {
|
|
||||||
session.entries.push(entry);
|
|
||||||
session.symbols[entry.name] = symbol;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private createCompletionEntry(symbol: ts.Symbol): CompletionEntry {
|
function getCompilerOptionsDiagnostics() {
|
||||||
|
synchronizeHostData();
|
||||||
|
return program.getGlobalDiagnostics();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Completion
|
||||||
|
function createCompletionEntry(symbol: ts.Symbol): CompletionEntry {
|
||||||
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
// Try to get a valid display name for this symbol, if we could not find one, then ignore it.
|
||||||
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
// We would like to only show things that can be added after a dot, so for instance numeric properties can
|
||||||
// not be accessed with a dot (a.1 <- invalid)
|
// not be accessed with a dot (a.1 <- invalid)
|
||||||
var displayName = CompletionHelpers.getValidCompletionEntryDisplayName(symbol.getName(), this.program.getCompilerOptions().target);
|
var displayName = CompletionHelpers.getValidCompletionEntryDisplayName(symbol.getName(), program.getCompilerOptions().target);
|
||||||
if (!displayName) {
|
if (!displayName) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
@ -563,21 +543,31 @@ module TypeScript.Services {
|
||||||
var firstDeclaration = [0];
|
var firstDeclaration = [0];
|
||||||
return {
|
return {
|
||||||
name: displayName,
|
name: displayName,
|
||||||
kind: this.getSymbolKind(symbol),
|
kind: getSymbolKind(symbol),
|
||||||
kindModifiers: declarations ? this.getNodeModifiers(declarations[0]) : ScriptElementKindModifier.none
|
kindModifiers: declarations ? getNodeModifiers(declarations[0]) : ScriptElementKindModifier.none
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getCompletionsAtPosition(filename: string, position: number, isMemberCompletion: boolean) {
|
function getCompletionsAtPosition(filename: string, position: number, isMemberCompletion: boolean) {
|
||||||
this.synchronizeHostData();
|
function getCompletionEntriesFromSymbols(symbols: ts.Symbol[], session: CompletionSession): void {
|
||||||
|
ts.forEach(symbols, (symbol) => {
|
||||||
|
var entry = createCompletionEntry(symbol);
|
||||||
|
if (entry) {
|
||||||
|
session.entries.push(entry);
|
||||||
|
session.symbols[entry.name] = symbol;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronizeHostData();
|
||||||
|
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var document = this.documentsByName[filename];
|
var document = documentsByName[filename];
|
||||||
var sourceUnit = document.sourceUnit();
|
var sourceUnit = document.sourceUnit();
|
||||||
|
|
||||||
if (CompletionHelpers.isCompletionListBlocker(document.syntaxTree().sourceUnit(), position)) {
|
if (CompletionHelpers.isCompletionListBlocker(document.syntaxTree().sourceUnit(), position)) {
|
||||||
this.logger.log("Returning an empty list because completion was blocked.");
|
logger.log("Returning an empty list because completion was blocked.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,30 +612,30 @@ module TypeScript.Services {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node
|
// TODO: this is a hack for now, we need a proper walking mechanism to verify that we have the correct node
|
||||||
var mappedNode = this.getNodeAtPosition(document.sourceFile(), end(node) - 1);
|
var mappedNode = getNodeAtPosition(document.sourceFile(), end(node) - 1);
|
||||||
|
|
||||||
Debug.assert(mappedNode, "Could not map a Fidelity node to an AST node");
|
Debug.assert(mappedNode, "Could not map a Fidelity node to an AST node");
|
||||||
|
|
||||||
// Get the completions
|
// Get the completions
|
||||||
this.activeCompletionSession = {
|
activeCompletionSession = {
|
||||||
filename: filename,
|
filename: filename,
|
||||||
position: position,
|
position: position,
|
||||||
entries: [],
|
entries: [],
|
||||||
symbols: {},
|
symbols: {},
|
||||||
location: mappedNode,
|
location: mappedNode,
|
||||||
typeChecker: this.typeChecker
|
typeChecker: typeChecker
|
||||||
};
|
};
|
||||||
|
|
||||||
// Right of dot member completion list
|
// Right of dot member completion list
|
||||||
if (isRightOfDot) {
|
if (isRightOfDot) {
|
||||||
var type: ts.Type = this.typeChecker.getTypeOfExpression(mappedNode);
|
var type: ts.Type = typeChecker.getTypeOfExpression(mappedNode);
|
||||||
if (!type) {
|
if (!type) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var symbols = type.getApparentProperties();
|
var symbols = type.getApparentProperties();
|
||||||
isMemberCompletion = true;
|
isMemberCompletion = true;
|
||||||
this.getCompletionEntriesFromSymbols(symbols, this.activeCompletionSession);
|
getCompletionEntriesFromSymbols(symbols, activeCompletionSession);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var containingObjectLiteral = CompletionHelpers.getContainingObjectLiteralApplicableForCompletion(document.syntaxTree().sourceUnit(), position);
|
var containingObjectLiteral = CompletionHelpers.getContainingObjectLiteralApplicableForCompletion(document.syntaxTree().sourceUnit(), position);
|
||||||
|
@ -669,13 +659,13 @@ module TypeScript.Services {
|
||||||
isMemberCompletion = true;
|
isMemberCompletion = true;
|
||||||
|
|
||||||
//// Try to get the object members form contextual typing
|
//// Try to get the object members form contextual typing
|
||||||
//var contextualMembers = this.compiler.getContextualMembersFromAST(node, document);
|
//var contextualMembers = compiler.getContextualMembersFromAST(node, document);
|
||||||
//if (contextualMembers && contextualMembers.symbols && contextualMembers.symbols.length > 0) {
|
//if (contextualMembers && contextualMembers.symbols && contextualMembers.symbols.length > 0) {
|
||||||
// // get existing members
|
// // get existing members
|
||||||
// var existingMembers = this.compiler.getVisibleMemberSymbolsFromAST(node, document);
|
// var existingMembers = compiler.getVisibleMemberSymbolsFromAST(node, document);
|
||||||
|
|
||||||
// // Add filtterd items to the completion list
|
// // Add filtterd items to the completion list
|
||||||
// this.getCompletionEntriesFromSymbols({
|
// getCompletionEntriesFromSymbols({
|
||||||
// symbols: CompletionHelpers.filterContextualMembersList(contextualMembers.symbols, existingMembers, filename, position),
|
// symbols: CompletionHelpers.filterContextualMembersList(contextualMembers.symbols, existingMembers, filename, position),
|
||||||
// enclosingScopeSymbol: contextualMembers.enclosingScopeSymbol
|
// enclosingScopeSymbol: contextualMembers.enclosingScopeSymbol
|
||||||
// }, entries);
|
// }, entries);
|
||||||
|
@ -686,45 +676,46 @@ module TypeScript.Services {
|
||||||
isMemberCompletion = false;
|
isMemberCompletion = false;
|
||||||
/// TODO filter meaning based on the current context
|
/// TODO filter meaning based on the current context
|
||||||
var symbolMeanings = ts.SymbolFlags.Type | ts.SymbolFlags.Value | ts.SymbolFlags.Namespace;
|
var symbolMeanings = ts.SymbolFlags.Type | ts.SymbolFlags.Value | ts.SymbolFlags.Namespace;
|
||||||
var symbols = this.typeChecker.getSymbolsInScope(mappedNode, symbolMeanings);
|
var symbols = typeChecker.getSymbolsInScope(mappedNode, symbolMeanings);
|
||||||
|
|
||||||
this.getCompletionEntriesFromSymbols(symbols, this.activeCompletionSession);
|
getCompletionEntriesFromSymbols(symbols, activeCompletionSession);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add keywords if this is not a member completion list
|
// Add keywords if this is not a member completion list
|
||||||
if (!isMemberCompletion) {
|
if (!isMemberCompletion) {
|
||||||
Array.prototype.push.apply(this.activeCompletionSession.entries, KeywordCompletions.getKeywordCompltions());
|
Array.prototype.push.apply(activeCompletionSession.entries, KeywordCompletions.getKeywordCompltions());
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isMemberCompletion: isMemberCompletion,
|
isMemberCompletion: isMemberCompletion,
|
||||||
entries: this.activeCompletionSession.entries
|
entries: activeCompletionSession.entries
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
getCompletionEntryDetails(filename: string, position: number, entryName: string) {
|
|
||||||
|
function getCompletionEntryDetails(filename: string, position: number, entryName: string) {
|
||||||
// Note: No need to call synchronizeHostData, as we have captured all the data we need
|
// Note: No need to call synchronizeHostData, as we have captured all the data we need
|
||||||
// in the getCompletionsAtPosition erlier
|
// in the getCompletionsAtPosition erlier
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var session = this.activeCompletionSession;
|
var session = activeCompletionSession;
|
||||||
|
|
||||||
// Ensure that the current active completion session is still valid for this request
|
// Ensure that the current active completion session is still valid for this request
|
||||||
if (!session || session.filename !== filename || session.position !== position) {
|
if (!session || session.filename !== filename || session.position !== position) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
var symbol = this.activeCompletionSession.symbols[entryName];
|
var symbol = activeCompletionSession.symbols[entryName];
|
||||||
if (symbol) {
|
if (symbol) {
|
||||||
var type = session.typeChecker.getTypeOfSymbol(symbol);
|
var type = session.typeChecker.getTypeOfSymbol(symbol);
|
||||||
Debug.assert(type, "Could not find type for symbol");
|
Debug.assert(type, "Could not find type for symbol");
|
||||||
var completionEntry = this.createCompletionEntry(symbol);
|
var completionEntry = createCompletionEntry(symbol);
|
||||||
return {
|
return {
|
||||||
name: entryName,
|
name: entryName,
|
||||||
kind: completionEntry.kind,
|
kind: completionEntry.kind,
|
||||||
kindModifiers: completionEntry.kindModifiers,
|
kindModifiers: completionEntry.kindModifiers,
|
||||||
type: session.typeChecker.typeToString(type, session.location),
|
type: session.typeChecker.typeToString(type, session.location),
|
||||||
fullSymbolName: this.typeChecker.symbolToString(symbol, session.location),
|
fullSymbolName: typeChecker.symbolToString(symbol, session.location),
|
||||||
docComment: ""
|
docComment: ""
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -741,7 +732,7 @@ module TypeScript.Services {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getNodeAtPosition(sourceFile: ts.SourceFile, position: number) {
|
function getNodeAtPosition(sourceFile: ts.SourceFile, position: number) {
|
||||||
var current: ts.Node = sourceFile;
|
var current: ts.Node = sourceFile;
|
||||||
outer: while (true) {
|
outer: while (true) {
|
||||||
// find the child that has this
|
// find the child that has this
|
||||||
|
@ -757,7 +748,7 @@ module TypeScript.Services {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEnclosingDeclaration(node: ts.Node): ts.Node {
|
function getEnclosingDeclaration(node: ts.Node): ts.Node {
|
||||||
while (true) {
|
while (true) {
|
||||||
node = node.parent;
|
node = node.parent;
|
||||||
if (!node) {
|
if (!node) {
|
||||||
|
@ -778,7 +769,7 @@ module TypeScript.Services {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getSymbolKind(symbol: ts.Symbol): string {
|
function getSymbolKind(symbol: ts.Symbol): string {
|
||||||
var flags = symbol.getFlags();
|
var flags = symbol.getFlags();
|
||||||
|
|
||||||
if (flags & ts.SymbolFlags.Module) return ScriptElementKind.moduleElement;
|
if (flags & ts.SymbolFlags.Module) return ScriptElementKind.moduleElement;
|
||||||
|
@ -801,7 +792,7 @@ module TypeScript.Services {
|
||||||
return ScriptElementKind.unknown;
|
return ScriptElementKind.unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeKind(type: ts.Type): string {
|
function getTypeKind(type: ts.Type): string {
|
||||||
var flags = type.getFlags();
|
var flags = type.getFlags();
|
||||||
|
|
||||||
if (flags & ts.TypeFlags.Enum) return ScriptElementKind.enumElement;
|
if (flags & ts.TypeFlags.Enum) return ScriptElementKind.enumElement;
|
||||||
|
@ -814,7 +805,7 @@ module TypeScript.Services {
|
||||||
return ScriptElementKind.unknown;
|
return ScriptElementKind.unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeModifiers(node: ts.Node): string {
|
function getNodeModifiers(node: ts.Node): string {
|
||||||
var flags = node.flags;
|
var flags = node.flags;
|
||||||
var result: string[] = [];
|
var result: string[] = [];
|
||||||
|
|
||||||
|
@ -827,12 +818,13 @@ module TypeScript.Services {
|
||||||
return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none;
|
return result.length > 0 ? result.join(',') : ScriptElementKindModifier.none;
|
||||||
}
|
}
|
||||||
|
|
||||||
getTypeAtPosition(filename: string, position: number): TypeInfo {
|
/// QuickInfo
|
||||||
this.synchronizeHostData();
|
function getTypeAtPosition(filename: string, position: number): TypeInfo {
|
||||||
|
synchronizeHostData();
|
||||||
|
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
var document = this.documentsByName[filename];
|
var document = documentsByName[filename];
|
||||||
var node = this.getNodeAtPosition(document.sourceFile(), position);
|
var node = getNodeAtPosition(document.sourceFile(), position);
|
||||||
if (!node) return undefined;
|
if (!node) return undefined;
|
||||||
|
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
|
@ -842,15 +834,15 @@ module TypeScript.Services {
|
||||||
// TODO: handle new and call expressions
|
// TODO: handle new and call expressions
|
||||||
}
|
}
|
||||||
|
|
||||||
var symbol = this.typeChecker. getSymbolOfIdentifier(<ts.Identifier>node);
|
var symbol = typeChecker. getSymbolOfIdentifier(<ts.Identifier>node);
|
||||||
Debug.assert(symbol, "getTypeAtPosition: Could not find symbol for node");
|
Debug.assert(symbol, "getTypeAtPosition: Could not find symbol for node");
|
||||||
var type = this.typeChecker.getTypeOfSymbol(symbol);
|
var type = typeChecker.getTypeOfSymbol(symbol);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
memberName: new MemberNameString(this.typeChecker.typeToString(type)),
|
memberName: new MemberNameString(typeChecker.typeToString(type)),
|
||||||
docComment: "",
|
docComment: "",
|
||||||
fullSymbolName: this.typeChecker.symbolToString(symbol, this.getEnclosingDeclaration(node)),
|
fullSymbolName: typeChecker.symbolToString(symbol, getEnclosingDeclaration(node)),
|
||||||
kind: this.getSymbolKind(symbol),
|
kind: getSymbolKind(symbol),
|
||||||
minChar: node.pos,
|
minChar: node.pos,
|
||||||
limChar: node.end
|
limChar: node.end
|
||||||
};
|
};
|
||||||
|
@ -860,43 +852,29 @@ module TypeScript.Services {
|
||||||
case ts.SyntaxKind.QualifiedName:
|
case ts.SyntaxKind.QualifiedName:
|
||||||
case ts.SyntaxKind.SuperKeyword:
|
case ts.SyntaxKind.SuperKeyword:
|
||||||
case ts.SyntaxKind.StringLiteral:
|
case ts.SyntaxKind.StringLiteral:
|
||||||
var type = this.typeChecker.getTypeOfExpression(node);
|
var type = typeChecker.getTypeOfExpression(node);
|
||||||
Debug.assert(type, "getTypeAtPosition: Could not find type for node");
|
Debug.assert(type, "getTypeAtPosition: Could not find type for node");
|
||||||
return {
|
return {
|
||||||
memberName: new MemberNameString(""),
|
memberName: new MemberNameString(""),
|
||||||
docComment: "",
|
docComment: "",
|
||||||
fullSymbolName: this.typeChecker.typeToString(type, this.getEnclosingDeclaration(node)),
|
fullSymbolName: typeChecker.typeToString(type, getEnclosingDeclaration(node)),
|
||||||
kind: this.getTypeKind(type),
|
kind: getTypeKind(type),
|
||||||
minChar: node.pos,
|
minChar: node.pos,
|
||||||
limChar: node.end
|
limChar: node.end
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getSignatureAtPosition(filename: string, position: number) {
|
|
||||||
return undefined;
|
/// Syntactic features
|
||||||
}
|
function getSyntaxTree(filename: string): TypeScript.SyntaxTree {
|
||||||
getDefinitionAtPosition(filename: string, position: number) {
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
return [];
|
return _syntaxTreeCache.getCurrentFileSyntaxTree(filename);
|
||||||
}
|
|
||||||
getReferencesAtPosition(filename: string, position: number) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
getOccurrencesAtPosition(filename: string, position: number) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
getImplementorsAtPosition(filename: string, position: number) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
getNavigateToItems(searchValue: string) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
getEmitOutput(filename: string) {
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTypeInfoEligiblePath(filename: string, position: number, isConstructorValidPosition: boolean) {
|
function getNameOrDottedNameSpan(filename: string, startPos: number, endPos: number): SpanInfo {
|
||||||
var sourceUnit = this._syntaxTreeCache.getCurrentFileSyntaxTree(filename).sourceUnit();
|
function getTypeInfoEligiblePath(filename: string, position: number, isConstructorValidPosition: boolean) {
|
||||||
|
var sourceUnit = _syntaxTreeCache.getCurrentFileSyntaxTree(filename).sourceUnit();
|
||||||
|
|
||||||
var ast = TypeScript.ASTHelpers.getAstAtPosition(sourceUnit, position, /*useTrailingTriviaAsLimChar*/ false, /*forceInclusive*/ true);
|
var ast = TypeScript.ASTHelpers.getAstAtPosition(sourceUnit, position, /*useTrailingTriviaAsLimChar*/ false, /*forceInclusive*/ true);
|
||||||
if (ast === null) {
|
if (ast === null) {
|
||||||
|
@ -929,10 +907,10 @@ module TypeScript.Services {
|
||||||
return ast;
|
return ast;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getNameOrDottedNameSpan(filename: string, startPos: number, endPos: number): SpanInfo {
|
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var node = this.getTypeInfoEligiblePath(filename, startPos, false);
|
var node = getTypeInfoEligiblePath(filename, startPos, false);
|
||||||
if (!node) return null;
|
if (!node) return null;
|
||||||
|
|
||||||
while (node) {
|
while (node) {
|
||||||
|
@ -950,93 +928,129 @@ module TypeScript.Services {
|
||||||
limChar: end(node)
|
limChar: end(node)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
getBreakpointStatementAtPosition(filename: string, position: number) {
|
|
||||||
|
function getBreakpointStatementAtPosition(filename: string, position: number) {
|
||||||
// doesn't use compiler - no need to synchronize with host
|
// doesn't use compiler - no need to synchronize with host
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var syntaxtree = this.getSyntaxTree(filename);
|
var syntaxtree = getSyntaxTree(filename);
|
||||||
return TypeScript.Services.Breakpoints.getBreakpointLocation(syntaxtree, position);
|
return TypeScript.Services.Breakpoints.getBreakpointLocation(syntaxtree, position);
|
||||||
}
|
}
|
||||||
getScriptLexicalStructure(filename: string) {
|
|
||||||
|
function getScriptLexicalStructure(filename: string) {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
var syntaxTree = this.getSyntaxTree(filename);
|
var syntaxTree = getSyntaxTree(filename);
|
||||||
var items: NavigateToItem[] = [];
|
var items: NavigateToItem[] = [];
|
||||||
GetScriptLexicalStructureWalker.getListsOfAllScriptLexicalStructure(items, filename, syntaxTree.sourceUnit());
|
GetScriptLexicalStructureWalker.getListsOfAllScriptLexicalStructure(items, filename, syntaxTree.sourceUnit());
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
getOutliningRegions(filename: string) {
|
|
||||||
|
function getOutliningRegions(filename: string) {
|
||||||
// doesn't use compiler - no need to synchronize with host
|
// doesn't use compiler - no need to synchronize with host
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
var syntaxTree = this.getSyntaxTree(filename);
|
var syntaxTree = getSyntaxTree(filename);
|
||||||
return OutliningElementsCollector.collectElements(syntaxTree.sourceUnit());
|
return OutliningElementsCollector.collectElements(syntaxTree.sourceUnit());
|
||||||
}
|
}
|
||||||
getBraceMatchingAtPosition(filename: string, position: number) {
|
|
||||||
|
function getBraceMatchingAtPosition(filename: string, position: number) {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
var syntaxTree = this.getSyntaxTree(filename);
|
var syntaxTree = getSyntaxTree(filename);
|
||||||
return BraceMatcher.getMatchSpans(syntaxTree, position);
|
return BraceMatcher.getMatchSpans(syntaxTree, position);
|
||||||
}
|
}
|
||||||
getIndentationAtPosition(filename: string, position: number, editorOptions: EditorOptions) {
|
|
||||||
|
function getIndentationAtPosition(filename: string, position: number, editorOptions: EditorOptions) {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var syntaxTree = this.getSyntaxTree(filename);
|
var syntaxTree = getSyntaxTree(filename);
|
||||||
|
|
||||||
var scriptSnapshot = this._syntaxTreeCache.getCurrentScriptSnapshot(filename);
|
var scriptSnapshot = _syntaxTreeCache.getCurrentScriptSnapshot(filename);
|
||||||
var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
|
var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
|
||||||
var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText);
|
var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText);
|
||||||
var options = new FormattingOptions(!editorOptions.ConvertTabsToSpaces, editorOptions.TabSize, editorOptions.IndentSize, editorOptions.NewLineCharacter)
|
var options = new FormattingOptions(!editorOptions.ConvertTabsToSpaces, editorOptions.TabSize, editorOptions.IndentSize, editorOptions.NewLineCharacter)
|
||||||
|
|
||||||
return TypeScript.Services.Formatting.SingleTokenIndenter.getIndentationAmount(position, syntaxTree.sourceUnit(), textSnapshot, options);
|
return TypeScript.Services.Formatting.SingleTokenIndenter.getIndentationAmount(position, syntaxTree.sourceUnit(), textSnapshot, options);
|
||||||
}
|
}
|
||||||
getFormattingEditsForRange(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] {
|
|
||||||
|
function getFormattingManager(filename: string, options: FormatCodeOptions) {
|
||||||
|
// Ensure rules are initialized and up to date wrt to formatting options
|
||||||
|
if (formattingRulesProvider == null) {
|
||||||
|
formattingRulesProvider = new TypeScript.Services.Formatting.RulesProvider(logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
formattingRulesProvider.ensureUpToDate(options);
|
||||||
|
|
||||||
|
// Get the Syntax Tree
|
||||||
|
var syntaxTree = getSyntaxTree(filename);
|
||||||
|
|
||||||
|
// Convert IScriptSnapshot to ITextSnapshot
|
||||||
|
var scriptSnapshot = _syntaxTreeCache.getCurrentScriptSnapshot(filename);
|
||||||
|
var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
|
||||||
|
var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText);
|
||||||
|
|
||||||
|
var manager = new TypeScript.Services.Formatting.FormattingManager(syntaxTree, textSnapshot, formattingRulesProvider, options);
|
||||||
|
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFormattingEditsForRange(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var manager = this.getFormattingManager(filename, options);
|
var manager = getFormattingManager(filename, options);
|
||||||
return manager.formatSelection(minChar, limChar);
|
return manager.formatSelection(minChar, limChar);
|
||||||
}
|
}
|
||||||
getFormattingEditsForDocument(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] {
|
|
||||||
|
function getFormattingEditsForDocument(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var manager = this.getFormattingManager(filename, options);
|
var manager = getFormattingManager(filename, options);
|
||||||
return manager.formatDocument(minChar, limChar);
|
return manager.formatDocument(minChar, limChar);
|
||||||
}
|
}
|
||||||
getFormattingEditsOnPaste(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] {
|
|
||||||
|
function getFormattingEditsOnPaste(filename: string, minChar: number, limChar: number, options: FormatCodeOptions): TextEdit[] {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var manager = this.getFormattingManager(filename, options);
|
var manager = getFormattingManager(filename, options);
|
||||||
return manager.formatOnPaste(minChar, limChar);
|
return manager.formatOnPaste(minChar, limChar);
|
||||||
}
|
}
|
||||||
getFormattingEditsAfterKeystroke(filename: string, position: number, key: string, options: FormatCodeOptions): TextEdit[] {
|
|
||||||
|
function getFormattingEditsAfterKeystroke(filename: string, position: number, key: string, options: FormatCodeOptions): TextEdit[] {
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
filename = TypeScript.switchToForwardSlashes(filename);
|
||||||
|
|
||||||
var manager = this.getFormattingManager(filename, options);
|
var manager = getFormattingManager(filename, options);
|
||||||
if (key === "}") return manager.formatOnClosingCurlyBrace(position);
|
if (key === "}") return manager.formatOnClosingCurlyBrace(position);
|
||||||
else if (key === ";") return manager.formatOnSemicolon(position);
|
else if (key === ";") return manager.formatOnSemicolon(position);
|
||||||
else if (key === "\n") return manager.formatOnEnter(position);
|
else if (key === "\n") return manager.formatOnEnter(position);
|
||||||
else return [];
|
else return [];
|
||||||
}
|
}
|
||||||
private getFormattingManager(filename: string, options: FormatCodeOptions) {
|
|
||||||
// Ensure rules are initialized and up to date wrt to formatting options
|
|
||||||
if (this.formattingRulesProvider == null) {
|
|
||||||
this.formattingRulesProvider = new TypeScript.Services.Formatting.RulesProvider(this.logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.formattingRulesProvider.ensureUpToDate(options);
|
|
||||||
|
|
||||||
// Get the Syntax Tree
|
return {
|
||||||
var syntaxTree = this.getSyntaxTree(filename);
|
dispose: dispose,
|
||||||
|
refresh: () => { },
|
||||||
// Convert IScriptSnapshot to ITextSnapshot
|
cleanupSemanticCache: () => { },
|
||||||
var scriptSnapshot = this._syntaxTreeCache.getCurrentScriptSnapshot(filename);
|
getSyntacticDiagnostics: getSyntacticDiagnostics,
|
||||||
var scriptText = TypeScript.SimpleText.fromScriptSnapshot(scriptSnapshot);
|
getSemanticDiagnostics: getSemanticDiagnostics,
|
||||||
var textSnapshot = new TypeScript.Services.Formatting.TextSnapshot(scriptText);
|
getCompilerOptionsDiagnostics: getCompilerOptionsDiagnostics,
|
||||||
|
getCompletionsAtPosition: getCompletionsAtPosition,
|
||||||
var manager = new TypeScript.Services.Formatting.FormattingManager(syntaxTree, textSnapshot, this.formattingRulesProvider, options);
|
getCompletionEntryDetails: getCompletionEntryDetails,
|
||||||
|
getTypeAtPosition: getTypeAtPosition,
|
||||||
return manager;
|
getSignatureAtPosition: (filename, position) => undefined,
|
||||||
}
|
getDefinitionAtPosition: (filename, position) => [],
|
||||||
getSyntaxTree(filename: string): TypeScript.SyntaxTree {
|
getReferencesAtPosition: (filename, position) => [],
|
||||||
filename = TypeScript.switchToForwardSlashes(filename);
|
getOccurrencesAtPosition: (filename, position) => [],
|
||||||
return this._syntaxTreeCache.getCurrentFileSyntaxTree(filename);
|
getImplementorsAtPosition: (filename, position) => [],
|
||||||
}
|
getNameOrDottedNameSpan: getNameOrDottedNameSpan,
|
||||||
|
getBreakpointStatementAtPosition: getBreakpointStatementAtPosition,
|
||||||
|
getNavigateToItems: (searchValue) => [],
|
||||||
|
getScriptLexicalStructure: getScriptLexicalStructure,
|
||||||
|
getOutliningRegions: getOutliningRegions,
|
||||||
|
getBraceMatchingAtPosition: getBraceMatchingAtPosition,
|
||||||
|
getIndentationAtPosition: getIndentationAtPosition,
|
||||||
|
getFormattingEditsForRange: getFormattingEditsForRange,
|
||||||
|
getFormattingEditsForDocument: getFormattingEditsForDocument,
|
||||||
|
getFormattingEditsOnPaste: getFormattingEditsOnPaste,
|
||||||
|
getFormattingEditsAfterKeystroke: getFormattingEditsAfterKeystroke,
|
||||||
|
getEmitOutput: (filename) => undefined,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -162,7 +162,7 @@ module TypeScript.Services {
|
||||||
|
|
||||||
public createPullLanguageService(host: TypeScript.Services.ILanguageServiceHost): TypeScript.Services.ILanguageService {
|
public createPullLanguageService(host: TypeScript.Services.ILanguageServiceHost): TypeScript.Services.ILanguageService {
|
||||||
try {
|
try {
|
||||||
return new TypeScript.Services.LanguageService(host, this.documentRegistry);
|
return TypeScript.Services.createLanguageService(host, this.documentRegistry);
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
TypeScript.Services.logInternalError(host, err);
|
TypeScript.Services.logInternalError(host, err);
|
||||||
|
|
Loading…
Reference in a new issue