Skip to content

Commit db07723

Browse files
committed
Cleanup provideCodeActions vscode hook
1 parent ad3a2d7 commit db07723

File tree

1 file changed

+59
-55
lines changed

1 file changed

+59
-55
lines changed

editors/code/src/client.ts

Lines changed: 59 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as lc from "vscode-languageclient/node";
33
import * as vscode from "vscode";
44
import * as ra from "../src/lsp_ext";
55
import * as Is from "vscode-languageclient/lib/common/utils/is";
6-
import { assert, unwrapUndefinable } from "./util";
6+
import { assert } from "./util";
77
import * as diagnostics from "./diagnostics";
88
import { WorkspaceEdit } from "vscode";
99
import { type Config, prepareVSCodeConfig } from "./config";
@@ -188,11 +188,17 @@ export async function createClient(
188188
context: await client.code2ProtocolConverter.asCodeActionContext(context, token),
189189
};
190190
const callback = async (
191-
values: (lc.Command | lc.CodeAction)[] | null,
191+
values: (lc.Command | lc.CodeAction | object)[] | null,
192192
): Promise<(vscode.Command | vscode.CodeAction)[] | undefined> => {
193193
if (values === null) return undefined;
194194
const result: (vscode.CodeAction | vscode.Command)[] = [];
195-
const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
195+
const groups = new Map<
196+
string,
197+
{
198+
primary: vscode.CodeAction;
199+
items: { label: string; arguments: lc.CodeAction }[];
200+
}
201+
>();
196202
for (const item of values) {
197203
// In our case we expect to get code edits only from diagnostics
198204
if (lc.CodeAction.is(item)) {
@@ -204,62 +210,55 @@ export async function createClient(
204210
result.push(action);
205211
continue;
206212
}
207-
assert(
208-
isCodeActionWithoutEditsAndCommands(item),
209-
"We don't expect edits or commands here",
210-
);
211-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
212-
const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
213-
const action = new vscode.CodeAction(item.title, kind);
214-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
215-
const group = (item as any).group;
216-
action.command = {
217-
command: "rust-analyzer.resolveCodeAction",
218-
title: item.title,
219-
arguments: [item],
220-
};
213+
assertIsCodeActionWithoutEditsAndCommands(item);
214+
const kind = client.protocol2CodeConverter.asCodeActionKind(item.kind);
215+
const group = item.group;
221216

222-
// Set a dummy edit, so that VS Code doesn't try to resolve this.
223-
action.edit = new WorkspaceEdit();
217+
const mkAction = () => {
218+
const action = new vscode.CodeAction(item.title, kind);
219+
action.command = {
220+
command: "rust-analyzer.resolveCodeAction",
221+
title: item.title,
222+
arguments: [item],
223+
};
224+
// Set a dummy edit, so that VS Code doesn't try to resolve this.
225+
action.edit = new WorkspaceEdit();
226+
return action;
227+
};
224228

225229
if (group) {
226230
let entry = groups.get(group);
227231
if (!entry) {
228-
entry = { index: result.length, items: [] };
232+
entry = { primary: mkAction(), items: [] };
229233
groups.set(group, entry);
230-
result.push(action);
234+
} else {
235+
entry.items.push({
236+
label: item.title,
237+
arguments: item,
238+
});
231239
}
232-
entry.items.push(action);
233240
} else {
234-
result.push(action);
241+
result.push(mkAction());
235242
}
236243
}
237-
for (const [group, { index, items }] of groups) {
238-
if (items.length === 1) {
239-
const item = unwrapUndefinable(items[0]);
240-
result[index] = item;
241-
} else {
242-
const action = new vscode.CodeAction(group);
243-
const item = unwrapUndefinable(items[0]);
244-
action.kind = item.kind;
245-
action.command = {
244+
for (const [group, { items, primary }] of groups) {
245+
// This group contains more than one item, so rewrite it to be a group action
246+
if (items.length !== 0) {
247+
const args = [
248+
{
249+
label: primary.title,
250+
arguments: primary.command!.arguments![0],
251+
},
252+
...items,
253+
];
254+
primary.title = group;
255+
primary.command = {
246256
command: "rust-analyzer.applyActionGroup",
247257
title: "",
248-
arguments: [
249-
items.map((item) => {
250-
return {
251-
label: item.title,
252-
arguments: item.command!.arguments![0],
253-
};
254-
}),
255-
],
258+
arguments: [args],
256259
};
257-
258-
// Set a dummy edit, so that VS Code doesn't try to resolve this.
259-
action.edit = new WorkspaceEdit();
260-
261-
result[index] = action;
262260
}
261+
result.push(primary);
263262
}
264263
return result;
265264
};
@@ -363,17 +362,22 @@ class OverrideFeatures implements lc.StaticFeature {
363362
clear(): void {}
364363
}
365364

366-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
367-
function isCodeActionWithoutEditsAndCommands(value: any): boolean {
368-
const candidate: lc.CodeAction = value;
369-
return (
365+
function assertIsCodeActionWithoutEditsAndCommands(
366+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
367+
candidate: any,
368+
): asserts candidate is lc.CodeAction & {
369+
group?: string;
370+
} {
371+
assert(
370372
candidate &&
371-
Is.string(candidate.title) &&
372-
(candidate.diagnostics === void 0 ||
373-
Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
374-
(candidate.kind === void 0 || Is.string(candidate.kind)) &&
375-
candidate.edit === void 0 &&
376-
candidate.command === void 0
373+
Is.string(candidate.title) &&
374+
(candidate.diagnostics === undefined ||
375+
Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
376+
(candidate.group === undefined || Is.string(candidate.group)) &&
377+
(candidate.kind === undefined || Is.string(candidate.kind)) &&
378+
candidate.edit === undefined &&
379+
candidate.command === undefined,
380+
`Expected a CodeAction without edits or commands, got: ${JSON.stringify(candidate)}`,
377381
);
378382
}
379383

0 commit comments

Comments
 (0)