about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/editors/code
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2024-06-17 14:06:01 +0200
committerLukas Wirth <lukastw97@gmail.com>2024-06-17 14:06:01 +0200
commitebb32f5d1b65700a4c717c91cd8697b7718472b7 (patch)
tree975facbc177ab5e64b8871a9fe4ec7ba8f00207e /src/tools/rust-analyzer/editors/code
parente0c1b2bf75c742af8a6637e25e9b5148de8c444a (diff)
downloadrust-ebb32f5d1b65700a4c717c91cd8697b7718472b7.tar.gz
rust-ebb32f5d1b65700a4c717c91cd8697b7718472b7.zip
Fix and cleanup VSCode task building
Diffstat (limited to 'src/tools/rust-analyzer/editors/code')
-rw-r--r--src/tools/rust-analyzer/editors/code/src/commands.ts11
-rw-r--r--src/tools/rust-analyzer/editors/code/src/lsp_ext.ts12
-rw-r--r--src/tools/rust-analyzer/editors/code/src/run.ts18
-rw-r--r--src/tools/rust-analyzer/editors/code/src/tasks.ts115
-rw-r--r--src/tools/rust-analyzer/editors/code/src/toolchain.ts1
5 files changed, 85 insertions, 72 deletions
diff --git a/src/tools/rust-analyzer/editors/code/src/commands.ts b/src/tools/rust-analyzer/editors/code/src/commands.ts
index 5cec2c61a5b..5048f820e4c 100644
--- a/src/tools/rust-analyzer/editors/code/src/commands.ts
+++ b/src/tools/rust-analyzer/editors/code/src/commands.ts
@@ -9,7 +9,12 @@ import {
     applySnippetTextEdits,
     type SnippetTextDocumentEdit,
 } from "./snippets";
-import { type RunnableQuickPick, selectRunnable, createTask, createCargoArgs } from "./run";
+import {
+    type RunnableQuickPick,
+    selectRunnable,
+    createTaskFromRunnable,
+    createCargoArgs,
+} from "./run";
 import { AstInspector } from "./ast_inspector";
 import {
     isRustDocument,
@@ -1096,7 +1101,7 @@ export function run(ctx: CtxInit): Cmd {
 
         item.detail = "rerun";
         prevRunnable = item;
-        const task = await createTask(item.runnable, ctx.config);
+        const task = await createTaskFromRunnable(item.runnable, ctx.config);
         return await vscode.tasks.executeTask(task);
     };
 }
@@ -1139,7 +1144,7 @@ export function runSingle(ctx: CtxInit): Cmd {
         const editor = ctx.activeRustEditor;
         if (!editor) return;
 
-        const task = await createTask(runnable, ctx.config);
+        const task = await createTaskFromRunnable(runnable, ctx.config);
         task.group = vscode.TaskGroup.Build;
         task.presentationOptions = {
             reveal: vscode.TaskRevealKind.Always,
diff --git a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
index 2462f06f18b..699052e4d44 100644
--- a/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
+++ b/src/tools/rust-analyzer/editors/code/src/lsp_ext.ts
@@ -223,8 +223,16 @@ export type OpenCargoTomlParams = {
 export type Runnable = {
     label: string;
     location?: lc.LocationLink;
-    kind: "cargo" | "shell";
-    args: CargoRunnableArgs | ShellRunnableArgs;
+} & (RunnableCargo | RunnableShell);
+
+type RunnableCargo = {
+    kind: "cargo";
+    args: CargoRunnableArgs;
+};
+
+type RunnableShell = {
+    kind: "shell";
+    args: ShellRunnableArgs;
 };
 
 export type ShellRunnableArgs = {
diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts
index 52117a442a1..1206137b6f4 100644
--- a/src/tools/rust-analyzer/editors/code/src/run.ts
+++ b/src/tools/rust-analyzer/editors/code/src/run.ts
@@ -110,10 +110,13 @@ export function prepareEnv(
     return env;
 }
 
-export async function createTask(runnable: ra.Runnable, config: Config): Promise<vscode.Task> {
+export async function createTaskFromRunnable(
+    runnable: ra.Runnable,
+    config: Config,
+): Promise<vscode.Task> {
     let definition: tasks.RustTargetDefinition;
     if (runnable.kind === "cargo") {
-        const runnableArgs = runnable.args as ra.CargoRunnableArgs;
+        const runnableArgs = runnable.args;
         let args = createCargoArgs(runnableArgs);
 
         let program: string;
@@ -128,17 +131,16 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
         }
 
         definition = {
-            type: tasks.TASK_TYPE,
+            type: tasks.CARGO_TASK_TYPE,
             command: program,
             args,
             cwd: runnableArgs.workspaceRoot || ".",
             env: prepareEnv(runnable.label, runnableArgs, config.runnablesExtraEnv),
         };
     } else {
-        const runnableArgs = runnable.args as ra.ShellRunnableArgs;
-
+        const runnableArgs = runnable.args;
         definition = {
-            type: "shell",
+            type: tasks.SHELL_TASK_TYPE,
             command: runnableArgs.program,
             args: runnableArgs.args,
             cwd: runnableArgs.cwd,
@@ -148,13 +150,13 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
 
     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
+    const exec = await tasks.targetToExecution(definition, config.cargoRunner, true);
     const task = await tasks.buildRustTask(
         target,
         definition,
         runnable.label,
         config.problemMatcher,
-        config.cargoRunner,
-        true,
+        exec,
     );
 
     task.presentationOptions.clear = true;
diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts
index c14f7a1175c..870b1ffb71c 100644
--- a/src/tools/rust-analyzer/editors/code/src/tasks.ts
+++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts
@@ -1,27 +1,30 @@
 import * as vscode from "vscode";
 import type { Config } from "./config";
 import { log } from "./util";
-import { expectNotUndefined, unwrapUndefinable } from "./undefinable";
+import { unwrapUndefinable } from "./undefinable";
+import * as toolchain from "./toolchain";
 
 // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
 // our configuration should be compatible with it so use the same key.
-export const TASK_TYPE = "cargo";
+export const CARGO_TASK_TYPE = "cargo";
+export const SHELL_TASK_TYPE = "shell";
 
-export const TASK_SOURCE = "rust";
+export const RUST_TASK_SOURCE = "rust";
 
-export interface RustTargetDefinition extends vscode.TaskDefinition {
-    // The cargo command, such as "run" or "check".
+export type RustTargetDefinition = {
+    readonly type: typeof CARGO_TASK_TYPE | typeof SHELL_TASK_TYPE;
+} & vscode.TaskDefinition &
+    RustTarget;
+export type RustTarget = {
+    // The command to run, usually `cargo`.
     command: string;
-    // Additional arguments passed to the cargo command.
+    // Additional arguments passed to the command.
     args?: string[];
-    // The working directory to run the cargo command in.
+    // The working directory to run the command in.
     cwd?: string;
     // The shell environment.
     env?: { [key: string]: string };
-    // Override the cargo executable name, such as
-    // "my_custom_cargo_bin".
-    overrideCargo?: string;
-}
+};
 
 class RustTaskProvider implements vscode.TaskProvider {
     private readonly config: Config;
@@ -31,6 +34,10 @@ class RustTaskProvider implements vscode.TaskProvider {
     }
 
     async provideTasks(): Promise<vscode.Task[]> {
+        if (!vscode.workspace.workspaceFolders) {
+            return [];
+        }
+
         // Detect Rust tasks. Currently we do not do any actual detection
         // of tasks (e.g. aliases in .cargo/config) and just return a fixed
         // set of tasks that always exist. These tasks cannot be removed in
@@ -45,15 +52,23 @@ class RustTaskProvider implements vscode.TaskProvider {
             { command: "run", group: undefined },
         ];
 
+        // FIXME: The server should provide this
+        const cargo = await toolchain.cargoPath();
+
         const tasks: vscode.Task[] = [];
-        for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
+        for (const workspaceTarget of vscode.workspace.workspaceFolders) {
             for (const def of defs) {
+                const definition = {
+                    command: cargo,
+                    args: [def.command],
+                };
+                const exec = await targetToExecution(definition, this.config.cargoRunner);
                 const vscodeTask = await buildRustTask(
                     workspaceTarget,
-                    { type: TASK_TYPE, command: def.command },
+                    { ...definition, type: CARGO_TASK_TYPE },
                     `cargo ${def.command}`,
                     this.config.problemMatcher,
-                    this.config.cargoRunner,
+                    exec,
                 );
                 vscodeTask.group = def.group;
                 tasks.push(vscodeTask);
@@ -67,16 +82,24 @@ class RustTaskProvider implements vscode.TaskProvider {
         // VSCode calls this for every cargo task in the user's tasks.json,
         // we need to inform VSCode how to execute that command by creating
         // a ShellExecution for it.
-
-        const definition = task.definition as RustTargetDefinition;
-
-        if (definition.type === TASK_TYPE) {
+        if (task.definition.type === CARGO_TASK_TYPE) {
+            const taskDefinition = task.definition as RustTargetDefinition;
+            const cargo = await toolchain.cargoPath();
+            const exec = await targetToExecution(
+                {
+                    command: cargo,
+                    args: [taskDefinition.command].concat(taskDefinition.args || []),
+                    cwd: taskDefinition.cwd,
+                    env: taskDefinition.env,
+                },
+                this.config.cargoRunner,
+            );
             return await buildRustTask(
                 task.scope,
-                definition,
+                taskDefinition,
                 task.name,
                 this.config.problemMatcher,
-                this.config.cargoRunner,
+                exec,
             );
         }
 
@@ -89,34 +112,31 @@ export async function buildRustTask(
     definition: RustTargetDefinition,
     name: string,
     problemMatcher: string[],
-    customRunner?: string,
-    throwOnError: boolean = false,
+    exec: vscode.ProcessExecution | vscode.ShellExecution,
 ): Promise<vscode.Task> {
-    const exec = await cargoToExecution(definition, customRunner, throwOnError);
-
     return new vscode.Task(
         definition,
         // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
         // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
         scope ?? vscode.TaskScope.Workspace,
         name,
-        TASK_SOURCE,
+        RUST_TASK_SOURCE,
         exec,
         problemMatcher,
     );
 }
 
-async function cargoToExecution(
-    definition: RustTargetDefinition,
-    customRunner: string | undefined,
-    throwOnError: boolean,
+export async function targetToExecution(
+    definition: RustTarget,
+    customRunner?: string,
+    throwOnError: boolean = false,
 ): Promise<vscode.ProcessExecution | vscode.ShellExecution> {
     if (customRunner) {
         const runnerCommand = `${customRunner}.buildShellExecution`;
 
         try {
             const runnerArgs = {
-                kind: TASK_TYPE,
+                kind: CARGO_TASK_TYPE,
                 args: definition.args,
                 cwd: definition.cwd,
                 env: definition.env,
@@ -136,37 +156,14 @@ async function cargoToExecution(
             // fallback to default processing
         }
     }
-
-    // this is a cargo task; do Cargo-esque processing
-    if (definition.type === TASK_TYPE) {
-        // Check whether we must use a user-defined substitute for cargo.
-        // Split on spaces to allow overrides like "wrapper cargo".
-        const cargoCommand = definition.overrideCargo?.split(" ") ?? [definition.command];
-
-        const definitionArgs = expectNotUndefined(
-            definition.args,
-            "args were not provided via runnables; this is a bug.",
-        );
-        const args = [...cargoCommand.slice(1), ...definitionArgs];
-        const processName = unwrapUndefinable(cargoCommand[0]);
-
-        return new vscode.ProcessExecution(processName, args, {
-            cwd: definition.cwd,
-            env: definition.env,
-        });
-    } else {
-        // we've been handed a process definition by rust-analyzer, trust all its inputs
-        // and make a shell execution.
-        const args = unwrapUndefinable(definition.args);
-
-        return new vscode.ProcessExecution(definition.command, args, {
-            cwd: definition.cwd,
-            env: definition.env,
-        });
-    }
+    const args = unwrapUndefinable(definition.args);
+    return new vscode.ProcessExecution(definition.command, args, {
+        cwd: definition.cwd,
+        env: definition.env,
+    });
 }
 
 export function activateTaskProvider(config: Config): vscode.Disposable {
     const provider = new RustTaskProvider(config);
-    return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
+    return vscode.tasks.registerTaskProvider(CARGO_TASK_TYPE, provider);
 }
diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
index 58e5fc747a1..060b0245d2c 100644
--- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts
+++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
@@ -151,6 +151,7 @@ export async function getRustcId(dir: string): Promise<string> {
 }
 
 /** Mirrors `toolchain::cargo()` implementation */
+// FIXME: The server should provide this
 export function cargoPath(): Promise<string> {
     return getPathForExecutable("cargo");
 }