about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/tools/rust-analyzer/editors/code/src/client.ts114
1 files changed, 59 insertions, 55 deletions
diff --git a/src/tools/rust-analyzer/editors/code/src/client.ts b/src/tools/rust-analyzer/editors/code/src/client.ts
index cdeea7333a6..073ff2f4703 100644
--- a/src/tools/rust-analyzer/editors/code/src/client.ts
+++ b/src/tools/rust-analyzer/editors/code/src/client.ts
@@ -3,7 +3,7 @@ import * as lc from "vscode-languageclient/node";
 import * as vscode from "vscode";
 import * as ra from "../src/lsp_ext";
 import * as Is from "vscode-languageclient/lib/common/utils/is";
-import { assert, unwrapUndefinable } from "./util";
+import { assert } from "./util";
 import * as diagnostics from "./diagnostics";
 import { WorkspaceEdit } from "vscode";
 import { type Config, prepareVSCodeConfig } from "./config";
@@ -188,11 +188,17 @@ export async function createClient(
                 context: await client.code2ProtocolConverter.asCodeActionContext(context, token),
             };
             const callback = async (
-                values: (lc.Command | lc.CodeAction)[] | null,
+                values: (lc.Command | lc.CodeAction | object)[] | null,
             ): Promise<(vscode.Command | vscode.CodeAction)[] | undefined> => {
                 if (values === null) return undefined;
                 const result: (vscode.CodeAction | vscode.Command)[] = [];
-                const groups = new Map<string, { index: number; items: vscode.CodeAction[] }>();
+                const groups = new Map<
+                    string,
+                    {
+                        primary: vscode.CodeAction;
+                        items: { label: string; arguments: lc.CodeAction }[];
+                    }
+                >();
                 for (const item of values) {
                     // In our case we expect to get code edits only from diagnostics
                     if (lc.CodeAction.is(item)) {
@@ -204,62 +210,55 @@ export async function createClient(
                         result.push(action);
                         continue;
                     }
-                    assert(
-                        isCodeActionWithoutEditsAndCommands(item),
-                        "We don't expect edits or commands here",
-                    );
-                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-                    const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
-                    const action = new vscode.CodeAction(item.title, kind);
-                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-                    const group = (item as any).group;
-                    action.command = {
-                        command: "rust-analyzer.resolveCodeAction",
-                        title: item.title,
-                        arguments: [item],
-                    };
+                    assertIsCodeActionWithoutEditsAndCommands(item);
+                    const kind = client.protocol2CodeConverter.asCodeActionKind(item.kind);
+                    const group = item.group;
 
-                    // Set a dummy edit, so that VS Code doesn't try to resolve this.
-                    action.edit = new WorkspaceEdit();
+                    const mkAction = () => {
+                        const action = new vscode.CodeAction(item.title, kind);
+                        action.command = {
+                            command: "rust-analyzer.resolveCodeAction",
+                            title: item.title,
+                            arguments: [item],
+                        };
+                        // Set a dummy edit, so that VS Code doesn't try to resolve this.
+                        action.edit = new WorkspaceEdit();
+                        return action;
+                    };
 
                     if (group) {
                         let entry = groups.get(group);
                         if (!entry) {
-                            entry = { index: result.length, items: [] };
+                            entry = { primary: mkAction(), items: [] };
                             groups.set(group, entry);
-                            result.push(action);
+                        } else {
+                            entry.items.push({
+                                label: item.title,
+                                arguments: item,
+                            });
                         }
-                        entry.items.push(action);
                     } else {
-                        result.push(action);
+                        result.push(mkAction());
                     }
                 }
-                for (const [group, { index, items }] of groups) {
-                    if (items.length === 1) {
-                        const item = unwrapUndefinable(items[0]);
-                        result[index] = item;
-                    } else {
-                        const action = new vscode.CodeAction(group);
-                        const item = unwrapUndefinable(items[0]);
-                        action.kind = item.kind;
-                        action.command = {
+                for (const [group, { items, primary }] of groups) {
+                    // This group contains more than one item, so rewrite it to be a group action
+                    if (items.length !== 0) {
+                        const args = [
+                            {
+                                label: primary.title,
+                                arguments: primary.command!.arguments![0],
+                            },
+                            ...items,
+                        ];
+                        primary.title = group;
+                        primary.command = {
                             command: "rust-analyzer.applyActionGroup",
                             title: "",
-                            arguments: [
-                                items.map((item) => {
-                                    return {
-                                        label: item.title,
-                                        arguments: item.command!.arguments![0],
-                                    };
-                                }),
-                            ],
+                            arguments: [args],
                         };
-
-                        // Set a dummy edit, so that VS Code doesn't try to resolve this.
-                        action.edit = new WorkspaceEdit();
-
-                        result[index] = action;
                     }
+                    result.push(primary);
                 }
                 return result;
             };
@@ -363,17 +362,22 @@ class OverrideFeatures implements lc.StaticFeature {
     clear(): void {}
 }
 
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-function isCodeActionWithoutEditsAndCommands(value: any): boolean {
-    const candidate: lc.CodeAction = value;
-    return (
+function assertIsCodeActionWithoutEditsAndCommands(
+    // eslint-disable-next-line @typescript-eslint/no-explicit-any
+    candidate: any,
+): asserts candidate is lc.CodeAction & {
+    group?: string;
+} {
+    assert(
         candidate &&
-        Is.string(candidate.title) &&
-        (candidate.diagnostics === void 0 ||
-            Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
-        (candidate.kind === void 0 || Is.string(candidate.kind)) &&
-        candidate.edit === void 0 &&
-        candidate.command === void 0
+            Is.string(candidate.title) &&
+            (candidate.diagnostics === undefined ||
+                Is.typedArray(candidate.diagnostics, lc.Diagnostic.is)) &&
+            (candidate.group === undefined || Is.string(candidate.group)) &&
+            (candidate.kind === undefined || Is.string(candidate.kind)) &&
+            candidate.edit === undefined &&
+            candidate.command === undefined,
+        `Expected a CodeAction without edits or commands, got: ${JSON.stringify(candidate)}`,
     );
 }