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:
parent
62663a10ba
commit
dcbd7b10f1
|
@ -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", () => {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue