From 4046bb15acc12a2840e6b326f4ce5eb1f0cc25f6 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 13 Mar 2018 15:09:13 -0700 Subject: [PATCH 1/2] Do not send first missing file event as well. --- src/compiler/sys.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 6fce65c3f3620..e3c304afb8f36 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -752,16 +752,17 @@ namespace ts { }; function fileChanged(curr: any, prev: any) { + // previous event kind check is to ensure we recongnize the file as previously also missing when it is restored or renamed twice (that is it disappears and reappears) + // In such case, prevTime returned is same as prev time of event when file was deleted as per node documentation + const isPreviouslyDeleted = +prev.mtime === 0 || eventKind === FileWatcherEventKind.Deleted; if (+curr.mtime === 0) { - if (eventKind === FileWatcherEventKind.Deleted) { + if (isPreviouslyDeleted) { // Already deleted file, no need to callback again return; } eventKind = FileWatcherEventKind.Deleted; } - // previous event kind check is to ensure we send created event when file is restored or renamed twice (that is it disappears and reappears) - // since in that case the prevTime returned is same as prev time of event when file was deleted as per node documentation - else if (+prev.mtime === 0 || eventKind === FileWatcherEventKind.Deleted) { + else if (isPreviouslyDeleted) { eventKind = FileWatcherEventKind.Created; } // If there is no change in modified time, ignore the event From e48ed3f9c20e3b33399a03502a35c8ccd0621cfe Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 15 Mar 2018 10:44:09 -0700 Subject: [PATCH 2/2] Do not close the file watches just to create new ones in the type installer Fixes #22581 --- .../typingsInstaller/typingsInstaller.ts | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 80c1c7be6ea7c..f87e6d7e8ab6a 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -43,7 +43,7 @@ namespace ts.server.typingsInstaller { private readonly packageNameToTypingLocation: Map = createMap(); private readonly missingTypingsSet: Map = createMap(); private readonly knownCachesSet: Map = createMap(); - private readonly projectWatchers: Map = createMap(); + private readonly projectWatchers = createMap>(); private safeList: JsTyping.SafeList | undefined; readonly pendingRunRequests: PendingRequest[] = []; @@ -80,10 +80,7 @@ namespace ts.server.typingsInstaller { } return; } - for (const w of watchers) { - w.close(); - } - + clearMap(watchers, closeFileWatcher); this.projectWatchers.delete(projectName); if (this.log.isEnabled()) { @@ -345,27 +342,49 @@ namespace ts.server.typingsInstaller { private watchFiles(projectName: string, files: string[]) { if (!files.length) { + // shut down existing watchers + this.closeWatchers(projectName); return; } - // shut down existing watchers - this.closeWatchers(projectName); + + let watchers = this.projectWatchers.get(projectName); + if (!watchers) { + watchers = createMap(); + this.projectWatchers.set(projectName, watchers); + } // handler should be invoked once for the entire set of files since it will trigger full rediscovery of typings let isInvoked = false; - const watchers: FileWatcher[] = []; - for (const file of files) { - const w = this.installTypingHost.watchFile(file, f => { - if (this.log.isEnabled()) { - this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`); - } - if (!isInvoked) { - this.sendResponse({ projectName, kind: ActionInvalidate }); - isInvoked = true; - } - }, /*pollingInterval*/ 2000); - watchers.push(w); - } - this.projectWatchers.set(projectName, watchers); + const isLoggingEnabled = this.log.isEnabled(); + mutateMap( + watchers, + arrayToSet(files), + { + // Watch the missing files + createNewValue: file => { + if (isLoggingEnabled) { + this.log.writeLine(`FileWatcher:: Added:: WatchInfo: ${file}`); + } + const watcher = this.installTypingHost.watchFile(file, (f, eventKind) => { + if (isLoggingEnabled) { + this.log.writeLine(`FileWatcher:: Triggered with ${f} eventKind: ${FileWatcherEventKind[eventKind]}:: WatchInfo: ${file}:: handler is already invoked '${isInvoked}'`); + } + if (!isInvoked) { + this.sendResponse({ projectName, kind: ActionInvalidate }); + isInvoked = true; + } + }, /*pollingInterval*/ 2000); + return isLoggingEnabled ? { + close: () => { + this.log.writeLine(`FileWatcher:: Closed:: WatchInfo: ${file}`); + } + } : watcher; + }, + // Files that are no longer missing (e.g. because they are no longer required) + // should no longer be watched. + onDeleteValue: closeFileWatcher + } + ); } private createSetTypings(request: DiscoverTypings, typings: string[]): SetTypings {