mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-21 18:22:58 -05:00
@@ -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
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user