Merge pull request #19786 from Microsoft/directoryRename
Handle the watch when folders are added/removed/renamed in wild card folder
This commit is contained in:
commit
fe40873664
|
@ -3097,10 +3097,9 @@ namespace ts {
|
||||||
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
|
function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) {
|
||||||
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
|
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
|
||||||
if (existingResult) {
|
if (existingResult) {
|
||||||
// This was a folder already present, remove it if this doesnt exist any more
|
// Just clear the cache for now
|
||||||
if (!host.directoryExists(fileOrDirectory)) {
|
// For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated
|
||||||
cachedReadDirectoryResult.delete(fileOrDirectoryPath);
|
clearCache();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// This was earlier a file (hence not in cached directory contents)
|
// This was earlier a file (hence not in cached directory contents)
|
||||||
|
@ -3113,8 +3112,14 @@ namespace ts {
|
||||||
fileExists: host.fileExists(fileOrDirectoryPath),
|
fileExists: host.fileExists(fileOrDirectoryPath),
|
||||||
directoryExists: host.directoryExists(fileOrDirectoryPath)
|
directoryExists: host.directoryExists(fileOrDirectoryPath)
|
||||||
};
|
};
|
||||||
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
|
if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) {
|
||||||
updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
|
// Folder added or removed, clear the cache instead of updating the folder and its structure
|
||||||
|
clearCache();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// No need to update the directory structure, just files
|
||||||
|
updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists);
|
||||||
|
}
|
||||||
return fsQueryResult;
|
return fsQueryResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,7 @@ namespace ts {
|
||||||
|
|
||||||
function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike<WatchDirectoryFlags>, optionsToExtendForConfigFile?: CompilerOptions) {
|
function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike<WatchDirectoryFlags>, optionsToExtendForConfigFile?: CompilerOptions) {
|
||||||
let program: Program;
|
let program: Program;
|
||||||
let needsReload: boolean; // true if the config file changed and needs to reload it from the disk
|
let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc
|
||||||
let missingFilesMap: Map<FileWatcher>; // Map of file watchers for the missing files
|
let missingFilesMap: Map<FileWatcher>; // Map of file watchers for the missing files
|
||||||
let watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
|
let watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
|
||||||
let timerToUpdateProgram: any; // timer callback to recompile the program
|
let timerToUpdateProgram: any; // timer callback to recompile the program
|
||||||
|
@ -488,7 +488,7 @@ namespace ts {
|
||||||
|
|
||||||
function scheduleProgramReload() {
|
function scheduleProgramReload() {
|
||||||
Debug.assert(!!configFileName);
|
Debug.assert(!!configFileName);
|
||||||
needsReload = true;
|
reloadLevel = ConfigFileProgramReloadLevel.Full;
|
||||||
scheduleProgramUpdate();
|
scheduleProgramUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,17 +496,30 @@ namespace ts {
|
||||||
timerToUpdateProgram = undefined;
|
timerToUpdateProgram = undefined;
|
||||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
|
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
|
||||||
|
|
||||||
if (needsReload) {
|
switch (reloadLevel) {
|
||||||
reloadConfigFile();
|
case ConfigFileProgramReloadLevel.Partial:
|
||||||
|
return reloadFileNamesFromConfigFile();
|
||||||
|
case ConfigFileProgramReloadLevel.Full:
|
||||||
|
return reloadConfigFile();
|
||||||
|
default:
|
||||||
|
return synchronizeProgram();
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
synchronizeProgram();
|
|
||||||
|
function reloadFileNamesFromConfigFile() {
|
||||||
|
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
|
||||||
|
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
||||||
|
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||||
}
|
}
|
||||||
|
rootFileNames = result.fileNames;
|
||||||
|
|
||||||
|
// Update the program
|
||||||
|
synchronizeProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadConfigFile() {
|
function reloadConfigFile() {
|
||||||
writeLog(`Reloading config file: ${configFileName}`);
|
writeLog(`Reloading config file: ${configFileName}`);
|
||||||
needsReload = false;
|
reloadLevel = ConfigFileProgramReloadLevel.None;
|
||||||
|
|
||||||
const cachedHost = directoryStructureHost as CachedDirectoryStructureHost;
|
const cachedHost = directoryStructureHost as CachedDirectoryStructureHost;
|
||||||
cachedHost.clearCache();
|
cachedHost.clearCache();
|
||||||
|
@ -611,18 +624,14 @@ namespace ts {
|
||||||
|
|
||||||
// If the the added or created file or directory is not supported file name, ignore the file
|
// If the the added or created file or directory is not supported file name, ignore the file
|
||||||
// But when watched directory is added/removed, we need to reload the file list
|
// But when watched directory is added/removed, we need to reload the file list
|
||||||
if (fileOrDirectoryPath !== directory && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
|
if (fileOrDirectoryPath !== directory && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, compilerOptions)) {
|
||||||
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
|
writeLog(`Project: ${configFileName} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload is pending, do the reload
|
// Reload is pending, do the reload
|
||||||
if (!needsReload) {
|
if (reloadLevel !== ConfigFileProgramReloadLevel.Full) {
|
||||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
|
reloadLevel = ConfigFileProgramReloadLevel.Partial;
|
||||||
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
|
||||||
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
|
||||||
}
|
|
||||||
rootFileNames = result.fileNames;
|
|
||||||
|
|
||||||
// Schedule Update the program
|
// Schedule Update the program
|
||||||
scheduleProgramUpdate();
|
scheduleProgramUpdate();
|
||||||
|
|
|
@ -2,6 +2,14 @@
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
namespace ts {
|
namespace ts {
|
||||||
|
export enum ConfigFileProgramReloadLevel {
|
||||||
|
None,
|
||||||
|
/** Update the file name list from the disk */
|
||||||
|
Partial,
|
||||||
|
/** Reload completely by re-reading contents of config file from disk and updating program */
|
||||||
|
Full
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the existing missing file watches with the new set of missing files after new program is created
|
* Updates the existing missing file watches with the new set of missing files after new program is created
|
||||||
*/
|
*/
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -346,6 +346,39 @@ interface Array<T> {}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renameFolder(folderName: string, newFolderName: string) {
|
||||||
|
const fullPath = getNormalizedAbsolutePath(folderName, this.currentDirectory);
|
||||||
|
const path = this.toPath(fullPath);
|
||||||
|
const folder = this.fs.get(path) as Folder;
|
||||||
|
Debug.assert(!!folder);
|
||||||
|
|
||||||
|
// Only remove the folder
|
||||||
|
this.removeFileOrFolder(folder, returnFalse, /*isRenaming*/ true);
|
||||||
|
|
||||||
|
// Add updated folder with new folder name
|
||||||
|
const newFullPath = getNormalizedAbsolutePath(newFolderName, this.currentDirectory);
|
||||||
|
const newFolder = this.toFolder(newFullPath);
|
||||||
|
const newPath = newFolder.path;
|
||||||
|
const basePath = getDirectoryPath(path);
|
||||||
|
Debug.assert(basePath !== path);
|
||||||
|
Debug.assert(basePath === getDirectoryPath(newPath));
|
||||||
|
const baseFolder = this.fs.get(basePath) as Folder;
|
||||||
|
this.addFileOrFolderInFolder(baseFolder, newFolder);
|
||||||
|
|
||||||
|
// Invoke watches for files in the folder as deleted (from old path)
|
||||||
|
for (const entry of folder.entries) {
|
||||||
|
Debug.assert(isFile(entry));
|
||||||
|
this.fs.delete(entry.path);
|
||||||
|
this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Deleted);
|
||||||
|
|
||||||
|
entry.fullPath = combinePaths(newFullPath, getBaseFileName(entry.fullPath));
|
||||||
|
entry.path = this.toPath(entry.fullPath);
|
||||||
|
newFolder.entries.push(entry);
|
||||||
|
this.fs.set(entry.path, entry);
|
||||||
|
this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Created);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ensureFileOrFolder(fileOrDirectory: FileOrFolder, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) {
|
ensureFileOrFolder(fileOrDirectory: FileOrFolder, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) {
|
||||||
if (isString(fileOrDirectory.content)) {
|
if (isString(fileOrDirectory.content)) {
|
||||||
const file = this.toFile(fileOrDirectory);
|
const file = this.toFile(fileOrDirectory);
|
||||||
|
@ -393,7 +426,7 @@ interface Array<T> {}`
|
||||||
this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath);
|
this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeFileOrFolder(fileOrDirectory: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean) {
|
private removeFileOrFolder(fileOrDirectory: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean, isRenaming?: boolean) {
|
||||||
const basePath = getDirectoryPath(fileOrDirectory.path);
|
const basePath = getDirectoryPath(fileOrDirectory.path);
|
||||||
const baseFolder = this.fs.get(basePath) as Folder;
|
const baseFolder = this.fs.get(basePath) as Folder;
|
||||||
if (basePath !== fileOrDirectory.path) {
|
if (basePath !== fileOrDirectory.path) {
|
||||||
|
@ -406,7 +439,7 @@ interface Array<T> {}`
|
||||||
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
|
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Debug.assert(fileOrDirectory.entries.length === 0);
|
Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming);
|
||||||
const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath);
|
const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath);
|
||||||
// Invoke directory and recursive directory watcher for the folder
|
// Invoke directory and recursive directory watcher for the folder
|
||||||
// Here we arent invoking recursive directory watchers for the base folders
|
// Here we arent invoking recursive directory watchers for the base folders
|
||||||
|
|
|
@ -801,17 +801,14 @@ namespace ts.server {
|
||||||
|
|
||||||
// If the the added or created file or directory is not supported file name, ignore the file
|
// If the the added or created file or directory is not supported file name, ignore the file
|
||||||
// But when watched directory is added/removed, we need to reload the file list
|
// But when watched directory is added/removed, we need to reload the file list
|
||||||
if (fileOrDirectoryPath !== directory && !isSupportedSourceFileName(fileOrDirectory, project.getCompilationSettings(), this.hostConfiguration.extraFileExtensions)) {
|
if (fileOrDirectoryPath !== directory && hasExtension(fileOrDirectoryPath) && !isSupportedSourceFileName(fileOrDirectory, project.getCompilationSettings(), this.hostConfiguration.extraFileExtensions)) {
|
||||||
this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
|
this.logger.info(`Project: ${configFilename} Detected file add/remove of non supported extension: ${fileOrDirectory}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reload is pending, do the reload
|
// Reload is pending, do the reload
|
||||||
if (!project.pendingReload) {
|
if (project.pendingReload !== ConfigFileProgramReloadLevel.Full) {
|
||||||
const configFileSpecs = project.configFileSpecs;
|
project.pendingReload = ConfigFileProgramReloadLevel.Partial;
|
||||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFilename), project.getCompilationSettings(), project.getCachedDirectoryStructureHost(), this.hostConfiguration.extraFileExtensions);
|
|
||||||
project.updateErrorOnNoInputFiles(result.fileNames.length !== 0);
|
|
||||||
this.updateNonInferredProjectFiles(project, result.fileNames, fileNamePropertyReader);
|
|
||||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -844,7 +841,7 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.logConfigFileWatchUpdate(project.getConfigFilePath(), project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingInferredRootFiles);
|
this.logConfigFileWatchUpdate(project.getConfigFilePath(), project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingInferredRootFiles);
|
||||||
project.pendingReload = true;
|
project.pendingReload = ConfigFileProgramReloadLevel.Full;
|
||||||
this.delayUpdateProjectGraph(project);
|
this.delayUpdateProjectGraph(project);
|
||||||
// As we scheduled the update on configured project graph,
|
// As we scheduled the update on configured project graph,
|
||||||
// we would need to schedule the project reload for only the root of inferred projects
|
// we would need to schedule the project reload for only the root of inferred projects
|
||||||
|
@ -1592,6 +1589,19 @@ namespace ts.server {
|
||||||
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, newTypeAcquisition);
|
this.addFilesToNonInferredProjectAndUpdateGraph(project, newUncheckedFiles, propertyReader, newTypeAcquisition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload the file names from config file specs and update the project graph
|
||||||
|
*/
|
||||||
|
/*@internal*/
|
||||||
|
reloadFileNamesOfConfiguredProject(project: ConfiguredProject): boolean {
|
||||||
|
const configFileSpecs = project.configFileSpecs;
|
||||||
|
const configFileName = project.getConfigFilePath();
|
||||||
|
const fileNamesResult = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), project.getCompilationSettings(), project.getCachedDirectoryStructureHost(), this.hostConfiguration.extraFileExtensions);
|
||||||
|
project.updateErrorOnNoInputFiles(fileNamesResult.fileNames.length !== 0);
|
||||||
|
this.updateNonInferredProjectFiles(project, fileNamesResult.fileNames, fileNamePropertyReader);
|
||||||
|
return project.updateGraph();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the config file of the project again and update the project
|
* Read the config file of the project again and update the project
|
||||||
*/
|
*/
|
||||||
|
@ -1886,7 +1896,7 @@ namespace ts.server {
|
||||||
}
|
}
|
||||||
else if (!updatedProjects.has(configFileName)) {
|
else if (!updatedProjects.has(configFileName)) {
|
||||||
if (delayReload) {
|
if (delayReload) {
|
||||||
project.pendingReload = true;
|
project.pendingReload = ConfigFileProgramReloadLevel.Full;
|
||||||
this.delayUpdateProjectGraph(project);
|
this.delayUpdateProjectGraph(project);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -1122,7 +1122,7 @@ namespace ts.server {
|
||||||
readonly canonicalConfigFilePath: NormalizedPath;
|
readonly canonicalConfigFilePath: NormalizedPath;
|
||||||
|
|
||||||
/* @internal */
|
/* @internal */
|
||||||
pendingReload: boolean;
|
pendingReload: ConfigFileProgramReloadLevel;
|
||||||
|
|
||||||
/*@internal*/
|
/*@internal*/
|
||||||
configFileSpecs: ConfigFileSpecs;
|
configFileSpecs: ConfigFileSpecs;
|
||||||
|
@ -1162,12 +1162,17 @@ namespace ts.server {
|
||||||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||||
*/
|
*/
|
||||||
updateGraph(): boolean {
|
updateGraph(): boolean {
|
||||||
if (this.pendingReload) {
|
const reloadLevel = this.pendingReload;
|
||||||
this.pendingReload = false;
|
this.pendingReload = ConfigFileProgramReloadLevel.None;
|
||||||
this.projectService.reloadConfiguredProject(this);
|
switch (reloadLevel) {
|
||||||
return true;
|
case ConfigFileProgramReloadLevel.Partial:
|
||||||
|
return this.projectService.reloadFileNamesOfConfiguredProject(this);
|
||||||
|
case ConfigFileProgramReloadLevel.Full:
|
||||||
|
this.projectService.reloadConfiguredProject(this);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return super.updateGraph();
|
||||||
}
|
}
|
||||||
return super.updateGraph();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@internal*/
|
/*@internal*/
|
||||||
|
|
Loading…
Reference in a new issue