diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 21cf4275cca..fb92f59d12d 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1482,6 +1482,7 @@ namespace ts { return error.code === Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code; } + /*@internal*/ export function getErrorForNoInputFiles({ includeSpecs, excludeSpecs }: ConfigFileSpecs, configFileName: string | undefined) { return createCompilerDiagnostic( Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2, @@ -1928,9 +1929,9 @@ namespace ts { /** * Expands an array of file specifications. * - * @param fileNames The literal file names to include. - * @param include The wildcard file specifications to include. - * @param exclude The wildcard file specifications to exclude. + * @param filesSpecs The literal file names to include. + * @param includeSpecs The wildcard file specifications to include. + * @param excludeSpecs The wildcard file specifications to exclude. * @param basePath The base path for any relative file specifications. * @param options Compiler options. * @param host The host used to resolve files and directories. diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 14cab78bc84..76d242e8b08 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3607,10 +3607,6 @@ namespace ts { export function getCombinedLocalAndExportSymbolFlags(symbol: Symbol): SymbolFlags { return symbol.exportSymbol ? symbol.exportSymbol.flags | symbol.flags : symbol.flags; } - - export function isRecursiveDirectoryWatch(flags: WatchDirectoryFlags) { - return (flags & WatchDirectoryFlags.Recursive) !== 0; - } } namespace ts { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index deaa17c2afe..f4513cf7113 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -534,7 +534,7 @@ namespace ts.projectSystem { } /** - * This will call the directory watcher for the foldeFullPath and recursive directory watchers for this and base folders + * This will call the directory watcher for the folderFullPath and recursive directory watchers for this and base folders */ private invokeDirectoryWatcher(folderFullPath: string, fileName: string) { const relativePath = this.getRelativePathToDirectory(folderFullPath, fileName); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 27dbee2ebf5..bf480e355fe 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -246,7 +246,7 @@ namespace ts.server { export const enum WatchType { ConfigFilePath = "Config file for the program", MissingFilePath = "Missing file from program", - WildCardDirectories = "Wild card directory", + WildcardDirectories = "Wild card directory", TypeRoot = "Type root of the project", ClosedScriptInfo = "Closed Script info", ConfigFileForInferredRoot = "Config file for the inferred project root" @@ -363,7 +363,7 @@ namespace ts.server { * - Or it is present if we have configured project open with config file at that location * In this case the exists property is always true */ - private readonly mapOfConfigFileExistenceInfo = createMap(); + private readonly configFileExistenceInfoCache = createMap(); private readonly throttledOperations: ThrottledOperations; private readonly hostConfiguration: HostConfiguration; @@ -693,7 +693,7 @@ namespace ts.server { } private onConfigChangedForConfiguredProject(project: ConfiguredProject, eventKind: FileWatcherEventKind) { - const configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(project.canonicalConfigFilePath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); if (eventKind === FileWatcherEventKind.Deleted) { // Update the cached status // We arent updating or removing the cached config file presence info as that will be taken care of by @@ -725,7 +725,7 @@ namespace ts.server { private onConfigFileChangeForOpenScriptInfo(configFileName: NormalizedPath, eventKind: FileWatcherEventKind) { // This callback is called only if we dont have config file project for this config file const canonicalConfigPath = normalizedPathToPath(configFileName, this.currentDirectory, this.toCanonicalFileName); - const configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(canonicalConfigPath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigPath); configFileExistenceInfo.exists = (eventKind !== FileWatcherEventKind.Deleted); this.logConfigFileWatchUpdate(configFileName, canonicalConfigPath, configFileExistenceInfo, ConfigFileWatcherStatus.ReloadingFiles); @@ -852,7 +852,7 @@ namespace ts.server { } private configFileExists(configFileName: NormalizedPath, canonicalConfigFilePath: string, info: ScriptInfo) { - let configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(canonicalConfigFilePath); + let configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (configFileExistenceInfo) { // By default the info would get impacted by presence of config file since its in the detection path // Only adding the info as a root to inferred project will need the existence to be watched by file watcher @@ -877,13 +877,13 @@ namespace ts.server { openFilesImpactedByConfigFile.set(info.path, false); const exists = this.host.fileExists(configFileName); configFileExistenceInfo = { exists, openFilesImpactedByConfigFile }; - this.mapOfConfigFileExistenceInfo.set(canonicalConfigFilePath, configFileExistenceInfo); + this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFileExistenceInfo); this.logConfigFileWatchUpdate(configFileName, canonicalConfigFilePath, configFileExistenceInfo, ConfigFileWatcherStatus.OpenFilesImpactedByConfigFileAdd); return exists; } private setConfigFileExistenceByNewConfiguredProject(project: ConfiguredProject) { - const configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(project.canonicalConfigFilePath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(project.canonicalConfigFilePath); if (configFileExistenceInfo) { Debug.assert(configFileExistenceInfo.exists); // close existing watcher @@ -900,7 +900,7 @@ namespace ts.server { else { // We could be in this scenario if project is the configured project tracked by external project // Since that route doesnt check if the config file is present or not - this.mapOfConfigFileExistenceInfo.set(project.canonicalConfigFilePath, { + this.configFileExistenceInfoCache.set(project.canonicalConfigFilePath, { exists: true, openFilesImpactedByConfigFile: createMap() }); @@ -915,7 +915,7 @@ namespace ts.server { } private setConfigFileExistenceInfoByClosedConfiguredProject(closedProject: ConfiguredProject) { - const configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(closedProject.canonicalConfigFilePath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(closedProject.canonicalConfigFilePath); Debug.assert(!!configFileExistenceInfo); if (configFileExistenceInfo.openFilesImpactedByConfigFile.size) { const configFileName = closedProject.getConfigFilePath(); @@ -933,7 +933,7 @@ namespace ts.server { } else { // There is not a single file open thats tracking the status of this config file. Remove from cache - this.mapOfConfigFileExistenceInfo.delete(closedProject.canonicalConfigFilePath); + this.configFileExistenceInfoCache.delete(closedProject.canonicalConfigFilePath); } } @@ -983,7 +983,7 @@ namespace ts.server { private stopWatchingConfigFilesForClosedScriptInfo(info: ScriptInfo) { Debug.assert(!info.isScriptOpen()); this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { - const configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(canonicalConfigFilePath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (configFileExistenceInfo) { const infoIsRootOfInferredProject = configFileExistenceInfo.openFilesImpactedByConfigFile.get(info.path); @@ -1006,7 +1006,7 @@ namespace ts.server { if (!configFileExistenceInfo.openFilesImpactedByConfigFile.size && !this.getConfiguredProjectByCanonicalConfigFilePath(canonicalConfigFilePath)) { Debug.assert(!configFileExistenceInfo.configFileWatcherForRootOfInferredProject); - this.mapOfConfigFileExistenceInfo.delete(canonicalConfigFilePath); + this.configFileExistenceInfoCache.delete(canonicalConfigFilePath); } } }); @@ -1019,14 +1019,14 @@ namespace ts.server { startWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo) { Debug.assert(info.isScriptOpen()); this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { - let configFilePresenceInfo = this.mapOfConfigFileExistenceInfo.get(canonicalConfigFilePath); + let configFilePresenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (!configFilePresenceInfo) { // Create the cache configFilePresenceInfo = { exists: this.host.fileExists(configFileName), openFilesImpactedByConfigFile: createMap() }; - this.mapOfConfigFileExistenceInfo.set(canonicalConfigFilePath, configFilePresenceInfo); + this.configFileExistenceInfoCache.set(canonicalConfigFilePath, configFilePresenceInfo); } // Set this file as the root of inferred project @@ -1050,7 +1050,7 @@ namespace ts.server { /* @internal */ stopWatchingConfigFilesForInferredProjectRoot(info: ScriptInfo, reason: WatcherCloseReason) { this.forEachConfigFileLocation(info, (configFileName, canonicalConfigFilePath) => { - const configFileExistenceInfo = this.mapOfConfigFileExistenceInfo.get(canonicalConfigFilePath); + const configFileExistenceInfo = this.configFileExistenceInfoCache.get(canonicalConfigFilePath); if (configFileExistenceInfo && configFileExistenceInfo.openFilesImpactedByConfigFile.has(info.path)) { Debug.assert(info.isScriptOpen()); @@ -1607,14 +1607,14 @@ namespace ts.server { /* @internal */ closeDirectoryWatcher(watchType: WatchType, project: Project, directory: string, watcher: FileWatcher, flags: WatchDirectoryFlags, reason: WatcherCloseReason) { - const recursive = isRecursiveDirectoryWatch(flags); + const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0; this.logger.info(`DirectoryWatcher ${recursive ? "recursive" : ""}:: Close: ${directory} Project: ${project.getProjectName()} WatchType: ${watchType} Reason: ${reason}`); watcher.close(); } /* @internal */ addDirectoryWatcher(watchType: WatchType, project: Project, directory: string, cb: ServerDirectoryWatcherCallback, flags: WatchDirectoryFlags) { - const recursive = isRecursiveDirectoryWatch(flags); + const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0; this.logger.info(`DirectoryWatcher ${recursive ? "recursive" : ""}:: Added: ${directory} Project: ${project.getProjectName()} WatchType: ${watchType}`); return this.host.watchDirectory(directory, fileName => { const path = toNormalizedPath(getNormalizedAbsolutePath(fileName, directory)); diff --git a/src/server/project.ts b/src/server/project.ts index a8506982bec..e2b536017c5 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -1153,22 +1153,25 @@ namespace ts.server { createNewValue: (directory, flags) => { return { watcher: this.projectService.addDirectoryWatcher( - WatchType.WildCardDirectories, this, directory, + WatchType.WildcardDirectories, this, directory, path => this.projectService.onFileAddOrRemoveInWatchedDirectoryOfProject(this, path), flags ), flags }; }, - // Close existing watch thats not needed any more or doesnt match recursive flags - onDeleteExistingValue: (directory, wildcardDirectoryWatcher, isNotSame) => - this.closeWildcardDirectoryWatcher(directory, wildcardDirectoryWatcher, isNotSame ? WatcherCloseReason.RecursiveChanged : WatcherCloseReason.NotNeeded), + // Close existing watch thats not needed any more + onDeleteExistingValue: (directory, wildcardDirectoryWatcher) => + this.closeWildcardDirectoryWatcher(directory, wildcardDirectoryWatcher, WatcherCloseReason.NotNeeded), + // Close existing watch that doesnt match in the recursive flags + onDeleteExistingMismatchValue: (directory, wildcardDirectoryWatcher) => + this.closeWildcardDirectoryWatcher(directory, wildcardDirectoryWatcher, WatcherCloseReason.RecursiveChanged), } ); } private closeWildcardDirectoryWatcher(directory: string, { watcher, flags }: WildcardDirectoryWatcher, closeReason: WatcherCloseReason) { - this.projectService.closeDirectoryWatcher(WatchType.WildCardDirectories, this, directory, watcher, flags, closeReason); + this.projectService.closeDirectoryWatcher(WatchType.WildcardDirectories, this, directory, watcher, flags, closeReason); } stopWatchingWildCards(reason: WatcherCloseReason) { diff --git a/src/server/utilities.ts b/src/server/utilities.ts index 79065f48ff5..0699b4c9b6c 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -299,14 +299,14 @@ namespace ts.server { } /** - * clears already present map (!== undefined) by calling onDeleteExistingValue callback before deleting that key/value + * clears already present map by calling onDeleteExistingValue callback before deleting that key/value */ export function clearMap(map: Map, onDeleteExistingValue: (key: string, existingValue: T) => void) { // Remove all map.forEach((existingValue, key) => { - map.delete(key); onDeleteExistingValue(key, existingValue); }); + map.clear(); } export interface MutateMapOptions { @@ -314,6 +314,7 @@ namespace ts.server { onDeleteExistingValue(key: string, existingValue: T, isNotSame?: boolean): void; isSameValue?(existingValue: T, valueInNewMap: U): boolean; + onDeleteExistingMismatchValue?(key: string, existingValue: T): void; } /** @@ -322,16 +323,20 @@ namespace ts.server { export function mutateMap(map: Map, newMap: ReadonlyMap, options: MutateMapOptions) { // If there are new values update them if (newMap) { - const { isSameValue, createNewValue, onDeleteExistingValue } = options; + const { isSameValue, createNewValue, onDeleteExistingValue, onDeleteExistingMismatchValue } = options; // Needs update map.forEach((existingValue, key) => { const valueInNewMap = newMap.get(key); - if (valueInNewMap === undefined || - // different value - remove it - (isSameValue && !isSameValue(existingValue, valueInNewMap))) { - const isNotSame = valueInNewMap !== undefined; + // Not present any more in new map, remove it + if (valueInNewMap === undefined) { map.delete(key); - onDeleteExistingValue(key, existingValue, isNotSame); + onDeleteExistingValue(key, existingValue); + } + // different value - remove it + else if (isSameValue && !isSameValue(existingValue, valueInNewMap)) { + Debug.assert(!!onDeleteExistingMismatchValue); + map.delete(key); + onDeleteExistingMismatchValue(key, existingValue); } });