Switch LanguageService from a class to a function

This commit is contained in:
Mohamed Hegazy 2014-07-24 11:24:09 -07:00
parent b4d5b984ed
commit 52688af148
2 changed files with 228 additions and 214 deletions

View file

@ -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,
};
} }
} }

View file

@ -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);