Combine the logic to find config file as well as the watch.

This should speed up the file open scenarios where the file belongs to same configured project as we would use cache to answer those fileExists answers
This commit is contained in:
Sheetal Nandi 2017-07-15 22:26:10 -07:00
parent 62663a10ba
commit dcbd7b10f1
4 changed files with 109 additions and 129 deletions

View file

@ -809,7 +809,9 @@ namespace ts.projectSystem {
const project = projectService.inferredProjects[0]; const project = projectService.inferredProjects[0];
checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]); checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]);
checkWatchedFiles(host, ["/a/b/c/tsconfig.json", "/a/b/tsconfig.json", "/a/tsconfig.json", libFile.path, moduleFile.path]); const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"];
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path));
}); });
it("can handle tsconfig file name with difference casing", () => { it("can handle tsconfig file name with difference casing", () => {
@ -879,7 +881,8 @@ namespace ts.projectSystem {
checkProjectActualFiles(project, [file1.path, libFile.path, file2.path, configFile.path]); checkProjectActualFiles(project, [file1.path, libFile.path, file2.path, configFile.path]);
checkProjectRootFiles(project, [file1.path, file2.path]); checkProjectRootFiles(project, [file1.path, file2.path]);
// watching all files except one that was open // watching all files except one that was open
checkWatchedFiles(host, [configFile.path, file2.path, libFile.path]); // And also tsconfig files for the open files
checkWatchedFiles(host, [configFile.path, file2.path, libFile.path, "/a/b/c/tsconfig.json", "/a/b/c/jsconfig.json"]);
checkWatchedDirectories(host, [getDirectoryPath(configFile.path)], /*recursive*/ true); checkWatchedDirectories(host, [getDirectoryPath(configFile.path)], /*recursive*/ true);
}); });
@ -899,15 +902,16 @@ namespace ts.projectSystem {
projectService.openClientFile(commonFile2.path); projectService.openClientFile(commonFile2.path);
checkNumberOfInferredProjects(projectService, 2); checkNumberOfInferredProjects(projectService, 2);
checkWatchedFiles(host, [configFile.path, "/a/tsconfig.json", libFile.path]); const configFileLocations = ["/", "/a/", "/a/b/"];
const watchedFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]).concat(libFile.path);
checkWatchedFiles(host, watchedFiles);
// Add a tsconfig file // Add a tsconfig file
host.reloadFS(filesWithConfig); host.reloadFS(filesWithConfig);
host.checkTimeoutQueueLengthAndRun(1); host.checkTimeoutQueueLengthAndRun(1);
checkNumberOfInferredProjects(projectService, 1); checkNumberOfInferredProjects(projectService, 1);
checkNumberOfConfiguredProjects(projectService, 1); checkNumberOfConfiguredProjects(projectService, 1);
// watching all files except one that was open checkWatchedFiles(host, watchedFiles);
checkWatchedFiles(host, [libFile.path, configFile.path, "/a/tsconfig.json"]);
// remove the tsconfig file // remove the tsconfig file
host.reloadFS(filesWithoutConfig); host.reloadFS(filesWithoutConfig);
@ -917,7 +921,7 @@ namespace ts.projectSystem {
checkNumberOfInferredProjects(projectService, 2); checkNumberOfInferredProjects(projectService, 2);
checkNumberOfConfiguredProjects(projectService, 0); checkNumberOfConfiguredProjects(projectService, 0);
checkWatchedFiles(host, ["/a/b/tsconfig.json", "/a/tsconfig.json", libFile.path]); checkWatchedFiles(host, watchedFiles);
}); });
it("add new files to a configured project without file list", () => { it("add new files to a configured project without file list", () => {

View file

@ -755,7 +755,7 @@ namespace ts.projectSystem {
checkNumberOfProjects(projectService, { configuredProjects: 1 }); checkNumberOfProjects(projectService, { configuredProjects: 1 });
const p = configuredProjectAt(projectService, 0); const p = configuredProjectAt(projectService, 0);
checkProjectActualFiles(p, [app.path, jsconfig.path]); checkProjectActualFiles(p, [app.path, jsconfig.path]);
checkWatchedFiles(host, [jsconfig.path, "/bower_components", "/node_modules", libFile.path]); checkWatchedFiles(host, ["/tsconfig.json", jsconfig.path, "/bower_components", "/node_modules", libFile.path]);
installer.installAll(/*expectedCount*/ 1); installer.installAll(/*expectedCount*/ 1);

View file

@ -252,7 +252,7 @@ namespace ts.server {
WildCardDirectories = "Wild card directory", WildCardDirectories = "Wild card directory",
TypeRoot = "Type root of the project", TypeRoot = "Type root of the project",
ClosedScriptInfo = "Closed Script info", ClosedScriptInfo = "Closed Script info",
ConfigFileForInferredRoot = "Config file for the root script info of the inferred project" ConfigFileForOpenFile = "Config file changes for the open script info"
} }
/* @internal */ /* @internal */
@ -266,12 +266,12 @@ namespace ts.server {
OrphanScriptInfo = "Removing Orphan script info as part of cleanup", OrphanScriptInfo = "Removing Orphan script info as part of cleanup",
FileDeleted = "File was deleted", FileDeleted = "File was deleted",
FileOpened = "File opened", FileOpened = "File opened",
ConfigProjectCreated = "Config file project created" ConfigProjectCreated = "Config file project created",
FileClosed = "File is closed"
} }
const enum ConfigFileWatcherStatus { const enum ConfigFileWatcherStatus {
ReloadingFiles = "Reloading configured projects files", ReloadingFiles = "Reloading configured projects files",
NoAction = "No action on files",
UpdatedCallback = "Updated the callback", UpdatedCallback = "Updated the callback",
TrackingFileAdded = "Tracking file added", TrackingFileAdded = "Tracking file added",
TrackingFileRemoved = "Tracking file removed" TrackingFileRemoved = "Tracking file removed"
@ -282,7 +282,7 @@ namespace ts.server {
type ConfigFileExistence = { type ConfigFileExistence = {
exists: boolean; exists: boolean;
trackingOpenFiles?: ScriptInfo[]; trackingOpenFileSet?: Map<true>;
configFileWatcher?: FileWatcher; configFileWatcher?: FileWatcher;
}; };
@ -672,48 +672,31 @@ namespace ts.server {
// Update the cached status // Update the cached status
// No action needed on tracking open files since the existing config file anyways didnt affect the tracking file // No action needed on tracking open files since the existing config file anyways didnt affect the tracking file
configFilePresenceInfo.exists = false; configFilePresenceInfo.exists = false;
this.logTrackingFiles(project.getConfigFilePath(), configFilePresenceInfo, ConfigFileWatcherStatus.NoAction);
this.removeProject(project); this.removeProject(project);
// Reload the configured projects for these open files in the project as // Reload the configured projects for the open files in the map as they are affectected by this config file
// they could be held up by another config file somewhere in the parent directory this.logConfigFileWatch(project.getConfigFilePath(), configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles);
const orphanFiles = filter(this.openFiles, file => file.containingProjects.length === 0); this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFileSet);
this.delayReloadConfiguredProjectForFiles(orphanFiles);
} }
else { else {
this.logConfigFileWatch(project.getConfigFilePath(), configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles);
project.pendingReload = true; project.pendingReload = true;
this.logTrackingFiles(project.getConfigFilePath(), configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles); this.delayUpdateProjectGraph(project);
if (configFilePresenceInfo.trackingOpenFiles) { this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFileSet);
this.delayUpdateProjectGraph(project);
this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFiles);
}
else {
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
}
} }
} }
/** /**
* This is the callback function for the config file add/remove/change for the root in the inferred project * This is the callback function for the config file add/remove/change at any location that matters to open
* script info but doesnt have configured project open for the config file
*/ */
private onConfigFileAddedForInferredProject(configFileName: NormalizedPath, eventKind: FileWatcherEventKind) { private onConfigFileChangeForOpenScriptInfo(configFileName: NormalizedPath, eventKind: FileWatcherEventKind) {
// This callback is called only if we dont have config file project for this config file // This callback is called only if we dont have config file project for this config file
const cononicalConfigPath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName); const cononicalConfigPath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName);
const configFilePresenceInfo = this.mapOfConfigFilePresence.get(cononicalConfigPath); const configFilePresenceInfo = this.mapOfConfigFilePresence.get(cononicalConfigPath);
configFilePresenceInfo.exists = (eventKind !== FileWatcherEventKind.Deleted);
if (eventKind === FileWatcherEventKind.Deleted) { this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles);
// No action needed if the event was for deletion of the file this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFileSet);
// - because the existing config file didnt affect the inferred project roots anyways
configFilePresenceInfo.exists = false;
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.NoAction);
}
else {
// Either the config file was created or changed
// Reload the projects
configFilePresenceInfo.exists = true;
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.ReloadingFiles);
this.delayReloadConfiguredProjectForFiles(configFilePresenceInfo.trackingOpenFiles);
}
} }
private removeProject(project: Project) { private removeProject(project: Project) {
@ -729,7 +712,7 @@ namespace ts.server {
this.projectToSizeMap.delete((project as ExternalProject).externalProjectName); this.projectToSizeMap.delete((project as ExternalProject).externalProjectName);
break; break;
case ProjectKind.Configured: case ProjectKind.Configured:
// Update the map of mapOfKnownTsConfigFiles this.setConfigFilePresenceByClosedConfigFile(<ConfiguredProject>project);
this.configuredProjects.delete((<ConfiguredProject>project).canonicalConfigFilePath); this.configuredProjects.delete((<ConfiguredProject>project).canonicalConfigFilePath);
this.projectToSizeMap.delete((project as ConfiguredProject).canonicalConfigFilePath); this.projectToSizeMap.delete((project as ConfiguredProject).canonicalConfigFilePath);
break; break;
@ -791,6 +774,7 @@ namespace ts.server {
// because the user may chose to discard the buffer content before saving // because the user may chose to discard the buffer content before saving
// to the disk, and the server's version of the file can be out of sync. // to the disk, and the server's version of the file can be out of sync.
info.close(); info.close();
this.stopWatchingConfigFileForScriptInfo(info);
removeItemFromSet(this.openFiles, info); removeItemFromSet(this.openFiles, info);
@ -863,28 +847,24 @@ namespace ts.server {
}); });
} }
private configFileExists(configFileName: NormalizedPath) { private configFileExists(configFileName: NormalizedPath, info: ScriptInfo) {
const canonicalConfigFilePath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName); const canonicalConfigFilePath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName);
const configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath); return this.watchConfigFileForScriptInfo(configFileName, canonicalConfigFilePath, info).exists;
if (configFilePresenceInfo) {
return configFilePresenceInfo.exists;
}
return this.host.fileExists(configFileName);
} }
private setConfigFilePresenceByNewConfiguredProject(project: ConfiguredProject) { private setConfigFilePresenceByNewConfiguredProject(project: ConfiguredProject) {
const configFilePresenceInfo = this.mapOfConfigFilePresence.get(project.canonicalConfigFilePath); const configFilePresenceInfo = this.mapOfConfigFilePresence.get(project.canonicalConfigFilePath);
if (configFilePresenceInfo) { if (configFilePresenceInfo) {
configFilePresenceInfo.exists = true; Debug.assert(configFilePresenceInfo.exists);
// close existing watcher // close existing watcher
if (configFilePresenceInfo.configFileWatcher) { if (configFilePresenceInfo.configFileWatcher) {
const configFileName = project.getConfigFilePath(); const configFileName = project.getConfigFilePath();
this.closeFileWatcher( this.closeFileWatcher(
WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, WatchType.ConfigFileForOpenFile, /*project*/ undefined, configFileName,
configFilePresenceInfo.configFileWatcher, WatcherCloseReason.ConfigProjectCreated configFilePresenceInfo.configFileWatcher, WatcherCloseReason.ConfigProjectCreated
); );
configFilePresenceInfo.configFileWatcher = undefined; configFilePresenceInfo.configFileWatcher = undefined;
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback); this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback);
} }
} }
else { else {
@ -893,17 +873,16 @@ namespace ts.server {
} }
} }
/* @internal */ private setConfigFilePresenceByClosedConfigFile(closedProject: ConfiguredProject) {
setConfigFilePresenceByClosedConfigFile(closedProject: ConfiguredProject) {
const configFilePresenceInfo = this.mapOfConfigFilePresence.get(closedProject.canonicalConfigFilePath); const configFilePresenceInfo = this.mapOfConfigFilePresence.get(closedProject.canonicalConfigFilePath);
Debug.assert(!!configFilePresenceInfo); Debug.assert(!!configFilePresenceInfo);
if (configFilePresenceInfo.trackingOpenFiles) { if (configFilePresenceInfo.trackingOpenFileSet) {
const configFileName = closedProject.getConfigFilePath(); const configFileName = closedProject.getConfigFilePath();
configFilePresenceInfo.configFileWatcher = this.addFileWatcher( configFilePresenceInfo.configFileWatcher = this.addFileWatcher(
WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName, WatchType.ConfigFileForOpenFile, /*project*/ undefined, configFileName,
(_filename, eventKind) => this.onConfigFileAddedForInferredProject(configFileName, eventKind) (_filename, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind)
); );
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback); this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.UpdatedCallback);
} }
else { else {
// There is no one tracking anymore. Remove the status // There is no one tracking anymore. Remove the status
@ -911,85 +890,83 @@ namespace ts.server {
} }
} }
private logTrackingFiles(configFileName: NormalizedPath, configFilePresenceInfo: ConfigFileExistence, status: ConfigFileWatcherStatus) { private logConfigFileWatch(configFileName: NormalizedPath, configFilePresenceInfo: ConfigFileExistence, status: ConfigFileWatcherStatus) {
const watchType = configFilePresenceInfo.configFileWatcher ? WatchType.ConfigFileForInferredRoot : WatchType.ConfigFilePath; const watchType = configFilePresenceInfo.configFileWatcher ? WatchType.ConfigFileForOpenFile : WatchType.ConfigFilePath;
const files = map(configFilePresenceInfo.trackingOpenFiles, info => info.fileName); const files = configFilePresenceInfo.trackingOpenFileSet ?
arrayFrom(configFilePresenceInfo.trackingOpenFileSet.keys(), key =>
this.getScriptInfoForPath(key as Path).fileName) :
[];
this.logger.info(`FileWatcher:: ${watchType}: File: ${configFileName} Currently Tracking for files: ${files} Status: ${status}`); this.logger.info(`FileWatcher:: ${watchType}: File: ${configFileName} Currently Tracking for files: ${files} Status: ${status}`);
} }
private watchConfigFileForInferredRoot(configFileName: NormalizedPath, canonicalConfigFilePath: string, root: ScriptInfo) { private watchConfigFileForScriptInfo(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) {
let configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath); let configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath);
if (configFilePresenceInfo) { if (configFilePresenceInfo) {
// Existing information - just add to tracking files // Existing information - just add to tracking files
(configFilePresenceInfo.trackingOpenFiles || (configFilePresenceInfo.trackingOpenFiles = [])).push(root); if (!configFilePresenceInfo.trackingOpenFileSet) {
} configFilePresenceInfo.trackingOpenFileSet = createMap<true>();
else { configFilePresenceInfo.trackingOpenFileSet.set(info.path, true);
// Add new callback this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileAdded);
configFilePresenceInfo = { }
exists: this.host.fileExists(configFileName), else if (!configFilePresenceInfo.trackingOpenFileSet.has(info.path)) {
trackingOpenFiles: [root], configFilePresenceInfo.trackingOpenFileSet.set(info.path, true);
configFileWatcher: this.addFileWatcher( this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileAdded);
WatchType.ConfigFileForInferredRoot, /*project*/ undefined, configFileName,
(_fileName, eventKind) => this.onConfigFileAddedForInferredProject(configFileName, eventKind)
)
};
this.mapOfConfigFilePresence.set(canonicalConfigFilePath, configFilePresenceInfo);
}
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileAdded);
}
private closeWatchConfigFileForInferredRoot(configFileName: NormalizedPath, canonicalConfigFilePath: string, root: ScriptInfo, reason: WatcherCloseReason) {
const configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath);
Debug.assert(!!configFilePresenceInfo);
if (configFilePresenceInfo.trackingOpenFiles.length === 1) {
configFilePresenceInfo.trackingOpenFiles = undefined;
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileRemoved);
if (configFilePresenceInfo.configFileWatcher) {
this.closeFileWatcher(
WatchType.ConfigFileForInferredRoot, /*project*/ undefined,
configFileName, configFilePresenceInfo.configFileWatcher, reason
);
this.mapOfConfigFilePresence.delete(canonicalConfigFilePath);
} }
} }
else { else {
removeItemFromSet(configFilePresenceInfo.trackingOpenFiles, root); // Add new callback
this.logTrackingFiles(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileRemoved); const trackingOpenFileSet = createMap<true>();
trackingOpenFileSet.set(info.path, true);
const exists = this.host.fileExists(configFileName);
configFilePresenceInfo = {
exists,
trackingOpenFileSet,
configFileWatcher: this.addFileWatcher(
WatchType.ConfigFileForOpenFile, /*project*/ undefined, configFileName,
(_fileName, eventKind) => this.onConfigFileChangeForOpenScriptInfo(configFileName, eventKind)
)
};
this.mapOfConfigFilePresence.set(canonicalConfigFilePath, configFilePresenceInfo);
this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileAdded);
}
return configFilePresenceInfo;
}
private closeConfigFileWatchForScriptInfo(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) {
const configFilePresenceInfo = this.mapOfConfigFilePresence.get(canonicalConfigFilePath);
if (configFilePresenceInfo) {
if (configFilePresenceInfo.trackingOpenFileSet.size === 1) {
configFilePresenceInfo.trackingOpenFileSet = undefined;
this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileRemoved);
if (configFilePresenceInfo.configFileWatcher) {
this.closeFileWatcher(
WatchType.ConfigFileForOpenFile, /*project*/ undefined, configFileName,
configFilePresenceInfo.configFileWatcher, WatcherCloseReason.FileClosed
);
this.mapOfConfigFilePresence.delete(canonicalConfigFilePath);
}
}
else {
configFilePresenceInfo.trackingOpenFileSet.delete(info.path);
this.logConfigFileWatch(configFileName, configFilePresenceInfo, ConfigFileWatcherStatus.TrackingFileRemoved);
}
} }
} }
private enumerateWatchingRootOfInferredProject(root: ScriptInfo, private stopWatchingConfigFileForScriptInfo(info: ScriptInfo) {
action: (configFileName: NormalizedPath, canonicalConfigFilePath: string, root: ScriptInfo) => void) { Debug.assert(!info.isScriptOpen());
let current = root.fileName; let current = info.fileName;
let currentPath = getDirectoryPath(root.path); let currentPath = getDirectoryPath(info.path);
let parentPath = getDirectoryPath(currentPath); let parentPath = getDirectoryPath(currentPath);
while (currentPath !== parentPath) { while (currentPath !== parentPath) {
current = asNormalizedPath(getDirectoryPath(current)); current = asNormalizedPath(getDirectoryPath(current));
action(asNormalizedPath(combinePaths(current, "tsconfig.json")), combinePaths(currentPath, "tsconfig.json"), root); this.closeConfigFileWatchForScriptInfo(asNormalizedPath(combinePaths(current, "tsconfig.json")), combinePaths(currentPath, "tsconfig.json"), info);
//if (root.isJavaScript()) { this.closeConfigFileWatchForScriptInfo(asNormalizedPath(combinePaths(current, "jsconfig.json")), combinePaths(currentPath, "jsconfig.json"), info);
// this.watchConfigFileForInferredRoot(asNormalizedPath(combinePaths(current, "jsconfig.json")), combinePaths(currentPath, "jsconfig.json"), root);
//}
currentPath = parentPath; currentPath = parentPath;
parentPath = getDirectoryPath(parentPath); parentPath = getDirectoryPath(parentPath);
} }
} }
/*@internal*/
startWatchingRootOfInferredProject(root: ScriptInfo) {
this.enumerateWatchingRootOfInferredProject(root,
(configFileName, canonicalConfigFilePath, root) =>
this.watchConfigFileForInferredRoot(configFileName, canonicalConfigFilePath, root)
);
}
/*@internal*/
stopWatchingRootOfInferredProject(root: ScriptInfo, reason: WatcherCloseReason) {
this.enumerateWatchingRootOfInferredProject(root,
(configFileName, canonicalConfigFilePath, root) =>
this.closeWatchConfigFileForInferredRoot(configFileName, canonicalConfigFilePath, root, reason)
);
}
/** /**
* This function tries to search for a tsconfig.json for the given file. * This function tries to search for a tsconfig.json for the given file.
* This is different from the method the compiler uses because * This is different from the method the compiler uses because
@ -998,20 +975,21 @@ namespace ts.server {
* The server must start searching from the directory containing * The server must start searching from the directory containing
* the newly opened file. * the newly opened file.
*/ */
private getConfigFileNameForFile(fileName: NormalizedPath, projectRootPath?: NormalizedPath) { private getConfigFileNameForFile(info: ScriptInfo, projectRootPath?: NormalizedPath) {
let searchPath = getDirectoryPath(fileName); Debug.assert(info.isScriptOpen());
let searchPath = getDirectoryPath(info.fileName);
this.logger.info(`Search path: ${searchPath}`); this.logger.info(`Search path: ${searchPath}`);
// check if this file is already included in one of external projects // check if this file is already included in one of external projects
while (!projectRootPath || searchPath.indexOf(projectRootPath) >= 0) { while (!projectRootPath || searchPath.indexOf(projectRootPath) >= 0) {
const tsconfigFileName = asNormalizedPath(combinePaths(searchPath, "tsconfig.json")); const tsconfigFileName = asNormalizedPath(combinePaths(searchPath, "tsconfig.json"));
if (this.configFileExists(tsconfigFileName)) { if (this.configFileExists(tsconfigFileName, info)) {
this.logger.info(`Config file name: ${tsconfigFileName}`); this.logger.info(`Config file name: ${tsconfigFileName}`);
return tsconfigFileName; return tsconfigFileName;
} }
const jsconfigFileName = asNormalizedPath(combinePaths(searchPath, "jsconfig.json")); const jsconfigFileName = asNormalizedPath(combinePaths(searchPath, "jsconfig.json"));
if (this.configFileExists(jsconfigFileName)) { if (this.configFileExists(jsconfigFileName, info)) {
this.logger.info(`Config file name: ${jsconfigFileName}`); this.logger.info(`Config file name: ${jsconfigFileName}`);
return jsconfigFileName; return jsconfigFileName;
} }
@ -1359,7 +1337,6 @@ namespace ts.server {
: new InferredProject(this, this.documentRegistry, this.compilerOptionsForInferredProjects); : new InferredProject(this, this.documentRegistry, this.compilerOptionsForInferredProjects);
project.addRoot(root); project.addRoot(root);
this.startWatchingRootOfInferredProject(root);
project.updateGraph(); project.updateGraph();
if (!useExistingProject) { if (!useExistingProject) {
@ -1513,8 +1490,11 @@ namespace ts.server {
this.refreshInferredProjects(); this.refreshInferredProjects();
} }
delayReloadConfiguredProjectForFiles(openFiles: ScriptInfo[]) { delayReloadConfiguredProjectForFiles(openFileSet: Map<true>) {
this.reloadConfiguredProjectForFiles(openFiles, /*delayReload*/ true); if (openFileSet) {
const openFiles = arrayFrom(openFileSet.keys(), path => this.getScriptInfoForPath(path as Path));
this.reloadConfiguredProjectForFiles(openFiles, /*delayReload*/ true);
}
this.delayInferredProjectsRefresh(); this.delayInferredProjectsRefresh();
} }
@ -1532,7 +1512,7 @@ namespace ts.server {
// we first detect if there is already a configured project created for it: if so, // we first detect if there is already a configured project created for it: if so,
// we re- read the tsconfig file content and update the project only if we havent already done so // we re- read the tsconfig file content and update the project only if we havent already done so
// otherwise we create a new one. // otherwise we create a new one.
const configFileName = this.getConfigFileNameForFile(info.fileName); const configFileName = this.getConfigFileNameForFile(info);
if (configFileName) { if (configFileName) {
let project = this.findConfiguredProjectByProjectName(configFileName); let project = this.findConfiguredProjectByProjectName(configFileName);
if (!project) { if (!project) {
@ -1614,9 +1594,10 @@ namespace ts.server {
let configFileName: NormalizedPath; let configFileName: NormalizedPath;
let configFileErrors: Diagnostic[]; let configFileErrors: Diagnostic[];
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName); let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
if (!project) { if (!project) {
configFileName = this.getConfigFileNameForFile(fileName, projectRootPath); configFileName = this.getConfigFileNameForFile(info, projectRootPath);
if (configFileName) { if (configFileName) {
project = this.findConfiguredProjectByProjectName(configFileName); project = this.findConfiguredProjectByProjectName(configFileName);
if (!project) { if (!project) {
@ -1640,7 +1621,6 @@ namespace ts.server {
} }
// at this point if file is the part of some configured/external project then this project should be created // at this point if file is the part of some configured/external project then this project should be created
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true); this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true);
// Delete the orphan files here because there might be orphan script infos (which are not part of project) // Delete the orphan files here because there might be orphan script infos (which are not part of project)
// when some file/s were closed which resulted in project removal. // when some file/s were closed which resulted in project removal.

View file

@ -924,7 +924,6 @@ namespace ts.server {
} }
addRoot(info: ScriptInfo) { addRoot(info: ScriptInfo) {
this.projectService.startWatchingRootOfInferredProject(info);
if (!this._isJsInferredProject && info.isJavaScript()) { if (!this._isJsInferredProject && info.isJavaScript()) {
this.toggleJsInferredProject(/*isJsInferredProject*/ true); this.toggleJsInferredProject(/*isJsInferredProject*/ true);
} }
@ -932,7 +931,6 @@ namespace ts.server {
} }
removeRoot(info: ScriptInfo) { removeRoot(info: ScriptInfo) {
this.projectService.stopWatchingRootOfInferredProject(info, WatcherCloseReason.NotNeeded);
super.removeRoot(info); super.removeRoot(info);
if (this._isJsInferredProject && info.isJavaScript()) { if (this._isJsInferredProject && info.isJavaScript()) {
if (!some(this.getRootScriptInfos(), info => info.isJavaScript())) { if (!some(this.getRootScriptInfos(), info => info.isJavaScript())) {
@ -957,7 +955,6 @@ namespace ts.server {
} }
close() { close() {
forEach(this.getRootScriptInfos(), root => this.projectService.stopWatchingRootOfInferredProject(root, WatcherCloseReason.ProjectClose));
super.close(); super.close();
} }
@ -1208,7 +1205,6 @@ namespace ts.server {
if (this.configFileWatcher) { if (this.configFileWatcher) {
this.projectService.closeFileWatcher(WatchType.ConfigFilePath, this, this.getConfigFilePath(), this.configFileWatcher, WatcherCloseReason.ProjectClose); this.projectService.closeFileWatcher(WatchType.ConfigFilePath, this, this.getConfigFilePath(), this.configFileWatcher, WatcherCloseReason.ProjectClose);
this.configFileWatcher = undefined; this.configFileWatcher = undefined;
this.projectService.setConfigFilePresenceByClosedConfigFile(this);
} }
this.stopWatchingTypeRoots(WatcherCloseReason.ProjectClose); this.stopWatchingTypeRoots(WatcherCloseReason.ProjectClose);