@opensumi/ide-file-service 3.7.2-next-1740448398.0 → 3.7.2-next-1741226843.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/lib/browser/file-service-client.js +4 -4
  2. package/lib/browser/file-service-client.js.map +1 -1
  3. package/lib/browser/file-service-provider-client.d.ts +2 -2
  4. package/lib/browser/file-service-provider-client.d.ts.map +1 -1
  5. package/lib/browser/file-service-provider-client.js +2 -2
  6. package/lib/browser/file-service-provider-client.js.map +1 -1
  7. package/lib/common/files.d.ts +2 -2
  8. package/lib/common/files.d.ts.map +1 -1
  9. package/lib/common/watcher.d.ts +2 -3
  10. package/lib/common/watcher.d.ts.map +1 -1
  11. package/lib/common/watcher.js.map +1 -1
  12. package/lib/node/disk-file-system.provider.d.ts +2 -3
  13. package/lib/node/disk-file-system.provider.d.ts.map +1 -1
  14. package/lib/node/disk-file-system.provider.js +2 -3
  15. package/lib/node/disk-file-system.provider.js.map +1 -1
  16. package/lib/node/hosted/recursive/file-service-watcher.d.ts +13 -14
  17. package/lib/node/hosted/recursive/file-service-watcher.d.ts.map +1 -1
  18. package/lib/node/hosted/recursive/file-service-watcher.js +97 -158
  19. package/lib/node/hosted/recursive/file-service-watcher.js.map +1 -1
  20. package/lib/node/hosted/un-recursive/file-service-watcher.d.ts +11 -5
  21. package/lib/node/hosted/un-recursive/file-service-watcher.d.ts.map +1 -1
  22. package/lib/node/hosted/un-recursive/file-service-watcher.js +31 -15
  23. package/lib/node/hosted/un-recursive/file-service-watcher.js.map +1 -1
  24. package/lib/node/hosted/watcher.host.service.d.ts +2 -7
  25. package/lib/node/hosted/watcher.host.service.d.ts.map +1 -1
  26. package/lib/node/hosted/watcher.host.service.js +28 -60
  27. package/lib/node/hosted/watcher.host.service.js.map +1 -1
  28. package/lib/node/hosted/watcher.process.js +1 -1
  29. package/lib/node/hosted/watcher.process.js.map +1 -1
  30. package/lib/node/watcher-process-manager.d.ts +2 -3
  31. package/lib/node/watcher-process-manager.d.ts.map +1 -1
  32. package/lib/node/watcher-process-manager.js +3 -4
  33. package/lib/node/watcher-process-manager.js.map +1 -1
  34. package/package.json +9 -9
  35. package/src/browser/file-service-client.ts +4 -4
  36. package/src/browser/file-service-provider-client.ts +2 -3
  37. package/src/common/files.ts +2 -2
  38. package/src/common/watcher.ts +3 -10
  39. package/src/node/disk-file-system.provider.ts +3 -8
  40. package/src/node/hosted/recursive/file-service-watcher.ts +108 -190
  41. package/src/node/hosted/un-recursive/file-service-watcher.ts +46 -22
  42. package/src/node/hosted/watcher.host.service.ts +33 -100
  43. package/src/node/hosted/watcher.process.ts +1 -1
  44. package/src/node/watcher-process-manager.ts +5 -13
@@ -1,18 +1,10 @@
1
- import { tmpdir } from 'os';
2
- import paths, { join } from 'path';
1
+ import paths from 'path';
3
2
 
4
3
  import ParcelWatcher from '@parcel/watcher';
5
4
  import fs from 'fs-extra';
6
5
  import debounce from 'lodash/debounce';
7
6
  import uniqBy from 'lodash/uniqBy';
8
7
 
9
- import {
10
- FileChangeType,
11
- FileSystemWatcherClient,
12
- IWatcher,
13
- RecursiveWatcherBackend,
14
- WatchOptions,
15
- } from '@opensumi/ide-core-common';
16
8
  import { ILogService } from '@opensumi/ide-core-common/lib/log';
17
9
  import {
18
10
  Disposable,
@@ -20,13 +12,18 @@ import {
20
12
  FileUri,
21
13
  IDisposable,
22
14
  ParsedPattern,
23
- RunOnceScheduler,
24
15
  isLinux,
25
16
  isWindows,
26
17
  parseGlob,
27
18
  } from '@opensumi/ide-core-common/lib/utils';
28
19
 
29
- import { INsfw } from '../../../common/watcher';
20
+ import {
21
+ FileChangeType,
22
+ FileSystemWatcherClient,
23
+ IFileSystemWatcherServer,
24
+ INsfw,
25
+ WatchOptions,
26
+ } from '../../../common';
30
27
  import { FileChangeCollection } from '../../file-change-collection';
31
28
  import { shouldIgnorePath } from '../shared';
32
29
 
@@ -35,6 +32,13 @@ export interface WatcherOptions {
35
32
  excludes: string[];
36
33
  }
37
34
 
35
+ const watcherPlaceHolder = {
36
+ disposable: {
37
+ dispose: () => {},
38
+ },
39
+ handlers: [],
40
+ };
41
+
38
42
  /**
39
43
  * @deprecated
40
44
  */
@@ -44,27 +48,21 @@ export interface NsfwFileSystemWatcherOption {
44
48
  error?: (message: string, ...args: any[]) => void;
45
49
  }
46
50
 
47
- export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
51
+ export class FileSystemWatcherServer extends Disposable implements IFileSystemWatcherServer {
48
52
  private static readonly PARCEL_WATCHER_BACKEND = isWindows ? 'windows' : isLinux ? 'inotify' : 'fs-events';
49
53
 
50
- private static DEFAULT_POLLING_INTERVAL = 100;
51
-
52
54
  private WATCHER_HANDLERS = new Map<
53
- string,
55
+ number,
54
56
  { path: string; handlers: ParcelWatcher.SubscribeCallback[]; disposable: IDisposable }
55
57
  >();
56
-
57
- protected watcherOptions = new Map<string, WatcherOptions>();
58
+ private static WATCHER_SEQUENCE = 1;
59
+ protected watcherOptions = new Map<number, WatcherOptions>();
58
60
 
59
61
  protected client: FileSystemWatcherClient | undefined;
60
62
 
61
63
  protected changes = new FileChangeCollection();
62
64
 
63
- constructor(
64
- private excludes: string[] = [],
65
- private readonly logger: ILogService,
66
- private backend: RecursiveWatcherBackend = RecursiveWatcherBackend.NSFW,
67
- ) {
65
+ constructor(private excludes: string[] = [], private readonly logger: ILogService) {
68
66
  super();
69
67
  this.addDispose(
70
68
  Disposable.create(() => {
@@ -73,41 +71,37 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
73
71
  );
74
72
  }
75
73
 
74
+ /**
75
+ * 查找某个路径是否已被监听
76
+ * @param watcherPath
77
+ */
78
+ checkIsAlreadyWatched(watcherPath: string): number | undefined {
79
+ for (const [watcherId, watcher] of this.WATCHER_HANDLERS) {
80
+ if (watcherPath.indexOf(watcher.path) === 0) {
81
+ return watcherId;
82
+ }
83
+ }
84
+ }
85
+
76
86
  /**
77
87
  * 如果监听路径不存在,则会监听父目录
78
88
  * @param uri 要监听的路径
79
89
  * @param options
80
90
  * @returns
81
91
  */
82
- async watchFileChanges(uri: string, options?: WatchOptions) {
83
- return new Promise<void>((resolve, rej) => {
84
- const timer = setTimeout(() => {
85
- rej(`Watch ${uri} Timeout`);
86
- // FIXME:暂时写死3秒
87
- }, 3000);
88
-
89
- if (options?.excludes) {
90
- this.updateWatcherFileExcludes(options.excludes);
91
- }
92
-
93
- this.doWatchFileChange(uri, options).then(() => {
94
- resolve(void 0);
95
- if (timer) {
96
- clearTimeout(timer);
97
- }
98
- });
99
- });
100
- }
92
+ async watchFileChanges(uri: string, options?: WatchOptions): Promise<number> {
93
+ const basePath = FileUri.fsPath(uri);
101
94
 
102
- private async doWatchFileChange(uri: string, options?: WatchOptions) {
103
- if (this.WATCHER_HANDLERS.has(uri)) {
104
- const handler = this.WATCHER_HANDLERS.get(uri);
105
- handler?.disposable.dispose();
106
- this.WATCHER_HANDLERS.delete(uri);
95
+ let watcherId = this.checkIsAlreadyWatched(basePath);
96
+ if (watcherId) {
97
+ return watcherId;
107
98
  }
108
99
 
109
- const basePath = FileUri.fsPath(uri);
110
- this.logger.log('[Recursive] watch file changes: ', uri);
100
+ watcherId = FileSystemWatcherServer.WATCHER_SEQUENCE++;
101
+ this.WATCHER_HANDLERS.set(watcherId, {
102
+ ...watcherPlaceHolder,
103
+ path: basePath,
104
+ });
111
105
 
112
106
  const toDisposeWatcher = new DisposableCollection();
113
107
  let watchPath: string;
@@ -123,10 +117,10 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
123
117
  } else {
124
118
  watchPath = await this.lookup(basePath);
125
119
  }
126
-
120
+ this.logger.log('Starting watching:', watchPath, options);
127
121
  const handler = (err, events: ParcelWatcher.Event[]) => {
128
122
  if (err) {
129
- this.logger.error(`[Recursive] Watching ${watchPath} error: `, err);
123
+ this.logger.error(`Watching ${watchPath} error: `, err);
130
124
  return;
131
125
  }
132
126
  events = this.trimChangeEvent(events);
@@ -145,15 +139,15 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
145
139
  }
146
140
  };
147
141
 
148
- this.WATCHER_HANDLERS.set(watchPath, {
142
+ this.WATCHER_HANDLERS.set(watcherId, {
149
143
  path: watchPath,
150
144
  disposable: toDisposeWatcher,
151
145
  handlers: [handler],
152
146
  });
153
-
154
- toDisposeWatcher.push(Disposable.create(() => this.WATCHER_HANDLERS.delete(watchPath)));
155
- toDisposeWatcher.push(await this.start(watchPath, options));
147
+ toDisposeWatcher.push(Disposable.create(() => this.WATCHER_HANDLERS.delete(watcherId as number)));
148
+ toDisposeWatcher.push(await this.start(watcherId, watchPath, options));
156
149
  this.addDispose(toDisposeWatcher);
150
+ return watcherId;
157
151
  }
158
152
 
159
153
  /**
@@ -190,63 +184,16 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
190
184
  this.excludes = excludes;
191
185
  }
192
186
 
193
- protected async start(basePath: string, rawOptions: WatchOptions | undefined): Promise<DisposableCollection> {
194
- this.logger.log('[Recursive] Start watching', basePath);
195
-
187
+ protected async start(
188
+ watcherId: number,
189
+ basePath: string,
190
+ rawOptions: WatchOptions | undefined,
191
+ ): Promise<DisposableCollection> {
192
+ const disposables = new DisposableCollection();
196
193
  if (!(await fs.pathExists(basePath))) {
197
- return new DisposableCollection();
194
+ return disposables;
198
195
  }
199
-
200
196
  const realPath = await fs.realpath(basePath);
201
-
202
- if (this.isEnableNSFW()) {
203
- return this.watchWithNsfw(realPath, rawOptions);
204
- } else {
205
- // polling
206
- if (rawOptions?.pollingWatch) {
207
- this.logger.log('[Recursive] Start polling watch:', realPath);
208
- return this.pollingWatch(realPath, rawOptions);
209
- }
210
-
211
- return this.watchWithParcel(realPath, rawOptions);
212
- }
213
- }
214
-
215
- private async watchWithNsfw(realPath: string, rawOptions?: WatchOptions | undefined) {
216
- const disposables = new DisposableCollection();
217
- const nsfw = await this.withNSFWModule();
218
- const watcher: INsfw.NSFW = await nsfw(
219
- realPath,
220
- (events: INsfw.ChangeEvent[]) => this.handleNSFWEvents(events, realPath),
221
- {
222
- errorCallback: (err) => {
223
- this.logger.error('[Recursive] NSFW watcher encountered an error and will stop watching.', err);
224
- // see https://github.com/atom/github/issues/342
225
- this.unwatchFileChanges(realPath);
226
- },
227
- },
228
- );
229
-
230
- await watcher.start();
231
-
232
- disposables.push(
233
- Disposable.create(async () => {
234
- this.watcherOptions.delete(realPath);
235
- await watcher.stop();
236
- }),
237
- );
238
-
239
- const excludes = this.excludes.concat(rawOptions?.excludes || []);
240
-
241
- this.watcherOptions.set(realPath, {
242
- excludesPattern: excludes.map((pattern) => parseGlob(pattern)),
243
- excludes,
244
- });
245
- return disposables;
246
- }
247
-
248
- private async watchWithParcel(realPath: string, rawOptions?: WatchOptions | undefined) {
249
- const disposables = new DisposableCollection();
250
197
  const tryWatchDir = async (maxRetries = 3, retryDelay = 1000) => {
251
198
  for (let times = 0; times < maxRetries; times++) {
252
199
  try {
@@ -258,30 +205,23 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
258
205
  // FIXME: 研究此处屏蔽的影响,考虑下阈值应该设置多少,或者更加优雅的方式
259
206
  return;
260
207
  }
261
- const handlers = this.WATCHER_HANDLERS.get(realPath)?.handlers;
208
+ const handlers = this.WATCHER_HANDLERS.get(watcherId)?.handlers;
262
209
 
263
210
  if (!handlers) {
264
- this.logger.log('[Recursive] No handler found for watcher', realPath);
265
211
  return;
266
212
  }
267
-
268
- this.logger.log('[Recursive] Received events:', events);
269
- if (events.length === 0) {
270
- return;
271
- }
272
-
273
213
  for (const handler of handlers) {
274
214
  (handler as ParcelWatcher.SubscribeCallback)(err, events);
275
215
  }
276
216
  },
277
217
  {
278
- backend: RecursiveFileSystemWatcher.PARCEL_WATCHER_BACKEND,
218
+ backend: FileSystemWatcherServer.PARCEL_WATCHER_BACKEND,
279
219
  ignore: this.excludes.concat(rawOptions?.excludes ?? []),
280
220
  },
281
221
  );
282
222
  } catch (e) {
283
223
  // Watcher 启动失败,尝试重试
284
- this.logger.error('[Recursive] watcher subscribe failed ', e, ' try times ', times);
224
+ this.logger.error('watcher subscribe failed ', e, ' try times ', times);
285
225
  await new Promise((resolve) => {
286
226
  setTimeout(resolve, retryDelay);
287
227
  });
@@ -289,84 +229,64 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
289
229
  }
290
230
 
291
231
  // 经过若干次的尝试后,Parcel Watcher 依然启动失败,此时就不再尝试重试
292
- this.logger.error(`[Recursive] watcher subscribe finally failed after ${maxRetries} times`);
232
+ this.logger.error(`watcher subscribe finally failed after ${maxRetries} times`);
293
233
  return undefined; // watch 失败则返回 undefined
294
234
  };
295
235
 
296
- const hanlder: ParcelWatcher.AsyncSubscription | undefined = await tryWatchDir();
236
+ if (this.isEnableNSFW()) {
237
+ const nsfw = await this.withNSFWModule();
238
+ const watcher: INsfw.NSFW = await nsfw(
239
+ realPath,
240
+ (events: INsfw.ChangeEvent[]) => this.handleNSFWEvents(events, watcherId),
241
+ {
242
+ errorCallback: (err) => {
243
+ this.logger.error('NSFW watcher encountered an error and will stop watching.', err);
244
+ // see https://github.com/atom/github/issues/342
245
+ this.unwatchFileChanges(watcherId);
246
+ },
247
+ },
248
+ );
249
+
250
+ await watcher.start();
297
251
 
298
- if (hanlder) {
299
- // watch 成功才加入 disposables,否则也就无需 dispose
300
252
  disposables.push(
301
253
  Disposable.create(async () => {
302
- if (hanlder) {
303
- await hanlder.unsubscribe();
304
- }
254
+ this.watcherOptions.delete(watcherId);
255
+ await watcher.stop();
305
256
  }),
306
257
  );
307
- }
308
258
 
309
- return disposables;
310
- }
311
-
312
- private async pollingWatch(realPath: string, rawOptions?: WatchOptions | undefined) {
313
- const disposables = new DisposableCollection();
314
- const snapshotFile = join(tmpdir(), `watcher-snapshot-${realPath}`);
315
- let counter = 0;
316
-
317
- const pollingWatcher = new RunOnceScheduler(async () => {
318
- counter++;
319
- if (counter > 1) {
320
- const parcelEvents = await ParcelWatcher.getEventsSince(realPath, snapshotFile, {
321
- ignore: rawOptions?.excludes,
322
- backend: RecursiveFileSystemWatcher.PARCEL_WATCHER_BACKEND,
323
- });
324
-
325
- const handlers = this.WATCHER_HANDLERS.get(realPath)?.handlers;
326
-
327
- if (!handlers) {
328
- this.logger.log('[Recursive] No handler found for watcher', realPath);
329
- return;
330
- }
331
-
332
- this.logger.log('[Recursive] Received events:', parcelEvents);
333
- for (const handler of handlers) {
334
- (handler as ParcelWatcher.SubscribeCallback)(null, parcelEvents);
335
- }
336
- }
259
+ const excludes = this.excludes.concat(rawOptions?.excludes || []);
337
260
 
338
- await ParcelWatcher.writeSnapshot(realPath, snapshotFile, {
339
- ignore: rawOptions?.excludes,
340
- backend: RecursiveFileSystemWatcher.PARCEL_WATCHER_BACKEND,
261
+ this.watcherOptions.set(watcherId, {
262
+ excludesPattern: excludes.map((pattern) => parseGlob(pattern)),
263
+ excludes,
341
264
  });
342
-
343
- pollingWatcher.schedule();
344
- }, RecursiveFileSystemWatcher.DEFAULT_POLLING_INTERVAL);
345
-
346
- pollingWatcher.schedule(0);
347
-
348
- disposables.push(pollingWatcher);
265
+ } else {
266
+ const hanlder: ParcelWatcher.AsyncSubscription | undefined = await tryWatchDir();
267
+
268
+ if (hanlder) {
269
+ // watch 成功才加入 disposables,否则也就无需 dispose
270
+ disposables.push(
271
+ Disposable.create(async () => {
272
+ if (hanlder) {
273
+ await hanlder.unsubscribe();
274
+ }
275
+ }),
276
+ );
277
+ }
278
+ }
349
279
 
350
280
  return disposables;
351
281
  }
352
282
 
353
- private disposeWatcher(path: string) {
354
- const watcher = this.WATCHER_HANDLERS.get(path);
283
+ unwatchFileChanges(watcherId: number): Promise<void> {
284
+ const watcher = this.WATCHER_HANDLERS.get(watcherId);
355
285
  if (watcher) {
356
- try {
357
- watcher.disposable.dispose();
358
- } catch (err) {
359
- this.logger.error(`Dispose watcher failed for ${path}`, err);
360
- } finally {
361
- this.WATCHER_HANDLERS.delete(path);
362
- }
286
+ this.WATCHER_HANDLERS.delete(watcherId);
287
+ watcher.disposable.dispose();
363
288
  }
364
- }
365
-
366
- unwatchFileChanges(uri: string): void {
367
- this.logger.log('[Recursive] Un watch: ', uri);
368
- const basePath = FileUri.fsPath(uri);
369
- this.disposeWatcher(basePath);
289
+ return Promise.resolve();
370
290
  }
371
291
 
372
292
  setClient(client: FileSystemWatcherClient | undefined) {
@@ -381,22 +301,20 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
381
301
  * 社区相关 issue: https://github.com/parcel-bundler/watcher/issues/49
382
302
  */
383
303
  private isEnableNSFW(): boolean {
384
- return this.backend === RecursiveWatcherBackend.NSFW || isLinux;
304
+ return isLinux;
385
305
  }
386
306
 
387
- private async handleNSFWEvents(events: INsfw.ChangeEvent[], realPath: string): Promise<void> {
307
+ private async handleNSFWEvents(events: INsfw.ChangeEvent[], watcherId: number): Promise<void> {
388
308
  if (events.length > 5000) {
389
309
  return;
390
310
  }
391
311
 
392
- const isIgnored = (realPath: string, path: string): boolean => {
393
- const options = this.watcherOptions.get(realPath);
312
+ const isIgnored = (watcherId: number, path: string): boolean => {
313
+ const options = this.watcherOptions.get(watcherId);
394
314
  if (!options || !options.excludes || options.excludes.length < 1) {
395
315
  return false;
396
316
  }
397
-
398
- const excludesPattern = [...this.excludes.map((pattern) => parseGlob(pattern)), ...options.excludesPattern];
399
- return excludesPattern.some((match) => match(path));
317
+ return options.excludesPattern.some((match) => match(path));
400
318
  };
401
319
 
402
320
  const filterEvents = events.filter((event) => {
@@ -424,7 +342,7 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
424
342
  case INsfw.actions.RENAMED:
425
343
  {
426
344
  const deletedPath = await this.resolvePath(event.directory, event.oldFile!);
427
- if (isIgnored(realPath, deletedPath)) {
345
+ if (isIgnored(watcherId, deletedPath)) {
428
346
  return;
429
347
  }
430
348
 
@@ -432,14 +350,14 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
432
350
 
433
351
  if (event.newDirectory) {
434
352
  const path = await this.resolvePath(event.newDirectory, event.newFile!);
435
- if (isIgnored(realPath, path)) {
353
+ if (isIgnored(watcherId, path)) {
436
354
  return;
437
355
  }
438
356
 
439
357
  this.pushAdded(path);
440
358
  } else {
441
359
  const path = await this.resolvePath(event.directory, event.newFile!);
442
- if (isIgnored(realPath, path)) {
360
+ if (isIgnored(watcherId, path)) {
443
361
  return;
444
362
  }
445
363
 
@@ -450,7 +368,7 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
450
368
  default:
451
369
  {
452
370
  const path = await this.resolvePath(event.directory, event.file!);
453
- if (isIgnored(realPath, path)) {
371
+ if (isIgnored(watcherId, path)) {
454
372
  return;
455
373
  }
456
374
 
@@ -2,15 +2,24 @@ import fs, { watch } from 'fs-extra';
2
2
  import debounce from 'lodash/debounce';
3
3
 
4
4
  import { ILogService } from '@opensumi/ide-core-common/lib/log';
5
- import { Disposable, DisposableCollection, FileUri, isMacintosh, path } from '@opensumi/ide-utils/lib';
5
+ import { Disposable, DisposableCollection, FileUri, IDisposable, isMacintosh, path } from '@opensumi/ide-utils/lib';
6
6
 
7
- import { FileChangeType, FileSystemWatcherClient, IWatcher } from '../../../common/index';
7
+ import { FileChangeType, FileSystemWatcherClient, IFileSystemWatcherServer } from '../../../common/index';
8
8
  import { FileChangeCollection } from '../../file-change-collection';
9
9
  import { shouldIgnorePath } from '../shared';
10
10
  const { join, basename, normalize } = path;
11
11
 
12
- export class UnRecursiveFileSystemWatcher implements IWatcher {
13
- private watcherCollections: Map<string, fs.FSWatcher> = new Map();
12
+ export class UnRecursiveFileSystemWatcher implements IFileSystemWatcherServer {
13
+ private WATCHER_HANDLERS = new Map<
14
+ number,
15
+ {
16
+ path: string;
17
+ handlers: any;
18
+ disposable: IDisposable;
19
+ }
20
+ >();
21
+
22
+ private static WATCHER_SEQUENCE = 1;
14
23
 
15
24
  private static readonly FILE_DELETE_HANDLER_DELAY = 500;
16
25
 
@@ -25,12 +34,25 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
25
34
 
26
35
  dispose(): void {
27
36
  this.toDispose.dispose();
37
+ this.WATCHER_HANDLERS.clear();
38
+ }
39
+
40
+ /**
41
+ * 查找某个路径是否已被监听
42
+ * @param watcherPath
43
+ */
44
+ checkIsAlreadyWatched(watcherPath: string): number | undefined {
45
+ for (const [watcherId, watcher] of this.WATCHER_HANDLERS) {
46
+ if (watcherPath.indexOf(watcher.path) === 0) {
47
+ return watcherId;
48
+ }
49
+ }
28
50
  }
29
51
 
30
52
  private async doWatch(basePath: string) {
31
53
  try {
32
54
  const watcher = watch(basePath);
33
- this.logger.log('[Un-Recursive] start watching', basePath);
55
+ this.logger.log('start watching', basePath);
34
56
  const isDirectory = fs.lstatSync(basePath).isDirectory();
35
57
 
36
58
  const docChildren = new Set<string>();
@@ -52,9 +74,7 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
52
74
 
53
75
  // 开始走监听流程
54
76
  watcher.on('error', (code: number, signal: string) => {
55
- this.logger.error(
56
- `[Un-Recursive] Failed to watch ${basePath} for changes using fs.watch() (${code}, ${signal})`,
57
- );
77
+ this.logger.error(`Failed to watch ${basePath} for changes using fs.watch() (${code}, ${signal})`);
58
78
  watcher.close();
59
79
  });
60
80
 
@@ -110,7 +130,7 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
110
130
  }
111
131
  });
112
132
  } catch (error) {
113
- this.logger.error(`[Un-Recursive] Failed to watch ${basePath} for change using fs.watch() (${error.toString()})`);
133
+ this.logger.error(`Failed to watch ${basePath} for change using fs.watch() (${error.toString()})`);
114
134
  }
115
135
  }
116
136
 
@@ -118,20 +138,29 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
118
138
  const basePath = FileUri.fsPath(uri);
119
139
  const exist = await fs.pathExists(basePath);
120
140
 
141
+ let watcherId = this.checkIsAlreadyWatched(basePath);
142
+
143
+ if (watcherId) {
144
+ return watcherId;
145
+ }
146
+
147
+ watcherId = UnRecursiveFileSystemWatcher.WATCHER_SEQUENCE++;
148
+
121
149
  const disposables = new DisposableCollection(); // 管理可释放的资源
122
150
 
123
151
  let watchPath = '';
124
152
 
125
153
  if (exist) {
126
- const stat = await fs.lstat(basePath);
154
+ const stat = await fs.lstatSync(basePath);
127
155
  if (stat) {
128
156
  watchPath = basePath;
129
157
  }
130
158
  } else {
131
- this.logger.warn('[Un-Recursive] This path does not exist. Please try again');
159
+ this.logger.warn('This path does not exist. Please try again');
132
160
  }
133
161
  disposables.push(await this.start(watchPath));
134
162
  this.toDispose.push(disposables);
163
+ return watcherId;
135
164
  }
136
165
 
137
166
  protected async start(basePath: string): Promise<DisposableCollection> {
@@ -141,10 +170,6 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
141
170
  }
142
171
 
143
172
  const realPath = await fs.realpath(basePath);
144
- if (this.watcherCollections.has(realPath)) {
145
- return disposables;
146
- }
147
-
148
173
  const tryWatchDir = async (retryDelay = 1000) => {
149
174
  try {
150
175
  this.doWatch(realPath);
@@ -158,14 +183,13 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
158
183
  await tryWatchDir();
159
184
  return disposables;
160
185
  }
161
-
162
- unwatchFileChanges(uri: string): void {
163
- const basePath = FileUri.fsPath(uri);
164
- if (this.watcherCollections.has(basePath)) {
165
- const watcher = this.watcherCollections.get(basePath);
166
- watcher?.close();
167
- this.watcherCollections.delete(basePath);
186
+ unwatchFileChanges(watcherId: number): Promise<void> {
187
+ const watcher = this.WATCHER_HANDLERS.get(watcherId);
188
+ if (watcher) {
189
+ this.WATCHER_HANDLERS.delete(watcherId);
190
+ watcher.disposable.dispose();
168
191
  }
192
+ return Promise.resolve();
169
193
  }
170
194
 
171
195
  protected pushAdded(path: string): void {