Merge pull request #11002 from RyanCavanaugh/watchForTypes

Watch for changes in types roots
This commit is contained in:
Ryan Cavanaugh 2016-09-20 17:12:18 -07:00 committed by GitHub
commit 53232b9680
6 changed files with 70 additions and 0 deletions

View file

@ -218,6 +218,9 @@ namespace Harness.LanguageService {
const snapshot = this.getScriptSnapshot(path); const snapshot = this.getScriptSnapshot(path);
return snapshot.getText(0, snapshot.getLength()); return snapshot.getText(0, snapshot.getLength());
} }
getTypeRootsVersion() {
return 0;
}
log(s: string): void { } log(s: string): void { }

View file

@ -363,6 +363,15 @@ namespace ts.server {
this.printProjects(); this.printProjects();
} }
private onTypeRootFileChanged(project: ConfiguredProject, fileName: string) {
this.logger.info(`Type root file ${fileName} changed`);
this.throttledOperations.schedule(project.configFileName + " * type root", /*delay*/ 250, () => {
project.updateTypes();
this.updateConfiguredProject(project); // TODO: Figure out why this is needed (should be redundant?)
this.refreshInferredProjects();
});
}
/** /**
* This is the callback function when a watched directory has added or removed source code files. * This is the callback function when a watched directory has added or removed source code files.
* @param project the project that associates with this directory watcher * @param project the project that associates with this directory watcher
@ -395,6 +404,8 @@ namespace ts.server {
// For configured projects, the change is made outside the tsconfig file, and // For configured projects, the change is made outside the tsconfig file, and
// it is not likely to affect the project for other files opened by the client. We can // it is not likely to affect the project for other files opened by the client. We can
// just update the current project. // just update the current project.
this.logger.info("Updating configured project");
this.updateConfiguredProject(project); this.updateConfiguredProject(project);
// Call refreshInferredProjects to clean up inferred projects we may have // Call refreshInferredProjects to clean up inferred projects we may have
@ -771,6 +782,7 @@ namespace ts.server {
this.watchConfigDirectoryForProject(project, projectOptions); this.watchConfigDirectoryForProject(project, projectOptions);
} }
project.watchWildcards((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path)); project.watchWildcards((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
project.watchTypeRoots((project, path) => this.onTypeRootFileChanged(project, path));
this.configuredProjects.push(project); this.configuredProjects.push(project);
return project; return project;

View file

@ -145,6 +145,10 @@ namespace ts.server {
return this.project.getRootFilesLSHost(); return this.project.getRootFilesLSHost();
} }
getTypeRootsVersion() {
return this.project.typesVersion;
}
getScriptKind(fileName: string) { getScriptKind(fileName: string) {
const info = this.project.getScriptInfoLSHost(fileName); const info = this.project.getScriptInfoLSHost(fileName);
return info && info.scriptKind; return info && info.scriptKind;

View file

@ -69,6 +69,8 @@ namespace ts.server {
protected projectErrors: Diagnostic[]; protected projectErrors: Diagnostic[];
public typesVersion = 0;
public isJsOnlyProject() { public isJsOnlyProject() {
this.updateGraph(); this.updateGraph();
return allFilesAreJsOrDts(this); return allFilesAreJsOrDts(this);
@ -153,6 +155,12 @@ namespace ts.server {
return this.program.getSourceFileByPath(path); return this.program.getSourceFileByPath(path);
} }
updateTypes() {
this.typesVersion++;
this.markAsDirty();
this.updateGraph();
}
close() { close() {
if (this.program) { if (this.program) {
// if we have a program - release all files that are enlisted in program // if we have a program - release all files that are enlisted in program
@ -569,6 +577,7 @@ namespace ts.server {
private projectFileWatcher: FileWatcher; private projectFileWatcher: FileWatcher;
private directoryWatcher: FileWatcher; private directoryWatcher: FileWatcher;
private directoriesWatchedForWildcards: Map<FileWatcher>; private directoriesWatchedForWildcards: Map<FileWatcher>;
private typeRootsWatchers: FileWatcher[];
/** Used for configured projects which may have multiple open roots */ /** Used for configured projects which may have multiple open roots */
openRefCount = 0; openRefCount = 0;
@ -608,6 +617,16 @@ namespace ts.server {
this.projectFileWatcher = this.projectService.host.watchFile(this.configFileName, _ => callback(this)); this.projectFileWatcher = this.projectService.host.watchFile(this.configFileName, _ => callback(this));
} }
watchTypeRoots(callback: (project: ConfiguredProject, path: string) => void) {
const roots = this.getEffectiveTypeRoots();
const watchers: FileWatcher[] = [];
for (const root of roots) {
this.projectService.logger.info(`Add type root watcher for: ${root}`);
watchers.push(this.projectService.host.watchDirectory(root, path => callback(this, path), /*recursive*/ false));
}
this.typeRootsWatchers = watchers;
}
watchConfigDirectory(callback: (project: ConfiguredProject, path: string) => void) { watchConfigDirectory(callback: (project: ConfiguredProject, path: string) => void) {
if (this.directoryWatcher) { if (this.directoryWatcher) {
return; return;
@ -651,6 +670,13 @@ namespace ts.server {
this.projectFileWatcher.close(); this.projectFileWatcher.close();
} }
if (this.typeRootsWatchers) {
for (const watcher of this.typeRootsWatchers) {
watcher.close();
}
this.typeRootsWatchers = undefined;
}
for (const id in this.directoriesWatchedForWildcards) { for (const id in this.directoriesWatchedForWildcards) {
this.directoriesWatchedForWildcards[id].close(); this.directoriesWatchedForWildcards[id].close();
} }
@ -667,6 +693,10 @@ namespace ts.server {
this.openRefCount--; this.openRefCount--;
return this.openRefCount; return this.openRefCount;
} }
getEffectiveTypeRoots() {
return ts.getEffectiveTypeRoots(this.getCompilerOptions(), this.projectService.host) || [];
}
} }
export class ExternalProject extends Project { export class ExternalProject extends Project {

View file

@ -1185,6 +1185,11 @@ namespace ts {
readFile?(path: string, encoding?: string): string; readFile?(path: string, encoding?: string): string;
fileExists?(path: string): boolean; fileExists?(path: string): boolean;
/*
* LS host can optionally implement these methods to support automatic updating when new type libraries are installed
*/
getTypeRootsVersion?(): number;
/* /*
* LS host can optionally implement this method if it wants to be completely in charge of module name resolution. * LS host can optionally implement this method if it wants to be completely in charge of module name resolution.
* if implementation is omitted then language service will use built-in module resolution logic and get answers to * if implementation is omitted then language service will use built-in module resolution logic and get answers to
@ -3099,6 +3104,7 @@ namespace ts {
let ruleProvider: formatting.RulesProvider; let ruleProvider: formatting.RulesProvider;
let program: Program; let program: Program;
let lastProjectVersion: string; let lastProjectVersion: string;
let lastTypesRootVersion = 0;
const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames(); const useCaseSensitivefileNames = host.useCaseSensitiveFileNames && host.useCaseSensitiveFileNames();
const cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken()); const cancellationToken = new CancellationTokenObject(host.getCancellationToken && host.getCancellationToken());
@ -3215,6 +3221,13 @@ namespace ts {
}; };
} }
const typeRootsVersion = host.getTypeRootsVersion ? host.getTypeRootsVersion() : 0;
if (lastTypesRootVersion !== typeRootsVersion) {
log("TypeRoots version has changed; provide new program");
program = undefined;
lastTypesRootVersion = typeRootsVersion;
}
const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings); const documentRegistryBucketKey = documentRegistry.getKeyForCompilationSettings(newSettings);
const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program); const newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program);

View file

@ -67,6 +67,7 @@ namespace ts {
getProjectVersion?(): string; getProjectVersion?(): string;
useCaseSensitiveFileNames?(): boolean; useCaseSensitiveFileNames?(): boolean;
getTypeRootsVersion?(): number;
readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string; readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string;
readFile(path: string, encoding?: string): string; readFile(path: string, encoding?: string): string;
fileExists(path: string): boolean; fileExists(path: string): boolean;
@ -358,6 +359,13 @@ namespace ts {
return this.shimHost.getProjectVersion(); return this.shimHost.getProjectVersion();
} }
public getTypeRootsVersion(): number {
if (!this.shimHost.getTypeRootsVersion) {
return 0;
}
return this.shimHost.getTypeRootsVersion();
}
public useCaseSensitiveFileNames(): boolean { public useCaseSensitiveFileNames(): boolean {
return this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false; return this.shimHost.useCaseSensitiveFileNames ? this.shimHost.useCaseSensitiveFileNames() : false;
} }