From 10ea5bf4607fd07b0ad23c6b6c410668ddd5d782 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 25 Aug 2017 20:20:14 -0700 Subject: [PATCH] Script infos while opening/closing shouldnt mark project as dirty if the contents dont change --- .../unittests/cachingInServerLSHost.ts | 2 +- src/harness/unittests/textStorage.ts | 12 +- src/server/editorServices.ts | 96 ++++++----- src/server/project.ts | 9 +- src/server/scriptInfo.ts | 162 +++++++++++------- src/server/scriptVersionCache.ts | 21 +-- 6 files changed, 168 insertions(+), 134 deletions(-) diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index 1d22dc0417e..a1095e7dc48 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -61,7 +61,7 @@ namespace ts { typingsInstaller: undefined }; const projectService = new server.ProjectService(svcOpts); - const rootScriptInfo = projectService.getOrCreateScriptInfo(rootFile, /* openedByClient */ true, serverHost); + const rootScriptInfo = projectService.getOrCreateScriptInfoForNormalizedPath(server.toNormalizedPath(rootFile), /*openedByClient*/ true); const project = projectService.assignOrphanScriptInfoToInferredProject(rootScriptInfo); project.setCompilerOptions({ module: ts.ModuleKind.AMD, noLib: true } ); diff --git a/src/harness/unittests/textStorage.ts b/src/harness/unittests/textStorage.ts index 545d090df9b..aa8231aa31a 100644 --- a/src/harness/unittests/textStorage.ts +++ b/src/harness/unittests/textStorage.ts @@ -21,7 +21,7 @@ namespace ts.textStorage { const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path)); const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path)); - ts1.useScriptVersionCache(); + ts1.useScriptVersionCache_TestOnly(); ts2.useText(); const lineMap = computeLineStarts(f.content); @@ -55,16 +55,16 @@ namespace ts.textStorage { const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path)); ts1.getSnapshot(); - assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 1"); + assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 1"); ts1.edit(0, 5, " "); - assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 1"); + assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 1"); ts1.useText(); - assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 2"); + assert.isTrue(!ts1.hasScriptVersionCache_TestOnly(), "should not have script version cache - 2"); ts1.getLineInfo(0); - assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 2"); + assert.isTrue(ts1.hasScriptVersionCache_TestOnly(), "have script version cache - 2"); }); }); -} \ No newline at end of file +} diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a85ebebec5e..9195083bb68 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -687,7 +687,7 @@ namespace ts.server { else { // file has been changed which might affect the set of referenced files in projects that include // this file and set of inferred projects - info.reloadFromFile(); + info.delayReloadNonMixedContentFile(); this.delayUpdateProjectGraphs(info.containingProjects); } } @@ -753,7 +753,7 @@ namespace ts.server { const configFileSpecs = project.configFileSpecs; const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilationSettings(), project.getCachedPartialSystem(), this.hostConfiguration.extraFileExtensions); project.updateErrorOnNoInputFiles(result.fileNames.length !== 0); - this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader, /*clientFileName*/ undefined); + this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader); this.delayUpdateProjectGraphAndInferredProjectsRefresh(project); } }, @@ -1352,7 +1352,7 @@ namespace ts.server { /*languageServiceEnabled*/ !this.exceededTotalSizeLimitForNonTsFiles(projectFileName, compilerOptions, files, externalFilePropertyReader), options.compileOnSave === undefined ? true : options.compileOnSave); - this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, /*clientFileName*/ undefined, typeAcquisition); + this.addFilesToNonInferredProjectAndUpdateGraph(project, files, externalFilePropertyReader, typeAcquisition); this.externalProjects.push(project); this.sendProjectTelemetry(project.externalProjectName, project); return project; @@ -1401,14 +1401,14 @@ namespace ts.server { } } - private addFilesToNonInferredProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, clientFileName: string, typeAcquisition: TypeAcquisition): void { - this.updateNonInferredProjectFiles(project, files, propertyReader, clientFileName); + private addFilesToNonInferredProjectAndUpdateGraph(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader, typeAcquisition: TypeAcquisition): void { + this.updateNonInferredProjectFiles(project, files, propertyReader); project.setTypeAcquisition(typeAcquisition); // This doesnt need scheduling since its either creation or reload of the project project.updateGraph(); } - private createConfiguredProject(configFileName: NormalizedPath, clientFileName?: string) { + private createConfiguredProject(configFileName: NormalizedPath) { const cachedPartialSystem = createCachedPartialSystem(this.host); const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedPartialSystem); this.logger.info(`Opened configuration file ${configFileName}`); @@ -1438,14 +1438,14 @@ namespace ts.server { } project.setProjectErrors(configFileErrors); - this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition); + this.addFilesToNonInferredProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, projectOptions.typeAcquisition); this.configuredProjects.set(project.canonicalConfigFilePath, project); this.setConfigFileExistenceByNewConfiguredProject(project); this.sendProjectTelemetry(project.getConfigFilePath(), project, projectOptions); return project; } - private updateNonInferredProjectFiles(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader, clientFileName?: string) { + private updateNonInferredProjectFiles(project: ExternalProject | ConfiguredProject, files: T[], propertyReader: FilePropertyReader) { const projectRootFilesMap = project.getRootFilesMap(); const newRootScriptInfoMap = createMap(); @@ -1467,7 +1467,7 @@ namespace ts.server { else { const scriptKind = propertyReader.getScriptKind(f); const hasMixedContent = propertyReader.hasMixedContent(f, this.hostConfiguration.extraFileExtensions); - scriptInfo = this.getOrCreateScriptInfoForNormalizedPath(normalizedPath, /*openedByClient*/ clientFileName === newRootFile, /*fileContent*/ undefined, scriptKind, hasMixedContent, project.partialSystem); + scriptInfo = this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(normalizedPath, scriptKind, hasMixedContent, project.partialSystem); path = scriptInfo.path; // If this script info is not already a root add it if (!project.isRoot(scriptInfo)) { @@ -1509,7 +1509,7 @@ namespace ts.server { if (compileOnSave !== undefined) { project.compileOnSaveEnabled = compileOnSave; } - this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, /*clientFileName*/ undefined, newTypeAcquisition); + this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, newTypeAcquisition); } /** @@ -1617,15 +1617,11 @@ namespace ts.server { return project; } - /** - * @param uncheckedFileName is absolute pathname - * @param fileContent is a known version of the file content that is more up to date than the one on disk - */ /*@internal*/ - getOrCreateScriptInfo(uncheckedFileName: string, openedByClient: boolean, hostToQueryFileExistsOn: PartialSystem) { - return this.getOrCreateScriptInfoForNormalizedPath( - toNormalizedPath(uncheckedFileName), openedByClient, /*fileContent*/ undefined, - /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, hostToQueryFileExistsOn + getOrCreateScriptInfoNotOpenedByClient(uncheckedFileName: string, hostToQueryFileExistsOn: PartialSystem) { + return this.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath( + toNormalizedPath(uncheckedFileName), /*scriptKind*/ undefined, + /*hasMixedContent*/ undefined, hostToQueryFileExistsOn ); } @@ -1654,38 +1650,41 @@ namespace ts.server { } } + getOrCreateScriptInfoNotOpenedByClientForNormalizedPath(fileName: NormalizedPath, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) { + return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ false, /*fileContent*/ undefined, scriptKind, hasMixedContent, hostToQueryFileExistsOn); + } + + getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) { + return this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn); + } + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: PartialSystem) { + Debug.assert(fileContent === undefined || openedByClient, "ScriptInfo needs to be opened by client to be able to set its user defined content"); const path = normalizedPathToPath(fileName, this.currentDirectory, this.toCanonicalFileName); let info = this.getScriptInfoForPath(path); if (!info) { - if (openedByClient || (hostToQueryFileExistsOn || this.host).fileExists(fileName)) { - info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path); - - this.filenameToScriptInfo.set(info.path, info); - - if (openedByClient) { - if (fileContent === undefined) { - // if file is opened by client and its content is not specified - use file text - fileContent = this.host.readFile(fileName) || ""; - } - } - else { - this.watchClosedScriptInfo(info); - } + // If the file is not opened by client and the file doesnot exist on the disk, return + if (!openedByClient && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) { + return; + } + info = new ScriptInfo(this.host, fileName, scriptKind, hasMixedContent, path); + this.filenameToScriptInfo.set(info.path, info); + if (!openedByClient) { + this.watchClosedScriptInfo(info); } } - if (info) { - if (openedByClient && !info.isScriptOpen()) { - this.stopWatchingScriptInfo(info); - info.open(fileContent); - if (hasMixedContent) { - info.registerFileUpdate(); - } - } - else if (fileContent !== undefined) { - info.reload(fileContent); + if (openedByClient && !info.isScriptOpen()) { + // Opening closed script info + // either it was created just now, or was part of projects but was closed + this.stopWatchingScriptInfo(info); + info.open(fileContent); + if (hasMixedContent) { + info.registerFileUpdate(); } } + else { + Debug.assert(fileContent === undefined); + } return info; } @@ -1730,9 +1729,16 @@ namespace ts.server { /** * This function rebuilds the project for every file opened by the client + * This does not reload contents of open files from disk. But we could do that if needed */ reloadProjects() { this.logger.info("reload projects."); + // If we want this to also reload open files from disk, we could do that, + // but then we need to make sure we arent calling this function + // (and would separate out below reloading of projects to be called when immediate reload is needed) + // as there is no need to load contents of the files from the disk + + // Reload Projects this.reloadConfiguredProjectForFiles(this.openFiles, /*delayReload*/ false); this.refreshInferredProjects(); } @@ -1771,7 +1777,7 @@ namespace ts.server { if (configFileName) { const project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { - this.createConfiguredProject(configFileName, info.fileName); + this.createConfiguredProject(configFileName); updatedProjects.set(configFileName, true); } else if (!updatedProjects.has(configFileName)) { @@ -1862,14 +1868,14 @@ namespace ts.server { let configFileName: NormalizedPath; let configFileErrors: ReadonlyArray; - const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent); + const info = this.getOrCreateScriptInfoOpenedByClientForNormalizedPath(fileName, fileContent, scriptKind, hasMixedContent); let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName); if (!project) { configFileName = this.getConfigFileNameForFile(info, projectRootPath); if (configFileName) { project = this.findConfiguredProjectByProjectName(configFileName); if (!project) { - project = this.createConfiguredProject(configFileName, fileName); + project = this.createConfiguredProject(configFileName); // even if opening config file was successful, it could still // contain errors that were tolerated. diff --git a/src/server/project.ts b/src/server/project.ts index bf73d4d175c..1f6b762b674 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -263,7 +263,7 @@ namespace ts.server { } private getScriptInfoLSHost(fileName: string) { - const scriptInfo = this.projectService.getOrCreateScriptInfo(fileName, /*openedByClient*/ false, this.partialSystem); + const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.partialSystem); if (scriptInfo) { const existingValue = this.rootFilesMap.get(scriptInfo.path); if (existingValue !== undefined && existingValue !== scriptInfo) { @@ -804,7 +804,7 @@ namespace ts.server { // by the LSHost for files in the program when the program is retrieved above but // the program doesn't contain external files so this must be done explicitly. inserted => { - const scriptInfo = this.projectService.getOrCreateScriptInfo(inserted, /*openedByClient*/ false, this.partialSystem); + const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClient(inserted, this.partialSystem); scriptInfo.attachToProject(this); }, removed => { @@ -845,9 +845,8 @@ namespace ts.server { } getScriptInfoForNormalizedPath(fileName: NormalizedPath) { - const scriptInfo = this.projectService.getOrCreateScriptInfoForNormalizedPath( - fileName, /*openedByClient*/ false, /*fileContent*/ undefined, - /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem + const scriptInfo = this.projectService.getOrCreateScriptInfoNotOpenedByClientForNormalizedPath( + fileName, /*scriptKind*/ undefined, /*hasMixedContent*/ undefined, this.partialSystem ); if (scriptInfo && !scriptInfo.isAttached(this)) { return Errors.ThrowProjectDoesNotContainDocument(fileName, this); diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index d76f930f5ec..bc3e11baa0e 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -4,13 +4,19 @@ namespace ts.server { /* @internal */ export class TextStorage { + // Generated only on demand and removes the text if any edits private svc: ScriptVersionCache | undefined; private svcVersion = 0; + // Store text when there is no svc or svc has no change, on edit, remove the text private text: string; private lineMap: number[]; private textVersion = 0; + public isOpen: boolean; + private ownFileText: boolean; + private pendingReloadFromDisk: boolean; + constructor(private readonly host: ServerHost, private readonly fileName: NormalizedPath) { } @@ -20,43 +26,75 @@ namespace ts.server { : `Text-${this.textVersion}`; } - public hasScriptVersionCache() { + public hasScriptVersionCache_TestOnly() { return this.svc !== undefined; } - public useScriptVersionCache(newText?: string) { - this.switchToScriptVersionCache(newText); + public useScriptVersionCache_TestOnly() { + this.switchToScriptVersionCache(); } public useText(newText?: string) { this.svc = undefined; - this.setText(newText); + this.text = newText; + this.lineMap = undefined; + this.textVersion++; } public edit(start: number, end: number, newText: string) { this.switchToScriptVersionCache().edit(start, end - start, newText); + this.ownFileText = false; + this.text = undefined; + this.lineMap = undefined; } - public reload(text: string) { - if (this.svc) { - this.svc.reload(text); - } - else { - this.setText(text); + /** returns true if text changed */ + public reload(newText: string) { + Debug.assert(newText !== undefined); + + // Reload always has fresh content + this.pendingReloadFromDisk = false; + + // If text changed set the text + // This also ensures that if we had switched to version cache, + // we are switching back to text. + // The change to version cache will happen when needed + // Thus avoiding the computation if there are no changes + if (this.text !== newText) { + this.useText(newText); + // We cant guarantee new text is own file text + this.ownFileText = false; + return true; } } - public reloadFromFile(tempFileName?: string) { - if (this.svc || (tempFileName !== this.fileName)) { - this.reload(this.getFileText(tempFileName)); + /** returns true if text changed */ + public reloadFromDisk() { + let reloaded = false; + if (!this.pendingReloadFromDisk && !this.ownFileText) { + reloaded = this.reload(this.getFileText()); + this.ownFileText = true; } - else { - this.setText(undefined); + return reloaded; + } + + public delayReloadFromFileIntoText() { + this.pendingReloadFromDisk = true; + } + + /** returns true if text changed */ + public reloadFromFile(tempFileName: string) { + let reloaded = false; + // Reload if different file or we dont know if we are working with own file text + if (tempFileName !== this.fileName || !this.ownFileText) { + reloaded = this.reload(this.getFileText(tempFileName)); + this.ownFileText = !tempFileName || tempFileName === this.fileName; } + return reloaded; } public getSnapshot(): IScriptSnapshot { - return this.svc + return this.useScriptVersionCacheIfValidOrOpen() ? this.svc.getSnapshot() : ScriptSnapshot.fromString(this.getOrLoadText()); } @@ -68,7 +106,7 @@ namespace ts.server { * @param line 0 based index */ lineToTextSpan(line: number): TextSpan { - if (!this.svc) { + if (!this.useScriptVersionCacheIfValidOrOpen()) { const lineMap = this.getLineMap(); const start = lineMap[line]; // -1 since line is 1-based const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length; @@ -82,7 +120,7 @@ namespace ts.server { * @param offset 1 based index */ lineOffsetToPosition(line: number, offset: number): number { - if (!this.svc) { + if (!this.useScriptVersionCacheIfValidOrOpen()) { return computePositionOfLineAndCharacter(this.getLineMap(), line - 1, offset - 1, this.text); } @@ -91,7 +129,7 @@ namespace ts.server { } positionToLineOffset(position: number): protocol.Location { - if (!this.svc) { + if (!this.useScriptVersionCacheIfValidOrOpen()) { const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position); return { line: line + 1, offset: character + 1 }; } @@ -102,43 +140,39 @@ namespace ts.server { return this.host.readFile(tempFileName || this.fileName) || ""; } - private ensureNoScriptVersionCache() { - Debug.assert(!this.svc, "ScriptVersionCache should not be set"); - } - - private switchToScriptVersionCache(newText?: string): ScriptVersionCache { - if (!this.svc) { - this.svc = ScriptVersionCache.fromString(newText !== undefined ? newText : this.getOrLoadText()); + private switchToScriptVersionCache(): ScriptVersionCache { + if (!this.svc || this.pendingReloadFromDisk) { + this.svc = ScriptVersionCache.fromString(this.getOrLoadText()); this.svcVersion++; - this.text = undefined; } return this.svc; } + private useScriptVersionCacheIfValidOrOpen(): ScriptVersionCache | undefined { + // If this is open script, use the cache + if (this.isOpen) { + return this.switchToScriptVersionCache(); + } + + // Else if the svc is uptodate with the text, we are good + return !this.pendingReloadFromDisk && this.svc; + } + private getOrLoadText() { - this.ensureNoScriptVersionCache(); - if (this.text === undefined) { - this.setText(this.getFileText()); + if (this.text === undefined || this.pendingReloadFromDisk) { + Debug.assert(!this.svc || this.pendingReloadFromDisk, "ScriptVersionCache should not be set when reloading from disk"); + this.reload(this.getFileText()); + this.ownFileText = true; } return this.text; } private getLineMap() { - this.ensureNoScriptVersionCache(); + Debug.assert(!this.svc, "ScriptVersionCache should not be set"); return this.lineMap || (this.lineMap = computeLineStarts(this.getOrLoadText())); } - - private setText(newText: string) { - this.ensureNoScriptVersionCache(); - if (newText === undefined || this.text !== newText) { - this.text = newText; - this.lineMap = undefined; - this.textVersion++; - } - } } - export class ScriptInfo { /** * All projects that include this file @@ -150,8 +184,6 @@ namespace ts.server { fileWatcher: FileWatcher; private textStorage: TextStorage; - private isOpen: boolean; - constructor( private readonly host: ServerHost, readonly fileName: NormalizedPath, @@ -169,19 +201,28 @@ namespace ts.server { } public isScriptOpen() { - return this.isOpen; + return this.textStorage.isOpen; } public open(newText: string) { - this.isOpen = true; - this.textStorage.useScriptVersionCache(newText); - this.markContainingProjectsAsDirty(); + this.textStorage.isOpen = true; + if (newText !== undefined && + this.textStorage.reload(newText)) { + // reload new contents only if the existing contents changed + this.markContainingProjectsAsDirty(); + } } public close() { - this.isOpen = false; - this.textStorage.useText(this.hasMixedContent ? "" : undefined); - this.markContainingProjectsAsDirty(); + this.textStorage.isOpen = false; + if (this.hasMixedContent) { + if (this.textStorage.reload("")) { + this.markContainingProjectsAsDirty(); + } + } + else if (this.textStorage.reloadFromDisk()) { + this.markContainingProjectsAsDirty(); + } } public getSnapshot() { @@ -293,26 +334,31 @@ namespace ts.server { return this.textStorage.getVersion(); } - reload(script: string) { - this.textStorage.reload(script); - this.markContainingProjectsAsDirty(); - } - saveTo(fileName: string) { const snap = this.textStorage.getSnapshot(); this.host.writeFile(fileName, snap.getText(0, snap.getLength())); } + /*@internal*/ + delayReloadNonMixedContentFile() { + Debug.assert(!this.hasMixedContent); + this.textStorage.delayReloadFromFileIntoText(); + this.markContainingProjectsAsDirty(); + } + reloadFromFile(tempFileName?: NormalizedPath) { if (this.hasMixedContent) { - this.reload(""); + this.textStorage.reload(""); + this.markContainingProjectsAsDirty(); } else { - this.textStorage.reloadFromFile(tempFileName); - this.markContainingProjectsAsDirty(); + if (this.textStorage.reloadFromFile(tempFileName)) { + this.markContainingProjectsAsDirty(); + } } } + /*@internal*/ getLineInfo(line: number): AbsolutePositionAndLineText { return this.textStorage.getLineInfo(line); } diff --git a/src/server/scriptVersionCache.ts b/src/server/scriptVersionCache.ts index 451536a0caa..7c25e4cc3cb 100644 --- a/src/server/scriptVersionCache.ts +++ b/src/server/scriptVersionCache.ts @@ -2,6 +2,7 @@ /// /// +/*@internal*/ namespace ts.server { const lineCollectionCapacity = 4; @@ -285,24 +286,6 @@ namespace ts.server { } } - // reload whole script, leaving no change history behind reload - reload(script: string) { - this.currentVersion++; - this.changes = []; // history wiped out by reload - const snap = new LineIndexSnapshot(this.currentVersion, this, new LineIndex()); - - // delete all versions - for (let i = 0; i < this.versions.length; i++) { - this.versions[i] = undefined; - } - - this.versions[this.currentVersionToIndex()] = snap; - const lm = LineIndex.linesFromText(script); - snap.index.load(lm.lines); - - this.minVersion = this.currentVersion; - } - getSnapshot(): IScriptSnapshot { return this._getSnapshot(); } private _getSnapshot(): LineIndexSnapshot { @@ -843,4 +826,4 @@ namespace ts.server { return 1; } } -} \ No newline at end of file +}