@adecore/editor-react 0.38.0 → 0.40.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 +30 -55
- package/dist/CodeAuthorsCard.js +0 -1
- package/dist/CompletionPopup.js +150 -181
- package/dist/DiffView.d.ts +1 -1
- package/dist/DiffView.js +2 -29
- package/dist/PickPopup.js +54 -80
- package/dist/SplitDiffView.js +27 -57
- package/dist/SymbolPicker.js +2 -8
- package/dist/code-actions-model.js +2 -9
- package/dist/code-actions.js +10 -11
- package/dist/code-vision.js +2 -1
- package/dist/completion-model.js +2 -2
- package/dist/completion.js +13 -34
- package/dist/definition-link.d.ts +1 -0
- package/dist/definition-link.js +22 -18
- package/dist/diagnostics-model.d.ts +1 -0
- package/dist/diagnostics-model.js +3 -1
- package/dist/diagnostics.js +1 -2
- package/dist/diff-syntax.d.ts +1 -0
- package/dist/diff-syntax.js +31 -0
- package/dist/doc-comment-model.d.ts +1 -0
- package/dist/doc-comment-model.js +1 -1
- package/dist/folding.js +2 -3
- package/dist/format-on-type.js +6 -10
- package/dist/hover-content.d.ts +0 -2
- package/dist/hover-content.js +1 -9
- package/dist/inlay-hints.d.ts +0 -1
- package/dist/inlay-hints.js +11 -41
- package/dist/navigation.js +4 -14
- package/dist/peek.js +2 -12
- package/dist/popup-focus.d.ts +3 -1
- package/dist/popup-focus.js +33 -0
- package/dist/recent-choices.d.ts +0 -1
- package/dist/recent-choices.js +0 -4
- package/dist/rendered-docs.d.ts +1 -0
- package/dist/rendered-docs.js +15 -28
- package/dist/snippet-field-transforms.d.ts +5 -0
- package/dist/snippet-field-transforms.js +27 -16
- package/dist/snippet-session.js +8 -20
- package/dist/symbol-links.js +3 -10
- package/dist/symbol-picker-model.d.ts +1 -0
- package/dist/symbol-picker-model.js +7 -0
- package/dist/unique.d.ts +10 -0
- package/dist/unique.js +15 -0
- package/package.json +9 -9
- package/src/AnchoredPopup.tsx +2 -0
- package/src/BaselineStatus.tsx +1 -0
- package/src/CallHierarchyPanel.tsx +16 -6
- package/src/ChangeReview.tsx +7 -0
- package/src/CodeAuthorsCard.tsx +2 -5
- package/src/CompletionPopup.tsx +21 -15
- package/src/DiffView.tsx +53 -30
- package/src/EditorContextMenu.tsx +7 -0
- package/src/EditorView.tsx +28 -0
- package/src/FindReplace.tsx +8 -0
- package/src/HoverCard.tsx +8 -0
- package/src/HoverSections.tsx +11 -0
- package/src/LanguagePopups.tsx +1 -0
- package/src/PanelResizeHandle.tsx +8 -0
- package/src/PathText.tsx +1 -0
- package/src/PeekPanel.tsx +13 -0
- package/src/PickPopup.tsx +7 -6
- package/src/RenameCard.tsx +13 -0
- package/src/RenderedDocs.tsx +11 -0
- package/src/SplitDiffView.tsx +91 -56
- package/src/SymbolPicker.tsx +10 -18
- package/src/call-hierarchy-model.ts +10 -0
- package/src/call-hierarchy.ts +82 -0
- package/src/code-actions-model.ts +21 -9
- package/src/code-actions.ts +83 -7
- package/src/code-authors.ts +20 -0
- package/src/code-vision-model.ts +37 -0
- package/src/code-vision.ts +47 -3
- package/src/completion-call.ts +19 -0
- package/src/completion-model.ts +58 -2
- package/src/completion.ts +156 -30
- package/src/conflict-model.ts +4 -0
- package/src/context-menu-rows.ts +1 -0
- package/src/context-menu.ts +7 -0
- package/src/definition-link.ts +42 -18
- package/src/diagnostics-model.ts +19 -1
- package/src/diagnostics.ts +31 -2
- package/src/diff-expansion.ts +9 -0
- package/src/diff-overlays.tsx +8 -0
- package/src/diff-syntax.ts +54 -0
- package/src/diff-wordmarks.ts +8 -0
- package/src/doc-comment-model.ts +51 -1
- package/src/editor-language.ts +11 -0
- package/src/find-query.ts +7 -0
- package/src/folding.ts +10 -3
- package/src/format-on-type.ts +23 -10
- package/src/highlight-layers.ts +3 -0
- package/src/highlights.ts +18 -0
- package/src/history.ts +11 -0
- package/src/holder.ts +3 -0
- package/src/hover-content.ts +34 -17
- package/src/hover.ts +57 -0
- package/src/index.ts +4 -0
- package/src/inlay-hints.ts +86 -47
- package/src/key-priority.ts +2 -0
- package/src/line-edits.ts +8 -0
- package/src/models.ts +6 -0
- package/src/navigation-history.ts +5 -0
- package/src/navigation.ts +28 -13
- package/src/panel-size.ts +7 -0
- package/src/patch-decorations.ts +39 -0
- package/src/patch-layout.ts +47 -0
- package/src/peek-model.ts +21 -0
- package/src/peek.ts +49 -13
- package/src/pick.ts +35 -0
- package/src/popup-focus.ts +11 -1
- package/src/popup-placement.ts +5 -0
- package/src/popups.ts +2 -6
- package/src/project-language.ts +33 -0
- package/src/project-problems.ts +11 -0
- package/src/proposal-model.ts +40 -0
- package/src/recent-choices.ts +3 -5
- package/src/refresher.ts +3 -0
- package/src/rename-model.ts +28 -0
- package/src/rename.ts +56 -0
- package/src/rendered-docs.ts +89 -19
- package/src/review-group.ts +4 -0
- package/src/row-host.ts +12 -0
- package/src/selection-ranges.ts +6 -0
- package/src/semantic-delta.ts +7 -0
- package/src/semantic-model.ts +11 -0
- package/src/semantic-tokens.ts +37 -0
- package/src/shortcut-keys.ts +3 -0
- package/src/signature-model.ts +7 -0
- package/src/signature.ts +27 -0
- package/src/snippet-field-transforms.ts +48 -13
- package/src/snippet-indent.ts +10 -0
- package/src/snippet-range-order.ts +5 -0
- package/src/snippet-session.ts +173 -31
- package/src/snippet-transform-client.ts +14 -0
- package/src/snippet-transform-format.ts +98 -0
- package/src/snippet-transform-protocol.ts +5 -0
- package/src/snippet-transform-queue.ts +53 -0
- package/src/snippet-transform-worker.ts +14 -0
- package/src/snippet-variables.ts +9 -0
- package/src/snippet.ts +107 -0
- package/src/split-alignment.ts +10 -0
- package/src/split-scroll.ts +8 -0
- package/src/symbol-links.ts +9 -12
- package/src/symbol-picker-model.ts +25 -0
- package/src/symbol-picker.ts +9 -0
- package/src/symbols.ts +21 -0
- package/src/testing.ts +7 -0
- package/src/timers.ts +5 -0
- package/src/unique.ts +23 -0
- package/src/use-problem-counts.ts +4 -0
- package/src/view-state.ts +9 -0
- package/src/word-diff.ts +5 -0
- package/src/workspace-edit.ts +84 -0
package/src/code-actions.ts
CHANGED
|
@@ -26,8 +26,10 @@ const PAUSE_MS = 250;
|
|
|
26
26
|
const INVOKED = 1;
|
|
27
27
|
const AUTOMATIC = 2;
|
|
28
28
|
const TOAST_ID = 'language-action';
|
|
29
|
+
|
|
29
30
|
/* How many problems of the line are asked for their fixes besides the caret's own request. */
|
|
30
31
|
const LINE_PROBLEMS = 3;
|
|
32
|
+
|
|
31
33
|
/* The command of the PHP server that asks the client to rename the name a refactor just wrote; the server answers it with nothing. */
|
|
32
34
|
const INLINE_RENAME_COMMAND = 'php.rename';
|
|
33
35
|
|
|
@@ -66,17 +68,21 @@ interface AskOptions {
|
|
|
66
68
|
export class CodeActionsFeature {
|
|
67
69
|
private readonly language: EditorLanguage;
|
|
68
70
|
private readonly refresher: Refresher;
|
|
71
|
+
|
|
69
72
|
/* While a list is being asked for, the lightbulb waits: both are the same request and the newer one would cancel the older. */
|
|
70
73
|
private asking = false;
|
|
74
|
+
|
|
71
75
|
private readonly resolved = new Map<ActionEntry, CodeAction>();
|
|
72
76
|
|
|
73
77
|
constructor(language: EditorLanguage, timers: Timers = realTimers) {
|
|
74
78
|
this.language = language;
|
|
75
79
|
const { editor } = language;
|
|
76
80
|
this.refresher = new Refresher((signal) => this.hint(signal), PAUSE_MS, timers);
|
|
81
|
+
|
|
77
82
|
const providers = language.onProvidersChanged(() => {
|
|
78
83
|
this.refresher.later();
|
|
79
84
|
});
|
|
85
|
+
|
|
80
86
|
const offs = [
|
|
81
87
|
editor.onCaret(() => {
|
|
82
88
|
editor.setGutterAction(null);
|
|
@@ -89,21 +95,27 @@ export class CodeActionsFeature {
|
|
|
89
95
|
language.onKey((event) => {
|
|
90
96
|
if (isShortcut(this.language.shortcuts.codeActions, event)) {
|
|
91
97
|
void this.open();
|
|
98
|
+
|
|
92
99
|
return true;
|
|
93
100
|
}
|
|
101
|
+
|
|
94
102
|
if (isShortcut(this.language.shortcuts.refactorThis, event)) {
|
|
95
103
|
void this.refactorThis();
|
|
104
|
+
|
|
96
105
|
return true;
|
|
97
106
|
}
|
|
107
|
+
|
|
98
108
|
return false;
|
|
99
109
|
}, KEY_PRIORITY.codeActions),
|
|
100
110
|
language.diagnostics.onChange(() => this.refresher.later()),
|
|
101
111
|
providers
|
|
102
112
|
];
|
|
113
|
+
|
|
103
114
|
language.onDispose(() => {
|
|
104
115
|
for (const off of offs) {
|
|
105
116
|
off();
|
|
106
117
|
}
|
|
118
|
+
|
|
107
119
|
this.refresher.dispose();
|
|
108
120
|
editor.setGutterAction(null);
|
|
109
121
|
});
|
|
@@ -116,18 +128,21 @@ export class CodeActionsFeature {
|
|
|
116
128
|
/* The list under the caret, or under the selection's start. */
|
|
117
129
|
open(): Promise<void> {
|
|
118
130
|
const range = this.language.editor.getSelection();
|
|
131
|
+
|
|
119
132
|
return this.ask({ range, anchor: range.start, withLine: true });
|
|
120
133
|
}
|
|
121
134
|
|
|
122
135
|
/* Refactor This: only the refactorings, grouped by what they do, under the caret or the selection. */
|
|
123
136
|
refactorThis(): Promise<void> {
|
|
124
137
|
const range = this.language.editor.getSelection();
|
|
138
|
+
|
|
125
139
|
return this.ask({ range, only: ['refactor'], anchor: range.start, groups: REFACTOR_GROUPS });
|
|
126
140
|
}
|
|
127
141
|
|
|
128
142
|
/* The quick fixes of one problem, under it, as the Quick fix button of its card asks. */
|
|
129
143
|
quickFixFor(problem: Problem): Promise<void> {
|
|
130
144
|
const { range } = problem.diagnostic;
|
|
145
|
+
|
|
131
146
|
return this.ask({ range, only: ['quickfix'], anchor: range.start, diagnostics: [problem.diagnostic] });
|
|
132
147
|
}
|
|
133
148
|
|
|
@@ -136,21 +151,28 @@ export class CodeActionsFeature {
|
|
|
136
151
|
const everything = { start: { line: 0, character: 0 }, end: editor.positionAt(Number.MAX_SAFE_INTEGER) };
|
|
137
152
|
const entries = await this.request(everything, INVOKED, { only: ['source.organizeImports'] });
|
|
138
153
|
const entry = entries?.find((candidate) => candidate.action.kind?.startsWith('source.organizeImports') === true);
|
|
154
|
+
|
|
139
155
|
if (entry === undefined) {
|
|
140
156
|
this.tell('success', this.say(entries === null ? 'unavailable' : 'organized'));
|
|
157
|
+
|
|
141
158
|
return;
|
|
142
159
|
}
|
|
160
|
+
|
|
143
161
|
await this.apply(entry);
|
|
144
162
|
}
|
|
145
163
|
|
|
146
164
|
async formatDocument(): Promise<void> {
|
|
147
165
|
const { editor, project, uri } = this.language;
|
|
166
|
+
|
|
148
167
|
if (!project.service.supports('textDocument/formatting', uri)) {
|
|
149
168
|
this.tell('error', this.say('noFormatter'));
|
|
169
|
+
|
|
150
170
|
return;
|
|
151
171
|
}
|
|
172
|
+
|
|
152
173
|
try {
|
|
153
174
|
const edits = await project.service.formatting(uri, { ...editor.getIndentation() });
|
|
175
|
+
|
|
154
176
|
if (edits !== null && edits.length > 0 && !editor.applyEdits(edits.map((edit) => ({ range: edit.range, text: edit.newText })))) {
|
|
155
177
|
this.tell('error', this.say('readOnly'));
|
|
156
178
|
}
|
|
@@ -164,18 +186,22 @@ export class CodeActionsFeature {
|
|
|
164
186
|
if (!this.supported) {
|
|
165
187
|
return Promise.resolve(null);
|
|
166
188
|
}
|
|
189
|
+
|
|
167
190
|
return this.request(this.language.editor.getSelection(), AUTOMATIC, { only, order: only.includes('refactor') ? REFACTOR_GROUPS : ACTION_GROUPS });
|
|
168
191
|
}
|
|
169
192
|
|
|
170
193
|
private async ask(options: AskOptions): Promise<void> {
|
|
171
194
|
if (!this.supported) {
|
|
172
195
|
this.tell('error', this.say('unavailable'));
|
|
196
|
+
|
|
173
197
|
return;
|
|
174
198
|
}
|
|
199
|
+
|
|
175
200
|
// The press on a card or the gutter may have taken the focus, and the list answers to the editor's keys.
|
|
176
201
|
this.language.editor.focus();
|
|
177
202
|
this.asking = true;
|
|
178
203
|
let entries: ActionEntry[] | null;
|
|
204
|
+
|
|
179
205
|
try {
|
|
180
206
|
entries = options.withLine
|
|
181
207
|
? await this.requestWithLine(options.range, INVOKED)
|
|
@@ -183,24 +209,33 @@ export class CodeActionsFeature {
|
|
|
183
209
|
} finally {
|
|
184
210
|
this.asking = false;
|
|
185
211
|
}
|
|
212
|
+
|
|
186
213
|
if (entries === null) {
|
|
187
214
|
return;
|
|
188
215
|
}
|
|
216
|
+
|
|
189
217
|
if (entries.length === 0) {
|
|
190
218
|
this.tell('success', this.say('none'));
|
|
219
|
+
|
|
191
220
|
return;
|
|
192
221
|
}
|
|
222
|
+
|
|
193
223
|
const byId = new Map(entries.map((entry) => [entry.id, entry]));
|
|
224
|
+
|
|
194
225
|
const groups: PickGroup[] = (options.groups ?? ACTION_GROUPS).flatMap((group): PickGroup[] => {
|
|
195
226
|
const rows = entries.filter((entry) => entry.group === group).map((entry) => ({ id: entry.id, label: entry.action.title, detail: '' }));
|
|
227
|
+
|
|
196
228
|
return rows.length === 0 ? [] : [{ title: this.say(`groups.${group}`), rows }];
|
|
197
229
|
});
|
|
230
|
+
|
|
198
231
|
this.language.completion.close();
|
|
232
|
+
|
|
199
233
|
this.language.pick.open({
|
|
200
234
|
anchor: options.anchor,
|
|
201
235
|
groups,
|
|
202
236
|
accept: (id) => {
|
|
203
237
|
const entry = byId.get(id);
|
|
238
|
+
|
|
204
239
|
if (entry !== undefined) {
|
|
205
240
|
void this.apply(entry);
|
|
206
241
|
}
|
|
@@ -214,6 +249,7 @@ export class CodeActionsFeature {
|
|
|
214
249
|
private async request(range: EditorRange, triggerKind: number, options: RequestOptions = {}): Promise<ActionEntry[] | null> {
|
|
215
250
|
const { project, uri, diagnostics } = this.language;
|
|
216
251
|
const { only, signal, order } = options;
|
|
252
|
+
|
|
217
253
|
try {
|
|
218
254
|
const about =
|
|
219
255
|
options.about ??
|
|
@@ -221,18 +257,22 @@ export class CodeActionsFeature {
|
|
|
221
257
|
diagnostics.problems.map((problem) => problem.diagnostic),
|
|
222
258
|
range
|
|
223
259
|
);
|
|
260
|
+
|
|
224
261
|
const context = {
|
|
225
262
|
diagnostics: [...about],
|
|
226
263
|
...(only ? { only: [...only] } : {}),
|
|
227
264
|
triggerKind: triggerKind as 1 | 2
|
|
228
265
|
};
|
|
266
|
+
|
|
229
267
|
const entries = actionsOf(await project.service.codeActions(uri, range, context, { signal }), order);
|
|
268
|
+
|
|
230
269
|
// A server may answer with more than the kinds asked for.
|
|
231
270
|
return only ? entries.filter((entry) => only.some((kind) => entry.action.kind === kind || entry.action.kind?.startsWith(`${kind}.`))) : entries;
|
|
232
271
|
} catch (error) {
|
|
233
272
|
if (triggerKind === INVOKED) {
|
|
234
273
|
this.fail(error);
|
|
235
274
|
}
|
|
275
|
+
|
|
236
276
|
return null;
|
|
237
277
|
}
|
|
238
278
|
}
|
|
@@ -243,42 +283,56 @@ export class CodeActionsFeature {
|
|
|
243
283
|
*/
|
|
244
284
|
private async requestWithLine(range: EditorRange, triggerKind: number, signal?: AbortSignal): Promise<ActionEntry[] | null> {
|
|
245
285
|
const entries = await this.request(range, triggerKind, { signal });
|
|
286
|
+
|
|
246
287
|
if (entries === null || range.start.line !== range.end.line) {
|
|
247
288
|
return entries;
|
|
248
289
|
}
|
|
290
|
+
|
|
249
291
|
const all = this.language.diagnostics.problems.map((problem) => problem.diagnostic);
|
|
292
|
+
|
|
250
293
|
if (diagnosticsAt(all, range).length > 0) {
|
|
251
294
|
return entries;
|
|
252
295
|
}
|
|
296
|
+
|
|
253
297
|
const onLine = fixableOnLine(all, range.start.line).slice(0, LINE_PROBLEMS);
|
|
298
|
+
|
|
254
299
|
const fixes = await Promise.all(
|
|
255
300
|
onLine.map((diagnostic) => this.request(diagnostic.range, triggerKind, { only: ['quickfix'], signal, about: [diagnostic] }))
|
|
256
301
|
);
|
|
302
|
+
|
|
257
303
|
return mergeEntries([entries, ...fixes.map((list) => list ?? [])]);
|
|
258
304
|
}
|
|
259
305
|
|
|
260
306
|
private async hint(signal: AbortSignal): Promise<void> {
|
|
261
307
|
const { editor } = this.language;
|
|
308
|
+
|
|
262
309
|
if (this.asking || !this.supported) {
|
|
263
310
|
editor.setGutterAction(null);
|
|
311
|
+
|
|
264
312
|
return;
|
|
265
313
|
}
|
|
314
|
+
|
|
266
315
|
const range = editor.getSelection();
|
|
267
316
|
const entries = await this.requestWithLine(range, AUTOMATIC, signal);
|
|
317
|
+
|
|
268
318
|
if (signal.aborted || this.asking) {
|
|
269
319
|
return;
|
|
270
320
|
}
|
|
321
|
+
|
|
271
322
|
editor.setGutterAction(entries?.some(isHint) === true ? { line: editor.getCaret().line, label: this.say('show') } : null);
|
|
272
323
|
}
|
|
273
324
|
|
|
274
325
|
private async resolve(entry: ActionEntry, signal?: AbortSignal): Promise<CodeAction> {
|
|
275
326
|
const known = this.resolved.get(entry);
|
|
327
|
+
|
|
276
328
|
if (known !== undefined) {
|
|
277
329
|
return known;
|
|
278
330
|
}
|
|
331
|
+
|
|
279
332
|
const { project, uri } = this.language;
|
|
280
333
|
const { action } = entry;
|
|
281
334
|
let result = action;
|
|
335
|
+
|
|
282
336
|
if (action.edit === undefined && project.service.providerOptions(METHOD, uri)?.resolveProvider === true) {
|
|
283
337
|
try {
|
|
284
338
|
result = await project.service.resolveCodeAction(uri, action, { signal });
|
|
@@ -286,13 +340,16 @@ export class CodeActionsFeature {
|
|
|
286
340
|
if (isRefusal(error)) {
|
|
287
341
|
throw new ActionRefused((error as Error).message);
|
|
288
342
|
}
|
|
343
|
+
|
|
289
344
|
// An action the server cannot resolve is still tried as it is, unless the text moved under it.
|
|
290
345
|
if (error instanceof StaleResultError) {
|
|
291
346
|
throw error;
|
|
292
347
|
}
|
|
293
348
|
}
|
|
294
349
|
}
|
|
350
|
+
|
|
295
351
|
this.resolved.set(entry, result);
|
|
352
|
+
|
|
296
353
|
return result;
|
|
297
354
|
}
|
|
298
355
|
|
|
@@ -300,27 +357,38 @@ export class CodeActionsFeature {
|
|
|
300
357
|
if (entry === undefined) {
|
|
301
358
|
return null;
|
|
302
359
|
}
|
|
360
|
+
|
|
303
361
|
let action: CodeAction;
|
|
362
|
+
|
|
304
363
|
try {
|
|
305
364
|
action = await this.resolve(entry, signal);
|
|
306
365
|
} catch (error) {
|
|
307
366
|
if (error instanceof ActionRefused) {
|
|
308
367
|
return { removed: [], added: [], note: null, refusal: error.message };
|
|
309
368
|
}
|
|
369
|
+
|
|
310
370
|
throw error;
|
|
311
371
|
}
|
|
372
|
+
|
|
312
373
|
const { editor, uri } = this.language;
|
|
313
374
|
const found = action.edit === undefined ? null : previewOf(editor.getText(), action.edit, uri);
|
|
375
|
+
|
|
314
376
|
if (found === null) {
|
|
315
377
|
return null;
|
|
316
378
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
379
|
+
|
|
380
|
+
const counts = {
|
|
381
|
+
moreLines: found.hiddenLines,
|
|
382
|
+
otherFiles: found.otherFiles,
|
|
383
|
+
moves: found.moves,
|
|
384
|
+
creates: found.creates ?? 0,
|
|
385
|
+
deletes: found.deletes ?? 0
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const notes = Object.entries(counts)
|
|
389
|
+
.filter(([, count]) => count > 0)
|
|
390
|
+
.map(([key, count]) => this.say(key, { count }));
|
|
391
|
+
|
|
324
392
|
return { removed: found.removed, added: found.added, note: notes.length === 0 ? null : notes.join(' · ') };
|
|
325
393
|
}
|
|
326
394
|
|
|
@@ -329,8 +397,10 @@ export class CodeActionsFeature {
|
|
|
329
397
|
if (entry === undefined) {
|
|
330
398
|
return null;
|
|
331
399
|
}
|
|
400
|
+
|
|
332
401
|
try {
|
|
333
402
|
await this.resolve(entry);
|
|
403
|
+
|
|
334
404
|
return null;
|
|
335
405
|
} catch (error) {
|
|
336
406
|
return error instanceof ActionRefused ? error.message : null;
|
|
@@ -340,15 +410,20 @@ export class CodeActionsFeature {
|
|
|
340
410
|
/* Resolves the action, makes its edit as one undo step, and then runs its command. */
|
|
341
411
|
async apply(entry: ActionEntry): Promise<void> {
|
|
342
412
|
const { editor, project, uri } = this.language;
|
|
413
|
+
|
|
343
414
|
try {
|
|
344
415
|
const action = await this.resolve(entry);
|
|
416
|
+
|
|
345
417
|
if (action.edit !== undefined) {
|
|
346
418
|
const result = await project.applyWorkspaceEdit(action.edit);
|
|
419
|
+
|
|
347
420
|
if (!result.applied) {
|
|
348
421
|
this.tell('error', this.say('failed', { message: result.failureReason ?? this.say('refused') }));
|
|
422
|
+
|
|
349
423
|
return;
|
|
350
424
|
}
|
|
351
425
|
}
|
|
426
|
+
|
|
352
427
|
if (action.command?.command === INLINE_RENAME_COMMAND) {
|
|
353
428
|
await this.startRename(action.command);
|
|
354
429
|
} else if (action.command !== undefined) {
|
|
@@ -365,6 +440,7 @@ export class CodeActionsFeature {
|
|
|
365
440
|
/* The name a refactor wrote is offered for a rename right where it stands, the way the platform does after an extraction. */
|
|
366
441
|
private async startRename(command: Command): Promise<void> {
|
|
367
442
|
const target = (command.arguments?.[0] ?? null) as { textDocument?: { uri?: string }; position?: Position } | null;
|
|
443
|
+
|
|
368
444
|
if (target?.position !== undefined && target.textDocument?.uri === this.language.uri) {
|
|
369
445
|
this.language.editor.setCaret(target.position);
|
|
370
446
|
await this.language.rename.start();
|
package/src/code-authors.ts
CHANGED
|
@@ -13,23 +13,29 @@ export const UNCOMMITTED = -1;
|
|
|
13
13
|
*/
|
|
14
14
|
export function mapBlame(blame: GitBlameResult, base: string, current: readonly string[]): Int32Array | null {
|
|
15
15
|
const baseLines = splitLines(base);
|
|
16
|
+
|
|
16
17
|
if (blame.lines.length !== baseLines.length) {
|
|
17
18
|
return null;
|
|
18
19
|
}
|
|
20
|
+
|
|
19
21
|
const mapped = new Int32Array(current.length).fill(UNCOMMITTED);
|
|
20
22
|
let basePosition = 0;
|
|
21
23
|
let position = 0;
|
|
24
|
+
|
|
22
25
|
const carry = (until: number): void => {
|
|
23
26
|
for (; position < until; position++, basePosition++) {
|
|
24
27
|
mapped[position] = blame.lines[basePosition]!;
|
|
25
28
|
}
|
|
26
29
|
};
|
|
30
|
+
|
|
27
31
|
for (const change of diffLines(baseLines, current)) {
|
|
28
32
|
carry(change.otherStart);
|
|
29
33
|
position = change.otherEnd;
|
|
30
34
|
basePosition = change.baseEnd;
|
|
31
35
|
}
|
|
36
|
+
|
|
32
37
|
carry(current.length);
|
|
38
|
+
|
|
33
39
|
return mapped;
|
|
34
40
|
}
|
|
35
41
|
|
|
@@ -65,40 +71,54 @@ export function authorshipOf(commits: readonly GitBlameCommit[], mapped: Int32Ar
|
|
|
65
71
|
let uncommittedLines = 0;
|
|
66
72
|
let modified = false;
|
|
67
73
|
let latest: GitBlameCommit | null = null;
|
|
74
|
+
|
|
68
75
|
for (let line = from; line <= to && line < mapped.length; line++) {
|
|
69
76
|
const index = mapped[line]!;
|
|
77
|
+
|
|
70
78
|
if (index === UNCOMMITTED) {
|
|
71
79
|
modified = true;
|
|
72
80
|
}
|
|
81
|
+
|
|
73
82
|
if (lines[line]!.trim() === '') {
|
|
74
83
|
continue;
|
|
75
84
|
}
|
|
85
|
+
|
|
76
86
|
if (index === UNCOMMITTED) {
|
|
77
87
|
uncommittedLines++;
|
|
88
|
+
|
|
78
89
|
continue;
|
|
79
90
|
}
|
|
91
|
+
|
|
80
92
|
const commit = commits[index]!;
|
|
81
93
|
const name = shortName(commit.author);
|
|
94
|
+
|
|
82
95
|
if (name === '') {
|
|
83
96
|
continue;
|
|
84
97
|
}
|
|
98
|
+
|
|
85
99
|
const author = byName.get(name) ?? { name, email: commit.email, lines: 0 };
|
|
86
100
|
author.lines++;
|
|
87
101
|
byName.set(name, author);
|
|
102
|
+
|
|
88
103
|
if (latest === null || commit.at > latest.at) {
|
|
89
104
|
latest = commit;
|
|
90
105
|
}
|
|
91
106
|
}
|
|
107
|
+
|
|
92
108
|
const authors = [...byName.values()].sort((left, right) => right.lines - left.lines || (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
|
|
109
|
+
|
|
93
110
|
return { authors, uncommittedLines, modified, latest };
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
/* The row's words: the author with most lines, how many others wrote some, and a star when the range is edited since the commit. */
|
|
97
114
|
export function authorsText(authorship: CodeAuthorship, i18n: i18n): string {
|
|
98
115
|
const main = authorship.authors[0];
|
|
116
|
+
|
|
99
117
|
if (main === undefined) {
|
|
100
118
|
return i18n.t('editor:language.codeVision.newCode');
|
|
101
119
|
}
|
|
120
|
+
|
|
102
121
|
const others = authorship.authors.length - 1;
|
|
122
|
+
|
|
103
123
|
return `${main.name}${others > 0 ? ` +${formatNumber(others)}` : ''}${authorship.modified ? ' *' : ''}`;
|
|
104
124
|
}
|
package/src/code-vision-model.ts
CHANGED
|
@@ -7,6 +7,7 @@ export const MAX_DECLARATIONS = 3000;
|
|
|
7
7
|
|
|
8
8
|
/* LSP's SymbolKind: the kinds a row is drawn for and the ones whose children can be declarations too. */
|
|
9
9
|
const CLASS = 5;
|
|
10
|
+
|
|
10
11
|
const METHOD = 6;
|
|
11
12
|
const PROPERTY = 7;
|
|
12
13
|
const FIELD = 8;
|
|
@@ -62,6 +63,7 @@ function isUsageKind(kind: number, parentKind: number | null): boolean {
|
|
|
62
63
|
if (TYPES.has(kind) || CALLABLES.has(kind)) {
|
|
63
64
|
return true;
|
|
64
65
|
}
|
|
66
|
+
|
|
65
67
|
return (kind === PROPERTY || kind === FIELD || kind === CONSTANT) && (parentKind === CLASS || parentKind === STRUCT);
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -80,6 +82,7 @@ function walk(symbols: readonly DocumentSymbol[], path: readonly string[], paren
|
|
|
80
82
|
path,
|
|
81
83
|
parentKind
|
|
82
84
|
});
|
|
85
|
+
|
|
83
86
|
// What a function or a method holds is local to it, and a row for a callback is noise.
|
|
84
87
|
if (CONTAINERS.has(symbol.kind)) {
|
|
85
88
|
walk(symbol.children ?? [], [...path, symbol.name], symbol.kind, out);
|
|
@@ -89,20 +92,27 @@ function walk(symbols: readonly DocumentSymbol[], path: readonly string[], paren
|
|
|
89
92
|
|
|
90
93
|
function candidatesOf(result: DocumentSymbolResult): Candidate[] {
|
|
91
94
|
const out: Candidate[] = [];
|
|
95
|
+
|
|
92
96
|
if (result === null || result.length === 0) {
|
|
93
97
|
return out;
|
|
94
98
|
}
|
|
99
|
+
|
|
95
100
|
if ('range' in result[0]!) {
|
|
96
101
|
walk(result as DocumentSymbol[], [], null, out);
|
|
102
|
+
|
|
97
103
|
return out;
|
|
98
104
|
}
|
|
105
|
+
|
|
99
106
|
const flat = result as SymbolInformation[];
|
|
100
107
|
const containers = new Map(flat.filter((symbol) => CONTAINERS.has(symbol.kind)).map((symbol) => [symbol.name, symbol.kind]));
|
|
108
|
+
|
|
101
109
|
for (const symbol of flat) {
|
|
102
110
|
const container = symbol.containerName ?? '';
|
|
111
|
+
|
|
103
112
|
if (container !== '' && !containers.has(container)) {
|
|
104
113
|
continue;
|
|
105
114
|
}
|
|
115
|
+
|
|
106
116
|
out.push({
|
|
107
117
|
name: symbol.name,
|
|
108
118
|
kind: symbol.kind,
|
|
@@ -113,6 +123,7 @@ function candidatesOf(result: DocumentSymbolResult): Candidate[] {
|
|
|
113
123
|
parentKind: container === '' ? null : (containers.get(container) ?? null)
|
|
114
124
|
});
|
|
115
125
|
}
|
|
126
|
+
|
|
116
127
|
return out;
|
|
117
128
|
}
|
|
118
129
|
|
|
@@ -124,38 +135,51 @@ export function startAfterComments(source: DeclarationSource, from: Position, to
|
|
|
124
135
|
const hash = HASH_COMMENTS.has(source.languageId);
|
|
125
136
|
let line = from.line;
|
|
126
137
|
let character = from.character;
|
|
138
|
+
|
|
127
139
|
while (line <= to.line && line < source.lineCount) {
|
|
128
140
|
const text = source.lineAt(line);
|
|
129
141
|
let at = character;
|
|
142
|
+
|
|
130
143
|
while (at < text.length && (text[at] === ' ' || text[at] === '\t')) {
|
|
131
144
|
at++;
|
|
132
145
|
}
|
|
146
|
+
|
|
133
147
|
if (at >= text.length) {
|
|
134
148
|
line++;
|
|
135
149
|
character = 0;
|
|
150
|
+
|
|
136
151
|
continue;
|
|
137
152
|
}
|
|
153
|
+
|
|
138
154
|
if (text.startsWith('/*', at)) {
|
|
139
155
|
let end = text.indexOf('*/', at + 2);
|
|
140
156
|
let scan = line;
|
|
157
|
+
|
|
141
158
|
while (end === -1 && scan + 1 <= to.line && scan + 1 < source.lineCount) {
|
|
142
159
|
scan++;
|
|
143
160
|
end = source.lineAt(scan).indexOf('*/');
|
|
144
161
|
}
|
|
162
|
+
|
|
145
163
|
if (end === -1) {
|
|
146
164
|
return null;
|
|
147
165
|
}
|
|
166
|
+
|
|
148
167
|
line = scan;
|
|
149
168
|
character = end + 2;
|
|
169
|
+
|
|
150
170
|
continue;
|
|
151
171
|
}
|
|
172
|
+
|
|
152
173
|
if (text.startsWith('//', at) || (hash && text[at] === '#' && text[at + 1] !== '[')) {
|
|
153
174
|
line++;
|
|
154
175
|
character = 0;
|
|
176
|
+
|
|
155
177
|
continue;
|
|
156
178
|
}
|
|
179
|
+
|
|
157
180
|
return line > to.line ? null : { line, character: at };
|
|
158
181
|
}
|
|
182
|
+
|
|
159
183
|
return null;
|
|
160
184
|
}
|
|
161
185
|
|
|
@@ -168,12 +192,14 @@ function lastLineWithCode(source: DeclarationSource, from: number, to: number):
|
|
|
168
192
|
return line;
|
|
169
193
|
}
|
|
170
194
|
}
|
|
195
|
+
|
|
171
196
|
return from;
|
|
172
197
|
}
|
|
173
198
|
|
|
174
199
|
/* Where the name starts when a server gave only a range: its first occurrence on the line the declaration starts on. */
|
|
175
200
|
function nameIn(source: DeclarationSource, name: string, start: Position): Position | null {
|
|
176
201
|
const at = source.lineAt(start.line).indexOf(name, start.character);
|
|
202
|
+
|
|
177
203
|
return at === -1 ? null : { line: start.line, character: at };
|
|
178
204
|
}
|
|
179
205
|
|
|
@@ -185,26 +211,35 @@ function nameIn(source: DeclarationSource, name: string, start: Position): Posit
|
|
|
185
211
|
export function declarationsOf(result: DocumentSymbolResult, source: DeclarationSource): CodeVisionDeclaration[] {
|
|
186
212
|
const declarations: CodeVisionDeclaration[] = [];
|
|
187
213
|
const seen = new Map<string, number>();
|
|
214
|
+
|
|
188
215
|
for (const candidate of candidatesOf(result)) {
|
|
189
216
|
if (isLocalName(candidate.name)) {
|
|
190
217
|
continue;
|
|
191
218
|
}
|
|
219
|
+
|
|
192
220
|
const usages = isUsageKind(candidate.kind, candidate.parentKind) && !candidate.name.startsWith('__');
|
|
193
221
|
const authors = isAuthorKind(candidate.kind);
|
|
222
|
+
|
|
194
223
|
if (!usages && !authors) {
|
|
195
224
|
continue;
|
|
196
225
|
}
|
|
226
|
+
|
|
197
227
|
const start = startAfterComments(source, candidate.start, candidate.end);
|
|
228
|
+
|
|
198
229
|
if (start === null || source.lineAt(start.line).slice(0, start.character).trim() !== '') {
|
|
199
230
|
continue;
|
|
200
231
|
}
|
|
232
|
+
|
|
201
233
|
const position = candidate.namePosition ?? nameIn(source, candidate.name, start);
|
|
234
|
+
|
|
202
235
|
if (position === null) {
|
|
203
236
|
continue;
|
|
204
237
|
}
|
|
238
|
+
|
|
205
239
|
const key = `${candidate.path.join('>')}>${candidate.name}#${candidate.kind}`;
|
|
206
240
|
const index = seen.get(key) ?? 0;
|
|
207
241
|
seen.set(key, index + 1);
|
|
242
|
+
|
|
208
243
|
declarations.push({
|
|
209
244
|
id: `${key}#${index}`,
|
|
210
245
|
name: candidate.name,
|
|
@@ -215,10 +250,12 @@ export function declarationsOf(result: DocumentSymbolResult, source: Declaration
|
|
|
215
250
|
authorFrom: position.line,
|
|
216
251
|
authorTo: lastLineWithCode(source, position.line, candidate.end.line)
|
|
217
252
|
});
|
|
253
|
+
|
|
218
254
|
if (declarations.length > MAX_DECLARATIONS) {
|
|
219
255
|
return [];
|
|
220
256
|
}
|
|
221
257
|
}
|
|
258
|
+
|
|
222
259
|
return declarations;
|
|
223
260
|
}
|
|
224
261
|
|