@adecore/editor-react 0.31.12 → 0.33.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.
Files changed (137) hide show
  1. package/dist/AnchoredPopup.js +73 -19
  2. package/dist/AttributionCard.js +162 -4
  3. package/dist/BaselineStatus.js +62 -8
  4. package/dist/CallHierarchyPanel.js +977 -60
  5. package/dist/ChangeReview.js +76 -25
  6. package/dist/CodeAuthorsCard.js +314 -14
  7. package/dist/CompletionPopup.js +490 -83
  8. package/dist/DiffView.js +548 -181
  9. package/dist/EditorContextMenu.js +352 -34
  10. package/dist/EditorView.js +276 -99
  11. package/dist/FindReplace.js +525 -67
  12. package/dist/HoverCard.js +378 -38
  13. package/dist/HoverSections.js +367 -56
  14. package/dist/LanguagePopups.js +95 -34
  15. package/dist/Markdown.js +41 -6
  16. package/dist/PanelResizeHandle.js +199 -66
  17. package/dist/PathText.js +49 -7
  18. package/dist/PeekPanel.js +675 -72
  19. package/dist/PickPopup.js +304 -34
  20. package/dist/RenameCard.js +679 -85
  21. package/dist/RenderedDocs.js +329 -58
  22. package/dist/SignatureCard.js +91 -7
  23. package/dist/SplitDiffView.js +344 -254
  24. package/dist/SymbolPicker.js +440 -85
  25. package/dist/call-hierarchy-model.js +102 -68
  26. package/dist/call-hierarchy.js +458 -359
  27. package/dist/code-actions-model.js +88 -76
  28. package/dist/code-actions.js +372 -283
  29. package/dist/code-authors.js +64 -55
  30. package/dist/code-vision-model.js +134 -127
  31. package/dist/code-vision.js +224 -206
  32. package/dist/completion-call.js +94 -67
  33. package/dist/completion-model.js +334 -244
  34. package/dist/completion.js +840 -649
  35. package/dist/conflict-model.js +35 -29
  36. package/dist/context-menu-rows.js +42 -17
  37. package/dist/context-menu.js +64 -46
  38. package/dist/definition-link.js +88 -85
  39. package/dist/diagnostics-model.js +74 -55
  40. package/dist/diagnostics.js +121 -100
  41. package/dist/diff-expansion.js +38 -35
  42. package/dist/diff-overlays.js +144 -22
  43. package/dist/diff-syntax.js +65 -42
  44. package/dist/diff-wordmarks.js +26 -23
  45. package/dist/doc-comment-model.js +265 -216
  46. package/dist/editor-language.js +177 -152
  47. package/dist/error-message.js +2 -2
  48. package/dist/find-query.js +49 -36
  49. package/dist/folding.js +81 -62
  50. package/dist/format-on-type.js +86 -67
  51. package/dist/highlight-layers.js +19 -20
  52. package/dist/highlights.js +105 -83
  53. package/dist/history.js +44 -38
  54. package/dist/holder.js +18 -18
  55. package/dist/host-types.js +1 -1
  56. package/dist/hover-content.js +167 -129
  57. package/dist/hover.js +285 -245
  58. package/dist/index.js +10 -10
  59. package/dist/inlay-hints.js +369 -324
  60. package/dist/key-priority.js +34 -31
  61. package/dist/language-ids.js +45 -42
  62. package/dist/line-edits.js +74 -28
  63. package/dist/line-text.js +18 -3
  64. package/dist/locales/en.json +38 -38
  65. package/dist/locales/nl.json +41 -41
  66. package/dist/models.js +1 -1
  67. package/dist/navigation-history.js +40 -41
  68. package/dist/navigation.js +141 -123
  69. package/dist/panel-size.js +8 -8
  70. package/dist/patch-decorations.js +236 -160
  71. package/dist/patch-layout.js +331 -262
  72. package/dist/paths.js +3 -3
  73. package/dist/peek-model.js +86 -69
  74. package/dist/peek.js +254 -192
  75. package/dist/pick.js +164 -157
  76. package/dist/popup-focus.js +5 -5
  77. package/dist/popup-placement.js +36 -21
  78. package/dist/popups.js +13 -13
  79. package/dist/project-language.js +228 -211
  80. package/dist/project-problems.js +81 -69
  81. package/dist/proposal-model.js +163 -126
  82. package/dist/recent-choices.js +25 -25
  83. package/dist/refresher.js +44 -49
  84. package/dist/rename-model.js +113 -113
  85. package/dist/rename.js +321 -233
  86. package/dist/rendered-docs.d.ts +4 -2
  87. package/dist/rendered-docs.js +384 -318
  88. package/dist/rendering-context.js +5 -3
  89. package/dist/rendering.js +22 -4
  90. package/dist/review-group.js +42 -39
  91. package/dist/row-host.d.ts +4 -2
  92. package/dist/row-host.js +90 -78
  93. package/dist/selection-ranges.js +21 -17
  94. package/dist/semantic-delta.js +22 -19
  95. package/dist/semantic-model.js +86 -73
  96. package/dist/semantic-tokens.js +201 -162
  97. package/dist/shortcut-keys.js +16 -10
  98. package/dist/signature-model.js +34 -28
  99. package/dist/signature.js +177 -129
  100. package/dist/snippet-field-transforms.js +118 -106
  101. package/dist/snippet-indent.js +48 -43
  102. package/dist/snippet-range-order.js +11 -11
  103. package/dist/snippet-session.js +703 -667
  104. package/dist/snippet-transform-client.js +59 -59
  105. package/dist/snippet-transform-format.js +285 -263
  106. package/dist/snippet-transform-protocol.js +1 -1
  107. package/dist/snippet-transform-queue.js +163 -153
  108. package/dist/snippet-transform-worker.js +42 -35
  109. package/dist/snippet-variable-names.js +1 -36
  110. package/dist/snippet-variables.js +53 -63
  111. package/dist/snippet.js +477 -405
  112. package/dist/split-alignment.js +47 -35
  113. package/dist/split-scroll.js +42 -40
  114. package/dist/symbol-links.js +33 -25
  115. package/dist/symbol-picker-model.js +126 -116
  116. package/dist/symbol-picker.js +98 -59
  117. package/dist/symbols.js +117 -88
  118. package/dist/testing.js +248 -165
  119. package/dist/timers.js +35 -30
  120. package/dist/use-panel-body.js +49 -8
  121. package/dist/use-problem-counts.js +29 -15
  122. package/dist/view-state.js +54 -33
  123. package/dist/word-diff.js +18 -18
  124. package/dist/workspace-edit.js +300 -249
  125. package/package.json +6 -6
  126. package/src/CallHierarchyPanel.tsx +2 -2
  127. package/src/DiffView.tsx +3 -3
  128. package/src/FindReplace.tsx +2 -2
  129. package/src/LanguagePopups.tsx +2 -0
  130. package/src/PeekPanel.tsx +3 -3
  131. package/src/RenderedDocs.tsx +9 -10
  132. package/src/SymbolPicker.tsx +2 -2
  133. package/src/diff-overlays.tsx +6 -4
  134. package/src/locales/en.json +38 -38
  135. package/src/locales/nl.json +41 -41
  136. package/src/rendered-docs.ts +14 -3
  137. package/src/row-host.ts +14 -3
@@ -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
- return error instanceof LspError && error.code === ErrorCodes.InternalError;
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
- language;
30
- refresher;
31
- /* While a list is being asked for, the lightbulb waits: both are the same request and the newer one would cancel the older. */
32
- asking = false;
33
- resolved = new Map();
34
- constructor(language, timers = realTimers) {
35
- this.language = language;
36
- const { editor } = language;
37
- this.refresher = new Refresher((signal) => this.hint(signal), PAUSE_MS, timers);
38
- const providers = language.onProvidersChanged(() => {
39
- this.refresher.later();
40
- });
41
- const offs = [
42
- 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
- }),
48
- editor.onTextChange(() => editor.setGutterAction(null)),
49
- editor.onGutterAction(() => void this.open()),
50
- language.onKey((event) => {
51
- if (isShortcut(this.language.shortcuts.codeActions, event)) {
52
- void this.open();
53
- return true;
54
- }
55
- if (isShortcut(this.language.shortcuts.refactorThis, event)) {
56
- void this.refactorThis();
57
- return true;
58
- }
59
- return false;
60
- }, KEY_PRIORITY.codeActions),
61
- language.diagnostics.onChange(() => this.refresher.later()),
62
- providers
63
- ];
64
- language.onDispose(() => {
65
- for (const off of offs) {
66
- off();
67
- }
68
- this.refresher.dispose();
69
- editor.setGutterAction(null);
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
- get supported() {
73
- return this.language.project.service.supports(METHOD, this.language.uri);
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
- /* The list under the caret, or under the selection's start. */
76
- open() {
77
- const range = this.language.editor.getSelection();
78
- return this.ask({ range, anchor: range.start, withLine: true });
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
- /* Refactor This: only the refactorings, grouped by what they do, under the caret or the selection. */
81
- refactorThis() {
82
- const range = this.language.editor.getSelection();
83
- return this.ask({ range, only: ['refactor'], anchor: range.start, groups: REFACTOR_GROUPS });
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
- /* The quick fixes of one problem, under it, as the Quick fix button of its card asks. */
86
- quickFixFor(problem) {
87
- const { range } = problem.diagnostic;
88
- return this.ask({ range, only: ['quickfix'], anchor: range.start, diagnostics: [problem.diagnostic] });
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
- async organizeImports() {
91
- const { editor } = this.language;
92
- const everything = { start: { line: 0, character: 0 }, end: editor.positionAt(Number.MAX_SAFE_INTEGER) };
93
- const entries = await this.request(everything, INVOKED, { only: ['source.organizeImports'] });
94
- const entry = entries?.find((candidate) => candidate.action.kind?.startsWith('source.organizeImports') === true);
95
- if (entry === undefined) {
96
- this.tell('success', this.say(entries === null ? 'unavailable' : 'organized'));
97
- return;
98
- }
99
- await this.apply(entry);
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
- async formatDocument() {
102
- const { editor, project, uri } = this.language;
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
- /* The actions of one kind for the selection, as entries a menu can list and `apply` runs. */
118
- list(only) {
119
- if (!this.supported) {
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
- async ask(options) {
125
- if (!this.supported) {
126
- this.tell('error', this.say('unavailable'));
127
- return;
128
- }
129
- // The press on a card or the gutter may have taken the focus, and the list answers to the editor's keys.
130
- this.language.editor.focus();
131
- this.asking = true;
132
- let entries;
133
- try {
134
- entries = options.withLine
135
- ? await this.requestWithLine(options.range, INVOKED)
136
- : await this.request(options.range, INVOKED, { only: options.only, about: options.diagnostics, order: options.groups });
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
- finally {
139
- this.asking = false;
140
- }
141
- if (entries === null) {
142
- return;
143
- }
144
- if (entries.length === 0) {
145
- this.tell('success', this.say('none'));
146
- return;
147
- }
148
- const byId = new Map(entries.map((entry) => [entry.id, entry]));
149
- const groups = (options.groups ?? ACTION_GROUPS).flatMap((group) => {
150
- const rows = entries.filter((entry) => entry.group === group).map((entry) => ({ id: entry.id, label: entry.action.title, detail: '' }));
151
- return rows.length === 0 ? [] : [{ title: this.say(`groups.${group}`), rows }];
152
- });
153
- this.language.completion.close();
154
- this.language.pick.open({
155
- anchor: options.anchor,
156
- groups,
157
- accept: (id) => {
158
- const entry = byId.get(id);
159
- if (entry !== undefined) {
160
- void this.apply(entry);
161
- }
162
- },
163
- attempt: (id) => this.refusalOf(byId.get(id)),
164
- preview: (id, signal) => this.preview(byId.get(id), signal)
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
- /* Asks the servers; null when they could not answer, with the reason said aloud for a request a person made. */
168
- async request(range, triggerKind, options = {}) {
169
- const { project, uri, diagnostics } = this.language;
170
- const { only, signal, order } = options;
171
- try {
172
- const about = options.about ??
173
- diagnosticsAt(diagnostics.problems.map((problem) => problem.diagnostic), range);
174
- const context = {
175
- diagnostics: [...about],
176
- ...(only ? { only: [...only] } : {}),
177
- triggerKind: triggerKind
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
- * What the servers offer at the range, and when no problem touches it, the quick fixes of the problems
192
- * elsewhere on its line too, since the fix of an error is wanted from anywhere on the line it is on.
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
- async hint(signal) {
208
- const { editor } = this.language;
209
- if (this.asking || !this.supported) {
210
- editor.setGutterAction(null);
211
- return;
212
- }
213
- const range = editor.getSelection();
214
- const entries = await this.requestWithLine(range, AUTOMATIC, signal);
215
- if (signal.aborted || this.asking) {
216
- return;
217
- }
218
- editor.setGutterAction(entries?.some(isHint) === true ? { line: editor.getCaret().line, label: this.say('show') } : null);
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
- async resolve(entry, signal) {
221
- const known = this.resolved.get(entry);
222
- if (known !== undefined) {
223
- return known;
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
- async preview(entry, signal) {
246
- if (entry === undefined) {
247
- return null;
248
- }
249
- let action;
250
- try {
251
- action = await this.resolve(entry, signal);
252
- }
253
- catch (error) {
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
- /* Why a server would not make the action, or null when it will, which resolves it for `apply` to use. */
274
- async refusalOf(entry) {
275
- if (entry === undefined) {
276
- return null;
277
- }
278
- try {
279
- await this.resolve(entry);
280
- return null;
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
- catch (error) {
283
- return error instanceof ActionRefused ? error.message : null;
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
- /* Resolves the action, makes its edit as one undo step, and then runs its command. */
287
- async apply(entry) {
288
- const { editor, project, uri } = this.language;
289
- try {
290
- const action = await this.resolve(entry);
291
- if (action.edit !== undefined) {
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
- /* The name a refactor wrote is offered for a rename right where it stands, the way the platform does after an extraction. */
314
- async startRename(command) {
315
- const target = (command.arguments?.[0] ?? null);
316
- if (target?.position !== undefined && target.textDocument?.uri === this.language.uri) {
317
- this.language.editor.setCaret(target.position);
318
- await this.language.rename.start();
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
- tell(kind, title) {
322
- this.language.project.host.notify?.({ id: TOAST_ID, kind, title });
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
- fail(error) {
325
- if (!(error instanceof StaleResultError)) {
326
- this.tell('error', this.say('failed', { message: messageOf(error) }));
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
- say(key, options) {
330
- return this.language.project.i18n.t(`editor:language.actions.${key}`, options);
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
+ }