Skip to content

A TSServer implementation that just uses the files list to avoid looking all over the file system. #1

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 1 commit 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
123 changes: 123 additions & 0 deletions src/server/g3ServerHostProxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/// <reference path="./types.ts"/>

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 = (<any>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;
}
}
18 changes: 16 additions & 2 deletions src/server/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/// <reference path="lsHost.ts"/>
/// <reference path="typingsCache.ts"/>
/// <reference path="builder.ts"/>
/// <reference path="g3ServerHostProxy.ts"/>

namespace ts.server {

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/server/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"scriptInfo.ts",
"lsHost.ts",
"typingsCache.ts",
"g3ServerHostProxy.ts",
"project.ts",
"editorServices.ts",
"protocol.ts",
Expand Down