diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts index 87f7e50b76734..4470689cd8ca2 100644 --- a/src/tools/rust-analyzer/editors/code/src/run.ts +++ b/src/tools/rust-analyzer/editors/code/src/run.ts @@ -2,7 +2,6 @@ import * as vscode from "vscode"; import type * as lc from "vscode-languageclient"; import * as ra from "./lsp_ext"; import * as tasks from "./tasks"; -import * as toolchain from "./toolchain"; import type { CtxInit } from "./ctx"; import { makeDebugConfig } from "./debug"; @@ -112,22 +111,12 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise throw `Unexpected runnable kind: ${runnable.kind}`; } - let program: string; - let args = createArgs(runnable); - if (runnable.args.overrideCargo) { - // Split on spaces to allow overrides like "wrapper cargo". - const cargoParts = runnable.args.overrideCargo.split(" "); - - program = unwrapUndefinable(cargoParts[0]); - args = [...cargoParts.slice(1), ...args]; - } else { - program = await toolchain.cargoPath(); - } + const args = createArgs(runnable); const definition: tasks.CargoTaskDefinition = { type: tasks.TASK_TYPE, - program, - args, + command: unwrapUndefinable(args[0]), // run, test, etc... + args: args.slice(1), cwd: runnable.args.workspaceRoot || ".", env: prepareEnv(runnable, config.runnablesExtraEnv), overrideCargo: runnable.args.overrideCargo, diff --git a/src/tools/rust-analyzer/editors/code/src/tasks.ts b/src/tools/rust-analyzer/editors/code/src/tasks.ts index 00b644d5cc3d9..2b3abc5d65f9d 100644 --- a/src/tools/rust-analyzer/editors/code/src/tasks.ts +++ b/src/tools/rust-analyzer/editors/code/src/tasks.ts @@ -2,17 +2,26 @@ import * as vscode from "vscode"; import * as toolchain from "./toolchain"; import type { Config } from "./config"; import { log } from "./util"; +import { unwrapUndefinable } from "./undefinable"; // 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 TASK_SOURCE = "rust"; export interface CargoTaskDefinition extends vscode.TaskDefinition { - program: string; - args: string[]; + // The cargo command, such as "run" or "check". + command: string; + // Additional arguments passed to the cargo command. + args?: string[]; + // The working directory to run the cargo 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 { @@ -37,14 +46,12 @@ class RustTaskProvider implements vscode.TaskProvider { { command: "run", group: undefined }, ]; - const cargoPath = await toolchain.cargoPath(); - const tasks: vscode.Task[] = []; for (const workspaceTarget of vscode.workspace.workspaceFolders || []) { for (const def of defs) { const vscodeTask = await buildRustTask( workspaceTarget, - { type: TASK_TYPE, program: cargoPath, args: [def.command] }, + { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, this.config.problemMatcher, this.config.cargoRunner, @@ -86,10 +93,28 @@ export async function buildRustTask( customRunner?: string, throwOnError: boolean = false, ): Promise { - let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined; + 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, + exec, + problemMatcher, + ); +} + +async function cargoToExecution( + definition: CargoTaskDefinition, + customRunner: string | undefined, + throwOnError: boolean, +): Promise { if (customRunner) { const runnerCommand = `${customRunner}.buildShellExecution`; + try { const runnerArgs = { kind: TASK_TYPE, @@ -100,7 +125,7 @@ export async function buildRustTask( const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs); if (customExec) { if (customExec instanceof vscode.ShellExecution) { - exec = customExec; + return customExec; } else { log.debug("Invalid cargo ShellExecution", customExec); throw "Invalid cargo ShellExecution."; @@ -113,20 +138,20 @@ export async function buildRustTask( } } - if (!exec) { - exec = new vscode.ProcessExecution(definition.program, definition.args, definition); - } + // Check whether we must use a user-defined substitute for cargo. + // Split on spaces to allow overrides like "wrapper cargo". + const cargoPath = await toolchain.cargoPath(); + const cargoCommand = definition.overrideCargo?.split(" ") ?? [cargoPath]; - 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, - exec, - problemMatcher, - ); + const args = [definition.command].concat(definition.args ?? []); + const fullCommand = [...cargoCommand, ...args]; + + const processName = unwrapUndefinable(fullCommand[0]); + + return new vscode.ProcessExecution(processName, fullCommand.slice(1), { + cwd: definition.cwd, + env: definition.env, + }); } export function activateTaskProvider(config: Config): vscode.Disposable {