@adecore/editor-react 0.31.12 → 0.32.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/AnchoredPopup.js +73 -19
- package/dist/AttributionCard.js +162 -4
- package/dist/BaselineStatus.js +62 -8
- package/dist/CallHierarchyPanel.js +977 -60
- package/dist/ChangeReview.js +76 -25
- package/dist/CodeAuthorsCard.js +314 -14
- package/dist/CompletionPopup.js +490 -83
- package/dist/DiffView.js +548 -181
- package/dist/EditorContextMenu.js +352 -34
- package/dist/EditorView.js +276 -99
- package/dist/FindReplace.js +525 -67
- package/dist/HoverCard.js +378 -38
- package/dist/HoverSections.js +367 -56
- package/dist/LanguagePopups.js +95 -34
- package/dist/Markdown.js +41 -6
- package/dist/PanelResizeHandle.js +199 -66
- package/dist/PathText.js +49 -7
- package/dist/PeekPanel.js +675 -72
- package/dist/PickPopup.js +304 -34
- package/dist/RenameCard.js +679 -85
- package/dist/RenderedDocs.js +329 -58
- package/dist/SignatureCard.js +91 -7
- package/dist/SplitDiffView.js +344 -254
- package/dist/SymbolPicker.js +440 -85
- package/dist/call-hierarchy-model.js +102 -68
- package/dist/call-hierarchy.js +458 -359
- package/dist/code-actions-model.js +88 -76
- package/dist/code-actions.js +372 -283
- package/dist/code-authors.js +64 -55
- package/dist/code-vision-model.js +134 -127
- package/dist/code-vision.js +224 -206
- package/dist/completion-call.js +94 -67
- package/dist/completion-model.js +334 -244
- package/dist/completion.js +840 -649
- package/dist/conflict-model.js +35 -29
- package/dist/context-menu-rows.js +42 -17
- package/dist/context-menu.js +64 -46
- package/dist/definition-link.js +88 -85
- package/dist/diagnostics-model.js +74 -55
- package/dist/diagnostics.js +121 -100
- package/dist/diff-expansion.js +38 -35
- package/dist/diff-overlays.js +144 -22
- package/dist/diff-syntax.js +65 -42
- package/dist/diff-wordmarks.js +26 -23
- package/dist/doc-comment-model.js +265 -216
- package/dist/editor-language.js +177 -152
- package/dist/error-message.js +2 -2
- package/dist/find-query.js +49 -36
- package/dist/folding.js +81 -62
- package/dist/format-on-type.js +86 -67
- package/dist/highlight-layers.js +19 -20
- package/dist/highlights.js +105 -83
- package/dist/history.js +44 -38
- package/dist/holder.js +18 -18
- package/dist/host-types.js +1 -1
- package/dist/hover-content.js +167 -129
- package/dist/hover.js +285 -245
- package/dist/index.js +10 -10
- package/dist/inlay-hints.js +369 -324
- package/dist/key-priority.js +34 -31
- package/dist/language-ids.js +45 -42
- package/dist/line-edits.js +74 -28
- package/dist/line-text.js +18 -3
- package/dist/locales/en.json +38 -38
- package/dist/locales/nl.json +41 -41
- package/dist/models.js +1 -1
- package/dist/navigation-history.js +40 -41
- package/dist/navigation.js +141 -123
- package/dist/panel-size.js +8 -8
- package/dist/patch-decorations.js +236 -160
- package/dist/patch-layout.js +331 -262
- package/dist/paths.js +3 -3
- package/dist/peek-model.js +86 -69
- package/dist/peek.js +254 -192
- package/dist/pick.js +164 -157
- package/dist/popup-focus.js +5 -5
- package/dist/popup-placement.js +36 -21
- package/dist/popups.js +13 -13
- package/dist/project-language.js +228 -211
- package/dist/project-problems.js +81 -69
- package/dist/proposal-model.js +163 -126
- package/dist/recent-choices.js +25 -25
- package/dist/refresher.js +44 -49
- package/dist/rename-model.js +113 -113
- package/dist/rename.js +321 -233
- package/dist/rendered-docs.d.ts +4 -2
- package/dist/rendered-docs.js +384 -318
- package/dist/rendering-context.js +5 -3
- package/dist/rendering.js +22 -4
- package/dist/review-group.js +42 -39
- package/dist/row-host.d.ts +4 -2
- package/dist/row-host.js +90 -78
- package/dist/selection-ranges.js +21 -17
- package/dist/semantic-delta.js +22 -19
- package/dist/semantic-model.js +86 -73
- package/dist/semantic-tokens.js +201 -162
- package/dist/shortcut-keys.js +16 -10
- package/dist/signature-model.js +34 -28
- package/dist/signature.js +177 -129
- package/dist/snippet-field-transforms.js +118 -106
- package/dist/snippet-indent.js +48 -43
- package/dist/snippet-range-order.js +11 -11
- package/dist/snippet-session.js +703 -667
- package/dist/snippet-transform-client.js +59 -59
- package/dist/snippet-transform-format.js +285 -263
- package/dist/snippet-transform-protocol.js +1 -1
- package/dist/snippet-transform-queue.js +163 -153
- package/dist/snippet-transform-worker.js +42 -35
- package/dist/snippet-variable-names.js +1 -36
- package/dist/snippet-variables.js +53 -63
- package/dist/snippet.js +477 -405
- package/dist/split-alignment.js +47 -35
- package/dist/split-scroll.js +42 -40
- package/dist/symbol-links.js +33 -25
- package/dist/symbol-picker-model.js +126 -116
- package/dist/symbol-picker.js +98 -59
- package/dist/symbols.js +117 -88
- package/dist/testing.js +248 -165
- package/dist/timers.js +35 -30
- package/dist/use-panel-body.js +49 -8
- package/dist/use-problem-counts.js +29 -15
- package/dist/view-state.js +54 -33
- package/dist/word-diff.js +18 -18
- package/dist/workspace-edit.js +300 -249
- package/package.json +6 -6
- package/src/CallHierarchyPanel.tsx +2 -2
- package/src/DiffView.tsx +3 -3
- package/src/FindReplace.tsx +2 -2
- package/src/LanguagePopups.tsx +2 -0
- package/src/PeekPanel.tsx +3 -3
- package/src/RenderedDocs.tsx +9 -10
- package/src/SymbolPicker.tsx +2 -2
- package/src/diff-overlays.tsx +6 -4
- package/src/locales/en.json +38 -38
- package/src/locales/nl.json +41 -41
- package/src/rendered-docs.ts +14 -3
- package/src/row-host.ts +14 -3
package/dist/code-actions.js
CHANGED
|
@@ -15,10 +15,9 @@ const LINE_PROBLEMS = 3;
|
|
|
15
15
|
/* The command of the PHP server that asks the client to rename the name a refactor just wrote; the server answers it with nothing. */
|
|
16
16
|
const INLINE_RENAME_COMMAND = 'php.rename';
|
|
17
17
|
/* A server that would not make the action, with its reason. */
|
|
18
|
-
class ActionRefused extends Error {
|
|
19
|
-
}
|
|
18
|
+
class ActionRefused extends Error {}
|
|
20
19
|
function isRefusal(error) {
|
|
21
|
-
|
|
20
|
+
return error instanceof LspError && error.code === ErrorCodes.InternalError;
|
|
22
21
|
}
|
|
23
22
|
/*
|
|
24
23
|
* What the servers offer at the caret or the selection: the gutter's lightbulb, the list under the caret
|
|
@@ -26,307 +25,397 @@ function isRefusal(error) {
|
|
|
26
25
|
* before it is applied, since a server fills in its edit only when asked for it.
|
|
27
26
|
*/
|
|
28
27
|
export class CodeActionsFeature {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
28
|
+
language;
|
|
29
|
+
refresher;
|
|
30
|
+
/* While a list is being asked for, the lightbulb waits: both are the same request and the newer one would cancel the older. */
|
|
31
|
+
asking = false;
|
|
32
|
+
resolved = new Map();
|
|
33
|
+
constructor(language, timers = realTimers) {
|
|
34
|
+
this.language = language;
|
|
35
|
+
const {
|
|
36
|
+
editor
|
|
37
|
+
} = language;
|
|
38
|
+
this.refresher = new Refresher(signal => this.hint(signal), PAUSE_MS, timers);
|
|
39
|
+
const providers = language.onProvidersChanged(() => {
|
|
40
|
+
this.refresher.later();
|
|
41
|
+
});
|
|
42
|
+
const offs = [editor.onCaret(() => {
|
|
43
|
+
editor.setGutterAction(null);
|
|
44
|
+
// An answer for the caret's last place would light the bulb on the line it moved to.
|
|
45
|
+
this.refresher.cancel();
|
|
46
|
+
this.refresher.later();
|
|
47
|
+
}), editor.onTextChange(() => editor.setGutterAction(null)), editor.onGutterAction(() => void this.open()), language.onKey(event => {
|
|
48
|
+
if (isShortcut(this.language.shortcuts.codeActions, event)) {
|
|
49
|
+
void this.open();
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (isShortcut(this.language.shortcuts.refactorThis, event)) {
|
|
53
|
+
void this.refactorThis();
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}, KEY_PRIORITY.codeActions), language.diagnostics.onChange(() => this.refresher.later()), providers];
|
|
58
|
+
language.onDispose(() => {
|
|
59
|
+
for (const off of offs) {
|
|
60
|
+
off();
|
|
61
|
+
}
|
|
62
|
+
this.refresher.dispose();
|
|
63
|
+
editor.setGutterAction(null);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
get supported() {
|
|
67
|
+
return this.language.project.service.supports(METHOD, this.language.uri);
|
|
68
|
+
}
|
|
69
|
+
/* The list under the caret, or under the selection's start. */
|
|
70
|
+
open() {
|
|
71
|
+
const range = this.language.editor.getSelection();
|
|
72
|
+
return this.ask({
|
|
73
|
+
range,
|
|
74
|
+
anchor: range.start,
|
|
75
|
+
withLine: true
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/* Refactor This: only the refactorings, grouped by what they do, under the caret or the selection. */
|
|
79
|
+
refactorThis() {
|
|
80
|
+
const range = this.language.editor.getSelection();
|
|
81
|
+
return this.ask({
|
|
82
|
+
range,
|
|
83
|
+
only: ['refactor'],
|
|
84
|
+
anchor: range.start,
|
|
85
|
+
groups: REFACTOR_GROUPS
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/* The quick fixes of one problem, under it, as the Quick fix button of its card asks. */
|
|
89
|
+
quickFixFor(problem) {
|
|
90
|
+
const {
|
|
91
|
+
range
|
|
92
|
+
} = problem.diagnostic;
|
|
93
|
+
return this.ask({
|
|
94
|
+
range,
|
|
95
|
+
only: ['quickfix'],
|
|
96
|
+
anchor: range.start,
|
|
97
|
+
diagnostics: [problem.diagnostic]
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
async organizeImports() {
|
|
101
|
+
const {
|
|
102
|
+
editor
|
|
103
|
+
} = this.language;
|
|
104
|
+
const everything = {
|
|
105
|
+
start: {
|
|
106
|
+
line: 0,
|
|
107
|
+
character: 0
|
|
108
|
+
},
|
|
109
|
+
end: editor.positionAt(Number.MAX_SAFE_INTEGER)
|
|
110
|
+
};
|
|
111
|
+
const entries = await this.request(everything, INVOKED, {
|
|
112
|
+
only: ['source.organizeImports']
|
|
113
|
+
});
|
|
114
|
+
const entry = entries?.find(candidate => candidate.action.kind?.startsWith('source.organizeImports') === true);
|
|
115
|
+
if (entry === undefined) {
|
|
116
|
+
this.tell('success', this.say(entries === null ? 'unavailable' : 'organized'));
|
|
117
|
+
return;
|
|
71
118
|
}
|
|
72
|
-
|
|
73
|
-
|
|
119
|
+
await this.apply(entry);
|
|
120
|
+
}
|
|
121
|
+
async formatDocument() {
|
|
122
|
+
const {
|
|
123
|
+
editor,
|
|
124
|
+
project,
|
|
125
|
+
uri
|
|
126
|
+
} = this.language;
|
|
127
|
+
if (!project.service.supports('textDocument/formatting', uri)) {
|
|
128
|
+
this.tell('error', this.say('noFormatter'));
|
|
129
|
+
return;
|
|
74
130
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
131
|
+
try {
|
|
132
|
+
const edits = await project.service.formatting(uri, {
|
|
133
|
+
...editor.getIndentation()
|
|
134
|
+
});
|
|
135
|
+
if (edits !== null && edits.length > 0 && !editor.applyEdits(edits.map(edit => ({
|
|
136
|
+
range: edit.range,
|
|
137
|
+
text: edit.newText
|
|
138
|
+
})))) {
|
|
139
|
+
this.tell('error', this.say('readOnly'));
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
this.fail(error);
|
|
79
143
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
144
|
+
}
|
|
145
|
+
/* The actions of one kind for the selection, as entries a menu can list and `apply` runs. */
|
|
146
|
+
list(only) {
|
|
147
|
+
if (!this.supported) {
|
|
148
|
+
return Promise.resolve(null);
|
|
84
149
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
150
|
+
return this.request(this.language.editor.getSelection(), AUTOMATIC, {
|
|
151
|
+
only,
|
|
152
|
+
order: only.includes('refactor') ? REFACTOR_GROUPS : ACTION_GROUPS
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async ask(options) {
|
|
156
|
+
if (!this.supported) {
|
|
157
|
+
this.tell('error', this.say('unavailable'));
|
|
158
|
+
return;
|
|
89
159
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
160
|
+
// The press on a card or the gutter may have taken the focus, and the list answers to the editor's keys.
|
|
161
|
+
this.language.editor.focus();
|
|
162
|
+
this.asking = true;
|
|
163
|
+
let entries;
|
|
164
|
+
try {
|
|
165
|
+
entries = options.withLine ? await this.requestWithLine(options.range, INVOKED) : await this.request(options.range, INVOKED, {
|
|
166
|
+
only: options.only,
|
|
167
|
+
about: options.diagnostics,
|
|
168
|
+
order: options.groups
|
|
169
|
+
});
|
|
170
|
+
} finally {
|
|
171
|
+
this.asking = false;
|
|
100
172
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
if (!project.service.supports('textDocument/formatting', uri)) {
|
|
104
|
-
this.tell('error', this.say('noFormatter'));
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
try {
|
|
108
|
-
const edits = await project.service.formatting(uri, { ...editor.getIndentation() });
|
|
109
|
-
if (edits !== null && edits.length > 0 && !editor.applyEdits(edits.map((edit) => ({ range: edit.range, text: edit.newText })))) {
|
|
110
|
-
this.tell('error', this.say('readOnly'));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
this.fail(error);
|
|
115
|
-
}
|
|
173
|
+
if (entries === null) {
|
|
174
|
+
return;
|
|
116
175
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return Promise.resolve(null);
|
|
121
|
-
}
|
|
122
|
-
return this.request(this.language.editor.getSelection(), AUTOMATIC, { only, order: only.includes('refactor') ? REFACTOR_GROUPS : ACTION_GROUPS });
|
|
176
|
+
if (entries.length === 0) {
|
|
177
|
+
this.tell('success', this.say('none'));
|
|
178
|
+
return;
|
|
123
179
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
180
|
+
const byId = new Map(entries.map(entry => [entry.id, entry]));
|
|
181
|
+
const groups = (options.groups ?? ACTION_GROUPS).flatMap(group => {
|
|
182
|
+
const rows = entries.filter(entry => entry.group === group).map(entry => ({
|
|
183
|
+
id: entry.id,
|
|
184
|
+
label: entry.action.title,
|
|
185
|
+
detail: ''
|
|
186
|
+
}));
|
|
187
|
+
return rows.length === 0 ? [] : [{
|
|
188
|
+
title: this.say(`groups.${group}`),
|
|
189
|
+
rows
|
|
190
|
+
}];
|
|
191
|
+
});
|
|
192
|
+
this.language.completion.close();
|
|
193
|
+
this.language.pick.open({
|
|
194
|
+
anchor: options.anchor,
|
|
195
|
+
groups,
|
|
196
|
+
accept: id => {
|
|
197
|
+
const entry = byId.get(id);
|
|
198
|
+
if (entry !== undefined) {
|
|
199
|
+
void this.apply(entry);
|
|
137
200
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
201
|
+
},
|
|
202
|
+
attempt: id => this.refusalOf(byId.get(id)),
|
|
203
|
+
preview: (id, signal) => this.preview(byId.get(id), signal)
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/* Asks the servers; null when they could not answer, with the reason said aloud for a request a person made. */
|
|
207
|
+
async request(range, triggerKind, options = {}) {
|
|
208
|
+
const {
|
|
209
|
+
project,
|
|
210
|
+
uri,
|
|
211
|
+
diagnostics
|
|
212
|
+
} = this.language;
|
|
213
|
+
const {
|
|
214
|
+
only,
|
|
215
|
+
signal,
|
|
216
|
+
order
|
|
217
|
+
} = options;
|
|
218
|
+
try {
|
|
219
|
+
const about = options.about ?? diagnosticsAt(diagnostics.problems.map(problem => problem.diagnostic), range);
|
|
220
|
+
const context = {
|
|
221
|
+
diagnostics: [...about],
|
|
222
|
+
...(only ? {
|
|
223
|
+
only: [...only]
|
|
224
|
+
} : {}),
|
|
225
|
+
triggerKind: triggerKind
|
|
226
|
+
};
|
|
227
|
+
const entries = actionsOf(await project.service.codeActions(uri, range, context, {
|
|
228
|
+
signal
|
|
229
|
+
}), order);
|
|
230
|
+
// A server may answer with more than the kinds asked for.
|
|
231
|
+
return only ? entries.filter(entry => only.some(kind => entry.action.kind === kind || entry.action.kind?.startsWith(`${kind}.`))) : entries;
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (triggerKind === INVOKED) {
|
|
234
|
+
this.fail(error);
|
|
235
|
+
}
|
|
236
|
+
return null;
|
|
166
237
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
};
|
|
179
|
-
const entries = actionsOf(await project.service.codeActions(uri, range, context, { signal }), order);
|
|
180
|
-
// A server may answer with more than the kinds asked for.
|
|
181
|
-
return only ? entries.filter((entry) => only.some((kind) => entry.action.kind === kind || entry.action.kind?.startsWith(`${kind}.`))) : entries;
|
|
182
|
-
}
|
|
183
|
-
catch (error) {
|
|
184
|
-
if (triggerKind === INVOKED) {
|
|
185
|
-
this.fail(error);
|
|
186
|
-
}
|
|
187
|
-
return null;
|
|
188
|
-
}
|
|
238
|
+
}
|
|
239
|
+
/*
|
|
240
|
+
* What the servers offer at the range, and when no problem touches it, the quick fixes of the problems
|
|
241
|
+
* elsewhere on its line too, since the fix of an error is wanted from anywhere on the line it is on.
|
|
242
|
+
*/
|
|
243
|
+
async requestWithLine(range, triggerKind, signal) {
|
|
244
|
+
const entries = await this.request(range, triggerKind, {
|
|
245
|
+
signal
|
|
246
|
+
});
|
|
247
|
+
if (entries === null || range.start.line !== range.end.line) {
|
|
248
|
+
return entries;
|
|
189
249
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
*/
|
|
194
|
-
async requestWithLine(range, triggerKind, signal) {
|
|
195
|
-
const entries = await this.request(range, triggerKind, { signal });
|
|
196
|
-
if (entries === null || range.start.line !== range.end.line) {
|
|
197
|
-
return entries;
|
|
198
|
-
}
|
|
199
|
-
const all = this.language.diagnostics.problems.map((problem) => problem.diagnostic);
|
|
200
|
-
if (diagnosticsAt(all, range).length > 0) {
|
|
201
|
-
return entries;
|
|
202
|
-
}
|
|
203
|
-
const onLine = fixableOnLine(all, range.start.line).slice(0, LINE_PROBLEMS);
|
|
204
|
-
const fixes = await Promise.all(onLine.map((diagnostic) => this.request(diagnostic.range, triggerKind, { only: ['quickfix'], signal, about: [diagnostic] })));
|
|
205
|
-
return mergeEntries([entries, ...fixes.map((list) => list ?? [])]);
|
|
250
|
+
const all = this.language.diagnostics.problems.map(problem => problem.diagnostic);
|
|
251
|
+
if (diagnosticsAt(all, range).length > 0) {
|
|
252
|
+
return entries;
|
|
206
253
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
254
|
+
const onLine = fixableOnLine(all, range.start.line).slice(0, LINE_PROBLEMS);
|
|
255
|
+
const fixes = await Promise.all(onLine.map(diagnostic => this.request(diagnostic.range, triggerKind, {
|
|
256
|
+
only: ['quickfix'],
|
|
257
|
+
signal,
|
|
258
|
+
about: [diagnostic]
|
|
259
|
+
})));
|
|
260
|
+
return mergeEntries([entries, ...fixes.map(list => list ?? [])]);
|
|
261
|
+
}
|
|
262
|
+
async hint(signal) {
|
|
263
|
+
const {
|
|
264
|
+
editor
|
|
265
|
+
} = this.language;
|
|
266
|
+
if (this.asking || !this.supported) {
|
|
267
|
+
editor.setGutterAction(null);
|
|
268
|
+
return;
|
|
219
269
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
225
|
-
const { project, uri } = this.language;
|
|
226
|
-
const { action } = entry;
|
|
227
|
-
let result = action;
|
|
228
|
-
if (action.edit === undefined && project.service.providerOptions(METHOD, uri)?.resolveProvider === true) {
|
|
229
|
-
try {
|
|
230
|
-
result = await project.service.resolveCodeAction(uri, action, { signal });
|
|
231
|
-
}
|
|
232
|
-
catch (error) {
|
|
233
|
-
if (isRefusal(error)) {
|
|
234
|
-
throw new ActionRefused(error.message);
|
|
235
|
-
}
|
|
236
|
-
// An action the server cannot resolve is still tried as it is, unless the text moved under it.
|
|
237
|
-
if (error instanceof StaleResultError) {
|
|
238
|
-
throw error;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
this.resolved.set(entry, result);
|
|
243
|
-
return result;
|
|
270
|
+
const range = editor.getSelection();
|
|
271
|
+
const entries = await this.requestWithLine(range, AUTOMATIC, signal);
|
|
272
|
+
if (signal.aborted || this.asking) {
|
|
273
|
+
return;
|
|
244
274
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (error instanceof ActionRefused) {
|
|
255
|
-
return { removed: [], added: [], note: null, refusal: error.message };
|
|
256
|
-
}
|
|
257
|
-
throw error;
|
|
258
|
-
}
|
|
259
|
-
const { editor, uri } = this.language;
|
|
260
|
-
const found = action.edit === undefined ? null : previewOf(editor.getText(), action.edit, uri);
|
|
261
|
-
if (found === null) {
|
|
262
|
-
return null;
|
|
263
|
-
}
|
|
264
|
-
const notes = [
|
|
265
|
-
...(found.hiddenLines > 0 ? [this.say('moreLines', { count: found.hiddenLines })] : []),
|
|
266
|
-
...(found.otherFiles > 0 ? [this.say('otherFiles', { count: found.otherFiles })] : []),
|
|
267
|
-
...(found.moves > 0 ? [this.say('moves', { count: found.moves })] : []),
|
|
268
|
-
...((found.creates ?? 0) > 0 ? [this.say('creates', { count: found.creates })] : []),
|
|
269
|
-
...((found.deletes ?? 0) > 0 ? [this.say('deletes', { count: found.deletes })] : [])
|
|
270
|
-
];
|
|
271
|
-
return { removed: found.removed, added: found.added, note: notes.length === 0 ? null : notes.join(' · ') };
|
|
275
|
+
editor.setGutterAction(entries?.some(isHint) === true ? {
|
|
276
|
+
line: editor.getCaret().line,
|
|
277
|
+
label: this.say('show')
|
|
278
|
+
} : null);
|
|
279
|
+
}
|
|
280
|
+
async resolve(entry, signal) {
|
|
281
|
+
const known = this.resolved.get(entry);
|
|
282
|
+
if (known !== undefined) {
|
|
283
|
+
return known;
|
|
272
284
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
285
|
+
const {
|
|
286
|
+
project,
|
|
287
|
+
uri
|
|
288
|
+
} = this.language;
|
|
289
|
+
const {
|
|
290
|
+
action
|
|
291
|
+
} = entry;
|
|
292
|
+
let result = action;
|
|
293
|
+
if (action.edit === undefined && project.service.providerOptions(METHOD, uri)?.resolveProvider === true) {
|
|
294
|
+
try {
|
|
295
|
+
result = await project.service.resolveCodeAction(uri, action, {
|
|
296
|
+
signal
|
|
297
|
+
});
|
|
298
|
+
} catch (error) {
|
|
299
|
+
if (isRefusal(error)) {
|
|
300
|
+
throw new ActionRefused(error.message);
|
|
281
301
|
}
|
|
282
|
-
|
|
283
|
-
|
|
302
|
+
// An action the server cannot resolve is still tried as it is, unless the text moved under it.
|
|
303
|
+
if (error instanceof StaleResultError) {
|
|
304
|
+
throw error;
|
|
284
305
|
}
|
|
306
|
+
}
|
|
285
307
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
const result = await project.applyWorkspaceEdit(action.edit);
|
|
293
|
-
if (!result.applied) {
|
|
294
|
-
this.tell('error', this.say('failed', { message: result.failureReason ?? this.say('refused') }));
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
if (action.command?.command === INLINE_RENAME_COMMAND) {
|
|
299
|
-
await this.startRename(action.command);
|
|
300
|
-
}
|
|
301
|
-
else if (action.command !== undefined) {
|
|
302
|
-
await project.service.executeCommand(uri, action.command);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
catch (error) {
|
|
306
|
-
this.fail(error);
|
|
307
|
-
}
|
|
308
|
-
finally {
|
|
309
|
-
this.resolved.delete(entry);
|
|
310
|
-
editor.focus();
|
|
311
|
-
}
|
|
308
|
+
this.resolved.set(entry, result);
|
|
309
|
+
return result;
|
|
310
|
+
}
|
|
311
|
+
async preview(entry, signal) {
|
|
312
|
+
if (entry === undefined) {
|
|
313
|
+
return null;
|
|
312
314
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
315
|
+
let action;
|
|
316
|
+
try {
|
|
317
|
+
action = await this.resolve(entry, signal);
|
|
318
|
+
} catch (error) {
|
|
319
|
+
if (error instanceof ActionRefused) {
|
|
320
|
+
return {
|
|
321
|
+
removed: [],
|
|
322
|
+
added: [],
|
|
323
|
+
note: null,
|
|
324
|
+
refusal: error.message
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
throw error;
|
|
320
328
|
}
|
|
321
|
-
|
|
322
|
-
|
|
329
|
+
const {
|
|
330
|
+
editor,
|
|
331
|
+
uri
|
|
332
|
+
} = this.language;
|
|
333
|
+
const found = action.edit === undefined ? null : previewOf(editor.getText(), action.edit, uri);
|
|
334
|
+
if (found === null) {
|
|
335
|
+
return null;
|
|
323
336
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
337
|
+
const notes = [...(found.hiddenLines > 0 ? [this.say('moreLines', {
|
|
338
|
+
count: found.hiddenLines
|
|
339
|
+
})] : []), ...(found.otherFiles > 0 ? [this.say('otherFiles', {
|
|
340
|
+
count: found.otherFiles
|
|
341
|
+
})] : []), ...(found.moves > 0 ? [this.say('moves', {
|
|
342
|
+
count: found.moves
|
|
343
|
+
})] : []), ...((found.creates ?? 0) > 0 ? [this.say('creates', {
|
|
344
|
+
count: found.creates
|
|
345
|
+
})] : []), ...((found.deletes ?? 0) > 0 ? [this.say('deletes', {
|
|
346
|
+
count: found.deletes
|
|
347
|
+
})] : [])];
|
|
348
|
+
return {
|
|
349
|
+
removed: found.removed,
|
|
350
|
+
added: found.added,
|
|
351
|
+
note: notes.length === 0 ? null : notes.join(' · ')
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
/* Why a server would not make the action, or null when it will, which resolves it for `apply` to use. */
|
|
355
|
+
async refusalOf(entry) {
|
|
356
|
+
if (entry === undefined) {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
await this.resolve(entry);
|
|
361
|
+
return null;
|
|
362
|
+
} catch (error) {
|
|
363
|
+
return error instanceof ActionRefused ? error.message : null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/* Resolves the action, makes its edit as one undo step, and then runs its command. */
|
|
367
|
+
async apply(entry) {
|
|
368
|
+
const {
|
|
369
|
+
editor,
|
|
370
|
+
project,
|
|
371
|
+
uri
|
|
372
|
+
} = this.language;
|
|
373
|
+
try {
|
|
374
|
+
const action = await this.resolve(entry);
|
|
375
|
+
if (action.edit !== undefined) {
|
|
376
|
+
const result = await project.applyWorkspaceEdit(action.edit);
|
|
377
|
+
if (!result.applied) {
|
|
378
|
+
this.tell('error', this.say('failed', {
|
|
379
|
+
message: result.failureReason ?? this.say('refused')
|
|
380
|
+
}));
|
|
381
|
+
return;
|
|
327
382
|
}
|
|
383
|
+
}
|
|
384
|
+
if (action.command?.command === INLINE_RENAME_COMMAND) {
|
|
385
|
+
await this.startRename(action.command);
|
|
386
|
+
} else if (action.command !== undefined) {
|
|
387
|
+
await project.service.executeCommand(uri, action.command);
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
this.fail(error);
|
|
391
|
+
} finally {
|
|
392
|
+
this.resolved.delete(entry);
|
|
393
|
+
editor.focus();
|
|
328
394
|
}
|
|
329
|
-
|
|
330
|
-
|
|
395
|
+
}
|
|
396
|
+
/* The name a refactor wrote is offered for a rename right where it stands, the way the platform does after an extraction. */
|
|
397
|
+
async startRename(command) {
|
|
398
|
+
const target = command.arguments?.[0] ?? null;
|
|
399
|
+
if (target?.position !== undefined && target.textDocument?.uri === this.language.uri) {
|
|
400
|
+
this.language.editor.setCaret(target.position);
|
|
401
|
+
await this.language.rename.start();
|
|
331
402
|
}
|
|
332
|
-
}
|
|
403
|
+
}
|
|
404
|
+
tell(kind, title) {
|
|
405
|
+
this.language.project.host.notify?.({
|
|
406
|
+
id: TOAST_ID,
|
|
407
|
+
kind,
|
|
408
|
+
title
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
fail(error) {
|
|
412
|
+
if (!(error instanceof StaleResultError)) {
|
|
413
|
+
this.tell('error', this.say('failed', {
|
|
414
|
+
message: messageOf(error)
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
say(key, options) {
|
|
419
|
+
return this.language.project.i18n.t(`editor:language.actions.${key}`, options);
|
|
420
|
+
}
|
|
421
|
+
}
|