-
Notifications
You must be signed in to change notification settings - Fork 27.9k
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
Add opt-in multi-root aware file watcher based on Axosoft/nsfw #28948
Merged
Merged
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
2ebf375
Initial nsfw prototype
Tyriar 50879b0
Get nsfw file watcher sending events back
Tyriar 9515b97
Merge remote-tracking branch 'origin/master' into tyriar/nsfw
Tyriar 500c7ff
Convert nsfw rename events to delete and create events
Tyriar eed8149
Merge remote-tracking branch 'origin/master' into tyriar/nsfw
Tyriar a38b967
Improve nsfw types
Tyriar 00b0ae9
Add ThrottledDelayer and event normalization
Tyriar b7fab4b
Support ignored paths
Tyriar 6645b79
Improve typings
Tyriar 66154fa
Allow nsfw file watcher to work alongside others
Tyriar 25ab8aa
Start working on multiple root file watcher support
Tyriar da35479
Improve nsfw logging
Tyriar e6f03c8
Support basic multi root watching
Tyriar 9948719
Fix test compilation
Tyriar 7ffcc19
Clean up
Tyriar 437afca
Merge remote-tracking branch 'origin/master' into tyriar/nsfw
Tyriar File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
declare module 'nsfw' { | ||
interface NsfwWatcher { | ||
start(): void; | ||
stop(): void; | ||
} | ||
|
||
interface NsfwWatchingPromise { | ||
then(): void; | ||
} | ||
|
||
interface NsfwStartWatchingPromise { | ||
then(fn: (watcher: NsfwWatcher) => void): NsfwWatchingPromise; | ||
} | ||
|
||
interface NsfwEvent { | ||
action: number; | ||
directory: string; | ||
file?: string; | ||
newFile?: string; | ||
oldFile?: string; | ||
} | ||
|
||
interface NsfwFunction { | ||
(dir: string, eventHandler: (events: NsfwEvent[]) => void, options?: any): NsfwStartWatchingPromise; | ||
actions: { | ||
CREATED: number; | ||
DELETED: number; | ||
MODIFIED: number; | ||
RENAMED: number; | ||
} | ||
} | ||
|
||
var nsfw: NsfwFunction; | ||
export = nsfw; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
143 changes: 143 additions & 0 deletions
143
src/vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as glob from 'vs/base/common/glob'; | ||
import * as path from 'path'; | ||
import * as watcher from 'vs/workbench/services/files/node/watcher/common'; | ||
import * as nsfw from 'nsfw'; | ||
import { IWatcherService, IWatcherRequest } from 'vs/workbench/services/files/node/watcher/nsfw/watcher'; | ||
import { TPromise } from 'vs/base/common/winjs.base'; | ||
import { ThrottledDelayer } from 'vs/base/common/async'; | ||
import { FileChangeType } from 'vs/platform/files/common/files'; | ||
|
||
const nsfwActionToRawChangeType: { [key: number]: number } = []; | ||
nsfwActionToRawChangeType[nsfw.actions.CREATED] = FileChangeType.ADDED; | ||
nsfwActionToRawChangeType[nsfw.actions.MODIFIED] = FileChangeType.UPDATED; | ||
nsfwActionToRawChangeType[nsfw.actions.DELETED] = FileChangeType.DELETED; | ||
|
||
|
||
interface IPathWatcher { | ||
watcher?: { | ||
start(): void; | ||
stop(): void; | ||
}; | ||
} | ||
|
||
export class NsfwWatcherService implements IWatcherService { | ||
private static FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms) | ||
|
||
private _pathWatchers: { [watchPath: string]: IPathWatcher } = {}; | ||
|
||
public watch(request: IWatcherRequest): TPromise<void> { | ||
if (request.verboseLogging) { | ||
console.log('request', request); | ||
} | ||
|
||
let undeliveredFileEvents: watcher.IRawFileChange[] = []; | ||
const fileEventDelayer = new ThrottledDelayer(NsfwWatcherService.FS_EVENT_DELAY); | ||
|
||
console.log('starting to watch ' + request.basePath); | ||
this._pathWatchers[request.basePath] = {}; | ||
|
||
const promise = new TPromise<void>((c, e, p) => { | ||
nsfw(request.basePath, events => { | ||
for (let i = 0; i < events.length; i++) { | ||
const e = events[i]; | ||
|
||
// Logging | ||
if (request.verboseLogging) { | ||
const logPath = e.action === nsfw.actions.RENAMED ? path.join(e.directory, e.oldFile) + ' -> ' + e.newFile : path.join(e.directory, e.file); | ||
console.log(e.action === nsfw.actions.CREATED ? '[CREATED]' : e.action === nsfw.actions.DELETED ? '[DELETED]' : e.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]', logPath); | ||
} | ||
|
||
// Convert nsfw event to IRawFileChange and add to queue | ||
let absolutePath: string; | ||
if (e.action === nsfw.actions.RENAMED) { | ||
// Rename fires when a file's name changes within a single directory | ||
absolutePath = path.join(e.directory, e.oldFile); | ||
if (!this._isPathIgnored(absolutePath, request.ignored)) { | ||
undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath }); | ||
} | ||
absolutePath = path.join(e.directory, e.newFile); | ||
if (!this._isPathIgnored(absolutePath, request.ignored)) { | ||
undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath }); | ||
} | ||
} else { | ||
absolutePath = path.join(e.directory, e.file); | ||
if (!this._isPathIgnored(absolutePath, request.ignored)) { | ||
undeliveredFileEvents.push({ | ||
type: nsfwActionToRawChangeType[e.action], | ||
path: absolutePath | ||
}); | ||
} | ||
} | ||
} | ||
|
||
// Delay and send buffer | ||
fileEventDelayer.trigger(() => { | ||
const events = undeliveredFileEvents; | ||
undeliveredFileEvents = []; | ||
|
||
// Broadcast to clients normalized | ||
const res = watcher.normalize(events); | ||
p(res); | ||
|
||
// Logging | ||
if (request.verboseLogging) { | ||
res.forEach(r => { | ||
console.log(' >> normalized', r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]', r.path); | ||
}); | ||
} | ||
|
||
return TPromise.as(null); | ||
}); | ||
}).then(watcher => { | ||
console.log('watcher ready ' + request.basePath); | ||
this._pathWatchers[request.basePath].watcher = watcher; | ||
return watcher.start(); | ||
}); | ||
}); | ||
|
||
return promise; | ||
} | ||
|
||
public setRoots(roots: string[]): TPromise<void> { | ||
const rootsToStartWatching = roots.filter(r => !(r in this._pathWatchers)); | ||
const rootsToStopWatching = Object.keys(this._pathWatchers).filter(r => roots.indexOf(r) === -1); | ||
|
||
// TODO: Don't watch inner folders | ||
// TODO: Move verboseLogging to constructor | ||
// Logging | ||
if (true) { | ||
console.log(`Set watch roots: start: [${rootsToStartWatching.join(',')}], stop: [${rootsToStopWatching.join(',')}]`); | ||
} | ||
|
||
const promises: TPromise<void>[] = []; | ||
if (rootsToStartWatching.length) { | ||
rootsToStartWatching.forEach(root => { | ||
promises.push(this.watch({ | ||
basePath: root, | ||
ignored: [], | ||
// TODO: Inherit from initial request | ||
verboseLogging: true | ||
})); | ||
}); | ||
} | ||
|
||
if (rootsToStopWatching.length) { | ||
rootsToStopWatching.forEach(root => { | ||
this._pathWatchers[root].watcher.stop(); | ||
delete this._pathWatchers[root]; | ||
}); | ||
} | ||
|
||
// TODO: Don't watch sub-folders of folders | ||
return TPromise.join(promises).then(() => void 0); | ||
} | ||
|
||
private _isPathIgnored(absolutePath: string, ignored: string[]): boolean { | ||
return ignored && ignored.some(ignore => glob.match(ignore, absolutePath)); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
src/vs/workbench/services/files/node/watcher/nsfw/watcher.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
'use strict'; | ||
|
||
import { TPromise } from 'vs/base/common/winjs.base'; | ||
|
||
export interface IWatcherRequest { | ||
basePath: string; | ||
ignored: string[]; | ||
verboseLogging: boolean; | ||
} | ||
|
||
export interface IWatcherService { | ||
setRoots(roots: string[]): TPromise<void>; | ||
watch(request: IWatcherRequest): TPromise<void>; | ||
} | ||
|
||
export interface IFileWatcher { | ||
startWatching(): () => void; | ||
addFolder(folder: string): void; | ||
} |
14 changes: 14 additions & 0 deletions
14
src/vs/workbench/services/files/node/watcher/nsfw/watcherApp.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
'use strict'; | ||
|
||
import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; | ||
import { WatcherChannel } from 'vs/workbench/services/files/node/watcher/nsfw/watcherIpc'; | ||
import { NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/nsfwWatcherService'; | ||
|
||
const server = new Server(); | ||
const service = new NsfwWatcherService(); | ||
const channel = new WatcherChannel(service); | ||
server.registerChannel('watcher', channel); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bpasero merging this now as the tests are passing and the new code should all be blocked off by this setting. Please send comments my way though 😃
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Tyriar sounds good. should we enable it automatically when we detect multiple folders are used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not just yet, I haven't tested at all on Windows. I may do so after playing with it on Monday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bpasero also
getWorkspace2
seemed to be returning a single root at the time of initialization. Maybe I wasn't using it right but that may complicate things.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Tyriar yes this is something I have to follow up with Sandeep, currently multi-folder comes in late.