diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b2f3755ec46..f5155858b04 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -443,7 +443,7 @@ namespace ts { if (result && result.jsDocComment) { // because the jsDocComment was parsed out of the source file, it might // not be covered by the fixupParentReferences. - Parser.fixupParentReferences(result.jsDocComment); + fixupParentReferences(result.jsDocComment); } return result; @@ -455,6 +455,39 @@ namespace ts { return Parser.JSDocParser.parseJSDocTypeExpressionForTests(content, start, length); } + /* @internal */ + export function fixupParentReferences(rootNode: Node) { + // normally parent references are set during binding. However, for clients that only need + // a syntax tree, and no semantic features, then the binding process is an unnecessary + // overhead. This functions allows us to set all the parents, without all the expense of + // binding. + + let parent: Node = rootNode; + forEachChild(rootNode, visitNode); + return; + + function visitNode(n: Node): void { + // walk down setting parents that differ from the parent we think it should be. This + // allows us to quickly bail out of setting parents for subtrees during incremental + // parsing + if (n.parent !== parent) { + n.parent = parent; + + const saveParent = parent; + parent = n; + forEachChild(n, visitNode); + if (n.jsDocComments) { + for (const jsDocComment of n.jsDocComments) { + jsDocComment.parent = n; + parent = jsDocComment; + forEachChild(jsDocComment, visitNode); + } + } + parent = saveParent; + } + } + } + // Implement the parser as a singleton module. We do this for perf reasons because creating // parser instances can actually be expensive enough to impact us on projects with many source // files. @@ -658,38 +691,6 @@ namespace ts { return node; } - export function fixupParentReferences(rootNode: Node) { - // normally parent references are set during binding. However, for clients that only need - // a syntax tree, and no semantic features, then the binding process is an unnecessary - // overhead. This functions allows us to set all the parents, without all the expense of - // binding. - - let parent: Node = rootNode; - forEachChild(rootNode, visitNode); - return; - - function visitNode(n: Node): void { - // walk down setting parents that differ from the parent we think it should be. This - // allows us to quickly bail out of setting parents for subtrees during incremental - // parsing - if (n.parent !== parent) { - n.parent = parent; - - const saveParent = parent; - parent = n; - forEachChild(n, visitNode); - if (n.jsDocComments) { - for (const jsDocComment of n.jsDocComments) { - jsDocComment.parent = n; - parent = jsDocComment; - forEachChild(jsDocComment, visitNode); - } - } - parent = saveParent; - } - } - } - function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind: ScriptKind): SourceFile { // code from createNode is inlined here so createNode won't have to deal with special case of creating source files // this is quite rare comparing to other nodes and createNode should be as fast as possible diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 1977d95492c..6f895c37b19 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -367,7 +367,7 @@ namespace Utils { // call this on both nodes to ensure all propagated flags have been set (and thus can be // compared). assert.equal(ts.containsParseError(node1), ts.containsParseError(node2)); - assert.equal(node1.flags, node2.flags, "node1.flags !== node2.flags"); + assert.equal(node1.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, node2.flags & ~ts.NodeFlags.ReachabilityAndEmitFlags, "node1.flags !== node2.flags"); ts.forEachChild(node1, child1 => { diff --git a/src/services/services.ts b/src/services/services.ts index f8a6e7f665f..5728adb5b9d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1946,50 +1946,6 @@ namespace ts { } } - class SyntaxTreeCache { - // For our syntactic only features, we also keep a cache of the syntax tree for the - // currently edited file. - private currentFileName: string; - private currentFileVersion: string; - private currentFileScriptSnapshot: IScriptSnapshot; - private currentSourceFile: SourceFile; - - constructor(private host: LanguageServiceHost) { - } - - public getCurrentSourceFile(fileName: string): SourceFile { - const scriptSnapshot = this.host.getScriptSnapshot(fileName); - if (!scriptSnapshot) { - // The host does not know about this file. - throw new Error("Could not find file: '" + fileName + "'."); - } - - const scriptKind = getScriptKind(fileName, this.host); - const version = this.host.getScriptVersion(fileName); - let sourceFile: SourceFile; - - if (this.currentFileName !== fileName) { - // This is a new file, just parse it - sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, ScriptTarget.Latest, version, /*setNodeParents*/ true, scriptKind); - } - else if (this.currentFileVersion !== version) { - // This is the same file, just a newer version. Incrementally parse the file. - const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot); - sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange); - } - - if (sourceFile) { - // All done, ensure state is up to date - this.currentFileVersion = version; - this.currentFileName = fileName; - this.currentFileScriptSnapshot = scriptSnapshot; - this.currentSourceFile = sourceFile; - } - - return this.currentSourceFile; - } - } - function setSourceFileFields(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string) { sourceFile.version = version; sourceFile.scriptSnapshot = scriptSnapshot; @@ -2983,7 +2939,7 @@ namespace ts { export function createLanguageService(host: LanguageServiceHost, documentRegistry: DocumentRegistry = createDocumentRegistry(host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(), host.getCurrentDirectory())): LanguageService { - const syntaxTreeCache: SyntaxTreeCache = new SyntaxTreeCache(host); + const syntaxTreeCache = createSyntaxTreeCache(); let ruleProvider: formatting.RulesProvider; let program: Program; let lastProjectVersion: string; @@ -3218,6 +3174,64 @@ namespace ts { } } + function createSyntaxTreeCache() { + let currentFileName: string; + let currentFileVersion: string; + let currentFileScriptSnapshot: IScriptSnapshot; + let currentSourceFile: SourceFile; + let currentScriptKind: ScriptKind; + let currentCompilerOptions: CompilerOptions; + return { + getCurrentSourceFile: function (fileName: string) { + const scriptSnapshot = host.getScriptSnapshot(fileName); + if (!scriptSnapshot) { + // The host does not know about this file. + throw new Error("Could not find file: '" + fileName + "'."); + } + const version = host.getScriptVersion(fileName); + const scriptKind = ts.getScriptKind(fileName, host); + const compilerOptions = host.getCompilationSettings(); + let sourceFile: SourceFile; + if (currentFileName !== fileName) { + // Release the current document + if (currentFileName) { + documentRegistry.releaseDocument(currentFileName, currentCompilerOptions); + } + // This is a new file, just parse it + sourceFile = documentRegistry.acquireDocument(fileName, compilerOptions, scriptSnapshot, version, scriptKind); + } + else if (currentFileVersion !== version) { + // This is the same file, just a newer version. Incrementally parse the file. + sourceFile = documentRegistry.updateDocument(fileName, compilerOptions, scriptSnapshot, version, scriptKind); + } + if (sourceFile) { + // All done, ensure state is up to date + currentFileVersion = version; + currentFileName = fileName; + currentScriptKind = scriptKind; + currentFileScriptSnapshot = scriptSnapshot; + currentSourceFile = sourceFile; + currentCompilerOptions = compilerOptions; + } + if (currentSourceFile && !currentSourceFile.locals) { + fixupParentReferences(currentSourceFile); + } + return currentSourceFile; + }, + dispose: function () { + if (currentFileName) { + documentRegistry.releaseDocument(currentFileName, currentCompilerOptions); + currentFileVersion = undefined; + currentFileName = undefined; + currentScriptKind = undefined; + currentFileScriptSnapshot = undefined; + currentSourceFile = undefined; + currentCompilerOptions = undefined; + } + } + }; + } + function getProgram(): Program { synchronizeHostData(); @@ -3236,6 +3250,7 @@ namespace ts { documentRegistry.releaseDocumentWithKey(file.path, key); } } + syntaxTreeCache.dispose(); } /// Diagnostics