Skip to content

Do not send first missing file event as well, Do not close typing installers watches just to recreate them #22520

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Mar 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/compiler/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
61 changes: 40 additions & 21 deletions src/server/typingsInstaller/typingsInstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ namespace ts.server.typingsInstaller {
private readonly packageNameToTypingLocation: Map<JsTyping.CachedTyping> = createMap<JsTyping.CachedTyping>();
private readonly missingTypingsSet: Map<true> = createMap<true>();
private readonly knownCachesSet: Map<true> = createMap<true>();
private readonly projectWatchers: Map<FileWatcher[]> = createMap<FileWatcher[]>();
private readonly projectWatchers = createMap<Map<FileWatcher>>();
private safeList: JsTyping.SafeList | undefined;
readonly pendingRunRequests: PendingRequest[] = [];

Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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 {
Expand Down