forked from microsoft/vscode-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfileSystemWatcher.ts
More file actions
132 lines (123 loc) · 4.87 KB
/
fileSystemWatcher.ts
File metadata and controls
132 lines (123 loc) · 4.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
'use strict';
import * as chokidar from 'chokidar';
import * as path from 'path';
import { RelativePattern, workspace } from 'vscode';
import { traceError, traceVerbose } from '../logger';
import { Disposables, IDisposable } from '../utils/resourceLifecycle';
import { normCasePath } from './fs-paths';
/**
* Enumeration of file change types.
*/
export enum FileChangeType {
Changed = 'changed',
Created = 'created',
Deleted = 'deleted',
}
const POLLING_INTERVAL = 5000;
export function watchLocationForPattern(
baseDir: string,
pattern: string,
callback: (type: FileChangeType, absPath: string) => void,
): IDisposable {
// Use VSCode API iff base directory to exists within the current workspace folders
const found = workspace.workspaceFolders?.find((e) => normCasePath(baseDir).startsWith(normCasePath(e.uri.fsPath)));
if (found) {
return watchLocationUsingVSCodeAPI(baseDir, pattern, callback);
}
// Fallback to chokidar as base directory to lookup doesn't exist within the current workspace folders
return watchLocationUsingChokidar(baseDir, pattern, callback);
}
function watchLocationUsingVSCodeAPI(
baseDir: string,
pattern: string,
callback: (type: FileChangeType, absPath: string) => void,
): IDisposable {
const globPattern = new RelativePattern(baseDir, pattern);
const disposables = new Disposables();
traceVerbose(`Start watching: ${baseDir} with pattern ${pattern} using VSCode API`);
const watcher = workspace.createFileSystemWatcher(globPattern);
disposables.push(watcher.onDidCreate((e) => callback(FileChangeType.Created, e.fsPath)));
disposables.push(watcher.onDidChange((e) => callback(FileChangeType.Changed, e.fsPath)));
disposables.push(watcher.onDidDelete((e) => callback(FileChangeType.Deleted, e.fsPath)));
return disposables;
}
function watchLocationUsingChokidar(
baseDir: string,
pattern: string,
callback: (type: FileChangeType, absPath: string) => void,
): IDisposable {
const watcherOpts: chokidar.WatchOptions = {
cwd: baseDir,
ignoreInitial: true,
ignorePermissionErrors: true,
// While not used in normal cases, if any error causes chokidar to fallback to polling, increase its intervals
interval: POLLING_INTERVAL,
binaryInterval: POLLING_INTERVAL,
/* 'depth' doesn't matter given regex restricts the depth to 2, same goes for other properties below
* But using them just to be safe in case it's misused */
depth: 2,
ignored: [
'**/Lib/**',
'**/.git/**',
'**/node_modules/*/**',
'**/.hg/store/**',
'/dev/**',
'/proc/**',
'/sys/**',
'**/lib/**',
'**/includes/**',
], // https://github.com/microsoft/vscode/issues/23954
followSymlinks: true,
};
traceVerbose(`Start watching: ${baseDir} with pattern ${pattern} using chokidar`);
let watcher: chokidar.FSWatcher | null = chokidar.watch(pattern, watcherOpts);
watcher.on('all', (type: string, e: string) => {
const absPath = path.join(baseDir, e);
let eventType: FileChangeType;
switch (type) {
case 'change':
eventType = FileChangeType.Changed;
break;
case 'add':
case 'addDir':
eventType = FileChangeType.Created;
break;
case 'unlink':
case 'unlinkDir':
eventType = FileChangeType.Deleted;
break;
default:
return;
}
callback(eventType, absPath);
});
const stopWatcher = async () => {
if (watcher) {
const obj = watcher;
watcher = null;
try {
await obj.close();
} catch (err) {
traceError(`Failed to close FS watcher (${err})`);
}
}
};
watcher.on('error', async (error: NodeJS.ErrnoException) => {
if (error) {
// Specially handle ENOSPC errors that can happen when
// the watcher consumes so many file descriptors that
// we are running into a limit. We only want to warn
// once in this case to avoid log spam.
// See https://github.com/Microsoft/vscode/issues/7950
if (error.code === 'ENOSPC') {
traceError(`Inotify limit reached (ENOSPC) for ${baseDir} with pattern ${pattern}`);
await stopWatcher();
} else {
traceVerbose(error.toString());
}
}
});
return { dispose: () => stopWatcher().ignoreErrors() };
}