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