diff --git a/src/server/g3ServerHostProxy.ts b/src/server/g3ServerHostProxy.ts
new file mode 100644
index 0000000000000..ff89251edd577
--- /dev/null
+++ b/src/server/g3ServerHostProxy.ts
@@ -0,0 +1,123 @@
+///
+
+namespace ts.server {
+ function isGenerated(path: string) {
+ const GEN_EXT = ['ngsummary', 'ngstyle', 'ngfactory'];
+ const TS_EXT = ['ts', 'tsx', 'd.ts'];
+
+ for (const gen of GEN_EXT) {
+ for (const ext of TS_EXT) {
+ if (fileExtensionIs(path, gen + '.' + ext)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ export function getG3ServerHostProxy(
+ tsconfigPath: string,
+ host: ServerHost,
+ logger: Logger): ServerHost {
+
+ let proxyHost:ServerHost = (Object).assign({}, host);
+
+ const {config, error} = readConfigFile(tsconfigPath, proxyHost.readFile);
+ if (error) {
+ return host;
+ }
+ const projectDir = getDirectoryPath(tsconfigPath);
+
+ // Get the files list from the tsconfig.
+ let {options, errors, fileNames} =
+ parseJsonConfigFileContent(config, proxyHost, projectDir);
+
+ if (errors && errors.length !== 0) {
+ return host;
+ }
+
+ // All google3 projects have rootDits set. Don't proxy if rootDirs is
+ // not set.
+ if (!options.rootDirs) {
+ return host;
+ }
+
+ // Get the list of files into a map.
+ let fileMap: {[k: string]: boolean} = {};
+
+ // Just put the directory of the files in the known directories list.
+ // We don't rely on the behavior of walking up the directories to find
+ // the node_modules. (This part may not work for opensource)
+ let directoryMap: {[k: string]: boolean} = {};
+ let rootDirs = options.rootDirs;
+
+ // Add all the rootDirs to the known directories list.
+ rootDirs.forEach(d => {
+ logger.info('Adding rootdir: ' + d);
+ directoryMap[d] = true;
+ });
+
+ // Add the tsconfig.json as a valid project file.
+ fileMap[tsconfigPath] = true;
+
+ // For each file add to the filesMap and add their directory
+ // (and few directories above them) to the directoryMap.
+ fileNames.forEach(f => {
+ f = proxyHost.resolvePath(f);
+ logger.info('Adding file: ' + f);
+ fileMap[f] = true;
+ // TODO(viks): How deep should we go? Is 2 enough?
+ for (let i = 0; i < 2; i++) {
+ f = getDirectoryPath(f);
+ if (f) {
+ logger.info('Adding dir: ' + getDirectoryPath(f));
+ directoryMap[f] = true;
+ } else {
+ break;
+ }
+ }
+ });
+
+ // Override the fileExists in the ServerHost to reply using the fileMap
+ // instead of hitting the (network) file system.
+ proxyHost.fileExists = (path: string) => {
+ path = proxyHost.resolvePath(path);
+ if (path in fileMap) {
+ // File found in map!
+ logger.info('Found: ' + path);
+ return true;
+ } else {
+ // Only ever allow looking in the filesystem for files inside
+ // the project dir. Allows for discovery of new source files
+ // in the project without having to rebuild tsconfig.
+ // Skip generated files since the tsconfig would have to generated anyways
+ // while regenerating these.
+ if (!isGenerated(path)) {
+ for (const rootDir of rootDirs) {
+ if (path.indexOf(rootDir) === 0) {
+ logger.info('Search: ' + path);
+ return host.fileExists(path);
+ }
+ }
+ }
+ }
+ // File not in map. Just return false without hitting file system.
+ logger.info('Did not find: ' + path);
+ return false;
+ }
+
+ // Override the directoryExists in the ServerHost to reply using the
+ // directoryMap without hitting the file system.
+ proxyHost.directoryExists = (path: string) => {
+ path = proxyHost.resolvePath(path);
+ if (path in directoryMap) {
+ logger.info('Dir Found: ' + path);
+ return true;
+ }
+ logger.info('Dir NOT Found: ' + path);
+ return false;
+ }
+
+ return proxyHost;
+ }
+}
diff --git a/src/server/project.ts b/src/server/project.ts
index ac56a6e3b9802..a8b7570abcb21 100644
--- a/src/server/project.ts
+++ b/src/server/project.ts
@@ -4,6 +4,7 @@
///
///
///
+///
namespace ts.server {
@@ -119,6 +120,8 @@ namespace ts.server {
// wrapper over the real language service that will suppress all semantic operations
protected languageService: LanguageService;
+ public readonly proxyHost: ServerHost;
+
public languageServiceEnabled = true;
protected readonly lsHost: LSHost;
@@ -202,7 +205,18 @@ namespace ts.server {
this.setInternalCompilerOptionsForEmittingJsFiles();
- this.lsHost = new LSHost(this.projectService.host, this, this.projectService.cancellationToken);
+ // Create a proxy server host that uses the files list to respond to
+ // fileExists and directoryExists so as to avoid going to the
+ // file system every time.
+ this.proxyHost = this.projectService.host;
+ if (projectKind === ProjectKind.Configured && hasExplicitListOfFiles) {
+ this.proxyHost = getG3ServerHostProxy(
+ projectName,
+ this.projectService.host,
+ this.projectService.logger);
+ }
+
+ this.lsHost = new LSHost(this.proxyHost, this, this.projectService.cancellationToken);
this.lsHost.setCompilationSettings(this.compilerOptions);
this.languageService = ts.createLanguageService(this.lsHost, this.documentRegistry);
@@ -727,7 +741,7 @@ namespace ts.server {
}
const allFileNames = arrayFrom(referencedFiles.keys()) as Path[];
- return filter(allFileNames, file => this.projectService.host.fileExists(file));
+ return filter(allFileNames, file => this.proxyHost.fileExists(file));
}
// remove a root file from project
diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json
index b0eb4965ea687..14a2a58da16ff 100644
--- a/src/server/tsconfig.json
+++ b/src/server/tsconfig.json
@@ -17,6 +17,7 @@
"scriptInfo.ts",
"lsHost.ts",
"typingsCache.ts",
+ "g3ServerHostProxy.ts",
"project.ts",
"editorServices.ts",
"protocol.ts",