diff --git a/src/compiler/program.ts b/src/compiler/program.ts index caf3ebb1c7d90..6c7de0653962d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -653,6 +653,7 @@ namespace ts { rootFileNames: string[], newOptions: CompilerOptions, getSourceVersion: (path: Path, fileName: string) => string | undefined, + getScriptKind: ((path: Path, fileName: string) => ScriptKind) | undefined, fileExists: (fileName: string) => boolean, hasInvalidatedResolution: HasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames: HasChangedAutomaticTypeDirectiveNames | undefined, @@ -688,6 +689,7 @@ namespace ts { function sourceFileNotUptoDate(sourceFile: SourceFile) { return !sourceFileVersionUptoDate(sourceFile) || + !sourceFileScriptKindUptoDate(sourceFile) || hasInvalidatedResolution(sourceFile.path); } @@ -695,6 +697,11 @@ namespace ts { return sourceFile.version === getSourceVersion(sourceFile.resolvedPath, sourceFile.fileName); } + function sourceFileScriptKindUptoDate(sourceFile: SourceFile) { + return !getScriptKind || + sourceFile.scriptKind === ensureScriptKind(sourceFile.fileName, getScriptKind(sourceFile.resolvedPath, sourceFile.fileName)); + } + function projectReferenceUptoDate(oldRef: ProjectReference, newRef: ProjectReference, index: number) { return projectReferenceIsEqualTo(oldRef, newRef) && resolvedProjectReferenceUptoDate(program!.getResolvedProjectReferences()![index], oldRef); diff --git a/src/compiler/watchPublic.ts b/src/compiler/watchPublic.ts index 0296e285b4520..0a7db974d1898 100644 --- a/src/compiler/watchPublic.ts +++ b/src/compiler/watchPublic.ts @@ -426,7 +426,7 @@ namespace ts { // All resolutions are invalid if user provided resolutions const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution); - if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { + if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, /*getScriptKind*/ undefined, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { if (hasChangedConfigFileParsingErrors) { builderProgram = createProgram(/*rootNames*/ undefined, /*options*/ undefined, compilerHost, builderProgram, configFileParsingDiagnostics, projectReferences); hasChangedConfigFileParsingErrors = false; diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index ceebdc4020315..b34408d3bb5b2 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -672,12 +672,6 @@ namespace ts.server { /*@internal*/ readonly filenameToScriptInfo = new Map(); private readonly scriptInfoInNodeModulesWatchers = new Map(); - /** - * Contains all the deleted script info's version information so that - * it does not reset when creating script info again - * (and could have potentially collided with version where contents mismatch) - */ - private readonly filenameToScriptInfoVersion = new Map(); // Set of all '.js' files ever opened. private readonly allJsFilesForOpenFileTelemetry = new Map(); @@ -1603,7 +1597,6 @@ namespace ts.server { private deleteScriptInfo(info: ScriptInfo) { this.filenameToScriptInfo.delete(info.path); - this.filenameToScriptInfoVersion.set(info.path, info.getVersion()); const realpath = info.getRealpathIfDifferent(); if (realpath) { this.realpathToScriptInfos!.remove(realpath, info); // TODO: GH#18217 @@ -2737,9 +2730,8 @@ namespace ts.server { if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) { return; } - info = new ScriptInfo(this.host, fileName, scriptKind!, !!hasMixedContent, path, this.filenameToScriptInfoVersion.get(path)); // TODO: GH#18217 + info = new ScriptInfo(this.host, fileName, scriptKind!, !!hasMixedContent, path); // TODO: GH#18217 this.filenameToScriptInfo.set(info.path, info); - this.filenameToScriptInfoVersion.delete(info.path); if (!openedByClient) { this.watchClosedScriptInfo(info); } diff --git a/src/server/project.ts b/src/server/project.ts index ef48ebf530bc2..9d756aa3f23bc 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -412,7 +412,8 @@ namespace ts.server { } getScriptKind(fileName: string) { - const info = this.getOrCreateScriptInfoAndAttachToProject(fileName); + // Don't attach to the project if script kind is asked + const info = this.projectService.getOrCreateScriptInfoNotOpenedByClient(fileName, this.currentDirectory, this.directoryStructureHost); return (info && info.scriptKind)!; // TODO: GH#18217 } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index a26fcc6748932..a095c26737c83 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -1,12 +1,7 @@ namespace ts.server { - export interface ScriptInfoVersion { - svc: number; - text: number; - } - /* @internal */ export class TextStorage { - version: ScriptInfoVersion; + version: string | undefined; /** * Generated only on demand (based on edits, or information requested) @@ -46,14 +41,7 @@ namespace ts.server { */ private pendingReloadFromDisk = false; - constructor(private readonly host: ServerHost, private readonly info: ScriptInfo, initialVersion?: ScriptInfoVersion) { - this.version = initialVersion || { svc: 0, text: 0 }; - } - - public getVersion() { - return this.svc - ? `SVC-${this.version.svc}-${this.svc.getSnapshotVersion()}` - : `Text-${this.version.text}`; + constructor(private readonly host: ServerHost, private readonly info: ScriptInfo) { } public hasScriptVersionCache_TestOnly() { @@ -77,16 +65,17 @@ namespace ts.server { public useText(newText?: string) { this.svc = undefined; this.text = newText; + this.version = undefined; this.lineMap = undefined; this.fileSize = undefined; this.resetSourceMapInfo(); - this.version.text++; } public edit(start: number, end: number, newText: string) { this.switchToScriptVersionCache().edit(start, end - start, newText); this.ownFileText = false; this.text = undefined; + this.version = undefined; this.lineMap = undefined; this.fileSize = undefined; this.resetSourceMapInfo(); @@ -142,6 +131,7 @@ namespace ts.server { public delayReloadFromFileIntoText() { this.pendingReloadFromDisk = true; + this.version = undefined; } /** @@ -225,7 +215,6 @@ namespace ts.server { private switchToScriptVersionCache(): ScriptVersionCache { if (!this.svc || this.pendingReloadFromDisk) { this.svc = ScriptVersionCache.fromString(this.getOrLoadText()); - this.version.svc++; } return this.svc; } @@ -334,10 +323,10 @@ namespace ts.server { readonly scriptKind: ScriptKind, public readonly hasMixedContent: boolean, readonly path: Path, - initialVersion?: ScriptInfoVersion) { + ) { this.isDynamic = isDynamicFileName(fileName); - this.textStorage = new TextStorage(host, this, initialVersion); + this.textStorage = new TextStorage(host, this); if (hasMixedContent || this.isDynamic) { this.textStorage.reload(""); this.realpath = this.path; @@ -347,11 +336,6 @@ namespace ts.server { : getScriptKindFromFileName(fileName); } - /*@internal*/ - getVersion() { - return this.textStorage.version; - } - /*@internal*/ getTelemetryFileSize() { return this.textStorage.getTelemetryFileSize(); @@ -569,10 +553,15 @@ namespace ts.server { } } - getLatestVersion(): string { + getLatestVersion() { // Ensure we have updated snapshot to give back latest version - this.textStorage.getSnapshot(); - return this.textStorage.getVersion(); + const snapShot = this.textStorage.getSnapshot(); + if (this.textStorage.version === undefined) { + // Ensure we have updated snapshot to give back latest version + const text = getSnapshotText(snapShot); + this.textStorage.version = this.host.createHash ? this.host.createHash(text) : generateDjb2Hash(text); + } + return this.textStorage.version; } saveTo(fileName: string) { diff --git a/src/services/services.ts b/src/services/services.ts index afe43e32a79c2..8bd3b65c5f1d9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1312,7 +1312,7 @@ namespace ts { }; // If the program is already up-to-date, we can reuse it - if (isProgramUptoDate(program, rootFileNames, newSettings, (_path, fileName) => host.getScriptVersion(fileName), fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { + if (isProgramUptoDate(program, rootFileNames, newSettings, (_path, fileName) => host.getScriptVersion(fileName), host.getScriptKind ? (_path, fileName) => host.getScriptKind!(fileName) : undefined, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames, getParsedCommandLine, projectReferences)) { return; } diff --git a/src/testRunner/unittests/reuseProgramStructure.ts b/src/testRunner/unittests/reuseProgramStructure.ts index 57fe93fed14a6..e6d62ae23543e 100644 --- a/src/testRunner/unittests/reuseProgramStructure.ts +++ b/src/testRunner/unittests/reuseProgramStructure.ts @@ -913,7 +913,9 @@ namespace ts { ) { return isProgramUptoDate( program, newRootFileNames, newOptions, - path => program.getSourceFileByPath(path)!.version, /*fileExists*/ returnFalse, + path => program.getSourceFileByPath(path)!.version, + /*getScfriptKind*/ undefined, + /*fileExists*/ returnFalse, /*hasInvalidatedResolution*/ returnFalse, /*hasChangedAutomaticTypeDirectiveNames*/ undefined, /*getParsedCommandLine*/ returnUndefined, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index edd094bb4d964..8262be7f0d76c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -9440,10 +9440,6 @@ declare namespace ts.server.protocol { } } declare namespace ts.server { - interface ScriptInfoVersion { - svc: number; - text: number; - } function isDynamicFileName(fileName: NormalizedPath): boolean; class ScriptInfo { private readonly host; @@ -9458,7 +9454,7 @@ declare namespace ts.server { private formatSettings; private preferences; private textStorage; - constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path, initialVersion?: ScriptInfoVersion); + constructor(host: ServerHost, fileName: NormalizedPath, scriptKind: ScriptKind, hasMixedContent: boolean, path: Path); isScriptOpen(): boolean; open(newText: string): void; close(fileExists?: boolean): void; @@ -9921,12 +9917,6 @@ declare namespace ts.server { } export class ProjectService { private readonly scriptInfoInNodeModulesWatchers; - /** - * Contains all the deleted script info's version information so that - * it does not reset when creating script info again - * (and could have potentially collided with version where contents mismatch) - */ - private readonly filenameToScriptInfoVersion; private readonly allJsFilesForOpenFileTelemetry; /** * maps external project file name to list of config files that were the part of this project