@@ -9,12 +9,13 @@ import * as path from 'vs/base/common/path';
99import * as platform from 'vs/base/common/platform' ;
1010import { IDiskFileChange , normalizeFileChanges , ILogMessage } from 'vs/platform/files/node/watcher/watcher' ;
1111import * as nsfw from 'vscode-nsfw' ;
12- import { IWatcherService , IWatcherRequest , IWatcherOptions } from 'vs/platform/files/node/watcher/nsfw/watcher' ;
12+ import { IWatcherService , IWatcherRequest } from 'vs/platform/files/node/watcher/nsfw/watcher' ;
1313import { ThrottledDelayer } from 'vs/base/common/async' ;
1414import { FileChangeType } from 'vs/platform/files/common/files' ;
1515import { normalizeNFC } from 'vs/base/common/normalization' ;
1616import { Event , Emitter } from 'vs/base/common/event' ;
1717import { realcaseSync , realpathSync } from 'vs/base/node/extpath' ;
18+ import { Disposable } from 'vs/base/common/lifecycle' ;
1819
1920const nsfwActionToRawChangeType : { [ key : number ] : number } = [ ] ;
2021nsfwActionToRawChangeType [ nsfw . actions . CREATED ] = FileChangeType . ADDED ;
@@ -32,29 +33,61 @@ interface IPathWatcher {
3233 ignored : glob . ParsedPattern [ ] ;
3334}
3435
35- export class NsfwWatcherService implements IWatcherService {
36+ export class NsfwWatcherService extends Disposable implements IWatcherService {
37+
3638 private static readonly FS_EVENT_DELAY = 50 ; // aggregate and only emit events when changes have stopped for this duration (in ms)
3739
38- private _pathWatchers : { [ watchPath : string ] : IPathWatcher } = { } ;
39- private _verboseLogging : boolean | undefined ;
40+ private readonly _onDidChangeFile = this . _register ( new Emitter < IDiskFileChange [ ] > ( ) ) ;
41+ readonly onDidChangeFile = this . _onDidChangeFile . event ;
42+
43+ private readonly _onDidLogMessage = this . _register ( new Emitter < ILogMessage > ( ) ) ;
44+ readonly onDidLogMessage : Event < ILogMessage > = this . _onDidLogMessage . event ;
45+
46+ private pathWatchers : { [ watchPath : string ] : IPathWatcher } = { } ;
47+ private verboseLogging : boolean | undefined ;
4048 private enospcErrorLogged : boolean | undefined ;
4149
42- private readonly _onWatchEvent = new Emitter < IDiskFileChange [ ] > ( ) ;
43- readonly onWatchEvent = this . _onWatchEvent . event ;
50+ async setRoots ( roots : IWatcherRequest [ ] ) : Promise < void > {
51+ const normalizedRoots = this . _normalizeRoots ( roots ) ;
52+
53+ // Gather roots that are not currently being watched
54+ const rootsToStartWatching = normalizedRoots . filter ( r => {
55+ return ! ( r . path in this . pathWatchers ) ;
56+ } ) ;
57+
58+ // Gather current roots that don't exist in the new roots array
59+ const rootsToStopWatching = Object . keys ( this . pathWatchers ) . filter ( r => {
60+ return normalizedRoots . every ( normalizedRoot => normalizedRoot . path !== r ) ;
61+ } ) ;
62+
63+ // Logging
64+ if ( this . verboseLogging ) {
65+ this . log ( `Start watching: [${ rootsToStartWatching . map ( r => r . path ) . join ( ',' ) } ]\nStop watching: [${ rootsToStopWatching . join ( ',' ) } ]` ) ;
66+ }
67+
68+ // Stop watching some roots
69+ rootsToStopWatching . forEach ( root => {
70+ this . pathWatchers [ root ] . ready . then ( watcher => watcher . stop ( ) ) ;
71+ delete this . pathWatchers [ root ] ;
72+ } ) ;
4473
45- private readonly _onLogMessage = new Emitter < ILogMessage > ( ) ;
46- readonly onLogMessage : Event < ILogMessage > = this . _onLogMessage . event ;
74+ // Start watching some roots
75+ rootsToStartWatching . forEach ( root => this . doWatch ( root ) ) ;
4776
48- watch ( options : IWatcherOptions ) : Event < IDiskFileChange [ ] > {
49- return this . onWatchEvent ;
77+ // Refresh ignored arrays in case they changed
78+ roots . forEach ( root => {
79+ if ( root . path in this . pathWatchers ) {
80+ this . pathWatchers [ root . path ] . ignored = Array . isArray ( root . excludes ) ? root . excludes . map ( ignored => glob . parse ( ignored ) ) : [ ] ;
81+ }
82+ } ) ;
5083 }
5184
52- private _watch ( request : IWatcherRequest ) : void {
85+ private doWatch ( request : IWatcherRequest ) : void {
5386 let undeliveredFileEvents : IDiskFileChange [ ] = [ ] ;
5487 const fileEventDelayer = new ThrottledDelayer < void > ( NsfwWatcherService . FS_EVENT_DELAY ) ;
5588
5689 let readyPromiseResolve : ( watcher : IWatcherObjet ) => void ;
57- this . _pathWatchers [ request . path ] = {
90+ this . pathWatchers [ request . path ] = {
5891 ready : new Promise < IWatcherObjet > ( resolve => readyPromiseResolve = resolve ) ,
5992 ignored : Array . isArray ( request . excludes ) ? request . excludes . map ( ignored => glob . parse ( ignored ) ) : [ ]
6093 } ;
@@ -100,14 +133,14 @@ export class NsfwWatcherService implements IWatcherService {
100133 }
101134 }
102135
103- if ( this . _verboseLogging ) {
136+ if ( this . verboseLogging ) {
104137 this . log ( `Start watching with nsfw: ${ request . path } ` ) ;
105138 }
106139
107140 nsfw ( request . path , events => {
108141 for ( const e of events ) {
109142 // Logging
110- if ( this . _verboseLogging ) {
143+ if ( this . verboseLogging ) {
111144 const logPath = e . action === nsfw . actions . RENAMED ? path . join ( e . directory , e . oldFile || '' ) + ' -> ' + e . newFile : path . join ( e . directory , e . file || '' ) ;
112145 this . log ( `${ e . action === nsfw . actions . CREATED ? '[CREATED]' : e . action === nsfw . actions . DELETED ? '[DELETED]' : e . action === nsfw . actions . MODIFIED ? '[CHANGED]' : '[RENAMED]' } ${ logPath } ` ) ;
113146 }
@@ -117,25 +150,25 @@ export class NsfwWatcherService implements IWatcherService {
117150 if ( e . action === nsfw . actions . RENAMED ) {
118151 // Rename fires when a file's name changes within a single directory
119152 absolutePath = path . join ( e . directory , e . oldFile || '' ) ;
120- if ( ! this . _isPathIgnored ( absolutePath , this . _pathWatchers [ request . path ] . ignored ) ) {
153+ if ( ! this . isPathIgnored ( absolutePath , this . pathWatchers [ request . path ] . ignored ) ) {
121154 undeliveredFileEvents . push ( { type : FileChangeType . DELETED , path : absolutePath } ) ;
122- } else if ( this . _verboseLogging ) {
155+ } else if ( this . verboseLogging ) {
123156 this . log ( ` >> ignored ${ absolutePath } ` ) ;
124157 }
125158 absolutePath = path . join ( e . newDirectory || e . directory , e . newFile || '' ) ;
126- if ( ! this . _isPathIgnored ( absolutePath , this . _pathWatchers [ request . path ] . ignored ) ) {
159+ if ( ! this . isPathIgnored ( absolutePath , this . pathWatchers [ request . path ] . ignored ) ) {
127160 undeliveredFileEvents . push ( { type : FileChangeType . ADDED , path : absolutePath } ) ;
128- } else if ( this . _verboseLogging ) {
161+ } else if ( this . verboseLogging ) {
129162 this . log ( ` >> ignored ${ absolutePath } ` ) ;
130163 }
131164 } else {
132165 absolutePath = path . join ( e . directory , e . file || '' ) ;
133- if ( ! this . _isPathIgnored ( absolutePath , this . _pathWatchers [ request . path ] . ignored ) ) {
166+ if ( ! this . isPathIgnored ( absolutePath , this . pathWatchers [ request . path ] . ignored ) ) {
134167 undeliveredFileEvents . push ( {
135168 type : nsfwActionToRawChangeType [ e . action ] ,
136169 path : absolutePath
137170 } ) ;
138- } else if ( this . _verboseLogging ) {
171+ } else if ( this . verboseLogging ) {
139172 this . log ( ` >> ignored ${ absolutePath } ` ) ;
140173 }
141174 }
@@ -161,94 +194,59 @@ export class NsfwWatcherService implements IWatcherService {
161194
162195 // Broadcast to clients normalized
163196 const res = normalizeFileChanges ( events ) ;
164- this . _onWatchEvent . fire ( res ) ;
197+ this . _onDidChangeFile . fire ( res ) ;
165198
166199 // Logging
167- if ( this . _verboseLogging ) {
200+ if ( this . verboseLogging ) {
168201 res . forEach ( r => {
169202 this . log ( ` >> normalized ${ r . type === FileChangeType . ADDED ? '[ADDED]' : r . type === FileChangeType . DELETED ? '[DELETED]' : '[CHANGED]' } ${ r . path } ` ) ;
170203 } ) ;
171204 }
172205 } ) ;
173206 } ) . then ( watcher => {
174- this . _pathWatchers [ request . path ] . watcher = watcher ;
207+ this . pathWatchers [ request . path ] . watcher = watcher ;
175208 const startPromise = watcher . start ( ) ;
176209 startPromise . then ( ( ) => readyPromiseResolve ( watcher ) ) ;
177- return startPromise ;
178- } ) ;
179- }
180-
181- async setRoots ( roots : IWatcherRequest [ ] ) : Promise < void > {
182- const normalizedRoots = this . _normalizeRoots ( roots ) ;
183-
184- // Gather roots that are not currently being watched
185- const rootsToStartWatching = normalizedRoots . filter ( r => {
186- return ! ( r . path in this . _pathWatchers ) ;
187- } ) ;
188210
189- // Gather current roots that don't exist in the new roots array
190- const rootsToStopWatching = Object . keys ( this . _pathWatchers ) . filter ( r => {
191- return normalizedRoots . every ( normalizedRoot => normalizedRoot . path !== r ) ;
192- } ) ;
193-
194- // Logging
195- if ( this . _verboseLogging ) {
196- this . log ( `Start watching: [${ rootsToStartWatching . map ( r => r . path ) . join ( ',' ) } ]\nStop watching: [${ rootsToStopWatching . join ( ',' ) } ]` ) ;
197- }
198-
199- // Stop watching some roots
200- rootsToStopWatching . forEach ( root => {
201- this . _pathWatchers [ root ] . ready . then ( watcher => watcher . stop ( ) ) ;
202- delete this . _pathWatchers [ root ] ;
203- } ) ;
204-
205- // Start watching some roots
206- rootsToStartWatching . forEach ( root => this . _watch ( root ) ) ;
207-
208- // Refresh ignored arrays in case they changed
209- roots . forEach ( root => {
210- if ( root . path in this . _pathWatchers ) {
211- this . _pathWatchers [ root . path ] . ignored = Array . isArray ( root . excludes ) ? root . excludes . map ( ignored => glob . parse ( ignored ) ) : [ ] ;
212- }
211+ return startPromise ;
213212 } ) ;
214213 }
215214
216215 async setVerboseLogging ( enabled : boolean ) : Promise < void > {
217- this . _verboseLogging = enabled ;
216+ this . verboseLogging = enabled ;
218217 }
219218
220219 async stop ( ) : Promise < void > {
221- for ( let path in this . _pathWatchers ) {
222- let watcher = this . _pathWatchers [ path ] ;
220+ for ( let path in this . pathWatchers ) {
221+ let watcher = this . pathWatchers [ path ] ;
223222 watcher . ready . then ( watcher => watcher . stop ( ) ) ;
224- delete this . _pathWatchers [ path ] ;
223+ delete this . pathWatchers [ path ] ;
225224 }
226- this . _pathWatchers = Object . create ( null ) ;
225+
226+ this . pathWatchers = Object . create ( null ) ;
227227 }
228228
229- /**
230- * Normalizes a set of root paths by removing any root paths that are
231- * sub-paths of other roots.
232- */
233229 protected _normalizeRoots ( roots : IWatcherRequest [ ] ) : IWatcherRequest [ ] {
230+ // Normalizes a set of root paths by removing any root paths that are
231+ // sub-paths of other roots.
234232 return roots . filter ( r => roots . every ( other => {
235233 return ! ( r . path . length > other . path . length && extpath . isEqualOrParent ( r . path , other . path ) ) ;
236234 } ) ) ;
237235 }
238236
239- private _isPathIgnored ( absolutePath : string , ignored : glob . ParsedPattern [ ] ) : boolean {
237+ private isPathIgnored ( absolutePath : string , ignored : glob . ParsedPattern [ ] ) : boolean {
240238 return ignored && ignored . some ( i => i ( absolutePath ) ) ;
241239 }
242240
243241 private log ( message : string ) {
244- this . _onLogMessage . fire ( { type : 'trace' , message : `[File Watcher (nsfw)] ` + message } ) ;
242+ this . _onDidLogMessage . fire ( { type : 'trace' , message : `[File Watcher (nsfw)] ` + message } ) ;
245243 }
246244
247245 private warn ( message : string ) {
248- this . _onLogMessage . fire ( { type : 'warn' , message : `[File Watcher (nsfw)] ` + message } ) ;
246+ this . _onDidLogMessage . fire ( { type : 'warn' , message : `[File Watcher (nsfw)] ` + message } ) ;
249247 }
250248
251249 private error ( message : string ) {
252- this . _onLogMessage . fire ( { type : 'error' , message : `[File Watcher (nsfw)] ` + message } ) ;
250+ this . _onDidLogMessage . fire ( { type : 'error' , message : `[File Watcher (nsfw)] ` + message } ) ;
253251 }
254252}
0 commit comments