Skip to content

Commit 15e01f0

Browse files
author
Andy Hanson
committed
Move code to a new module documentRegistry.ts
1 parent b5d2707 commit 15e01f0

File tree

5 files changed

+243
-239
lines changed

5 files changed

+243
-239
lines changed

Jakefile.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ var servicesSources = [
126126
"classifier.ts",
127127
"completions.ts",
128128
"documentHighlights.ts",
129+
"documentRegistry.ts",
129130
"findAllReferences.ts",
130131
"goToDefinition.ts",
131132
"jsDoc.ts",

src/services/documentRegistry.ts

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
namespace ts {
2+
/**
3+
* The document registry represents a store of SourceFile objects that can be shared between
4+
* multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST)
5+
* of files in the context.
6+
* SourceFile objects account for most of the memory usage by the language service. Sharing
7+
* the same DocumentRegistry instance between different instances of LanguageService allow
8+
* for more efficient memory utilization since all projects will share at least the library
9+
* file (lib.d.ts).
10+
*
11+
* A more advanced use of the document registry is to serialize sourceFile objects to disk
12+
* and re-hydrate them when needed.
13+
*
14+
* To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it
15+
* to all subsequent createLanguageService calls.
16+
*/
17+
export interface DocumentRegistry {
18+
/**
19+
* Request a stored SourceFile with a given fileName and compilationSettings.
20+
* The first call to acquire will call createLanguageServiceSourceFile to generate
21+
* the SourceFile if was not found in the registry.
22+
*
23+
* @param fileName The name of the file requested
24+
* @param compilationSettings Some compilation settings like target affects the
25+
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
26+
* multiple copies of the same file for different compilation settings.
27+
* @parm scriptSnapshot Text of the file. Only used if the file was not found
28+
* in the registry and a new one was created.
29+
* @parm version Current version of the file. Only used if the file was not found
30+
* in the registry and a new one was created.
31+
*/
32+
acquireDocument(
33+
fileName: string,
34+
compilationSettings: CompilerOptions,
35+
scriptSnapshot: IScriptSnapshot,
36+
version: string,
37+
scriptKind?: ScriptKind): SourceFile;
38+
39+
acquireDocumentWithKey(
40+
fileName: string,
41+
path: Path,
42+
compilationSettings: CompilerOptions,
43+
key: DocumentRegistryBucketKey,
44+
scriptSnapshot: IScriptSnapshot,
45+
version: string,
46+
scriptKind?: ScriptKind): SourceFile;
47+
48+
/**
49+
* Request an updated version of an already existing SourceFile with a given fileName
50+
* and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile
51+
* to get an updated SourceFile.
52+
*
53+
* @param fileName The name of the file requested
54+
* @param compilationSettings Some compilation settings like target affects the
55+
* shape of a the resulting SourceFile. This allows the DocumentRegistry to store
56+
* multiple copies of the same file for different compilation settings.
57+
* @param scriptSnapshot Text of the file.
58+
* @param version Current version of the file.
59+
*/
60+
updateDocument(
61+
fileName: string,
62+
compilationSettings: CompilerOptions,
63+
scriptSnapshot: IScriptSnapshot,
64+
version: string,
65+
scriptKind?: ScriptKind): SourceFile;
66+
67+
updateDocumentWithKey(
68+
fileName: string,
69+
path: Path,
70+
compilationSettings: CompilerOptions,
71+
key: DocumentRegistryBucketKey,
72+
scriptSnapshot: IScriptSnapshot,
73+
version: string,
74+
scriptKind?: ScriptKind): SourceFile;
75+
76+
getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey;
77+
/**
78+
* Informs the DocumentRegistry that a file is not needed any longer.
79+
*
80+
* Note: It is not allowed to call release on a SourceFile that was not acquired from
81+
* this registry originally.
82+
*
83+
* @param fileName The name of the file to be released
84+
* @param compilationSettings The compilation settings used to acquire the file
85+
*/
86+
releaseDocument(fileName: string, compilationSettings: CompilerOptions): void;
87+
88+
releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void;
89+
90+
reportStats(): string;
91+
}
92+
93+
export type DocumentRegistryBucketKey = string & { __bucketKey: any };
94+
95+
interface DocumentRegistryEntry {
96+
sourceFile: SourceFile;
97+
98+
// The number of language services that this source file is referenced in. When no more
99+
// language services are referencing the file, then the file can be removed from the
100+
// registry.
101+
languageServiceRefCount: number;
102+
owners: string[];
103+
}
104+
105+
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
106+
// Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have
107+
// for those settings.
108+
const buckets = createMap<FileMap<DocumentRegistryEntry>>();
109+
const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames);
110+
111+
function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey {
112+
return <DocumentRegistryBucketKey>`_${settings.target}|${settings.module}|${settings.noResolve}|${settings.jsx}|${settings.allowJs}|${settings.baseUrl}|${JSON.stringify(settings.typeRoots)}|${JSON.stringify(settings.rootDirs)}|${JSON.stringify(settings.paths)}`;
113+
}
114+
115+
function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap<DocumentRegistryEntry> {
116+
let bucket = buckets[key];
117+
if (!bucket && createIfMissing) {
118+
buckets[key] = bucket = createFileMap<DocumentRegistryEntry>();
119+
}
120+
return bucket;
121+
}
122+
123+
function reportStats() {
124+
const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => {
125+
const entries = buckets[name];
126+
const sourceFiles: { name: string; refCount: number; references: string[]; }[] = [];
127+
entries.forEachValue((key, entry) => {
128+
sourceFiles.push({
129+
name: key,
130+
refCount: entry.languageServiceRefCount,
131+
references: entry.owners.slice(0)
132+
});
133+
});
134+
sourceFiles.sort((x, y) => y.refCount - x.refCount);
135+
return {
136+
bucket: name,
137+
sourceFiles
138+
};
139+
});
140+
return JSON.stringify(bucketInfoArray, undefined, 2);
141+
}
142+
143+
function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
144+
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
145+
const key = getKeyForCompilationSettings(compilationSettings);
146+
return acquireDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
147+
}
148+
149+
function acquireDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
150+
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ true, scriptKind);
151+
}
152+
153+
function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
154+
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
155+
const key = getKeyForCompilationSettings(compilationSettings);
156+
return updateDocumentWithKey(fileName, path, compilationSettings, key, scriptSnapshot, version, scriptKind);
157+
}
158+
159+
function updateDocumentWithKey(fileName: string, path: Path, compilationSettings: CompilerOptions, key: DocumentRegistryBucketKey, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile {
160+
return acquireOrUpdateDocument(fileName, path, compilationSettings, key, scriptSnapshot, version, /*acquiring*/ false, scriptKind);
161+
}
162+
163+
function acquireOrUpdateDocument(
164+
fileName: string,
165+
path: Path,
166+
compilationSettings: CompilerOptions,
167+
key: DocumentRegistryBucketKey,
168+
scriptSnapshot: IScriptSnapshot,
169+
version: string,
170+
acquiring: boolean,
171+
scriptKind?: ScriptKind): SourceFile {
172+
173+
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true);
174+
let entry = bucket.get(path);
175+
if (!entry) {
176+
Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?");
177+
178+
// Have never seen this file with these settings. Create a new source file for it.
179+
const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind);
180+
181+
entry = {
182+
sourceFile: sourceFile,
183+
languageServiceRefCount: 0,
184+
owners: []
185+
};
186+
bucket.set(path, entry);
187+
}
188+
else {
189+
// We have an entry for this file. However, it may be for a different version of
190+
// the script snapshot. If so, update it appropriately. Otherwise, we can just
191+
// return it as is.
192+
if (entry.sourceFile.version !== version) {
193+
entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version,
194+
scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot));
195+
}
196+
}
197+
198+
// If we're acquiring, then this is the first time this LS is asking for this document.
199+
// Increase our ref count so we know there's another LS using the document. If we're
200+
// not acquiring, then that means the LS is 'updating' the file instead, and that means
201+
// it has already acquired the document previously. As such, we do not need to increase
202+
// the ref count.
203+
if (acquiring) {
204+
entry.languageServiceRefCount++;
205+
}
206+
207+
return entry.sourceFile;
208+
}
209+
210+
function releaseDocument(fileName: string, compilationSettings: CompilerOptions): void {
211+
const path = toPath(fileName, currentDirectory, getCanonicalFileName);
212+
const key = getKeyForCompilationSettings(compilationSettings);
213+
return releaseDocumentWithKey(path, key);
214+
}
215+
216+
function releaseDocumentWithKey(path: Path, key: DocumentRegistryBucketKey): void {
217+
const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/false);
218+
Debug.assert(bucket !== undefined);
219+
220+
const entry = bucket.get(path);
221+
entry.languageServiceRefCount--;
222+
223+
Debug.assert(entry.languageServiceRefCount >= 0);
224+
if (entry.languageServiceRefCount === 0) {
225+
bucket.remove(path);
226+
}
227+
}
228+
229+
return {
230+
acquireDocument,
231+
acquireDocumentWithKey,
232+
updateDocument,
233+
updateDocumentWithKey,
234+
releaseDocument,
235+
releaseDocumentWithKey,
236+
reportStats,
237+
getKeyForCompilationSettings
238+
};
239+
}
240+
}

0 commit comments

Comments
 (0)