@adecore/lsp 0.17.0-beta.2 → 0.17.0

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.d.ts CHANGED
@@ -8,6 +8,7 @@ export interface PlannedDocumentEdit {
8
8
  version: number | null;
9
9
  before: string;
10
10
  text: string;
11
+ created?: true;
11
12
  }
12
13
  export declare function offsetAt(text: string, position: Position): number;
13
14
  export declare function positionAt(text: string, offset: number): Position;
package/dist/edits.js CHANGED
@@ -137,25 +137,47 @@ export function minimalChange(before, after) {
137
137
  text: after.slice(start, after.length - end)
138
138
  };
139
139
  }
140
- /* Plans text edits in memory. A create, rename or delete needs the host's own handling and is refused. */
140
+ function snapshotEdit(uri, snapshots) {
141
+ const snapshot = snapshots.get(uri);
142
+ if (!snapshot) {
143
+ throw new LspError(`Missing workspace snapshot: ${uri}`);
144
+ }
145
+ return { uri, version: snapshot.version, before: snapshot.text, text: snapshot.text };
146
+ }
147
+ /* A file that is there already is refused, unless the create says to empty it or to leave it as it is. */
148
+ function planCreate(change, snapshots, planned) {
149
+ const existing = planned.get(change.uri) ?? (snapshots.has(change.uri) ? snapshotEdit(change.uri, snapshots) : null);
150
+ if (existing === null) {
151
+ planned.set(change.uri, { uri: change.uri, version: null, before: '', text: '', created: true });
152
+ }
153
+ else if (change.options?.overwrite) {
154
+ planned.set(change.uri, { ...existing, text: '' });
155
+ }
156
+ else if (!change.options?.ignoreIfExists) {
157
+ throw new LspError(`${change.uri} already exists`);
158
+ }
159
+ }
160
+ /*
161
+ * Plans text edits and created files in memory, in the order the edit gives them. `snapshots` holds the
162
+ * 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.
163
+ */
141
164
  export function planWorkspaceEdit(edit, snapshots) {
142
165
  const planned = new Map();
143
166
  const changes = edit.documentChanges ?? Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({ textDocument: { uri, version: null }, edits }));
144
167
  for (const change of changes) {
145
168
  if ('kind' in change) {
146
- throw new LspError(`A host file-operation handler is required for ${change.kind}`);
169
+ if (change.kind !== 'create') {
170
+ throw new LspError(`A host file-operation handler is required for ${change.kind}`);
171
+ }
172
+ planCreate(change, snapshots, planned);
173
+ continue;
147
174
  }
148
175
  const { uri, version } = change.textDocument;
149
- const snapshot = snapshots.get(uri);
150
- if (!snapshot) {
151
- throw new LspError(`Missing workspace snapshot: ${uri}`);
152
- }
153
- if (version !== null && snapshot.version !== version) {
176
+ const current = planned.get(uri) ?? snapshotEdit(uri, snapshots);
177
+ if (version !== null && current.version !== version) {
154
178
  throw new LspError(`Workspace edit version mismatch: ${uri}`, ErrorCodes.ContentModified);
155
179
  }
156
- const before = planned.get(uri)?.text ?? snapshot.text;
157
- const text = applyTextEdits(before, change.edits);
158
- planned.set(uri, { uri, version: snapshot.version, before: snapshot.text, text });
180
+ planned.set(uri, { ...current, text: applyTextEdits(current.text, change.edits) });
159
181
  }
160
182
  return [...planned.values()];
161
183
  }
package/dist/session.js CHANGED
@@ -395,7 +395,7 @@ function clientCapabilities(options) {
395
395
  general: { positionEncodings: ['utf-16'] },
396
396
  workspace: {
397
397
  applyEdit: !!options.onApplyEdit,
398
- workspaceEdit: { documentChanges: true, resourceOperations: ['rename'], failureHandling: 'abort' },
398
+ workspaceEdit: { documentChanges: true, resourceOperations: ['create', 'rename'], failureHandling: 'abort' },
399
399
  fileOperations: { dynamicRegistration: true, willRename: true, didRename: true },
400
400
  configuration: true,
401
401
  workspaceFolders: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adecore/lsp",
3
- "version": "0.17.0-beta.2",
3
+ "version": "0.17.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/edits.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ErrorCodes, LspError } from './connection.ts';
2
- import type { ContentChange, Position, TextEdit, WorkspaceEdit } from './protocol.ts';
2
+ import type { ContentChange, CreateFile, Position, TextEdit, WorkspaceEdit } from './protocol.ts';
3
3
 
4
4
  export interface DocumentSnapshot {
5
5
  text: string;
@@ -11,6 +11,8 @@ export interface PlannedDocumentEdit {
11
11
  version: number | null;
12
12
  before: string;
13
13
  text: string;
14
+ /* A file the edit creates, which was not there: the host makes it with `text` and fails when it is there by then. */
15
+ created?: true;
14
16
  }
15
17
 
16
18
  /* A line ends at `\n`, `\r\n` or a lone `\r`, which is how the protocol counts lines. */
@@ -158,25 +160,47 @@ export function minimalChange(before: string, after: string): ContentChange {
158
160
  };
159
161
  }
160
162
 
161
- /* Plans text edits in memory. A create, rename or delete needs the host's own handling and is refused. */
163
+ function snapshotEdit(uri: string, snapshots: ReadonlyMap<string, DocumentSnapshot>): PlannedDocumentEdit {
164
+ const snapshot = snapshots.get(uri);
165
+ if (!snapshot) {
166
+ throw new LspError(`Missing workspace snapshot: ${uri}`);
167
+ }
168
+ return { uri, version: snapshot.version, before: snapshot.text, text: snapshot.text };
169
+ }
170
+
171
+ /* A file that is there already is refused, unless the create says to empty it or to leave it as it is. */
172
+ function planCreate(change: CreateFile, snapshots: ReadonlyMap<string, DocumentSnapshot>, planned: Map<string, PlannedDocumentEdit>): void {
173
+ const existing = planned.get(change.uri) ?? (snapshots.has(change.uri) ? snapshotEdit(change.uri, snapshots) : null);
174
+ if (existing === null) {
175
+ planned.set(change.uri, { uri: change.uri, version: null, before: '', text: '', created: true });
176
+ } else if (change.options?.overwrite) {
177
+ planned.set(change.uri, { ...existing, text: '' });
178
+ } else if (!change.options?.ignoreIfExists) {
179
+ throw new LspError(`${change.uri} already exists`);
180
+ }
181
+ }
182
+
183
+ /*
184
+ * Plans text edits and created files in memory, in the order the edit gives them. `snapshots` holds the
185
+ * 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.
186
+ */
162
187
  export function planWorkspaceEdit(edit: WorkspaceEdit, snapshots: ReadonlyMap<string, DocumentSnapshot>): PlannedDocumentEdit[] {
163
188
  const planned = new Map<string, PlannedDocumentEdit>();
164
189
  const changes = edit.documentChanges ?? Object.entries(edit.changes ?? {}).map(([uri, edits]) => ({ textDocument: { uri, version: null }, edits }));
165
190
  for (const change of changes) {
166
191
  if ('kind' in change) {
167
- throw new LspError(`A host file-operation handler is required for ${change.kind}`);
192
+ if (change.kind !== 'create') {
193
+ throw new LspError(`A host file-operation handler is required for ${change.kind}`);
194
+ }
195
+ planCreate(change, snapshots, planned);
196
+ continue;
168
197
  }
169
198
  const { uri, version } = change.textDocument;
170
- const snapshot = snapshots.get(uri);
171
- if (!snapshot) {
172
- throw new LspError(`Missing workspace snapshot: ${uri}`);
173
- }
174
- if (version !== null && snapshot.version !== version) {
199
+ const current = planned.get(uri) ?? snapshotEdit(uri, snapshots);
200
+ if (version !== null && current.version !== version) {
175
201
  throw new LspError(`Workspace edit version mismatch: ${uri}`, ErrorCodes.ContentModified);
176
202
  }
177
- const before = planned.get(uri)?.text ?? snapshot.text;
178
- const text = applyTextEdits(before, change.edits);
179
- planned.set(uri, { uri, version: snapshot.version, before: snapshot.text, text });
203
+ planned.set(uri, { ...current, text: applyTextEdits(current.text, change.edits) });
180
204
  }
181
205
  return [...planned.values()];
182
206
  }
package/src/session.ts CHANGED
@@ -480,7 +480,7 @@ function clientCapabilities(options: LspSessionOptions): object {
480
480
  general: { positionEncodings: ['utf-16'] },
481
481
  workspace: {
482
482
  applyEdit: !!options.onApplyEdit,
483
- workspaceEdit: { documentChanges: true, resourceOperations: ['rename'], failureHandling: 'abort' },
483
+ workspaceEdit: { documentChanges: true, resourceOperations: ['create', 'rename'], failureHandling: 'abort' },
484
484
  fileOperations: { dynamicRegistration: true, willRename: true, didRename: true },
485
485
  configuration: true,
486
486
  workspaceFolders: true,