@adecore/editor-react 0.42.0 → 0.43.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/CallHierarchyPanel.js +177 -181
- package/dist/EditorContextMenu.js +118 -105
- package/dist/LanguagePopups.js +7 -0
- package/dist/PickPopup.js +190 -70
- package/dist/WrapCard.d.ts +8 -0
- package/dist/WrapCard.js +172 -0
- package/dist/call-hierarchy-model.d.ts +4 -2
- package/dist/call-hierarchy-model.js +20 -0
- package/dist/call-hierarchy.d.ts +3 -0
- package/dist/call-hierarchy.js +54 -22
- package/dist/clipboard-history.d.ts +31 -0
- package/dist/clipboard-history.js +70 -0
- package/dist/code-actions.d.ts +3 -1
- package/dist/code-actions.js +50 -10
- package/dist/code-vision.js +2 -3
- package/dist/completion.js +10 -15
- package/dist/context-menu-rows.d.ts +1 -1
- package/dist/context-menu-rows.js +4 -0
- package/dist/definition-link.js +15 -5
- package/dist/document-colors.d.ts +24 -0
- package/dist/document-colors.js +194 -0
- package/dist/document-links.d.ts +22 -0
- package/dist/document-links.js +133 -0
- package/dist/document-query.d.ts +15 -0
- package/dist/document-query.js +54 -0
- package/dist/edit-locations.d.ts +20 -0
- package/dist/edit-locations.js +136 -0
- package/dist/editor-language.d.ts +19 -1
- package/dist/editor-language.js +77 -2
- package/dist/emmet.d.ts +13 -0
- package/dist/emmet.js +62 -0
- package/dist/folding.js +2 -3
- package/dist/format-on-type.js +3 -5
- package/dist/highlights.js +2 -3
- package/dist/history.d.ts +7 -0
- package/dist/history.js +79 -1
- package/dist/host-types.d.ts +6 -0
- package/dist/index.d.ts +11 -2
- package/dist/index.js +8 -0
- package/dist/inlay-hints.js +2 -2
- package/dist/inline-completion.d.ts +39 -0
- package/dist/inline-completion.js +221 -0
- package/dist/key-priority.d.ts +8 -5
- package/dist/key-priority.js +9 -6
- package/dist/linked-editing.d.ts +26 -0
- package/dist/linked-editing.js +215 -0
- package/dist/locales/en.json +31 -2
- package/dist/locales/nl.json +31 -2
- package/dist/markup-context.d.ts +37 -0
- package/dist/markup-context.js +291 -0
- package/dist/markup.d.ts +36 -0
- package/dist/markup.js +453 -0
- package/dist/navigation.js +4 -6
- package/dist/paste-history.d.ts +7 -0
- package/dist/paste-history.js +104 -0
- package/dist/peek.js +3 -4
- package/dist/pick.d.ts +20 -2
- package/dist/pick.js +141 -18
- package/dist/popups.d.ts +9 -0
- package/dist/popups.js +2 -1
- package/dist/project-language.d.ts +18 -1
- package/dist/project-language.js +129 -18
- package/dist/rename.js +8 -11
- package/dist/rendered-docs.js +1 -2
- package/dist/selection-ranges.js +2 -3
- package/dist/shortcut-keys.d.ts +19 -0
- package/dist/signature.js +3 -6
- package/dist/snippet-session.d.ts +2 -2
- package/dist/snippet-session.js +3 -3
- package/dist/symbol-picker.js +4 -6
- package/dist/symbols.js +22 -3
- package/dist/testing.d.ts +23 -1
- package/dist/testing.js +114 -0
- package/package.json +7 -6
- package/src/CallHierarchyPanel.tsx +26 -11
- package/src/EditorContextMenu.tsx +7 -0
- package/src/LanguagePopups.tsx +4 -0
- package/src/PickPopup.tsx +40 -5
- package/src/WrapCard.tsx +64 -0
- package/src/call-hierarchy-model.ts +25 -3
- package/src/call-hierarchy.ts +62 -20
- package/src/clipboard-history.ts +102 -0
- package/src/code-actions.ts +48 -11
- package/src/code-vision.ts +3 -3
- package/src/completion.ts +15 -15
- package/src/context-menu-rows.ts +3 -2
- package/src/definition-link.ts +17 -5
- package/src/document-colors.ts +189 -0
- package/src/document-links.ts +129 -0
- package/src/document-query.ts +71 -0
- package/src/edit-locations.ts +162 -0
- package/src/editor-language.ts +80 -3
- package/src/emmet-module.d.ts +44 -0
- package/src/emmet.ts +73 -0
- package/src/folding.ts +3 -3
- package/src/format-on-type.ts +5 -5
- package/src/highlights.ts +3 -3
- package/src/history.ts +81 -1
- package/src/host-types.ts +10 -0
- package/src/index.ts +12 -3
- package/src/inlay-hints.ts +2 -2
- package/src/inline-completion.ts +221 -0
- package/src/key-priority.ts +9 -6
- package/src/linked-editing.ts +199 -0
- package/src/locales/en.json +31 -2
- package/src/locales/nl.json +31 -2
- package/src/markup-context.ts +262 -0
- package/src/markup.ts +367 -0
- package/src/navigation.ts +6 -6
- package/src/paste-history.ts +111 -0
- package/src/peek.ts +4 -4
- package/src/pick.ts +201 -19
- package/src/popups.ts +19 -1
- package/src/project-language.ts +136 -19
- package/src/rename.ts +11 -11
- package/src/rendered-docs.ts +2 -2
- package/src/selection-ranges.ts +3 -3
- package/src/signature.ts +6 -6
- package/src/snippet-session.ts +9 -4
- package/src/symbol-picker.ts +6 -6
- package/src/symbols.ts +30 -4
- package/src/testing.ts +126 -1
package/dist/pick.js
CHANGED
|
@@ -10,8 +10,14 @@ export class PickFeature {
|
|
|
10
10
|
language;
|
|
11
11
|
timers;
|
|
12
12
|
spec = null;
|
|
13
|
+
/* The rows as the spec gave them, or as `remove` left them; `ids` are those a query lets through. */
|
|
14
|
+
groups = [];
|
|
13
15
|
ids = [];
|
|
14
16
|
active = '';
|
|
17
|
+
query = '';
|
|
18
|
+
/* The rows chosen as a run or one by one, in list order, and the row a run of them grows from. */
|
|
19
|
+
chosen = [];
|
|
20
|
+
rangeAnchor = null;
|
|
15
21
|
previewTimer;
|
|
16
22
|
controller = null;
|
|
17
23
|
/* The rows that said why they cannot be taken, by id. */
|
|
@@ -42,6 +48,10 @@ export class PickFeature {
|
|
|
42
48
|
}
|
|
43
49
|
this.spec = spec;
|
|
44
50
|
this.refusals = new Map();
|
|
51
|
+
this.groups = spec.groups;
|
|
52
|
+
this.query = '';
|
|
53
|
+
this.chosen = [];
|
|
54
|
+
this.rangeAnchor = null;
|
|
45
55
|
this.ids = ids;
|
|
46
56
|
this.active = spec.active !== undefined && ids.includes(spec.active) ? spec.active : ids[0];
|
|
47
57
|
this.publish(null);
|
|
@@ -59,16 +69,40 @@ export class PickFeature {
|
|
|
59
69
|
pick: null
|
|
60
70
|
});
|
|
61
71
|
}
|
|
62
|
-
move
|
|
72
|
+
/* Moves the active row; with `extend`, in a list of several choices, the rows between where the move began and the new row are chosen. */
|
|
73
|
+
move(delta, extend = false) {
|
|
74
|
+
if (this.ids.length === 0) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
63
77
|
const index = this.ids.indexOf(this.active);
|
|
64
|
-
this.
|
|
78
|
+
const id = this.ids[Math.max(0, Math.min(this.ids.length - 1, index + delta))];
|
|
79
|
+
if (extend && this.spec?.multiple === true) {
|
|
80
|
+
this.extendTo(id);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
this.settle(id);
|
|
84
|
+
}
|
|
85
|
+
/* Makes `id` the only row the list is on. */
|
|
86
|
+
settle(id) {
|
|
87
|
+
const hadChosen = this.chosen.length > 0;
|
|
88
|
+
this.chosen = [];
|
|
89
|
+
this.rangeAnchor = null;
|
|
90
|
+
this.activate(id, hadChosen);
|
|
65
91
|
}
|
|
66
|
-
/* Takes a row, by a click or Enter. */
|
|
67
|
-
choose(id) {
|
|
92
|
+
/* Takes a row, by a click or Enter. In a list of several choices a press with Shift chooses the run up to the row, and one with Mod adds or takes away the row. */
|
|
93
|
+
choose(id, modifiers = {}) {
|
|
68
94
|
const spec = this.spec;
|
|
69
95
|
if (spec === null || !this.ids.includes(id) || this.attempting) {
|
|
70
96
|
return;
|
|
71
97
|
}
|
|
98
|
+
if (spec.multiple === true && modifiers.range === true) {
|
|
99
|
+
this.extendTo(id);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (spec.multiple === true && modifiers.toggle === true) {
|
|
103
|
+
this.toggle(id);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
72
106
|
if (spec.attempt === undefined) {
|
|
73
107
|
this.close();
|
|
74
108
|
spec.accept(id);
|
|
@@ -89,25 +123,99 @@ export class PickFeature {
|
|
|
89
123
|
this.publish(null);
|
|
90
124
|
});
|
|
91
125
|
}
|
|
92
|
-
activate(id) {
|
|
93
|
-
|
|
126
|
+
activate(id, force = false) {
|
|
127
|
+
const moved = id !== this.active;
|
|
128
|
+
this.active = id;
|
|
129
|
+
if (moved) {
|
|
130
|
+
this.controller?.abort();
|
|
131
|
+
this.schedulePreview();
|
|
132
|
+
}
|
|
133
|
+
if (moved || force) {
|
|
134
|
+
this.publish(null);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
extendTo(id) {
|
|
138
|
+
const anchor = this.rangeAnchor ?? this.active;
|
|
139
|
+
const first = this.ids.indexOf(anchor);
|
|
140
|
+
const last = this.ids.indexOf(id);
|
|
141
|
+
const run = this.ids.slice(Math.min(first, last), Math.max(first, last) + 1);
|
|
142
|
+
this.rangeAnchor = anchor;
|
|
143
|
+
this.chosen = run.length > 1 ? run : [];
|
|
144
|
+
this.activate(id, true);
|
|
145
|
+
}
|
|
146
|
+
toggle(id) {
|
|
147
|
+
const held = this.chosen.length > 0 ? this.chosen : [this.active];
|
|
148
|
+
const next = held.includes(id) ? held.filter(other => other !== id) : [...held, id];
|
|
149
|
+
this.chosen = this.ids.filter(other => next.includes(other));
|
|
150
|
+
this.rangeAnchor = id;
|
|
151
|
+
this.activate(id, true);
|
|
152
|
+
}
|
|
153
|
+
/* Enter: the chosen rows, or the active one when none is chosen. */
|
|
154
|
+
takeActive() {
|
|
155
|
+
const spec = this.spec;
|
|
156
|
+
if (spec?.acceptMany === undefined) {
|
|
157
|
+
this.choose(this.active);
|
|
94
158
|
return;
|
|
95
159
|
}
|
|
96
|
-
this.
|
|
160
|
+
const ids = this.chosen.length > 0 ? this.chosen : [this.active];
|
|
161
|
+
if (this.active !== '' && !this.attempting) {
|
|
162
|
+
this.close();
|
|
163
|
+
spec.acceptMany(ids);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
visibleGroups() {
|
|
167
|
+
const query = this.query.toLowerCase();
|
|
168
|
+
if (query === '') {
|
|
169
|
+
return this.groups;
|
|
170
|
+
}
|
|
171
|
+
return this.groups.map(group => ({
|
|
172
|
+
...group,
|
|
173
|
+
rows: group.rows.filter(row => (row.search ?? row.label).toLowerCase().includes(query))
|
|
174
|
+
})).filter(group => group.rows.length > 0);
|
|
175
|
+
}
|
|
176
|
+
/* The query changed: the rows it lets through, with the first of them active. */
|
|
177
|
+
requery(query) {
|
|
178
|
+
this.query = query;
|
|
179
|
+
this.ids = this.visibleGroups().flatMap(group => group.rows.map(row => row.id));
|
|
180
|
+
this.chosen = [];
|
|
181
|
+
this.rangeAnchor = null;
|
|
182
|
+
this.active = this.ids[0] ?? '';
|
|
183
|
+
this.controller?.abort();
|
|
184
|
+
this.publish(null);
|
|
185
|
+
this.schedulePreview();
|
|
186
|
+
}
|
|
187
|
+
/* Takes the chosen rows, or the active one, out of the list; the list closes when no row is left. */
|
|
188
|
+
removeChosen() {
|
|
189
|
+
const spec = this.spec;
|
|
190
|
+
if (spec?.remove === undefined || this.active === '') {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const removed = this.chosen.length > 0 ? this.chosen : [this.active];
|
|
194
|
+
const index = this.ids.indexOf(removed[0]);
|
|
195
|
+
this.groups = spec.remove(removed);
|
|
196
|
+
if (this.groups.every(group => group.rows.length === 0)) {
|
|
197
|
+
this.close();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
this.ids = this.visibleGroups().flatMap(group => group.rows.map(row => row.id));
|
|
201
|
+
this.chosen = [];
|
|
202
|
+
this.rangeAnchor = null;
|
|
203
|
+
this.active = this.ids[Math.min(index, this.ids.length - 1)] ?? '';
|
|
97
204
|
this.controller?.abort();
|
|
98
205
|
this.publish(null);
|
|
99
206
|
this.schedulePreview();
|
|
100
207
|
}
|
|
101
208
|
key(event) {
|
|
102
|
-
|
|
209
|
+
const spec = this.spec;
|
|
210
|
+
if (spec === null || event.metaKey || event.ctrlKey || event.altKey) {
|
|
103
211
|
return false;
|
|
104
212
|
}
|
|
105
213
|
switch (event.key) {
|
|
106
214
|
case 'ArrowDown':
|
|
107
|
-
this.move(1);
|
|
215
|
+
this.move(1, event.shiftKey);
|
|
108
216
|
return true;
|
|
109
217
|
case 'ArrowUp':
|
|
110
|
-
this.move(-1);
|
|
218
|
+
this.move(-1, event.shiftKey);
|
|
111
219
|
return true;
|
|
112
220
|
case 'PageDown':
|
|
113
221
|
this.move(8);
|
|
@@ -116,17 +224,26 @@ export class PickFeature {
|
|
|
116
224
|
this.move(-8);
|
|
117
225
|
return true;
|
|
118
226
|
case 'Home':
|
|
119
|
-
this.activate(this.ids[0]);
|
|
120
|
-
return true;
|
|
121
227
|
case 'End':
|
|
122
|
-
|
|
228
|
+
if (this.ids.length > 0) {
|
|
229
|
+
this.settle(event.key === 'Home' ? this.ids[0] : this.ids[this.ids.length - 1]);
|
|
230
|
+
}
|
|
123
231
|
return true;
|
|
124
232
|
case 'Enter':
|
|
125
|
-
this.
|
|
233
|
+
this.takeActive();
|
|
126
234
|
return true;
|
|
235
|
+
case 'Delete':
|
|
236
|
+
this.removeChosen();
|
|
237
|
+
return spec.remove !== undefined;
|
|
238
|
+
case 'Backspace':
|
|
239
|
+
if (this.query !== '') {
|
|
240
|
+
this.requery(this.query.slice(0, -1));
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
this.removeChosen();
|
|
244
|
+
return spec.remove !== undefined || spec.filter === true;
|
|
127
245
|
case 'Tab':
|
|
128
246
|
{
|
|
129
|
-
const spec = this.spec;
|
|
130
247
|
if (spec.onTab !== undefined) {
|
|
131
248
|
this.close();
|
|
132
249
|
spec.onTab(event.shiftKey);
|
|
@@ -139,13 +256,17 @@ export class PickFeature {
|
|
|
139
256
|
this.close();
|
|
140
257
|
return true;
|
|
141
258
|
default:
|
|
259
|
+
if (spec.filter === true && event.key.length === 1) {
|
|
260
|
+
this.requery(this.query + event.key);
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
142
263
|
return false;
|
|
143
264
|
}
|
|
144
265
|
}
|
|
145
266
|
schedulePreview() {
|
|
146
267
|
const spec = this.spec;
|
|
147
268
|
this.timers.clear(this.previewTimer);
|
|
148
|
-
if (spec?.preview === undefined) {
|
|
269
|
+
if (spec?.preview === undefined || this.active === '') {
|
|
149
270
|
return;
|
|
150
271
|
}
|
|
151
272
|
const id = this.active;
|
|
@@ -174,10 +295,12 @@ export class PickFeature {
|
|
|
174
295
|
this.language.popups.setState({
|
|
175
296
|
pick: {
|
|
176
297
|
anchor: spec.anchor,
|
|
177
|
-
groups:
|
|
298
|
+
groups: this.visibleGroups(),
|
|
178
299
|
active: this.active,
|
|
179
300
|
preview: shown,
|
|
180
|
-
title: spec.title ?? null
|
|
301
|
+
title: spec.title ?? null,
|
|
302
|
+
query: spec.filter === true ? this.query : null,
|
|
303
|
+
chosen: this.chosen
|
|
181
304
|
}
|
|
182
305
|
});
|
|
183
306
|
}
|
package/dist/popups.d.ts
CHANGED
|
@@ -54,6 +54,7 @@ export interface PickRow {
|
|
|
54
54
|
readonly label: string;
|
|
55
55
|
readonly detail: string;
|
|
56
56
|
readonly path?: boolean;
|
|
57
|
+
readonly search?: string;
|
|
57
58
|
}
|
|
58
59
|
export interface PickGroup {
|
|
59
60
|
readonly title: string | null;
|
|
@@ -64,6 +65,7 @@ export interface PickPreview {
|
|
|
64
65
|
readonly added: readonly string[];
|
|
65
66
|
readonly note: string | null;
|
|
66
67
|
readonly refusal?: string;
|
|
68
|
+
readonly kind?: 'diff' | 'text';
|
|
67
69
|
}
|
|
68
70
|
export interface PickView {
|
|
69
71
|
readonly anchor: EditorPosition;
|
|
@@ -71,6 +73,8 @@ export interface PickView {
|
|
|
71
73
|
readonly active: string;
|
|
72
74
|
readonly preview: PickPreview | null;
|
|
73
75
|
readonly title: string | null;
|
|
76
|
+
readonly query: string | null;
|
|
77
|
+
readonly chosen: readonly string[];
|
|
74
78
|
}
|
|
75
79
|
export interface RenameFileView {
|
|
76
80
|
readonly uri: string;
|
|
@@ -134,6 +138,10 @@ export interface AuthorsView {
|
|
|
134
138
|
readonly authorship: CodeAuthorship;
|
|
135
139
|
readonly openCommit: (() => void) | null;
|
|
136
140
|
}
|
|
141
|
+
export interface WrapView {
|
|
142
|
+
readonly anchor: EditorPosition;
|
|
143
|
+
readonly error: string | null;
|
|
144
|
+
}
|
|
137
145
|
export interface PopupState {
|
|
138
146
|
readonly hover: HoverView | null;
|
|
139
147
|
readonly completion: CompletionView | null;
|
|
@@ -145,6 +153,7 @@ export interface PopupState {
|
|
|
145
153
|
readonly symbols: SymbolsView | null;
|
|
146
154
|
readonly menu: MenuView | null;
|
|
147
155
|
readonly authors: AuthorsView | null;
|
|
156
|
+
readonly wrap: WrapView | null;
|
|
148
157
|
}
|
|
149
158
|
export type PopupStore = StoreApi<PopupState>;
|
|
150
159
|
export declare function createPopupStore(): PopupStore;
|
package/dist/popups.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type i18n } from 'i18next';
|
|
2
2
|
import { type ApplyWorkspaceEditResult, type LanguageService, type WorkspaceEdit } from '@adecore/lsp';
|
|
3
3
|
import type { Editor, EditorPosition } from '@adecore/editor';
|
|
4
|
+
import { ClipboardHistory } from './clipboard-history.ts';
|
|
5
|
+
import { EditLocations } from './edit-locations.ts';
|
|
4
6
|
import { NavigationHistory, type Place } from './navigation-history.ts';
|
|
5
7
|
import { ProjectProblems } from './project-problems.ts';
|
|
6
8
|
import type { LanguageHost } from './host-types.ts';
|
|
@@ -10,6 +12,9 @@ export interface LanguageDocumentHandle {
|
|
|
10
12
|
readonly ready: Promise<void>;
|
|
11
13
|
release(): void;
|
|
12
14
|
}
|
|
15
|
+
export interface AcquireOptions {
|
|
16
|
+
readonly serve?: boolean;
|
|
17
|
+
}
|
|
13
18
|
export type FileOperationTarget = string | {
|
|
14
19
|
readonly path: string;
|
|
15
20
|
readonly directory?: boolean;
|
|
@@ -21,6 +26,8 @@ export declare class ProjectLanguage {
|
|
|
21
26
|
readonly folder: string;
|
|
22
27
|
readonly problems: ProjectProblems;
|
|
23
28
|
readonly history: NavigationHistory;
|
|
29
|
+
readonly edits: EditLocations;
|
|
30
|
+
readonly clipboard: ClipboardHistory;
|
|
24
31
|
private readonly holders;
|
|
25
32
|
private readonly files;
|
|
26
33
|
panelBodyHeight: number;
|
|
@@ -28,6 +35,8 @@ export declare class ProjectLanguage {
|
|
|
28
35
|
private pendingCaret;
|
|
29
36
|
private lastPlace;
|
|
30
37
|
private disposed;
|
|
38
|
+
private readonly subscriptions;
|
|
39
|
+
private readonly workspaceResults;
|
|
31
40
|
constructor(service: LanguageService, host?: LanguageHost);
|
|
32
41
|
onPanelBodyChange(listener: (height: number) => void): () => void;
|
|
33
42
|
resizePanelBody(height: number): void;
|
|
@@ -38,11 +47,19 @@ export declare class ProjectLanguage {
|
|
|
38
47
|
willDeleteFiles(targets: readonly FileOperationTarget[]): Promise<WorkspaceEdit | null>;
|
|
39
48
|
didDeleteFiles(targets: readonly FileOperationTarget[]): Promise<void>;
|
|
40
49
|
readText(uri: string): Promise<string | null>;
|
|
50
|
+
pullWorkspaceProblems(uri: string, options?: {
|
|
51
|
+
readonly signal?: AbortSignal;
|
|
52
|
+
}): Promise<boolean>;
|
|
53
|
+
private showDocument;
|
|
41
54
|
openPlace(place: Place): void;
|
|
42
55
|
noteCaret(place: Place): void;
|
|
43
56
|
jumpTo(place: Place): void;
|
|
44
57
|
takeCaret(uri: string, line: number): EditorPosition | null;
|
|
45
|
-
|
|
58
|
+
openEditors(): readonly {
|
|
59
|
+
uri: string;
|
|
60
|
+
editor: Editor;
|
|
61
|
+
}[];
|
|
62
|
+
acquire(uri: string, languageId: string, editor: Editor, options?: AcquireOptions): LanguageDocumentHandle;
|
|
46
63
|
private release;
|
|
47
64
|
dispose(): void;
|
|
48
65
|
}
|
package/dist/project-language.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { disposeSnippetTransforms } from './snippet-transform-queue.js';
|
|
2
2
|
import i18next from 'i18next';
|
|
3
|
-
import { fileUriToPath, pathToFileUri } from '@adecore/lsp';
|
|
3
|
+
import { fileUriToPath, pathToFileUri, unservedLanguageService } from '@adecore/lsp';
|
|
4
|
+
import { ClipboardHistory } from './clipboard-history.js';
|
|
5
|
+
import { EditLocations } from './edit-locations.js';
|
|
4
6
|
import { NavigationHistory } from './navigation-history.js';
|
|
5
7
|
import { ProjectProblems, serviceProblems } from './project-problems.js';
|
|
6
8
|
import { applyWorkspaceEdit } from './workspace-edit.js';
|
|
7
9
|
import { relativeTo } from './paths.js';
|
|
8
10
|
import { PANEL_BODY_HEIGHT } from './panel-size.js';
|
|
11
|
+
const UNSERVED = unservedLanguageService();
|
|
9
12
|
/* Sends one editor's edits to the document, holding them until the document is open. */
|
|
10
13
|
class DocumentSync {
|
|
11
14
|
ready;
|
|
@@ -86,6 +89,8 @@ export class ProjectLanguage {
|
|
|
86
89
|
folder;
|
|
87
90
|
problems;
|
|
88
91
|
history = new NavigationHistory();
|
|
92
|
+
edits;
|
|
93
|
+
clipboard;
|
|
89
94
|
holders = new Map();
|
|
90
95
|
files;
|
|
91
96
|
/* The body height of the peek and call hierarchy panels, shared by the editors of the project. */
|
|
@@ -94,14 +99,22 @@ export class ProjectLanguage {
|
|
|
94
99
|
pendingCaret = null;
|
|
95
100
|
lastPlace = null;
|
|
96
101
|
disposed = false;
|
|
102
|
+
subscriptions;
|
|
103
|
+
/* The result id the server gave each document in its last workspace report, which the next asks with. */
|
|
104
|
+
workspaceResults = new Map();
|
|
97
105
|
constructor(service, host = {}) {
|
|
98
106
|
this.service = service;
|
|
99
107
|
this.host = host;
|
|
100
108
|
this.i18n = host.i18n ?? i18next;
|
|
101
109
|
this.folder = host.folder ?? '';
|
|
102
110
|
this.files = host.files ?? null;
|
|
111
|
+
this.edits = new EditLocations(host.editHistoryLimit);
|
|
112
|
+
this.clipboard = host.clipboard ?? new ClipboardHistory();
|
|
103
113
|
this.panelBodyHeight = host.panelBodyHeight ?? PANEL_BODY_HEIGHT;
|
|
104
114
|
this.problems = new ProjectProblems(host.problemSource ?? serviceProblems(service, uri => this.pathOfLocation(uri) ?? uri));
|
|
115
|
+
const shows = service.onShowDocument?.(params => this.showDocument(params));
|
|
116
|
+
const messages = host.onServerMessage === undefined ? undefined : service.onServerMessage?.(message => host.onServerMessage(message));
|
|
117
|
+
this.subscriptions = [shows, messages].flatMap(subscription => subscription === undefined ? [] : [subscription]);
|
|
105
118
|
}
|
|
106
119
|
/* Open panels of other editors of the project follow a resize. */
|
|
107
120
|
onPanelBodyChange(listener) {
|
|
@@ -153,16 +166,86 @@ export class ProjectLanguage {
|
|
|
153
166
|
}
|
|
154
167
|
/* Tells the servers that files were deleted, once they are. */
|
|
155
168
|
didDeleteFiles(targets) {
|
|
156
|
-
|
|
169
|
+
const files = filesOf(targets);
|
|
170
|
+
this.edits.forget(uri => files.some(file => file.directory ? uri.startsWith(`${file.uri}/`) : uri === file.uri));
|
|
171
|
+
return this.service.didDeleteFiles?.(files) ?? Promise.resolve();
|
|
157
172
|
}
|
|
158
|
-
/*
|
|
173
|
+
/*
|
|
174
|
+
* The text of a file as it stands now: an open document's, else a draft's or the machine's; null when it is no
|
|
175
|
+
* text file. A document of a scheme a server owns, such as a decompiled class, is read from that server.
|
|
176
|
+
*/
|
|
159
177
|
async readText(uri) {
|
|
160
|
-
const open = this.holders.get(uri)?.
|
|
178
|
+
const open = this.holders.get(uri)?.editors[0]?.getText();
|
|
161
179
|
if (open !== undefined) {
|
|
162
180
|
return open;
|
|
163
181
|
}
|
|
164
182
|
const path = fileUriToPath(uri);
|
|
165
|
-
|
|
183
|
+
if (path === null) {
|
|
184
|
+
return (await this.service.textDocumentContent?.(uri).catch(() => null)) ?? null;
|
|
185
|
+
}
|
|
186
|
+
return this.files === null ? null : (await this.files.read(path))?.text ?? null;
|
|
187
|
+
}
|
|
188
|
+
/*
|
|
189
|
+
* Asks the server of `uri` for the problems of every file it knows and lists them in `problems`, for a server that
|
|
190
|
+
* offers workspace diagnostics. A file the server reports as unchanged keeps what it had. False when no server offers them.
|
|
191
|
+
*/
|
|
192
|
+
async pullWorkspaceProblems(uri, options = {}) {
|
|
193
|
+
const {
|
|
194
|
+
service
|
|
195
|
+
} = this;
|
|
196
|
+
if (service.workspaceDiagnostics === undefined || !service.supports('workspace/diagnostic', uri)) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
const previous = [...this.workspaceResults].map(([key, value]) => ({
|
|
200
|
+
uri: key,
|
|
201
|
+
value
|
|
202
|
+
}));
|
|
203
|
+
const report = await service.workspaceDiagnostics(uri, previous, options);
|
|
204
|
+
const server = this.host.serverNames?.(uri)[0] ?? 'workspace';
|
|
205
|
+
for (const item of report.items) {
|
|
206
|
+
if (item.resultId !== undefined) {
|
|
207
|
+
this.workspaceResults.set(item.uri, item.resultId);
|
|
208
|
+
}
|
|
209
|
+
if (item.kind === 'full') {
|
|
210
|
+
this.problems.report(this.pathOfLocation(item.uri) ?? item.uri, server, item.items);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
/* Shows what a server asks for: a web address outside the editor, anything else as a place of the host. */
|
|
216
|
+
async showDocument(params) {
|
|
217
|
+
const web = /^(https?|mailto):/i.test(params.uri);
|
|
218
|
+
if (params.external === true || web) {
|
|
219
|
+
if (this.host.openExternal !== undefined) {
|
|
220
|
+
this.host.openExternal(params.uri);
|
|
221
|
+
return {
|
|
222
|
+
success: true
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
if (typeof window === 'undefined') {
|
|
226
|
+
return {
|
|
227
|
+
success: false
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
success: window.open(params.uri, '_blank', 'noopener,noreferrer') !== null
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (this.host.openPlace === undefined) {
|
|
235
|
+
return {
|
|
236
|
+
success: false
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
this.openPlace({
|
|
240
|
+
uri: params.uri,
|
|
241
|
+
position: params.selection?.start ?? {
|
|
242
|
+
line: 0,
|
|
243
|
+
character: 0
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
return {
|
|
247
|
+
success: true
|
|
248
|
+
};
|
|
166
249
|
}
|
|
167
250
|
/*
|
|
168
251
|
* Opens the file of a place on its line. A file opens on a line only, so the column is kept here until the editor
|
|
@@ -192,27 +275,40 @@ export class ProjectLanguage {
|
|
|
192
275
|
this.pendingCaret = null;
|
|
193
276
|
return pending.position;
|
|
194
277
|
}
|
|
278
|
+
/* The editors that hold a document, one per uri in the order the documents were first opened. */
|
|
279
|
+
openEditors() {
|
|
280
|
+
return [...this.holders].flatMap(([uri, holder]) => holder.editors[0] === undefined ? [] : [{
|
|
281
|
+
uri,
|
|
282
|
+
editor: holder.editors[0]
|
|
283
|
+
}]);
|
|
284
|
+
}
|
|
195
285
|
/* Opens `uri` for the editor, or joins the editors that already hold it. */
|
|
196
|
-
acquire(uri, languageId, editor) {
|
|
286
|
+
acquire(uri, languageId, editor, options = {}) {
|
|
197
287
|
if (this.disposed) {
|
|
198
288
|
throw new Error('The project language is disposed');
|
|
199
289
|
}
|
|
290
|
+
const serve = options.serve ?? true;
|
|
200
291
|
let holder = this.holders.get(uri);
|
|
201
292
|
if (holder === undefined) {
|
|
202
293
|
holder = {
|
|
203
294
|
editors: [],
|
|
204
|
-
|
|
295
|
+
served: [],
|
|
296
|
+
sync: null,
|
|
205
297
|
languageId
|
|
206
298
|
};
|
|
207
299
|
this.holders.set(uri, holder);
|
|
208
300
|
}
|
|
209
301
|
holder.editors.push(editor);
|
|
302
|
+
if (serve) {
|
|
303
|
+
holder.served.push(editor);
|
|
304
|
+
holder.sync ??= new DocumentSync(this.service, uri, languageId, editor);
|
|
305
|
+
}
|
|
210
306
|
let released = false;
|
|
211
307
|
return {
|
|
212
308
|
uri,
|
|
213
|
-
service: this.service,
|
|
309
|
+
service: serve ? this.service : UNSERVED,
|
|
214
310
|
get ready() {
|
|
215
|
-
return holder.sync.
|
|
311
|
+
return serve ? holder.sync?.ready ?? Promise.resolve() : Promise.resolve();
|
|
216
312
|
},
|
|
217
313
|
release: () => {
|
|
218
314
|
if (!released) {
|
|
@@ -228,20 +324,29 @@ export class ProjectLanguage {
|
|
|
228
324
|
return;
|
|
229
325
|
}
|
|
230
326
|
holder.editors.splice(holder.editors.indexOf(editor), 1);
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
327
|
+
if (holder.served.includes(editor)) {
|
|
328
|
+
holder.served.splice(holder.served.indexOf(editor), 1);
|
|
329
|
+
}
|
|
330
|
+
const {
|
|
331
|
+
sync
|
|
332
|
+
} = holder;
|
|
333
|
+
if (holder.editors.length === 0) {
|
|
234
334
|
this.holders.delete(uri);
|
|
235
|
-
|
|
236
|
-
|
|
335
|
+
}
|
|
336
|
+
if (sync === null || sync.editor !== editor) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
sync.dispose();
|
|
340
|
+
const next = holder.served[0];
|
|
341
|
+
// The editor that sent the changes is gone; the next served one takes over and opens the document with its own text.
|
|
342
|
+
holder.sync = next === undefined ? null : new DocumentSync(this.service, uri, holder.languageId, next);
|
|
343
|
+
if (holder.sync === null) {
|
|
344
|
+
void sync.ready.catch(() => undefined).then(() => {
|
|
345
|
+
if (this.holders.get(uri)?.sync) {
|
|
237
346
|
return undefined;
|
|
238
347
|
}
|
|
239
348
|
return this.service.closeDocument(uri);
|
|
240
349
|
}).catch(() => undefined);
|
|
241
|
-
} else if (holder.sync.editor === editor) {
|
|
242
|
-
// The editor that sent the changes is gone; the next one takes over and opens the document with its own text.
|
|
243
|
-
holder.sync.dispose();
|
|
244
|
-
holder.sync = new DocumentSync(this.service, uri, holder.languageId, next);
|
|
245
350
|
}
|
|
246
351
|
}
|
|
247
352
|
dispose() {
|
|
@@ -249,8 +354,14 @@ export class ProjectLanguage {
|
|
|
249
354
|
return;
|
|
250
355
|
}
|
|
251
356
|
this.disposed = true;
|
|
357
|
+
for (const subscription of this.subscriptions) {
|
|
358
|
+
subscription.dispose();
|
|
359
|
+
}
|
|
252
360
|
disposeSnippetTransforms(this);
|
|
253
361
|
for (const [uri, holder] of this.holders) {
|
|
362
|
+
if (holder.sync === null) {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
254
365
|
holder.sync.dispose();
|
|
255
366
|
void holder.sync.ready.catch(() => undefined).then(() => this.service.closeDocument(uri)).catch(() => undefined);
|
|
256
367
|
}
|
package/dist/rename.js
CHANGED
|
@@ -44,19 +44,18 @@ export class RenameFeature {
|
|
|
44
44
|
async start() {
|
|
45
45
|
const {
|
|
46
46
|
editor,
|
|
47
|
-
project,
|
|
48
47
|
uri
|
|
49
48
|
} = this.language;
|
|
50
|
-
if (!
|
|
49
|
+
if (!this.language.service.supports(METHOD, uri)) {
|
|
51
50
|
this.tell('error', this.say('unavailable'));
|
|
52
51
|
return;
|
|
53
52
|
}
|
|
54
53
|
this.cancel();
|
|
55
54
|
const caret = editor.getCaret();
|
|
56
|
-
const prepare =
|
|
55
|
+
const prepare = this.language.service.providerOptions(METHOD, uri)?.prepareProvider === true;
|
|
57
56
|
let answer;
|
|
58
57
|
try {
|
|
59
|
-
answer = prepare ? await
|
|
58
|
+
answer = prepare ? await this.language.service.prepareRename(uri, caret) : {
|
|
60
59
|
defaultBehavior: true
|
|
61
60
|
};
|
|
62
61
|
} catch (error) {
|
|
@@ -110,7 +109,7 @@ export class RenameFeature {
|
|
|
110
109
|
uri
|
|
111
110
|
} = this.language;
|
|
112
111
|
try {
|
|
113
|
-
const edit = await
|
|
112
|
+
const edit = await this.language.service.rename(uri, session.target.range.start, next);
|
|
114
113
|
if (this.session !== session) {
|
|
115
114
|
return;
|
|
116
115
|
}
|
|
@@ -204,14 +203,13 @@ export class RenameFeature {
|
|
|
204
203
|
async light(session) {
|
|
205
204
|
const {
|
|
206
205
|
editor,
|
|
207
|
-
project,
|
|
208
206
|
uri
|
|
209
207
|
} = this.language;
|
|
210
|
-
if (!
|
|
208
|
+
if (!this.language.service.supports('textDocument/references', uri)) {
|
|
211
209
|
return null;
|
|
212
210
|
}
|
|
213
211
|
try {
|
|
214
|
-
const locations = await
|
|
212
|
+
const locations = await this.language.service.references(uri, session.target.range.start, true);
|
|
215
213
|
if (this.session !== session || locations === null) {
|
|
216
214
|
return null;
|
|
217
215
|
}
|
|
@@ -282,14 +280,13 @@ export class RenameFeature {
|
|
|
282
280
|
/* So a suggester knows a function from a variable; a rename on a use finds the kind through the declaration's place in this file. */
|
|
283
281
|
async kindOf(session, locations) {
|
|
284
282
|
const {
|
|
285
|
-
project,
|
|
286
283
|
uri
|
|
287
284
|
} = this.language;
|
|
288
|
-
if (!
|
|
285
|
+
if (!this.language.service.supports('textDocument/documentSymbol', uri)) {
|
|
289
286
|
return undefined;
|
|
290
287
|
}
|
|
291
288
|
try {
|
|
292
|
-
const symbols = await
|
|
289
|
+
const symbols = await this.language.service.documentSymbols(uri, {
|
|
293
290
|
signal: session.names.signal,
|
|
294
291
|
parallel: true
|
|
295
292
|
});
|
package/dist/rendered-docs.js
CHANGED
|
@@ -192,10 +192,9 @@ export class RenderedDocsFeature {
|
|
|
192
192
|
/* Whether names in `{@link}` and `@see` can be followed, which takes a server that finds symbols by name. */
|
|
193
193
|
get canFollow() {
|
|
194
194
|
const {
|
|
195
|
-
project,
|
|
196
195
|
uri
|
|
197
196
|
} = this.language;
|
|
198
|
-
return
|
|
197
|
+
return this.language.service.supports('workspace/symbol', uri);
|
|
199
198
|
}
|
|
200
199
|
/* Goes to what a link in a comment names. */
|
|
201
200
|
follow(name, comment) {
|
package/dist/selection-ranges.js
CHANGED
|
@@ -15,14 +15,13 @@ export class SelectionRangesFeature {
|
|
|
15
15
|
constructor(language) {
|
|
16
16
|
const {
|
|
17
17
|
editor,
|
|
18
|
-
project,
|
|
19
18
|
uri
|
|
20
19
|
} = language;
|
|
21
20
|
editor.setSelectionRanges(async positions => {
|
|
22
|
-
if (!
|
|
21
|
+
if (!language.service.supports(METHOD, uri)) {
|
|
23
22
|
return null;
|
|
24
23
|
}
|
|
25
|
-
const chains = await
|
|
24
|
+
const chains = await language.service.selectionRanges(uri, positions);
|
|
26
25
|
return chains === null || chains.length !== positions.length ? null : chains.map(selectionChainOf);
|
|
27
26
|
});
|
|
28
27
|
language.onDispose(() => editor.setSelectionRanges(null));
|