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) {
|
||||
const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath);
|
||||
if (existingResult) {
|
||||
// This was a folder already present, remove it if this doesnt exist any more
|
||||
if (!host.directoryExists(fileOrDirectory)) {
|
||||
cachedReadDirectoryResult.delete(fileOrDirectoryPath);
|
||||
}
|
||||
// Just clear the cache for now
|
||||
// For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated
|
||||
clearCache();
|
||||
}
|
||||
else {
|
||||
// This was earlier a file (hence not in cached directory contents)
|
||||
|
@ -3113,8 +3112,14 @@ namespace ts {
|
|||
fileExists: host.fileExists(fileOrDirectoryPath),
|
||||
directoryExists: host.directoryExists(fileOrDirectoryPath)
|
||||
};
|
||||
if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) {
|
||||
// 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);
|
||||
updateFileSystemEntry(parentResult.directories, baseName, fsQueryResult.directoryExists);
|
||||
}
|
||||
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) {
|
||||
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 watchedWildcardDirectories: Map<WildcardDirectoryWatcher>; // map of watchers for the wild card directories in the config file
|
||||
let timerToUpdateProgram: any; // timer callback to recompile the program
|
||||
|
@ -488,7 +488,7 @@ namespace ts {
|
|||
|
||||
function scheduleProgramReload() {
|
||||
Debug.assert(!!configFileName);
|
||||
needsReload = true;
|
||||
reloadLevel = ConfigFileProgramReloadLevel.Full;
|
||||
scheduleProgramUpdate();
|
||||
}
|
||||
|
||||
|
@ -496,17 +496,30 @@ namespace ts {
|
|||
timerToUpdateProgram = undefined;
|
||||
reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation));
|
||||
|
||||
if (needsReload) {
|
||||
reloadConfigFile();
|
||||
switch (reloadLevel) {
|
||||
case ConfigFileProgramReloadLevel.Partial:
|
||||
return reloadFileNamesFromConfigFile();
|
||||
case ConfigFileProgramReloadLevel.Full:
|
||||
return reloadConfigFile();
|
||||
default:
|
||||
return synchronizeProgram();
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
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() {
|
||||
writeLog(`Reloading config file: ${configFileName}`);
|
||||
needsReload = false;
|
||||
reloadLevel = ConfigFileProgramReloadLevel.None;
|
||||
|
||||
const cachedHost = directoryStructureHost as CachedDirectoryStructureHost;
|
||||
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
|
||||
// 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}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload is pending, do the reload
|
||||
if (!needsReload) {
|
||||
const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost);
|
||||
if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) {
|
||||
reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName));
|
||||
}
|
||||
rootFileNames = result.fileNames;
|
||||
if (reloadLevel !== ConfigFileProgramReloadLevel.Full) {
|
||||
reloadLevel = ConfigFileProgramReloadLevel.Partial;
|
||||
|
||||
// Schedule Update the program
|
||||
scheduleProgramUpdate();
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
|
||||
/* @internal */
|
||||
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
|
||||
*/
|
||||
|
|
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) {
|
||||
if (isString(fileOrDirectory.content)) {
|
||||
const file = this.toFile(fileOrDirectory);
|
||||
|
@ -393,7 +426,7 @@ interface Array<T> {}`
|
|||
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 baseFolder = this.fs.get(basePath) as Folder;
|
||||
if (basePath !== fileOrDirectory.path) {
|
||||
|
@ -406,7 +439,7 @@ interface Array<T> {}`
|
|||
this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted);
|
||||
}
|
||||
else {
|
||||
Debug.assert(fileOrDirectory.entries.length === 0);
|
||||
Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming);
|
||||
const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath);
|
||||
// Invoke directory and recursive directory watcher for the folder
|
||||
// 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
|
||||
// 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}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload is pending, do the reload
|
||||
if (!project.pendingReload) {
|
||||
const configFileSpecs = project.configFileSpecs;
|
||||
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);
|
||||
if (project.pendingReload !== ConfigFileProgramReloadLevel.Full) {
|
||||
project.pendingReload = ConfigFileProgramReloadLevel.Partial;
|
||||
this.delayUpdateProjectGraphAndInferredProjectsRefresh(project);
|
||||
}
|
||||
},
|
||||
|
@ -844,7 +841,7 @@ namespace ts.server {
|
|||
}
|
||||
else {
|
||||
this.logConfigFileWatchUpdate(project.getConfigFilePath(), project.canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingInferredRootFiles);
|
||||
project.pendingReload = true;
|
||||
project.pendingReload = ConfigFileProgramReloadLevel.Full;
|
||||
this.delayUpdateProjectGraph(project);
|
||||
// As we scheduled the update on configured project graph,
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
@ -1886,7 +1896,7 @@ namespace ts.server {
|
|||
}
|
||||
else if (!updatedProjects.has(configFileName)) {
|
||||
if (delayReload) {
|
||||
project.pendingReload = true;
|
||||
project.pendingReload = ConfigFileProgramReloadLevel.Full;
|
||||
this.delayUpdateProjectGraph(project);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -1122,7 +1122,7 @@ namespace ts.server {
|
|||
readonly canonicalConfigFilePath: NormalizedPath;
|
||||
|
||||
/* @internal */
|
||||
pendingReload: boolean;
|
||||
pendingReload: ConfigFileProgramReloadLevel;
|
||||
|
||||
/*@internal*/
|
||||
configFileSpecs: ConfigFileSpecs;
|
||||
|
@ -1162,13 +1162,18 @@ namespace ts.server {
|
|||
* @returns: true if set of files in the project stays the same and false - otherwise.
|
||||
*/
|
||||
updateGraph(): boolean {
|
||||
if (this.pendingReload) {
|
||||
this.pendingReload = false;
|
||||
const reloadLevel = this.pendingReload;
|
||||
this.pendingReload = ConfigFileProgramReloadLevel.None;
|
||||
switch (reloadLevel) {
|
||||
case ConfigFileProgramReloadLevel.Partial:
|
||||
return this.projectService.reloadFileNamesOfConfiguredProject(this);
|
||||
case ConfigFileProgramReloadLevel.Full:
|
||||
this.projectService.reloadConfiguredProject(this);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return super.updateGraph();
|
||||
}
|
||||
}
|
||||
|
||||
/*@internal*/
|
||||
getCachedDirectoryStructureHost() {
|
||||
|
|
Loading…
Reference in a new issue