@adecore/lsp 0.39.0 → 0.40.1
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/completion-defaults.js +2 -4
- package/dist/edits.d.ts +4 -0
- package/dist/edits.js +8 -5
- package/dist/file-operations.js +6 -3
- package/dist/session.d.ts +2 -0
- package/dist/session.js +14 -18
- package/dist/test-transport.js +1 -1
- package/dist/text-mirror.js +2 -5
- package/dist/transport.d.ts +1 -0
- package/dist/transport.js +7 -4
- package/dist/uris.d.ts +1 -0
- package/dist/uris.js +5 -2
- package/dist/watched-files.js +2 -2
- package/dist/workspace-operations.js +2 -3
- package/package.json +2 -2
- package/src/completion-defaults.ts +24 -4
- package/src/connection.ts +45 -0
- package/src/document.ts +39 -0
- package/src/edits.ts +58 -5
- package/src/file-operations.ts +13 -3
- package/src/glob.ts +6 -0
- package/src/session.ts +84 -18
- package/src/test-transport.ts +6 -1
- package/src/testing.ts +11 -0
- package/src/text-mirror.ts +27 -6
- package/src/transport.ts +58 -4
- package/src/uris.ts +17 -2
- package/src/vue.ts +15 -0
- package/src/watched-files.ts +14 -2
- package/src/workspace-operations.ts +51 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ErrorCodes, LspError } from './connection.ts';
|
|
2
|
-
import { applyTextEdits } from './edits.ts';
|
|
2
|
+
import { applyTextEdits, documentChangesOf } from './edits.ts';
|
|
3
3
|
import type { TextEdit, WorkspaceEdit } from './protocol.ts';
|
|
4
4
|
|
|
5
5
|
type DocumentChange = NonNullable<WorkspaceEdit['documentChanges']>[number];
|
|
@@ -106,6 +106,7 @@ interface MutableCreate {
|
|
|
106
106
|
/* The key a URI is known by: a folder spelled with a trailing slash is the same folder, except the root. */
|
|
107
107
|
function keyOf(uri: string): string {
|
|
108
108
|
const root = /^[^/:]+:\/\/[^/]*\/+$/.test(uri);
|
|
109
|
+
|
|
109
110
|
return root ? uri : uri.replace(/\/+$/, '');
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -116,10 +117,12 @@ function isUnder(uri: string, folder: string): boolean {
|
|
|
116
117
|
function ancestorsOf(uri: string): string[] {
|
|
117
118
|
const found: string[] = [];
|
|
118
119
|
let end = uri.lastIndexOf('/');
|
|
120
|
+
|
|
119
121
|
while (end > 0) {
|
|
120
122
|
found.push(uri.slice(0, end));
|
|
121
123
|
end = uri.lastIndexOf('/', end - 1);
|
|
122
124
|
}
|
|
125
|
+
|
|
123
126
|
return found;
|
|
124
127
|
}
|
|
125
128
|
|
|
@@ -153,33 +156,42 @@ export async function planWorkspaceOperations(edit: WorkspaceEdit, source: Works
|
|
|
153
156
|
|
|
154
157
|
const probe = (uri: string): Promise<Probe> => {
|
|
155
158
|
let found = probes.get(uri);
|
|
159
|
+
|
|
156
160
|
if (found === undefined) {
|
|
157
161
|
found = (async () => {
|
|
158
162
|
const text = await source.read(uri);
|
|
163
|
+
|
|
159
164
|
return { text, exists: text !== null ? true : source.exists === undefined ? null : await source.exists(uri) };
|
|
160
165
|
})();
|
|
166
|
+
|
|
161
167
|
probes.set(uri, found);
|
|
162
168
|
}
|
|
169
|
+
|
|
163
170
|
return found;
|
|
164
171
|
};
|
|
165
172
|
|
|
166
173
|
const folderAt = (uri: string): Promise<boolean> => {
|
|
167
174
|
let found = folders.get(uri);
|
|
175
|
+
|
|
168
176
|
if (found === undefined) {
|
|
169
177
|
found = source.isFolder === undefined ? Promise.resolve(true) : source.isFolder(uri);
|
|
170
178
|
folders.set(uri, found);
|
|
171
179
|
}
|
|
180
|
+
|
|
172
181
|
return found;
|
|
173
182
|
};
|
|
174
183
|
|
|
175
184
|
const originOf = (uri: string): string => {
|
|
176
185
|
let origin = uri;
|
|
186
|
+
|
|
177
187
|
for (let i = moves.length - 1; i >= 0; i--) {
|
|
178
188
|
const move = moves[i]!;
|
|
189
|
+
|
|
179
190
|
if (origin === move.newUri || isUnder(origin, move.newUri)) {
|
|
180
191
|
origin = move.oldUri + origin.slice(move.newUri.length);
|
|
181
192
|
}
|
|
182
193
|
}
|
|
194
|
+
|
|
183
195
|
return origin;
|
|
184
196
|
};
|
|
185
197
|
|
|
@@ -187,32 +199,40 @@ export async function planWorkspaceOperations(edit: WorkspaceEdit, source: Works
|
|
|
187
199
|
if (overlay.has(uri)) {
|
|
188
200
|
return overlay.get(uri)!;
|
|
189
201
|
}
|
|
202
|
+
|
|
190
203
|
for (const ancestor of ancestorsOf(uri)) {
|
|
191
204
|
if (overlay.has(ancestor)) {
|
|
192
205
|
const node = overlay.get(ancestor)!;
|
|
206
|
+
|
|
193
207
|
if (node === null || node.kind === 'text') {
|
|
194
208
|
return null;
|
|
195
209
|
}
|
|
210
|
+
|
|
196
211
|
break;
|
|
197
212
|
}
|
|
198
213
|
}
|
|
214
|
+
|
|
199
215
|
const origin = originOf(uri);
|
|
200
216
|
const found = await probe(origin);
|
|
201
217
|
let node: Node | null = null;
|
|
218
|
+
|
|
202
219
|
if (found.text !== null) {
|
|
203
220
|
node = { kind: 'text', text: found.text.text, version: found.text.version, open: found.text.open, readUri: origin, create: null };
|
|
204
221
|
} else if (found.exists !== false) {
|
|
205
222
|
node = { kind: found.exists === null ? 'maybe' : 'other', text: '', version: null, open: false, readUri: origin, create: null };
|
|
206
223
|
}
|
|
224
|
+
|
|
207
225
|
if (node !== null) {
|
|
208
226
|
overlay.set(uri, node);
|
|
209
227
|
}
|
|
228
|
+
|
|
210
229
|
return node;
|
|
211
230
|
};
|
|
212
231
|
|
|
213
232
|
const demand = (kind: ResourceOperationKind, uri: string): void => {
|
|
214
233
|
if (!source.operations.includes(kind)) {
|
|
215
234
|
const verb = kind === 'create' ? 'Creating' : kind === 'rename' ? 'Moving' : 'Deleting';
|
|
235
|
+
|
|
216
236
|
throw new LspError(`${verb} ${uri} is not supported here`);
|
|
217
237
|
}
|
|
218
238
|
};
|
|
@@ -229,30 +249,37 @@ export async function planWorkspaceOperations(edit: WorkspaceEdit, source: Works
|
|
|
229
249
|
if ('textDocument' in change) {
|
|
230
250
|
return { ...change, textDocument: { ...change.textDocument, uri: keyOf(change.textDocument.uri) } };
|
|
231
251
|
}
|
|
252
|
+
|
|
232
253
|
if (change.kind === 'rename') {
|
|
233
254
|
return { ...change, oldUri: keyOf(change.oldUri), newUri: keyOf(change.newUri) };
|
|
234
255
|
}
|
|
256
|
+
|
|
235
257
|
return { ...change, uri: keyOf(change.uri) };
|
|
236
258
|
};
|
|
237
259
|
|
|
238
|
-
const
|
|
239
|
-
for (const original of changes) {
|
|
260
|
+
for (const original of documentChangesOf(edit)) {
|
|
240
261
|
const change = keyed(original);
|
|
262
|
+
|
|
241
263
|
if ('textDocument' in change) {
|
|
242
264
|
const { uri, version } = change.textDocument;
|
|
243
265
|
const node = await lookup(uri);
|
|
266
|
+
|
|
244
267
|
if (node === null || node.kind !== 'text') {
|
|
245
268
|
throw new LspError(`${uri} cannot be read as text`);
|
|
246
269
|
}
|
|
270
|
+
|
|
247
271
|
if (version !== null && node.create === null) {
|
|
248
272
|
if (!node.open) {
|
|
249
273
|
throw new LspError(`The version of ${uri} cannot be checked because no editor holds it`, ErrorCodes.ContentModified);
|
|
250
274
|
}
|
|
275
|
+
|
|
251
276
|
if (node.version !== null && node.version !== version) {
|
|
252
277
|
throw new LspError(`Workspace edit version mismatch: ${uri}`, ErrorCodes.ContentModified);
|
|
253
278
|
}
|
|
254
279
|
}
|
|
280
|
+
|
|
255
281
|
const after = applyTextEdits(node.text, change.edits);
|
|
282
|
+
|
|
256
283
|
if (node.create !== null) {
|
|
257
284
|
node.create.text = after;
|
|
258
285
|
} else {
|
|
@@ -267,21 +294,26 @@ export async function planWorkspaceOperations(edit: WorkspaceEdit, source: Works
|
|
|
267
294
|
open: node.open
|
|
268
295
|
});
|
|
269
296
|
}
|
|
297
|
+
|
|
270
298
|
node.text = after;
|
|
271
299
|
} else if (change.kind === 'create') {
|
|
272
300
|
demand('create', change.uri);
|
|
273
301
|
const found = await lookup(change.uri);
|
|
274
302
|
const there = found?.kind === 'maybe' ? null : found;
|
|
275
303
|
const overwrite = change.options?.overwrite === true;
|
|
304
|
+
|
|
276
305
|
if (there !== null && !overwrite) {
|
|
277
306
|
if (change.options?.ignoreIfExists === true) {
|
|
278
307
|
continue;
|
|
279
308
|
}
|
|
309
|
+
|
|
280
310
|
throw new LspError(`${change.uri} already exists`);
|
|
281
311
|
}
|
|
312
|
+
|
|
282
313
|
if (there !== null && there.kind === 'other') {
|
|
283
314
|
throw new LspError(`${change.uri} already exists and is not a text file`);
|
|
284
315
|
}
|
|
316
|
+
|
|
285
317
|
const made: MutableCreate = { kind: 'create', uri: change.uri, text: '', overwrite: there !== null };
|
|
286
318
|
steps.push(made);
|
|
287
319
|
overlay.set(change.uri, { kind: 'text', text: '', version: null, open: false, readUri: null, create: made });
|
|
@@ -289,53 +321,69 @@ export async function planWorkspaceOperations(edit: WorkspaceEdit, source: Works
|
|
|
289
321
|
demand('rename', change.oldUri);
|
|
290
322
|
const { oldUri, newUri } = change;
|
|
291
323
|
const from = await lookup(oldUri);
|
|
324
|
+
|
|
292
325
|
if (from === null) {
|
|
293
326
|
throw new LspError(`${oldUri} does not exist`);
|
|
294
327
|
}
|
|
328
|
+
|
|
295
329
|
if (oldUri === newUri) {
|
|
296
330
|
continue;
|
|
297
331
|
}
|
|
332
|
+
|
|
298
333
|
const target = await lookup(newUri);
|
|
299
334
|
const taken = target?.kind === 'maybe' ? null : target;
|
|
300
335
|
const overwrite = change.options?.overwrite === true;
|
|
336
|
+
|
|
301
337
|
if (taken !== null && !overwrite) {
|
|
302
338
|
if (change.options?.ignoreIfExists === true) {
|
|
303
339
|
continue;
|
|
304
340
|
}
|
|
341
|
+
|
|
305
342
|
throw new LspError(`${newUri} already exists`);
|
|
306
343
|
}
|
|
344
|
+
|
|
307
345
|
const carried: Array<[string, Node | null]> = [];
|
|
346
|
+
|
|
308
347
|
for (const [key, node] of overlay) {
|
|
309
348
|
if (key !== oldUri && isUnder(key, oldUri)) {
|
|
310
349
|
carried.push([newUri + key.slice(oldUri.length), node]);
|
|
311
350
|
}
|
|
312
351
|
}
|
|
352
|
+
|
|
313
353
|
forget(newUri);
|
|
314
354
|
forget(oldUri);
|
|
315
355
|
overlay.set(oldUri, null);
|
|
316
356
|
overlay.set(newUri, from);
|
|
357
|
+
|
|
317
358
|
for (const [key, node] of carried) {
|
|
318
359
|
overlay.set(key, node);
|
|
319
360
|
}
|
|
361
|
+
|
|
320
362
|
moves.push({ oldUri, newUri });
|
|
321
363
|
steps.push({ kind: 'rename', oldUri, newUri, overwrite: taken !== null });
|
|
322
364
|
} else {
|
|
323
365
|
demand('delete', change.uri);
|
|
324
366
|
const found = await lookup(change.uri);
|
|
367
|
+
|
|
325
368
|
if (found === null) {
|
|
326
369
|
if (change.options?.ignoreIfNotExists === true) {
|
|
327
370
|
continue;
|
|
328
371
|
}
|
|
372
|
+
|
|
329
373
|
throw new LspError(`${change.uri} does not exist`);
|
|
330
374
|
}
|
|
375
|
+
|
|
331
376
|
const recursive = change.options?.recursive === true;
|
|
377
|
+
|
|
332
378
|
if (found.kind !== 'text' && !recursive && (await folderAt(found.readUri ?? change.uri))) {
|
|
333
379
|
throw new LspError(`${change.uri} is a folder, or cannot be told from one, and is only deleted with recursive set`);
|
|
334
380
|
}
|
|
381
|
+
|
|
335
382
|
forget(change.uri);
|
|
336
383
|
overlay.set(change.uri, null);
|
|
337
384
|
steps.push({ kind: 'delete', uri: change.uri, recursive });
|
|
338
385
|
}
|
|
339
386
|
}
|
|
387
|
+
|
|
340
388
|
return { steps: steps.map((step) => (step.kind === 'create' ? { ...step } : step)) };
|
|
341
389
|
}
|