Skip to content

Commit 81ca650

Browse files
committed
Cache the latest source file from document registry so we can keep it alive when script info is orphan
1 parent 59d1925 commit 81ca650

File tree

4 files changed

+60
-8
lines changed

4 files changed

+60
-8
lines changed

src/harness/unittests/tsserverProjectSystem.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8405,15 +8405,21 @@ new C();`
84058405
service.openClientFile(file.path);
84068406
const project = service.configuredProjects.get(configFile.path);
84078407
checkProject(/*moduleIsOrphan*/ false);
8408+
const moduleInfo = service.getScriptInfo(moduleFile.path);
8409+
const sourceFile = moduleInfo.cacheSourceFile.sourceFile;
8410+
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
84088411

84098412
// edit file
84108413
const info = service.getScriptInfo(file.path);
84118414
service.applyChangesToFile(info, [{ span: { start: 0, length: importModuleContent.length }, newText: "" }]);
84128415
checkProject(/*moduleIsOrphan*/ true);
8416+
assert.equal(moduleInfo.cacheSourceFile.sourceFile, sourceFile);
84138417

84148418
// write content back
84158419
service.applyChangesToFile(info, [{ span: { start: 0, length: 0 }, newText: importModuleContent }]);
84168420
checkProject(/*moduleIsOrphan*/ false);
8421+
assert.equal(moduleInfo.cacheSourceFile.sourceFile, sourceFile);
8422+
assert.equal(project.getSourceFile(moduleInfo.path), sourceFile);
84178423

84188424
function checkProject(moduleIsOrphan: boolean) {
84198425
// Update the project

src/server/editorServices.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ namespace ts.server {
475475
extraFileExtensions: []
476476
};
477477

478-
this.documentRegistry = createDocumentRegistry(this.host.useCaseSensitiveFileNames, this.currentDirectory);
478+
this.documentRegistry = createDocumentRegistryInternal(this.host.useCaseSensitiveFileNames, this.currentDirectory, this);
479479
const watchLogLevel = this.logger.hasLevel(LogLevel.verbose) ? WatchLogLevel.Verbose :
480480
this.logger.loggingEnabled() ? WatchLogLevel.TriggerOnly : WatchLogLevel.None;
481481
const log: (s: string) => void = watchLogLevel !== WatchLogLevel.None ? (s => this.logger.info(s)) : noop;
@@ -496,6 +496,19 @@ namespace ts.server {
496496
return getNormalizedAbsolutePath(fileName, this.host.getCurrentDirectory());
497497
}
498498

499+
/*@internal*/
500+
setDocument(key: DocumentRegistryBucketKey, path: Path, sourceFile: SourceFile) {
501+
const info = this.getScriptInfoForPath(path);
502+
Debug.assert(!!info);
503+
info.cacheSourceFile = { key, sourceFile };
504+
}
505+
506+
/*@internal*/
507+
getDocument(key: DocumentRegistryBucketKey, path: Path) {
508+
const info = this.getScriptInfoForPath(path);
509+
return info && info.cacheSourceFile && info.cacheSourceFile.key === key && info.cacheSourceFile.sourceFile;
510+
}
511+
499512
/* @internal */
500513
ensureInferredProjectsUpToDate_TestOnly() {
501514
this.ensureProjectStructuresUptoDate();

src/server/scriptInfo.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ namespace ts.server {
202202
return fileName[0] === "^" || getBaseFileName(fileName)[0] === "^";
203203
}
204204

205+
/*@internal*/
206+
export interface DocumentRegistrySourceFileCache {
207+
key: DocumentRegistryBucketKey;
208+
sourceFile: SourceFile;
209+
}
210+
205211
export class ScriptInfo {
206212
/**
207213
* All projects that include this file
@@ -221,6 +227,9 @@ namespace ts.server {
221227
/** Set to real path if path is different from info.path */
222228
private realpath: Path | undefined;
223229

230+
/*@internal*/
231+
cacheSourceFile: DocumentRegistrySourceFileCache;
232+
224233
constructor(
225234
private readonly host: ServerHost,
226235
readonly fileName: NormalizedPath,

src/services/documentRegistry.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,12 @@ namespace ts {
9393
reportStats(): string;
9494
}
9595

96+
/*@internal*/
97+
export interface ExternalDocumentCache {
98+
setDocument(key: DocumentRegistryBucketKey, path: Path, sourceFile: SourceFile): void;
99+
getDocument(key: DocumentRegistryBucketKey, path: Path): SourceFile | undefined;
100+
}
101+
96102
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
97103

98104
interface DocumentRegistryEntry {
@@ -102,10 +108,14 @@ namespace ts {
102108
// language services are referencing the file, then the file can be removed from the
103109
// registry.
104110
languageServiceRefCount: number;
105-
owners: string[];
106111
}
107112

108-
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
113+
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory?: string): DocumentRegistry {
114+
return createDocumentRegistryInternal(useCaseSensitiveFileNames, currentDirectory);
115+
}
116+
117+
/*@internal*/
118+
export function createDocumentRegistryInternal(useCaseSensitiveFileNames?: boolean, currentDirectory = "", externalCache?: ExternalDocumentCache): DocumentRegistry {
109119
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
110120
// for those settings.
111121
const buckets = createMap<Map<DocumentRegistryEntry>>();
@@ -126,12 +136,11 @@ namespace ts {
126136
function reportStats() {
127137
const bucketInfoArray = arrayFrom(buckets.keys()).filter(name => name && name.charAt(0) === "_").map(name => {
128138
const entries = buckets.get(name);
129-
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
139+
const sourceFiles: { name: string; refCount: number; }[] = [];
130140
entries.forEach((entry, name) => {
131141
sourceFiles.push({
132142
name,
133-
refCount: entry.languageServiceRefCount,
134-
references: entry.owners.slice(0)
143+
refCount: entry.languageServiceRefCount
135144
});
136145
});
137146
sourceFiles.sort((x, y) => y.refCount - x.refCount);
@@ -176,14 +185,26 @@ namespace ts {
176185
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
177186
let entry = bucket.get(path);
178187
const scriptTarget = scriptKind === ScriptKind.JSON ? ScriptTarget.JSON : compilationSettings.target;
188+
if (!entry && externalCache) {
189+
const sourceFile = externalCache.getDocument(key, path);
190+
if (sourceFile) {
191+
entry = {
192+
sourceFile,
193+
languageServiceRefCount: 1
194+
};
195+
bucket.set(path, entry);
196+
}
197+
}
198+
179199
if (!entry) {
180200
// Have never seen this file with these settings. Create a new source file for it.
181201
const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, /*setNodeParents*/ false, scriptKind);
182-
202+
if (externalCache) {
203+
externalCache.setDocument(key, path, sourceFile);
204+
}
183205
entry = {
184206
sourceFile,
185207
languageServiceRefCount: 1,
186-
owners: []
187208
};
188209
bucket.set(path, entry);
189210
}
@@ -194,6 +215,9 @@ namespace ts {
194215
if (entry.sourceFile.version !== version) {
195216
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version,
196217
scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot));
218+
if (externalCache) {
219+
externalCache.setDocument(key, path, entry.sourceFile);
220+
}
197221
}
198222

199223
// If we're acquiring, then this is the first time this LS is asking for this document.

0 commit comments

Comments
 (0)