1
>
/*---------------------------------------------------------------------------------------------
watcher.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { Event } from '../../../base/common/event.js';
7
>
import { GLOBSTAR, IRelativePattern, parse, ParsedPattern } from '../../../base/common/glob.js';
8
>
import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js';
9
>
import { isAbsolute } from '../../../base/common/path.js';
10
>
import { isLinux } from '../../../base/common/platform.js';
11
>
import { URI } from '../../../base/common/uri.js';
12
>
import { FileChangeFilter, FileChangeType, IFileChange, isParent } from './files.js';
13
>
14
>
interface IWatchRequest {
15
>
16
>
/**
17
>
* The path to watch.
18
>
*/
19
>
readonly path: string;
20
>
21
>
/**
22
>
* Whether to watch recursively or not.
23
>
*/
24
>
readonly recursive: boolean;
25
>
26
>
/**
27
>
* A set of glob patterns or paths to exclude from watching.
28
>
*/
29
>
readonly excludes: string[];
30
>
31
>
/**
32
>
* An optional set of glob patterns or paths to include for
33
>
* watching. If not provided, all paths are considered for
34
>
* events.
35
>
*/
36
>
readonly includes?: Array<string | IRelativePattern>;
37
>
38
>
/**
39
>
* If provided, file change events from the watcher that
40
>
* are a result of this watch request will carry the same
41
>
* id.
42
>
*/
43
>
readonly correlationId?: number;
44
>
45
>
/**
46
>
* If provided, allows to filter the events that the watcher should consider
47
>
* for emitting. If not provided, all events are emitted.
48
>
*
49
>
* For example, to emit added and updated events, set to:
50
>
* `FileChangeFilter.ADDED | FileChangeFilter.UPDATED`.
51
>
*/
52
>
readonly filter?: FileChangeFilter;
53
>
}
54
>
55
>
export interface IWatchRequestWithCorrelation extends IWatchRequest {
56
>
readonly correlationId: number;
57
>
}
58
>
59
>
export function isWatchRequestWithCorrelation(request: IWatchRequest): request is IWatchRequestWithCorrelation {
60
return typeof request.correlationId === 'number';
61
}
63
>
export interface INonRecursiveWatchRequest extends IWatchRequest {
64
>
65
>
/**
66
>
* The watcher will be non-recursive.
67
>
*/
68
>
readonly recursive: false;
69
>
}
70
>
71
>
export interface IRecursiveWatchRequest extends IWatchRequest {
72
>
73
>
/**
74
>
* The watcher will be recursive.
75
>
*/
76
>
readonly recursive: true;
77
>
78
>
/**
79
>
* @deprecated this only exists for WSL1 support and should never
80
>
* be used in any other case.
81
>
*/
82
>
pollingInterval?: number;
83
>
}
84
>
85
>
export function isRecursiveWatchRequest(request: IWatchRequest): request is IRecursiveWatchRequest {
86
return request.recursive === true;
87
}
89
>
export type IUniversalWatchRequest = IRecursiveWatchRequest | INonRecursiveWatchRequest;
90
>
91
>
export interface IWatcherErrorEvent {
92
>
readonly error: string;
93
>
readonly request?: IUniversalWatchRequest;
94
>
}
95
>
96
>
export interface IWatcher {
97
>
98
>
/**
99
>
* A normalized file change event from the raw events
100
>
* the watcher emits.
101
>
*/
102
>
readonly onDidChangeFile: Event<IFileChange[]>;
103
>
104
>
/**
105
>
* An event to indicate a message that should get logged.
106
>
*/
107
>
readonly onDidLogMessage: Event<ILogMessage>;
108
>
109
>
/**
110
>
* An event to indicate an error occurred from the watcher
111
>
* that is unrecoverable. Listeners should restart the
112
>
* watcher if possible.
113
>
*/
114
>
readonly onDidError: Event<IWatcherErrorEvent>;
115
>
116
>
/**
117
>
* Configures the watcher to watch according to the
118
>
* requests. Any existing watched path that is not
119
>
* in the array, will be removed from watching and
120
>
* any new path will be added to watching.
121
>
*/
122
>
watch(requests: IWatchRequest[]): Promise<void>;
123
>
124
>
/**
125
>
* Enable verbose logging in the watcher.
126
>
*/
127
>
setVerboseLogging(enabled: boolean): Promise<void>;
128
>
129
>
/**
130
>
* Stop all watchers.
131
>
*/
132
>
stop(): Promise<void>;
133
>
}
134
>
135
>
export interface IRecursiveWatcher extends IWatcher {
136
>
watch(requests: IRecursiveWatchRequest[]): Promise<void>;
137
>
}
138
>
139
>
export interface IRecursiveWatcherWithSubscribe extends IRecursiveWatcher {
140
>
141
>
/**
142
>
* Subscribe to file events for the given path. The callback is called
143
>
* whenever a file event occurs for the path. If the watcher failed,
144
>
* the error parameter is set to `true`.
145
>
*
146
>
* @returns an `IDisposable` to stop listening to events or `undefined`
147
>
* if no events can be watched for the path given the current set of
148
>
* recursive watch requests.
149
>
*/
150
>
subscribe(path: string, callback: (error: true | null, change?: IFileChange) => void): IDisposable | undefined;
151
>
}
152
>
153
>
export interface IRecursiveWatcherOptions {
154
>
155
>
/**
156
>
* If `true`, will enable polling for all watchers, otherwise
157
>
* will enable it for paths included in the string array.
158
>
*
159
>
* @deprecated this only exists for WSL1 support and should never
160
>
* be used in any other case.
161
>
*/
162
>
readonly usePolling: boolean | string[];
163
>
164
>
/**
165
>
* If polling is enabled (via `usePolling`), defines the duration
166
>
* in which the watcher will poll for changes.
167
>
*
168
>
* @deprecated this only exists for WSL1 support and should never
169
>
* be used in any other case.
170
>
*/
171
>
readonly pollingInterval?: number;
172
>
}
173
>
174
>
export interface INonRecursiveWatcher extends IWatcher {
175
>
watch(requests: INonRecursiveWatchRequest[]): Promise<void>;
176
>
}
177
>
178
>
export interface IUniversalWatcher extends IWatcher {
179
>
watch(requests: IUniversalWatchRequest[]): Promise<void>;
180
>
}
181
>
182
>
export abstract class AbstractWatcherClient extends Disposable {
183
>
184
>
private static readonly MAX_RESTARTS = 5;
185
>
186
>
private watcher: IWatcher | undefined;
187
>
private readonly watcherDisposables = this._register(new MutableDisposable());
188
>
189
>
private requests: IWatchRequest[] | undefined = undefined;
190
>
191
>
private restartCounter = 0;
192
>
193
>
constructor(
194
private readonly onFileChanges: (changes: IFileChange[]) => void,
195
private readonly onLogMessage: (msg: ILogMessage) => void,