Skip to content

Commit 3cede8f

Browse files
committed
A TSServer implementation that just uses the files list to avoid looking all over the file system.
Improves performance when running on a network file system.
1 parent 1a579d9 commit 3cede8f

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

src/server/g3ServerHostProxy.ts

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/// <reference path="./types.ts"/>
2+
3+
namespace ts.server {
4+
export function getG3ServerHostProxy(
5+
projectName: string,
6+
host: ServerHost,
7+
logger: Logger): ServerHost {
8+
9+
let proxyHost:ServerHost = (<any>Object).assign({}, host);
10+
11+
// projectName is the tsconfig. Get the files list from there.
12+
const {config, error} = readConfigFile(projectName, proxyHost.readFile);
13+
if (!error) {
14+
const projectDir = getDirectoryPath(projectName);
15+
16+
// Get the files list from the tsconfig.
17+
let {options, errors, fileNames} =
18+
parseJsonConfigFileContent(config, proxyHost, projectDir);
19+
if (!errors || errors.length === 0) {
20+
// Get the list of files into a map.
21+
let fileMap: {[k: string]: boolean} = {};
22+
23+
// Just put the directory of the files in the known directories list.
24+
// We don't rely on the behavior of walking up the directories to find
25+
// the node_modules. (This part may not work for opensource)
26+
let directoryMap: {[k: string]: boolean} = {};
27+
let rootDirs = options.rootDirs;
28+
29+
// Add all the rootDirs to the known directories list.
30+
options.rootDirs.forEach(d => {
31+
logger.info('Adding rootdir: ' + d);
32+
directoryMap[d] = true;
33+
});
34+
35+
// For each file add to the filesMap and add their directory
36+
// (and few directories above them) to the directoryMap.
37+
fileNames.forEach(f => {
38+
f = proxyHost.resolvePath(f);
39+
fileMap[f] = true;
40+
// TODO(viks): How deep should we go? Is 2 enough?
41+
for (let i = 0; i < 5; i++) {
42+
f = getDirectoryPath(f);
43+
logger.info('Adding dir: ' + getDirectoryPath(f));
44+
directoryMap[f] = true;
45+
}
46+
});
47+
48+
// Override the fileExists in the ServerHost to reply using the fileMap
49+
// instead of hitting the (network) file system.
50+
proxyHost.fileExists = (path: string) => {
51+
path = proxyHost.resolvePath(path);
52+
if (path in fileMap) {
53+
// File found in map!
54+
logger.info('Found: ' + path);
55+
return true;
56+
} else {
57+
// Only ever allow looking in the filesystem for files inside
58+
// the project dir. Allows for discovery of new files
59+
// in the project without having to rebuild tsconfig.
60+
for (const rootDir of rootDirs) {
61+
if (path.indexOf(rootDir) === 0) {
62+
logger.info('Search: ' + path);
63+
return host.fileExists(path);
64+
}
65+
}
66+
}
67+
// File not in map. Just return false without hitting file system.
68+
logger.info('Did not find: ' + path);
69+
return false;
70+
}
71+
72+
// Override the directoryExists in the ServerHost to reply using the
73+
// directoryMap without hitting the file system.
74+
proxyHost.directoryExists = (path: string) => {
75+
path = proxyHost.resolvePath(path);
76+
if (path in directoryMap) {
77+
logger.info('Dir Found: ' + path);
78+
return true;
79+
}
80+
// Allow looking around in the project directory.
81+
for (const rootDir of rootDirs) {
82+
if (path.indexOf(rootDir) === 0) {
83+
logger.info('Dir Search: ' + path);
84+
return host.directoryExists(path);
85+
}
86+
}
87+
logger.info('Dir NOT Found: ' + path);
88+
return false;
89+
}
90+
}
91+
}
92+
return proxyHost;
93+
}
94+
}

src/server/project.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
/// <reference path="lsHost.ts"/>
55
/// <reference path="typingsCache.ts"/>
66
/// <reference path="builder.ts"/>
7+
/// <reference path="g3ServerHostProxy.ts"/>
78

89
namespace ts.server {
910

@@ -202,7 +203,18 @@ namespace ts.server {
202203

203204
this.setInternalCompilerOptionsForEmittingJsFiles();
204205

205-
this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
206+
// Create a proxy server host that uses the files list to respond to
207+
// fileExists and directoryExists so as to avoid going to the
208+
// file system every time.
209+
let host = this.projectService.host;
210+
if (projectKind === ProjectKind.Configured && hasExplicitListOfFiles) {
211+
host = getG3ServerHostProxy(
212+
projectName,
213+
this.projectService.host,
214+
this.projectService.logger);
215+
}
216+
217+
this.lsHost = new LSHost(host, this, this.projectService.cancellationToken);
206218
this.lsHost.setCompilationSettings(this.compilerOptions);
207219

208220
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);

src/server/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"scriptInfo.ts",
1818
"lsHost.ts",
1919
"typingsCache.ts",
20+
"g3ServerHostProxy.ts",
2021
"project.ts",
2122
"editorServices.ts",
2223
"protocol.ts",

0 commit comments

Comments
 (0)