@opensumi/ide-file-service 3.7.1-next-1736321498.0 → 3.7.1-next-1736417771.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 -13
  17. package/lib/node/hosted/recursive/file-service-watcher.d.ts.map +1 -1
  18. package/lib/node/hosted/recursive/file-service-watcher.js +94 -139
  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 -16
  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 -55
  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 +105 -170
  41. package/src/node/hosted/un-recursive/file-service-watcher.ts +45 -24
  42. package/src/node/hosted/watcher.host.service.ts +33 -95
  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,35 +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
- this.doWatchFileChange(uri, options).then(() => {
90
- resolve(void 0);
91
- if (timer) {
92
- clearTimeout(timer);
93
- }
94
- });
95
- });
96
- }
92
+ async watchFileChanges(uri: string, options?: WatchOptions): Promise<number> {
93
+ const basePath = FileUri.fsPath(uri);
97
94
 
98
- private async doWatchFileChange(uri: string, options?: WatchOptions) {
99
- if (this.WATCHER_HANDLERS.has(uri)) {
100
- return;
95
+ let watcherId = this.checkIsAlreadyWatched(basePath);
96
+ if (watcherId) {
97
+ return watcherId;
101
98
  }
102
99
 
103
- const basePath = FileUri.fsPath(uri);
104
- 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
+ });
105
105
 
106
106
  const toDisposeWatcher = new DisposableCollection();
107
107
  let watchPath: string;
@@ -117,10 +117,10 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
117
117
  } else {
118
118
  watchPath = await this.lookup(basePath);
119
119
  }
120
-
120
+ this.logger.log('Starting watching:', watchPath, options);
121
121
  const handler = (err, events: ParcelWatcher.Event[]) => {
122
122
  if (err) {
123
- this.logger.error(`[Recursive] Watching ${watchPath} error: `, err);
123
+ this.logger.error(`Watching ${watchPath} error: `, err);
124
124
  return;
125
125
  }
126
126
  events = this.trimChangeEvent(events);
@@ -139,15 +139,15 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
139
139
  }
140
140
  };
141
141
 
142
- this.WATCHER_HANDLERS.set(uri, {
142
+ this.WATCHER_HANDLERS.set(watcherId, {
143
143
  path: watchPath,
144
144
  disposable: toDisposeWatcher,
145
145
  handlers: [handler],
146
146
  });
147
-
148
- toDisposeWatcher.push(Disposable.create(() => this.WATCHER_HANDLERS.delete(uri)));
149
- 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));
150
149
  this.addDispose(toDisposeWatcher);
150
+ return watcherId;
151
151
  }
152
152
 
153
153
  /**
@@ -184,63 +184,16 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
184
184
  this.excludes = excludes;
185
185
  }
186
186
 
187
- protected async start(basePath: string, rawOptions: WatchOptions | undefined): Promise<DisposableCollection> {
188
- this.logger.log('[Recursive] Start watching', basePath);
189
-
187
+ protected async start(
188
+ watcherId: number,
189
+ basePath: string,
190
+ rawOptions: WatchOptions | undefined,
191
+ ): Promise<DisposableCollection> {
192
+ const disposables = new DisposableCollection();
190
193
  if (!(await fs.pathExists(basePath))) {
191
- return new DisposableCollection();
194
+ return disposables;
192
195
  }
193
-
194
196
  const realPath = await fs.realpath(basePath);
195
-
196
- if (this.isEnableNSFW()) {
197
- return this.watchWithNsfw(realPath, rawOptions);
198
- } else {
199
- // polling
200
- if (rawOptions?.pollingWatch) {
201
- this.logger.log('[Recursive] Start polling watch:', realPath);
202
- return this.pollingWatch(realPath, rawOptions);
203
- }
204
-
205
- return this.watchWithParcel(realPath, rawOptions);
206
- }
207
- }
208
-
209
- private async watchWithNsfw(realPath: string, rawOptions?: WatchOptions | undefined) {
210
- const disposables = new DisposableCollection();
211
- const nsfw = await this.withNSFWModule();
212
- const watcher: INsfw.NSFW = await nsfw(
213
- realPath,
214
- (events: INsfw.ChangeEvent[]) => this.handleNSFWEvents(events, realPath),
215
- {
216
- errorCallback: (err) => {
217
- this.logger.error('[Recursive] NSFW watcher encountered an error and will stop watching.', err);
218
- // see https://github.com/atom/github/issues/342
219
- this.unwatchFileChanges(realPath);
220
- },
221
- },
222
- );
223
-
224
- await watcher.start();
225
-
226
- disposables.push(
227
- Disposable.create(async () => {
228
- this.watcherOptions.delete(realPath);
229
- await watcher.stop();
230
- }),
231
- );
232
-
233
- const excludes = this.excludes.concat(rawOptions?.excludes || []);
234
-
235
- this.watcherOptions.set(realPath, {
236
- excludesPattern: excludes.map((pattern) => parseGlob(pattern)),
237
- excludes,
238
- });
239
- return disposables;
240
- }
241
-
242
- private async watchWithParcel(realPath: string, rawOptions?: WatchOptions | undefined) {
243
- const disposables = new DisposableCollection();
244
197
  const tryWatchDir = async (maxRetries = 3, retryDelay = 1000) => {
245
198
  for (let times = 0; times < maxRetries; times++) {
246
199
  try {
@@ -252,30 +205,23 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
252
205
  // FIXME: 研究此处屏蔽的影响,考虑下阈值应该设置多少,或者更加优雅的方式
253
206
  return;
254
207
  }
255
- const handlers = this.WATCHER_HANDLERS.get(realPath)?.handlers;
208
+ const handlers = this.WATCHER_HANDLERS.get(watcherId)?.handlers;
256
209
 
257
210
  if (!handlers) {
258
- this.logger.log('[Recursive] No handler found for watcher', realPath);
259
211
  return;
260
212
  }
261
-
262
- this.logger.log('[Recursive] Received events:', events);
263
- if (events.length === 0) {
264
- return;
265
- }
266
-
267
213
  for (const handler of handlers) {
268
214
  (handler as ParcelWatcher.SubscribeCallback)(err, events);
269
215
  }
270
216
  },
271
217
  {
272
- backend: RecursiveFileSystemWatcher.PARCEL_WATCHER_BACKEND,
218
+ backend: FileSystemWatcherServer.PARCEL_WATCHER_BACKEND,
273
219
  ignore: this.excludes.concat(rawOptions?.excludes ?? []),
274
220
  },
275
221
  );
276
222
  } catch (e) {
277
223
  // Watcher 启动失败,尝试重试
278
- this.logger.error('[Recursive] watcher subscribe failed ', e, ' try times ', times);
224
+ this.logger.error('watcher subscribe failed ', e, ' try times ', times);
279
225
  await new Promise((resolve) => {
280
226
  setTimeout(resolve, retryDelay);
281
227
  });
@@ -283,72 +229,61 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
283
229
  }
284
230
 
285
231
  // 经过若干次的尝试后,Parcel Watcher 依然启动失败,此时就不再尝试重试
286
- this.logger.error(`[Recursive] watcher subscribe finally failed after ${maxRetries} times`);
232
+ this.logger.error(`watcher subscribe finally failed after ${maxRetries} times`);
287
233
  return undefined; // watch 失败则返回 undefined
288
234
  };
289
235
 
290
- 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();
291
251
 
292
- if (hanlder) {
293
- // watch 成功才加入 disposables,否则也就无需 dispose
294
252
  disposables.push(
295
253
  Disposable.create(async () => {
296
- if (hanlder) {
297
- await hanlder.unsubscribe();
298
- }
254
+ this.watcherOptions.delete(watcherId);
255
+ await watcher.stop();
299
256
  }),
300
257
  );
301
- }
302
-
303
- return disposables;
304
- }
305
258
 
306
- private async pollingWatch(realPath: string, rawOptions?: WatchOptions | undefined) {
307
- const disposables = new DisposableCollection();
308
- const snapshotFile = join(tmpdir(), `watcher-snapshot-${realPath}`);
309
- let counter = 0;
310
-
311
- const pollingWatcher = new RunOnceScheduler(async () => {
312
- counter++;
313
- if (counter > 1) {
314
- const parcelEvents = await ParcelWatcher.getEventsSince(realPath, snapshotFile, {
315
- ignore: rawOptions?.excludes,
316
- backend: RecursiveFileSystemWatcher.PARCEL_WATCHER_BACKEND,
317
- });
318
-
319
- const handlers = this.WATCHER_HANDLERS.get(realPath)?.handlers;
320
-
321
- if (!handlers) {
322
- this.logger.log('[Recursive] No handler found for watcher', realPath);
323
- return;
324
- }
259
+ const excludes = this.excludes.concat(rawOptions?.excludes || []);
325
260
 
326
- this.logger.log('[Recursive] Received events:', parcelEvents);
327
- for (const handler of handlers) {
328
- (handler as ParcelWatcher.SubscribeCallback)(null, parcelEvents);
329
- }
330
- }
331
-
332
- await ParcelWatcher.writeSnapshot(realPath, snapshotFile, {
333
- ignore: rawOptions?.excludes,
334
- backend: RecursiveFileSystemWatcher.PARCEL_WATCHER_BACKEND,
261
+ this.watcherOptions.set(watcherId, {
262
+ excludesPattern: excludes.map((pattern) => parseGlob(pattern)),
263
+ excludes,
335
264
  });
336
-
337
- pollingWatcher.schedule();
338
- }, RecursiveFileSystemWatcher.DEFAULT_POLLING_INTERVAL);
339
-
340
- pollingWatcher.schedule(0);
341
-
342
- 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
+ }
343
279
 
344
280
  return disposables;
345
281
  }
346
282
 
347
- unwatchFileChanges(uri: string): Promise<void> {
348
- this.logger.log('[Recursive] Un watch: ', uri);
349
- const watcher = this.WATCHER_HANDLERS.get(uri);
283
+ unwatchFileChanges(watcherId: number): Promise<void> {
284
+ const watcher = this.WATCHER_HANDLERS.get(watcherId);
350
285
  if (watcher) {
351
- this.WATCHER_HANDLERS.delete(uri);
286
+ this.WATCHER_HANDLERS.delete(watcherId);
352
287
  watcher.disposable.dispose();
353
288
  }
354
289
  return Promise.resolve();
@@ -366,16 +301,16 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
366
301
  * 社区相关 issue: https://github.com/parcel-bundler/watcher/issues/49
367
302
  */
368
303
  private isEnableNSFW(): boolean {
369
- return this.backend === RecursiveWatcherBackend.NSFW || isLinux;
304
+ return isLinux;
370
305
  }
371
306
 
372
- private async handleNSFWEvents(events: INsfw.ChangeEvent[], realPath: string): Promise<void> {
307
+ private async handleNSFWEvents(events: INsfw.ChangeEvent[], watcherId: number): Promise<void> {
373
308
  if (events.length > 5000) {
374
309
  return;
375
310
  }
376
311
 
377
- const isIgnored = (realPath: string, path: string): boolean => {
378
- const options = this.watcherOptions.get(realPath);
312
+ const isIgnored = (watcherId: number, path: string): boolean => {
313
+ const options = this.watcherOptions.get(watcherId);
379
314
  if (!options || !options.excludes || options.excludes.length < 1) {
380
315
  return false;
381
316
  }
@@ -407,7 +342,7 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
407
342
  case INsfw.actions.RENAMED:
408
343
  {
409
344
  const deletedPath = await this.resolvePath(event.directory, event.oldFile!);
410
- if (isIgnored(realPath, deletedPath)) {
345
+ if (isIgnored(watcherId, deletedPath)) {
411
346
  return;
412
347
  }
413
348
 
@@ -415,14 +350,14 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
415
350
 
416
351
  if (event.newDirectory) {
417
352
  const path = await this.resolvePath(event.newDirectory, event.newFile!);
418
- if (isIgnored(realPath, path)) {
353
+ if (isIgnored(watcherId, path)) {
419
354
  return;
420
355
  }
421
356
 
422
357
  this.pushAdded(path);
423
358
  } else {
424
359
  const path = await this.resolvePath(event.directory, event.newFile!);
425
- if (isIgnored(realPath, path)) {
360
+ if (isIgnored(watcherId, path)) {
426
361
  return;
427
362
  }
428
363
 
@@ -433,7 +368,7 @@ export class RecursiveFileSystemWatcher extends Disposable implements IWatcher {
433
368
  default:
434
369
  {
435
370
  const path = await this.resolvePath(event.directory, event.file!);
436
- if (isIgnored(realPath, path)) {
371
+ if (isIgnored(watcherId, path)) {
437
372
  return;
438
373
  }
439
374
 
@@ -1,17 +1,25 @@
1
1
  import fs, { watch } from 'fs-extra';
2
- import { upperFirst } from 'lodash';
3
2
  import debounce from 'lodash/debounce';
4
3
 
5
4
  import { ILogService } from '@opensumi/ide-core-common/lib/log';
6
5
  import { Disposable, DisposableCollection, FileUri, IDisposable, isMacintosh, path } from '@opensumi/ide-utils/lib';
7
6
 
8
- import { FileChangeType, FileSystemWatcherClient, IWatcher } from '../../../common/index';
7
+ import { FileChangeType, FileSystemWatcherClient, IFileSystemWatcherServer } from '../../../common/index';
9
8
  import { FileChangeCollection } from '../../file-change-collection';
10
9
  import { shouldIgnorePath } from '../shared';
11
10
  const { join, basename, normalize } = path;
12
11
 
13
- export class UnRecursiveFileSystemWatcher implements IWatcher {
14
- 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;
15
23
 
16
24
  private static readonly FILE_DELETE_HANDLER_DELAY = 500;
17
25
 
@@ -26,14 +34,25 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
26
34
 
27
35
  dispose(): void {
28
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
+ }
29
50
  }
30
51
 
31
52
  private async doWatch(basePath: string) {
32
53
  try {
33
54
  const watcher = watch(basePath);
34
- this.watcherCollections.set(basePath, watcher);
35
-
36
- this.logger.log('[Un-Recursive] start watching', basePath);
55
+ this.logger.log('start watching', basePath);
37
56
  const isDirectory = fs.lstatSync(basePath).isDirectory();
38
57
 
39
58
  const docChildren = new Set<string>();
@@ -55,9 +74,7 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
55
74
 
56
75
  // 开始走监听流程
57
76
  watcher.on('error', (code: number, signal: string) => {
58
- this.logger.error(
59
- `[Un-Recursive] Failed to watch ${basePath} for changes using fs.watch() (${code}, ${signal})`,
60
- );
77
+ this.logger.error(`Failed to watch ${basePath} for changes using fs.watch() (${code}, ${signal})`);
61
78
  watcher.close();
62
79
  });
63
80
 
@@ -113,7 +130,7 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
113
130
  }
114
131
  });
115
132
  } catch (error) {
116
- 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()})`);
117
134
  }
118
135
  }
119
136
 
@@ -121,20 +138,29 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
121
138
  const basePath = FileUri.fsPath(uri);
122
139
  const exist = await fs.pathExists(basePath);
123
140
 
141
+ let watcherId = this.checkIsAlreadyWatched(basePath);
142
+
143
+ if (watcherId) {
144
+ return watcherId;
145
+ }
146
+
147
+ watcherId = UnRecursiveFileSystemWatcher.WATCHER_SEQUENCE++;
148
+
124
149
  const disposables = new DisposableCollection(); // 管理可释放的资源
125
150
 
126
151
  let watchPath = '';
127
152
 
128
153
  if (exist) {
129
- const stat = await fs.lstat(basePath);
154
+ const stat = await fs.lstatSync(basePath);
130
155
  if (stat) {
131
156
  watchPath = basePath;
132
157
  }
133
158
  } else {
134
- 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');
135
160
  }
136
161
  disposables.push(await this.start(watchPath));
137
162
  this.toDispose.push(disposables);
163
+ return watcherId;
138
164
  }
139
165
 
140
166
  protected async start(basePath: string): Promise<DisposableCollection> {
@@ -144,10 +170,6 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
144
170
  }
145
171
 
146
172
  const realPath = await fs.realpath(basePath);
147
- if (this.watcherCollections.has(realPath)) {
148
- return disposables;
149
- }
150
-
151
173
  const tryWatchDir = async (retryDelay = 1000) => {
152
174
  try {
153
175
  this.doWatch(realPath);
@@ -161,14 +183,13 @@ export class UnRecursiveFileSystemWatcher implements IWatcher {
161
183
  await tryWatchDir();
162
184
  return disposables;
163
185
  }
164
-
165
- async unwatchFileChanges(uri: string): Promise<void> {
166
- const basePath = FileUri.fsPath(uri);
167
- if (this.watcherCollections.has(basePath)) {
168
- const watcher = this.watcherCollections.get(basePath);
169
- watcher?.close();
170
- 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();
171
191
  }
192
+ return Promise.resolve();
172
193
  }
173
194
 
174
195
  protected pushAdded(path: string): void {