Optimize wildcard watchers and config directory watching as now we have missing file watching as well

We dont need to explicitly watch config file directory as it will be watched:
- if there was no files specified, in wild card directories
- if there were files specified as missing file (if the file wasnt present)
This commit is contained in:
Sheetal Nandi 2017-07-07 14:43:00 -07:00
parent 6bd42b81ee
commit df6f75bc70
2 changed files with 74 additions and 60 deletions

View File

@ -611,7 +611,8 @@ namespace ts.server {
* @param project the project that associates with this directory watcher
* @param fileName the absolute file name that changed in watched directory
*/
private onSourceFileInDirectoryChangedForConfiguredProject(project: ConfiguredProject, fileName: string) {
/* @internal */
onFileAddOrRemoveInWatchedDirectoryOfProject(project: ConfiguredProject, fileName: string) {
// If a change was made inside "folder/file", node will trigger the callback twice:
// one with the fileName being "folder/file", and the other one with "folder".
// We don't respond to the second one.
@ -623,10 +624,13 @@ namespace ts.server {
this.throttledOperations.schedule(
project.getConfigFilePath(),
/*delay*/250,
() => this.handleChangeInSourceFileForConfiguredProject(project, fileName));
() => this.handleFileAddOrRemoveInWatchedDirectoryOfProject(project, fileName));
}
private handleChangeInSourceFileForConfiguredProject(project: ConfiguredProject, triggerFile: string) {
private handleFileAddOrRemoveInWatchedDirectoryOfProject(project: ConfiguredProject, triggerFile: string) {
// TODO: (sheetalkamat) this actually doesnt need to re-read the config file from the disk
// it just needs to update the file list of file names
// We might be able to do that by caching the info from first parse and add reusing this with the change in the file path
const { projectOptions, configFileErrors } = this.convertConfigFileContentToProjectOptions(project.getConfigFilePath());
this.reportConfigFileDiagnostics(project.getProjectName(), configFileErrors, triggerFile);
@ -676,6 +680,10 @@ namespace ts.server {
return;
}
// TODO: (sheetalkamat)
// 1. We should only watch tsconfig/jsconfig file here instead of watching directory
// 2. We should try reloading projects with open files in Inferred project only
// 3. We should use this watcher to answer questions to findConfigFile rather than calling host everytime
this.logger.info(`Detected newly added tsconfig file: ${fileName}`);
this.reloadProjects();
}
@ -1117,17 +1125,13 @@ namespace ts.server {
this.documentRegistry,
projectOptions.configHasFilesProperty,
projectOptions.compilerOptions,
projectOptions.wildcardDirectories,
/*languageServiceEnabled*/ !sizeLimitExceeded,
projectOptions.compileOnSave === undefined ? false : projectOptions.compileOnSave);
this.addFilesToProjectAndUpdateGraph(project, projectOptions.files, fileNamePropertyReader, clientFileName, projectOptions.typeAcquisition, configFileErrors);
project.watchConfigFile((project, eventKind) => this.onConfigChangedForConfiguredProject(project, eventKind));
if (!sizeLimitExceeded) {
this.watchConfigDirectoryForProject(project, projectOptions);
}
project.watchWildcards((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
project.watchWildcards(projectOptions.wildcardDirectories);
project.watchTypeRoots((project, path) => this.onTypeRootFileChanged(project, path));
this.configuredProjects.push(project);
@ -1135,12 +1139,6 @@ namespace ts.server {
return project;
}
private watchConfigDirectoryForProject(project: ConfiguredProject, options: ProjectOptions): void {
if (!options.configHasFilesProperty) {
project.watchConfigDirectory((project, path) => this.onSourceFileInDirectoryChangedForConfiguredProject(project, path));
}
}
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: Diagnostic[]): void {
for (const f of files) {
const rootFilename = propertyReader.getFileName(f);
@ -1271,12 +1269,12 @@ namespace ts.server {
project.setProjectErrors(configFileErrors);
}
project.disableLanguageService();
project.stopWatchingDirectory();
project.stopWatchingWildCards();
project.setProjectErrors(configFileErrors);
}
else {
project.enableLanguageService();
this.watchConfigDirectoryForProject(project, projectOptions);
project.watchWildcards(projectOptions.wildcardDirectories);
this.updateNonInferredProject(project, projectOptions.files, fileNamePropertyReader, projectOptions.compilerOptions, projectOptions.typeAcquisition, projectOptions.compileOnSave, configFileErrors);
}
}

View File

@ -957,6 +957,8 @@ namespace ts.server {
}
}
type WildCardDirectoryWatchers = { watcher: FileWatcher, recursive: boolean };
/**
* If a file is opened, the server will look for a tsconfig (or jsconfig)
* and if successfull create a ConfiguredProject for it.
@ -964,9 +966,8 @@ namespace ts.server {
*/
export class ConfiguredProject extends Project {
private typeAcquisition: TypeAcquisition;
private projectFileWatcher: FileWatcher;
private directoryWatcher: FileWatcher;
private directoriesWatchedForWildcards: Map<FileWatcher>;
private configFileWatcher: FileWatcher;
private directoriesWatchedForWildcards: Map<WildCardDirectoryWatchers>;
private typeRootsWatchers: FileWatcher[];
readonly canonicalConfigFilePath: NormalizedPath;
@ -980,7 +981,6 @@ namespace ts.server {
documentRegistry: ts.DocumentRegistry,
hasExplicitListOfFiles: boolean,
compilerOptions: CompilerOptions,
private wildcardDirectories: Map<WatchDirectoryFlags>,
languageServiceEnabled: boolean,
public compileOnSaveEnabled: boolean) {
super(configFileName, ProjectKind.Configured, projectService, documentRegistry, hasExplicitListOfFiles, languageServiceEnabled, compilerOptions, compileOnSaveEnabled);
@ -1098,7 +1098,7 @@ namespace ts.server {
}
watchConfigFile(callback: (project: ConfiguredProject, eventKind: FileWatcherEventKind) => void) {
this.projectFileWatcher = this.projectService.host.watchFile(this.getConfigFilePath(), (_fileName, eventKind) => callback(this, eventKind));
this.configFileWatcher = this.projectService.host.watchFile(this.getConfigFilePath(), (_fileName, eventKind) => callback(this, eventKind));
}
watchTypeRoots(callback: (project: ConfiguredProject, path: string) => void) {
@ -1111,49 +1111,70 @@ namespace ts.server {
this.typeRootsWatchers = watchers;
}
watchConfigDirectory(callback: (project: ConfiguredProject, path: string) => void) {
if (this.directoryWatcher) {
return;
}
const directoryToWatch = getDirectoryPath(this.getConfigFilePath());
this.projectService.logger.info(`Add recursive watcher for: ${directoryToWatch}`);
this.directoryWatcher = this.projectService.host.watchDirectory(directoryToWatch, path => callback(this, path), /*recursive*/ true);
}
watchWildcards(callback: (project: ConfiguredProject, path: string) => void) {
if (!this.wildcardDirectories) {
return;
}
const configDirectoryPath = getDirectoryPath(this.getConfigFilePath());
this.directoriesWatchedForWildcards = createMap<FileWatcher>();
this.wildcardDirectories.forEach((flag, directory) => {
if (comparePaths(configDirectoryPath, directory, ".", !this.projectService.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) {
const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0;
this.projectService.logger.info(`Add ${recursive ? "recursive " : ""}watcher for: ${directory}`);
this.directoriesWatchedForWildcards.set(directory, this.projectService.host.watchDirectory(
private addWatcherForDirectory(flag: WatchDirectoryFlags, directory: string, replaceExisting: boolean) {
if (replaceExisting || !this.directoriesWatchedForWildcards.has(directory)) {
const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0;
this.projectService.logger.info(`Add ${recursive ? "recursive " : ""} watcher for: ${directory}`);
this.directoriesWatchedForWildcards.set(directory, {
watcher: this.projectService.host.watchDirectory(
directory,
path => callback(this, path),
path => this.projectService.onFileAddOrRemoveInWatchedDirectoryOfProject(this, path),
recursive
));
}
});
),
recursive
});
}
}
stopWatchingDirectory() {
if (this.directoryWatcher) {
this.directoryWatcher.close();
this.directoryWatcher = undefined;
watchWildcards(wildcardDirectories: Map<WatchDirectoryFlags>) {
if (wildcardDirectories) {
if (this.directoriesWatchedForWildcards) {
this.directoriesWatchedForWildcards.forEach(({ watcher, recursive }, directory) => {
const currentFlag = wildcardDirectories.get(directory);
// Remove already watching wild card if it isnt in updated map
if (currentFlag === undefined) {
this.projectService.logger.info(`Removing ${recursive ? "recursive " : ""} watcher for: ${directory}`);
watcher.close();
this.directoriesWatchedForWildcards.delete(directory);
}
// Or if the recursive doesnt match (add the updated one here)
else {
const currentRecursive = (currentFlag & WatchDirectoryFlags.Recursive) !== 0;
if (currentRecursive !== recursive) {
this.projectService.logger.info(`Removing ${recursive ? "recursive " : ""} watcher for: ${directory}`);
watcher.close();
this.addWatcherForDirectory(currentFlag, directory, /*replaceExisting*/ true);
}
}
});
}
else {
this.directoriesWatchedForWildcards = createMap<WildCardDirectoryWatchers>();
}
wildcardDirectories.forEach((flag, directory) =>
this.addWatcherForDirectory(flag, directory, /*replaceExisting*/ false));
}
else {
this.stopWatchingWildCards();
}
}
stopWatchingWildCards() {
if (this.directoriesWatchedForWildcards) {
this.directoriesWatchedForWildcards.forEach(({ watcher, recursive }, directory) => {
this.projectService.logger.info(`Removing ${recursive ? "recursive " : ""} watcher for: ${directory}`);
watcher.close();
});
this.directoriesWatchedForWildcards = undefined;
}
}
close() {
super.close();
if (this.projectFileWatcher) {
this.projectFileWatcher.close();
this.projectFileWatcher = undefined;
if (this.configFileWatcher) {
this.configFileWatcher.close();
this.configFileWatcher = undefined;
}
if (this.typeRootsWatchers) {
@ -1163,12 +1184,7 @@ namespace ts.server {
this.typeRootsWatchers = undefined;
}
this.directoriesWatchedForWildcards.forEach(watcher => {
watcher.close();
});
this.directoriesWatchedForWildcards = undefined;
this.stopWatchingDirectory();
this.stopWatchingWildCards();
}
addOpenRef() {