@opensumi/ide-file-service 3.7.1 → 3.7.2-next-1739848467.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 +3 -2
- 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 +3 -2
- package/lib/node/disk-file-system.provider.d.ts.map +1 -1
- package/lib/node/disk-file-system.provider.js +3 -2
- package/lib/node/disk-file-system.provider.js.map +1 -1
- package/lib/node/hosted/recursive/file-service-watcher.d.ts +14 -13
- package/lib/node/hosted/recursive/file-service-watcher.d.ts.map +1 -1
- package/lib/node/hosted/recursive/file-service-watcher.js +158 -97
- package/lib/node/hosted/recursive/file-service-watcher.js.map +1 -1
- package/lib/node/hosted/un-recursive/file-service-watcher.d.ts +5 -11
- package/lib/node/hosted/un-recursive/file-service-watcher.d.ts.map +1 -1
- package/lib/node/hosted/un-recursive/file-service-watcher.js +15 -31
- package/lib/node/hosted/un-recursive/file-service-watcher.js.map +1 -1
- package/lib/node/hosted/watcher.host.service.d.ts +7 -2
- package/lib/node/hosted/watcher.host.service.d.ts.map +1 -1
- package/lib/node/hosted/watcher.host.service.js +60 -28
- 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 +3 -2
- package/lib/node/watcher-process-manager.d.ts.map +1 -1
- package/lib/node/watcher-process-manager.js +4 -3
- 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 +3 -2
- package/src/common/files.ts +2 -2
- package/src/common/watcher.ts +10 -3
- package/src/node/disk-file-system.provider.ts +8 -3
- package/src/node/hosted/recursive/file-service-watcher.ts +190 -108
- package/src/node/hosted/un-recursive/file-service-watcher.ts +22 -46
- package/src/node/hosted/watcher.host.service.ts +100 -33
- package/src/node/hosted/watcher.process.ts +1 -1
- package/src/node/watcher-process-manager.ts +13 -5
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import
|
|
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
|
|
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
|
-
|
|
53
|
+
string,
|
|
56
54
|
{ path: string; handlers: ParcelWatcher.SubscribeCallback[]; disposable: IDisposable }
|
|
57
55
|
>();
|
|
58
|
-
|
|
59
|
-
protected watcherOptions = new Map<
|
|
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(
|
|
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)
|
|
93
|
-
|
|
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
|
-
|
|
96
|
-
if (
|
|
97
|
-
|
|
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
|
-
|
|
101
|
-
this.
|
|
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
|
-
|
|
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(
|
|
148
|
+
this.WATCHER_HANDLERS.set(watchPath, {
|
|
143
149
|
path: watchPath,
|
|
144
150
|
disposable: toDisposeWatcher,
|
|
145
151
|
handlers: [handler],
|
|
146
152
|
});
|
|
147
|
-
|
|
148
|
-
toDisposeWatcher.push(
|
|
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
|
-
|
|
189
|
-
|
|
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
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
255
|
-
|
|
302
|
+
if (hanlder) {
|
|
303
|
+
await hanlder.unsubscribe();
|
|
304
|
+
}
|
|
256
305
|
}),
|
|
257
306
|
);
|
|
307
|
+
}
|
|
258
308
|
|
|
259
|
-
|
|
309
|
+
return disposables;
|
|
310
|
+
}
|
|
260
311
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
284
|
-
const watcher = this.WATCHER_HANDLERS.get(
|
|
353
|
+
private disposeWatcher(path: string) {
|
|
354
|
+
const watcher = this.WATCHER_HANDLERS.get(path);
|
|
285
355
|
if (watcher) {
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
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[],
|
|
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 = (
|
|
313
|
-
const options = this.watcherOptions.get(
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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,
|
|
5
|
+
import { Disposable, DisposableCollection, FileUri, isMacintosh, path } from '@opensumi/ide-utils/lib';
|
|
6
6
|
|
|
7
|
-
import { FileChangeType, FileSystemWatcherClient,
|
|
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
|
|
13
|
-
private
|
|
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(
|
|
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.
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
watcher.
|
|
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 {
|