@opensumi/ide-file-service 3.7.1-next-1736321498.0 → 3.7.1-next-1736420958.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.
- package/lib/browser/file-service-client.js +4 -4
- package/lib/browser/file-service-client.js.map +1 -1
- package/lib/browser/file-service-provider-client.d.ts +2 -2
- package/lib/browser/file-service-provider-client.d.ts.map +1 -1
- package/lib/browser/file-service-provider-client.js +2 -2
- package/lib/browser/file-service-provider-client.js.map +1 -1
- package/lib/common/files.d.ts +2 -2
- package/lib/common/files.d.ts.map +1 -1
- package/lib/common/watcher.d.ts +2 -3
- package/lib/common/watcher.d.ts.map +1 -1
- package/lib/common/watcher.js.map +1 -1
- package/lib/node/disk-file-system.provider.d.ts +2 -3
- package/lib/node/disk-file-system.provider.d.ts.map +1 -1
- package/lib/node/disk-file-system.provider.js +2 -3
- package/lib/node/disk-file-system.provider.js.map +1 -1
- package/lib/node/hosted/recursive/file-service-watcher.d.ts +13 -13
- package/lib/node/hosted/recursive/file-service-watcher.d.ts.map +1 -1
- package/lib/node/hosted/recursive/file-service-watcher.js +94 -139
- package/lib/node/hosted/recursive/file-service-watcher.js.map +1 -1
- package/lib/node/hosted/un-recursive/file-service-watcher.d.ts +11 -5
- package/lib/node/hosted/un-recursive/file-service-watcher.d.ts.map +1 -1
- package/lib/node/hosted/un-recursive/file-service-watcher.js +31 -16
- package/lib/node/hosted/un-recursive/file-service-watcher.js.map +1 -1
- package/lib/node/hosted/watcher.host.service.d.ts +2 -7
- package/lib/node/hosted/watcher.host.service.d.ts.map +1 -1
- package/lib/node/hosted/watcher.host.service.js +28 -55
- package/lib/node/hosted/watcher.host.service.js.map +1 -1
- package/lib/node/hosted/watcher.process.js +1 -1
- package/lib/node/hosted/watcher.process.js.map +1 -1
- package/lib/node/watcher-process-manager.d.ts +2 -3
- package/lib/node/watcher-process-manager.d.ts.map +1 -1
- package/lib/node/watcher-process-manager.js +3 -4
- package/lib/node/watcher-process-manager.js.map +1 -1
- package/package.json +9 -9
- package/src/browser/file-service-client.ts +4 -4
- package/src/browser/file-service-provider-client.ts +2 -3
- package/src/common/files.ts +2 -2
- package/src/common/watcher.ts +3 -10
- package/src/node/disk-file-system.provider.ts +3 -8
- package/src/node/hosted/recursive/file-service-watcher.ts +105 -170
- package/src/node/hosted/un-recursive/file-service-watcher.ts +45 -24
- package/src/node/hosted/watcher.host.service.ts +33 -95
- package/src/node/hosted/watcher.process.ts +1 -1
- package/src/node/watcher-process-manager.ts +5 -13
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import
|
|
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 {
|
|
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
|
|
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
|
-
|
|
55
|
+
number,
|
|
54
56
|
{ path: string; handlers: ParcelWatcher.SubscribeCallback[]; disposable: IDisposable }
|
|
55
57
|
>();
|
|
56
|
-
|
|
57
|
-
protected watcherOptions = new Map<
|
|
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
|
-
|
|
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
|
-
|
|
99
|
-
if (
|
|
100
|
-
return;
|
|
95
|
+
let watcherId = this.checkIsAlreadyWatched(basePath);
|
|
96
|
+
if (watcherId) {
|
|
97
|
+
return watcherId;
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
this.
|
|
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(`
|
|
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(
|
|
142
|
+
this.WATCHER_HANDLERS.set(watcherId, {
|
|
143
143
|
path: watchPath,
|
|
144
144
|
disposable: toDisposeWatcher,
|
|
145
145
|
handlers: [handler],
|
|
146
146
|
});
|
|
147
|
-
|
|
148
|
-
toDisposeWatcher.push(
|
|
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(
|
|
188
|
-
|
|
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
|
|
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(
|
|
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:
|
|
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('
|
|
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(`
|
|
232
|
+
this.logger.error(`watcher subscribe finally failed after ${maxRetries} times`);
|
|
287
233
|
return undefined; // watch 失败则返回 undefined
|
|
288
234
|
};
|
|
289
235
|
|
|
290
|
-
|
|
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
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
254
|
+
this.watcherOptions.delete(watcherId);
|
|
255
|
+
await watcher.stop();
|
|
299
256
|
}),
|
|
300
257
|
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
return disposables;
|
|
304
|
-
}
|
|
305
258
|
|
|
306
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
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(
|
|
348
|
-
this.
|
|
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(
|
|
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
|
|
304
|
+
return isLinux;
|
|
370
305
|
}
|
|
371
306
|
|
|
372
|
-
private async handleNSFWEvents(events: INsfw.ChangeEvent[],
|
|
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 = (
|
|
378
|
-
const options = this.watcherOptions.get(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
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
|
|
14
|
-
private
|
|
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.
|
|
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(`
|
|
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.
|
|
154
|
+
const stat = await fs.lstatSync(basePath);
|
|
130
155
|
if (stat) {
|
|
131
156
|
watchPath = basePath;
|
|
132
157
|
}
|
|
133
158
|
} else {
|
|
134
|
-
this.logger.warn('
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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 {
|