@adecore/lsp 0.29.1 → 0.29.2
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/dist/edits.js +13 -9
- package/dist/session.js +8 -7
- package/dist/uris.d.ts +1 -0
- package/dist/uris.js +11 -0
- package/dist/watched-files.d.ts +1 -1
- package/dist/watched-files.js +13 -4
- package/package.json +1 -1
- package/src/edits.ts +18 -11
- package/src/session.ts +8 -7
- package/src/uris.ts +12 -0
- package/src/watched-files.ts +14 -4
package/dist/edits.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ErrorCodes, LspError } from './connection.js';
|
|
2
|
+
import { fileUriKey } from './uris.js';
|
|
2
3
|
const CR = 13;
|
|
3
4
|
const LF = 10;
|
|
4
5
|
function checkPosition(position) {
|
|
@@ -171,20 +172,21 @@ export function minimalChange(before, after) {
|
|
|
171
172
|
};
|
|
172
173
|
}
|
|
173
174
|
function snapshotEdit(uri, snapshots) {
|
|
174
|
-
const
|
|
175
|
-
if (!
|
|
175
|
+
const found = snapshots.get(fileUriKey(uri));
|
|
176
|
+
if (!found) {
|
|
176
177
|
throw new LspError(`Missing workspace snapshot: ${uri}`);
|
|
177
178
|
}
|
|
178
|
-
return { uri, version: snapshot.version, before: snapshot.text, text: snapshot.text };
|
|
179
|
+
return { uri: found.uri, version: found.snapshot.version, before: found.snapshot.text, text: found.snapshot.text };
|
|
179
180
|
}
|
|
180
181
|
/* A file that is there already is refused, unless the create says to empty it or to leave it as it is. */
|
|
181
182
|
function planCreate(change, snapshots, planned) {
|
|
182
|
-
const
|
|
183
|
+
const key = fileUriKey(change.uri);
|
|
184
|
+
const existing = planned.get(key) ?? (snapshots.has(key) ? snapshotEdit(change.uri, snapshots) : null);
|
|
183
185
|
if (existing === null) {
|
|
184
|
-
planned.set(
|
|
186
|
+
planned.set(key, { uri: change.uri, version: null, before: '', text: '', created: true });
|
|
185
187
|
}
|
|
186
188
|
else if (change.options?.overwrite) {
|
|
187
|
-
planned.set(
|
|
189
|
+
planned.set(key, { ...existing, text: '' });
|
|
188
190
|
}
|
|
189
191
|
else if (!change.options?.ignoreIfExists) {
|
|
190
192
|
throw new LspError(`${change.uri} already exists`);
|
|
@@ -195,6 +197,7 @@ function planCreate(change, snapshots, planned) {
|
|
|
195
197
|
* files that are there; a file the edit creates is absent from it. A rename or delete needs the host's own handling and is refused.
|
|
196
198
|
*/
|
|
197
199
|
export function planWorkspaceEdit(edit, snapshots) {
|
|
200
|
+
const keyed = new Map([...snapshots].map(([uri, snapshot]) => [fileUriKey(uri), { uri, snapshot }]));
|
|
198
201
|
const planned = new Map();
|
|
199
202
|
const changes = edit.documentChanges ?? Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({ textDocument: { uri, version: null }, edits }));
|
|
200
203
|
for (const change of changes) {
|
|
@@ -202,15 +205,16 @@ export function planWorkspaceEdit(edit, snapshots) {
|
|
|
202
205
|
if (change.kind !== 'create') {
|
|
203
206
|
throw new LspError(`A host file-operation handler is required for ${change.kind}`);
|
|
204
207
|
}
|
|
205
|
-
planCreate(change,
|
|
208
|
+
planCreate(change, keyed, planned);
|
|
206
209
|
continue;
|
|
207
210
|
}
|
|
208
211
|
const { uri, version } = change.textDocument;
|
|
209
|
-
const
|
|
212
|
+
const key = fileUriKey(uri);
|
|
213
|
+
const current = planned.get(key) ?? snapshotEdit(uri, keyed);
|
|
210
214
|
if (version !== null && current.version !== version) {
|
|
211
215
|
throw new LspError(`Workspace edit version mismatch: ${uri}`, ErrorCodes.ContentModified);
|
|
212
216
|
}
|
|
213
|
-
planned.set(
|
|
217
|
+
planned.set(key, { ...current, text: applyTextEdits(current.text, change.edits) });
|
|
214
218
|
}
|
|
215
219
|
return [...planned.values()];
|
|
216
220
|
}
|
package/dist/session.js
CHANGED
|
@@ -2,6 +2,7 @@ import { ErrorCodes, JsonRpcConnection, LspError } from './connection.js';
|
|
|
2
2
|
import { LspDocument } from './document.js';
|
|
3
3
|
import { createsTaken, deletesTaken, renamesTaken } from './file-operations.js';
|
|
4
4
|
import { globMatch } from './glob.js';
|
|
5
|
+
import { fileUriKey } from './uris.js';
|
|
5
6
|
/* The capability that advertises each method statically. */
|
|
6
7
|
const PROVIDERS = {
|
|
7
8
|
'textDocument/completion': 'completionProvider',
|
|
@@ -111,11 +112,11 @@ export class LspSession {
|
|
|
111
112
|
});
|
|
112
113
|
this.connection.onNotification('textDocument/publishDiagnostics', (params) => {
|
|
113
114
|
const diagnostics = params;
|
|
114
|
-
const document = this.documents.get(diagnostics.uri);
|
|
115
|
+
const document = this.documents.get(fileUriKey(diagnostics.uri));
|
|
115
116
|
if (!document || document.isClosed || (diagnostics.version !== undefined && diagnostics.version !== document.version)) {
|
|
116
117
|
return;
|
|
117
118
|
}
|
|
118
|
-
this.emitDiagnostics(diagnostics);
|
|
119
|
+
this.emitDiagnostics({ ...diagnostics, uri: document.uri });
|
|
119
120
|
});
|
|
120
121
|
this.connection.onRequest('workspace/configuration', async (params) => {
|
|
121
122
|
const { items } = params;
|
|
@@ -202,15 +203,15 @@ export class LspSession {
|
|
|
202
203
|
}
|
|
203
204
|
openDocument(item) {
|
|
204
205
|
this.assertReady();
|
|
205
|
-
if (this.documents.has(item.uri)) {
|
|
206
|
+
if (this.documents.has(fileUriKey(item.uri))) {
|
|
206
207
|
throw new LspError(`Document already open: ${item.uri}`);
|
|
207
208
|
}
|
|
208
209
|
const document = new LspDocument(this, item);
|
|
209
|
-
this.documents.set(item.uri, document);
|
|
210
|
+
this.documents.set(fileUriKey(item.uri), document);
|
|
210
211
|
return document;
|
|
211
212
|
}
|
|
212
213
|
getDocument(uri) {
|
|
213
|
-
return this.documents.get(uri);
|
|
214
|
+
return this.documents.get(fileUriKey(uri));
|
|
214
215
|
}
|
|
215
216
|
/* The options the server gave a method, from its capabilities or a registration whose selector takes `document`. */
|
|
216
217
|
providerOptions(method, document) {
|
|
@@ -355,10 +356,10 @@ export class LspSession {
|
|
|
355
356
|
}
|
|
356
357
|
/* Called by a document that closed, so its diagnostics go with it. */
|
|
357
358
|
forgetDocument(document) {
|
|
358
|
-
if (this.documents.get(document.uri) !== document) {
|
|
359
|
+
if (this.documents.get(fileUriKey(document.uri)) !== document) {
|
|
359
360
|
return;
|
|
360
361
|
}
|
|
361
|
-
this.documents.delete(document.uri);
|
|
362
|
+
this.documents.delete(fileUriKey(document.uri));
|
|
362
363
|
this.emitDiagnostics({ uri: document.uri, diagnostics: [] });
|
|
363
364
|
}
|
|
364
365
|
assertReady() {
|
package/dist/uris.d.ts
CHANGED
package/dist/uris.js
CHANGED
|
@@ -37,3 +37,14 @@ export function fileUriToPath(uri) {
|
|
|
37
37
|
}
|
|
38
38
|
return path;
|
|
39
39
|
}
|
|
40
|
+
/*
|
|
41
|
+
* One key for every way a URI can name the same Windows file: servers write the drive in either
|
|
42
|
+
* case, and some escape its colon (`file:///c%3A/...`). Any other URI is its own key.
|
|
43
|
+
*/
|
|
44
|
+
export function fileUriKey(uri) {
|
|
45
|
+
const path = fileUriToPath(uri);
|
|
46
|
+
if (path === null || !/^[A-Za-z]:\\/.test(path)) {
|
|
47
|
+
return uri;
|
|
48
|
+
}
|
|
49
|
+
return pathToFileUri(`${path[0].toUpperCase()}${path.slice(1)}`);
|
|
50
|
+
}
|
package/dist/watched-files.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { FileSystemWatcher } from './protocol.ts';
|
|
2
2
|
export type FileChangeType = 1 | 2 | 3;
|
|
3
|
-
export declare function watchesFile(watchers: readonly FileSystemWatcher[], root: string,
|
|
3
|
+
export declare function watchesFile(watchers: readonly FileSystemWatcher[], root: string, changed: string, type: FileChangeType): boolean;
|
package/dist/watched-files.js
CHANGED
|
@@ -3,6 +3,14 @@ import { fileUriToPath } from './uris.js';
|
|
|
3
3
|
const EVERY_CHANGE = 7;
|
|
4
4
|
/* Folders whose files a server hears about only when a pattern of its own names the folder. */
|
|
5
5
|
const SPARING_FOLDERS = ['node_modules', 'vendor', '.git'];
|
|
6
|
+
/* A Windows path with forward slashes and its drive in capitals, so it compares with a pattern and a root however it was written. A POSIX path is left alone, a backslash being part of a name there. */
|
|
7
|
+
function slashed(path) {
|
|
8
|
+
if (!/^[A-Za-z]:[\\/]/.test(path) && !path.startsWith('\\\\')) {
|
|
9
|
+
return path;
|
|
10
|
+
}
|
|
11
|
+
const forward = path.replaceAll('\\', '/');
|
|
12
|
+
return /^[a-z]:/.test(forward) ? `${forward[0].toUpperCase()}${forward.slice(1)}` : forward;
|
|
13
|
+
}
|
|
6
14
|
function relativeTo(root, path) {
|
|
7
15
|
const prefix = root.endsWith('/') ? root : `${root}/`;
|
|
8
16
|
return path.startsWith(prefix) ? path.slice(prefix.length) : null;
|
|
@@ -14,15 +22,16 @@ function patternOf(watcher) {
|
|
|
14
22
|
return { base: undefined, pattern: globPattern };
|
|
15
23
|
}
|
|
16
24
|
const base = fileUriToPath(typeof globPattern.baseUri === 'string' ? globPattern.baseUri : globPattern.baseUri.uri);
|
|
17
|
-
return base === null ? null : { base, pattern: globPattern.pattern };
|
|
25
|
+
return base === null ? null : { base: slashed(base), pattern: globPattern.pattern };
|
|
18
26
|
}
|
|
19
27
|
/*
|
|
20
28
|
* Whether a server asked to hear about this change. A string pattern is relative to the project folder
|
|
21
29
|
* (or absolute), a relative pattern to its own base. A path inside `node_modules`, `vendor` or `.git` only
|
|
22
30
|
* matches a pattern that names that folder, since `**` over a dependency tree is what a server never means.
|
|
23
31
|
*/
|
|
24
|
-
export function watchesFile(watchers, root,
|
|
25
|
-
const
|
|
32
|
+
export function watchesFile(watchers, root, changed, type) {
|
|
33
|
+
const path = slashed(changed);
|
|
34
|
+
const inProject = relativeTo(slashed(root), path);
|
|
26
35
|
const segments = (inProject ?? path).split('/');
|
|
27
36
|
return watchers.some((watcher) => {
|
|
28
37
|
if (((watcher.kind ?? EVERY_CHANGE) & (1 << (type - 1))) === 0) {
|
|
@@ -33,7 +42,7 @@ export function watchesFile(watchers, root, path, type) {
|
|
|
33
42
|
return false;
|
|
34
43
|
}
|
|
35
44
|
const { base, pattern } = parts;
|
|
36
|
-
const subject = pattern.startsWith('/') ? path : base === undefined ? inProject : relativeTo(base, path);
|
|
45
|
+
const subject = pattern.startsWith('/') || /^[A-Za-z]:\//.test(pattern) ? path : base === undefined ? inProject : relativeTo(base, path);
|
|
37
46
|
if (subject === null || !globMatch(subject, pattern)) {
|
|
38
47
|
return false;
|
|
39
48
|
}
|
package/package.json
CHANGED
package/src/edits.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ErrorCodes, LspError } from './connection.ts';
|
|
2
2
|
import type { ContentChange, CreateFile, Position, TextEdit, WorkspaceEdit } from './protocol.ts';
|
|
3
|
+
import { fileUriKey } from './uris.ts';
|
|
3
4
|
|
|
4
5
|
export interface DocumentSnapshot {
|
|
5
6
|
text: string;
|
|
@@ -195,21 +196,25 @@ export function minimalChange(before: string, after: string): ContentChange {
|
|
|
195
196
|
};
|
|
196
197
|
}
|
|
197
198
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
199
|
+
// The snapshots by `fileUriKey`, each with the URI it was given under, which the plan keeps.
|
|
200
|
+
type KeyedSnapshots = ReadonlyMap<string, { uri: string; snapshot: DocumentSnapshot }>;
|
|
201
|
+
|
|
202
|
+
function snapshotEdit(uri: string, snapshots: KeyedSnapshots): PlannedDocumentEdit {
|
|
203
|
+
const found = snapshots.get(fileUriKey(uri));
|
|
204
|
+
if (!found) {
|
|
201
205
|
throw new LspError(`Missing workspace snapshot: ${uri}`);
|
|
202
206
|
}
|
|
203
|
-
return { uri, version: snapshot.version, before: snapshot.text, text: snapshot.text };
|
|
207
|
+
return { uri: found.uri, version: found.snapshot.version, before: found.snapshot.text, text: found.snapshot.text };
|
|
204
208
|
}
|
|
205
209
|
|
|
206
210
|
/* A file that is there already is refused, unless the create says to empty it or to leave it as it is. */
|
|
207
|
-
function planCreate(change: CreateFile, snapshots:
|
|
208
|
-
const
|
|
211
|
+
function planCreate(change: CreateFile, snapshots: KeyedSnapshots, planned: Map<string, PlannedDocumentEdit>): void {
|
|
212
|
+
const key = fileUriKey(change.uri);
|
|
213
|
+
const existing = planned.get(key) ?? (snapshots.has(key) ? snapshotEdit(change.uri, snapshots) : null);
|
|
209
214
|
if (existing === null) {
|
|
210
|
-
planned.set(
|
|
215
|
+
planned.set(key, { uri: change.uri, version: null, before: '', text: '', created: true });
|
|
211
216
|
} else if (change.options?.overwrite) {
|
|
212
|
-
planned.set(
|
|
217
|
+
planned.set(key, { ...existing, text: '' });
|
|
213
218
|
} else if (!change.options?.ignoreIfExists) {
|
|
214
219
|
throw new LspError(`${change.uri} already exists`);
|
|
215
220
|
}
|
|
@@ -220,6 +225,7 @@ function planCreate(change: CreateFile, snapshots: ReadonlyMap<string, DocumentS
|
|
|
220
225
|
* files that are there; a file the edit creates is absent from it. A rename or delete needs the host's own handling and is refused.
|
|
221
226
|
*/
|
|
222
227
|
export function planWorkspaceEdit(edit: WorkspaceEdit, snapshots: ReadonlyMap<string, DocumentSnapshot>): PlannedDocumentEdit[] {
|
|
228
|
+
const keyed: KeyedSnapshots = new Map([...snapshots].map(([uri, snapshot]) => [fileUriKey(uri), { uri, snapshot }]));
|
|
223
229
|
const planned = new Map<string, PlannedDocumentEdit>();
|
|
224
230
|
const changes = edit.documentChanges ?? Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({ textDocument: { uri, version: null }, edits }));
|
|
225
231
|
for (const change of changes) {
|
|
@@ -227,15 +233,16 @@ export function planWorkspaceEdit(edit: WorkspaceEdit, snapshots: ReadonlyMap<st
|
|
|
227
233
|
if (change.kind !== 'create') {
|
|
228
234
|
throw new LspError(`A host file-operation handler is required for ${change.kind}`);
|
|
229
235
|
}
|
|
230
|
-
planCreate(change,
|
|
236
|
+
planCreate(change, keyed, planned);
|
|
231
237
|
continue;
|
|
232
238
|
}
|
|
233
239
|
const { uri, version } = change.textDocument;
|
|
234
|
-
const
|
|
240
|
+
const key = fileUriKey(uri);
|
|
241
|
+
const current = planned.get(key) ?? snapshotEdit(uri, keyed);
|
|
235
242
|
if (version !== null && current.version !== version) {
|
|
236
243
|
throw new LspError(`Workspace edit version mismatch: ${uri}`, ErrorCodes.ContentModified);
|
|
237
244
|
}
|
|
238
|
-
planned.set(
|
|
245
|
+
planned.set(key, { ...current, text: applyTextEdits(current.text, change.edits) });
|
|
239
246
|
}
|
|
240
247
|
return [...planned.values()];
|
|
241
248
|
}
|
package/src/session.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ErrorCodes, JsonRpcConnection, LspError, type RequestHandler } from './
|
|
|
2
2
|
import { LspDocument } from './document.ts';
|
|
3
3
|
import { createsTaken, deletesTaken, renamesTaken, type CreatedFile, type DeletedFile, type RenamedFile } from './file-operations.ts';
|
|
4
4
|
import { globMatch } from './glob.ts';
|
|
5
|
+
import { fileUriKey } from './uris.ts';
|
|
5
6
|
import type { ResourceOperationKind } from './workspace-operations.ts';
|
|
6
7
|
import type {
|
|
7
8
|
ApplyWorkspaceEditParams,
|
|
@@ -173,11 +174,11 @@ export class LspSession {
|
|
|
173
174
|
});
|
|
174
175
|
this.connection.onNotification('textDocument/publishDiagnostics', (params) => {
|
|
175
176
|
const diagnostics = params as PublishDiagnosticsParams;
|
|
176
|
-
const document = this.documents.get(diagnostics.uri);
|
|
177
|
+
const document = this.documents.get(fileUriKey(diagnostics.uri));
|
|
177
178
|
if (!document || document.isClosed || (diagnostics.version !== undefined && diagnostics.version !== document.version)) {
|
|
178
179
|
return;
|
|
179
180
|
}
|
|
180
|
-
this.emitDiagnostics(diagnostics);
|
|
181
|
+
this.emitDiagnostics({ ...diagnostics, uri: document.uri });
|
|
181
182
|
});
|
|
182
183
|
this.connection.onRequest('workspace/configuration', async (params) => {
|
|
183
184
|
const { items } = params as { items: ConfigurationItem[] };
|
|
@@ -275,16 +276,16 @@ export class LspSession {
|
|
|
275
276
|
|
|
276
277
|
openDocument(item: DocumentItem): LspDocument {
|
|
277
278
|
this.assertReady();
|
|
278
|
-
if (this.documents.has(item.uri)) {
|
|
279
|
+
if (this.documents.has(fileUriKey(item.uri))) {
|
|
279
280
|
throw new LspError(`Document already open: ${item.uri}`);
|
|
280
281
|
}
|
|
281
282
|
const document = new LspDocument(this, item);
|
|
282
|
-
this.documents.set(item.uri, document);
|
|
283
|
+
this.documents.set(fileUriKey(item.uri), document);
|
|
283
284
|
return document;
|
|
284
285
|
}
|
|
285
286
|
|
|
286
287
|
getDocument(uri: string): LspDocument | undefined {
|
|
287
|
-
return this.documents.get(uri);
|
|
288
|
+
return this.documents.get(fileUriKey(uri));
|
|
288
289
|
}
|
|
289
290
|
|
|
290
291
|
/* The options the server gave a method, from its capabilities or a registration whose selector takes `document`. */
|
|
@@ -453,10 +454,10 @@ export class LspSession {
|
|
|
453
454
|
|
|
454
455
|
/* Called by a document that closed, so its diagnostics go with it. */
|
|
455
456
|
forgetDocument(document: LspDocument): void {
|
|
456
|
-
if (this.documents.get(document.uri) !== document) {
|
|
457
|
+
if (this.documents.get(fileUriKey(document.uri)) !== document) {
|
|
457
458
|
return;
|
|
458
459
|
}
|
|
459
|
-
this.documents.delete(document.uri);
|
|
460
|
+
this.documents.delete(fileUriKey(document.uri));
|
|
460
461
|
this.emitDiagnostics({ uri: document.uri, diagnostics: [] });
|
|
461
462
|
}
|
|
462
463
|
|
package/src/uris.ts
CHANGED
|
@@ -38,3 +38,15 @@ export function fileUriToPath(uri: string): string | null {
|
|
|
38
38
|
}
|
|
39
39
|
return path;
|
|
40
40
|
}
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* One key for every way a URI can name the same Windows file: servers write the drive in either
|
|
44
|
+
* case, and some escape its colon (`file:///c%3A/...`). Any other URI is its own key.
|
|
45
|
+
*/
|
|
46
|
+
export function fileUriKey(uri: string): string {
|
|
47
|
+
const path = fileUriToPath(uri);
|
|
48
|
+
if (path === null || !/^[A-Za-z]:\\/.test(path)) {
|
|
49
|
+
return uri;
|
|
50
|
+
}
|
|
51
|
+
return pathToFileUri(`${path[0]!.toUpperCase()}${path.slice(1)}`);
|
|
52
|
+
}
|
package/src/watched-files.ts
CHANGED
|
@@ -10,6 +10,15 @@ const EVERY_CHANGE = 7;
|
|
|
10
10
|
/* Folders whose files a server hears about only when a pattern of its own names the folder. */
|
|
11
11
|
const SPARING_FOLDERS = ['node_modules', 'vendor', '.git'];
|
|
12
12
|
|
|
13
|
+
/* A Windows path with forward slashes and its drive in capitals, so it compares with a pattern and a root however it was written. A POSIX path is left alone, a backslash being part of a name there. */
|
|
14
|
+
function slashed(path: string): string {
|
|
15
|
+
if (!/^[A-Za-z]:[\\/]/.test(path) && !path.startsWith('\\\\')) {
|
|
16
|
+
return path;
|
|
17
|
+
}
|
|
18
|
+
const forward = path.replaceAll('\\', '/');
|
|
19
|
+
return /^[a-z]:/.test(forward) ? `${forward[0]!.toUpperCase()}${forward.slice(1)}` : forward;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
function relativeTo(root: string, path: string): string | null {
|
|
14
23
|
const prefix = root.endsWith('/') ? root : `${root}/`;
|
|
15
24
|
return path.startsWith(prefix) ? path.slice(prefix.length) : null;
|
|
@@ -22,7 +31,7 @@ function patternOf(watcher: FileSystemWatcher): { base: string | undefined; patt
|
|
|
22
31
|
return { base: undefined, pattern: globPattern };
|
|
23
32
|
}
|
|
24
33
|
const base = fileUriToPath(typeof globPattern.baseUri === 'string' ? globPattern.baseUri : globPattern.baseUri.uri);
|
|
25
|
-
return base === null ? null : { base, pattern: globPattern.pattern };
|
|
34
|
+
return base === null ? null : { base: slashed(base), pattern: globPattern.pattern };
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
/*
|
|
@@ -30,8 +39,9 @@ function patternOf(watcher: FileSystemWatcher): { base: string | undefined; patt
|
|
|
30
39
|
* (or absolute), a relative pattern to its own base. A path inside `node_modules`, `vendor` or `.git` only
|
|
31
40
|
* matches a pattern that names that folder, since `**` over a dependency tree is what a server never means.
|
|
32
41
|
*/
|
|
33
|
-
export function watchesFile(watchers: readonly FileSystemWatcher[], root: string,
|
|
34
|
-
const
|
|
42
|
+
export function watchesFile(watchers: readonly FileSystemWatcher[], root: string, changed: string, type: FileChangeType): boolean {
|
|
43
|
+
const path = slashed(changed);
|
|
44
|
+
const inProject = relativeTo(slashed(root), path);
|
|
35
45
|
const segments = (inProject ?? path).split('/');
|
|
36
46
|
return watchers.some((watcher) => {
|
|
37
47
|
if (((watcher.kind ?? EVERY_CHANGE) & (1 << (type - 1))) === 0) {
|
|
@@ -42,7 +52,7 @@ export function watchesFile(watchers: readonly FileSystemWatcher[], root: string
|
|
|
42
52
|
return false;
|
|
43
53
|
}
|
|
44
54
|
const { base, pattern } = parts;
|
|
45
|
-
const subject = pattern.startsWith('/') ? path : base === undefined ? inProject : relativeTo(base, path);
|
|
55
|
+
const subject = pattern.startsWith('/') || /^[A-Za-z]:\//.test(pattern) ? path : base === undefined ? inProject : relativeTo(base, path);
|
|
46
56
|
if (subject === null || !globMatch(subject, pattern)) {
|
|
47
57
|
return false;
|
|
48
58
|
}
|