Skip to content

Use hash of source file text as version for the source file in tsserver #41938

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -688,13 +689,19 @@ namespace ts {

function sourceFileNotUptoDate(sourceFile: SourceFile) {
return !sourceFileVersionUptoDate(sourceFile) ||
!sourceFileScriptKindUptoDate(sourceFile) ||
hasInvalidatedResolution(sourceFile.path);
}

function sourceFileVersionUptoDate(sourceFile: SourceFile) {
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);
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/watchPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 1 addition & 9 deletions src/server/editorServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,12 +672,6 @@ namespace ts.server {
/*@internal*/
readonly filenameToScriptInfo = new Map<string, ScriptInfo>();
private readonly scriptInfoInNodeModulesWatchers = new Map<string, ScriptInfoInNodeModulesWatcher>();
/**
* 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<string, ScriptInfoVersion>();
// Set of all '.js' files ever opened.
private readonly allJsFilesForOpenFileTelemetry = new Map<string, true>();

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
41 changes: 15 additions & 26 deletions src/server/scriptInfo.ts
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -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() {
Expand All @@ -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();
Expand Down Expand Up @@ -142,6 +131,7 @@ namespace ts.server {

public delayReloadFromFileIntoText() {
this.pendingReloadFromDisk = true;
this.version = undefined;
}

/**
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -347,11 +336,6 @@ namespace ts.server {
: getScriptKindFromFileName(fileName);
}

/*@internal*/
getVersion() {
return this.textStorage.version;
}

/*@internal*/
getTelemetryFileSize() {
return this.textStorage.getTelemetryFileSize();
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 3 additions & 1 deletion src/testRunner/unittests/reuseProgramStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
12 changes: 1 addition & 11 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down