@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.
@@ -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 changes = edit.documentChanges ?? Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({ textDocument: { uri, version: null }, edits }));
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
  }