From a43a9103bc9a8c1bf735d51c952bc3b9352a00c3 Mon Sep 17 00:00:00 2001 From: vsrs Date: Thu, 18 Jun 2020 22:20:13 +0300 Subject: [PATCH] Add custom cargo runners --- editors/code/package.json | 8 ++++++ editors/code/src/commands.ts | 4 +-- editors/code/src/config.ts | 4 +++ editors/code/src/main.ts | 2 +- editors/code/src/run.ts | 55 +++++++++++------------------------- editors/code/src/tasks.ts | 54 ++++++++++++++++++++++++++--------- 6 files changed, 72 insertions(+), 55 deletions(-) diff --git a/editors/code/package.json b/editors/code/package.json index 68484a370b..f542a490a9 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -336,6 +336,14 @@ "default": null, "description": "List of features to activate. Defaults to `rust-analyzer.cargo.features`." }, + "rust-analyzer.cargoRunner": { + "type": [ + "null", + "string" + ], + "default": null, + "description": "Custom cargo runner extension ID." + }, "rust-analyzer.inlayHints.enable": { "type": "boolean", "default": true, diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 48a25495fb..8c9d7802ff 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -394,7 +394,7 @@ export function run(ctx: Ctx): Cmd { item.detail = 'rerun'; prevRunnable = item; - const task = createTask(item.runnable); + const task = await createTask(item.runnable, ctx.config); return await vscode.tasks.executeTask(task); }; } @@ -404,7 +404,7 @@ export function runSingle(ctx: Ctx): Cmd { const editor = ctx.activeRustEditor; if (!editor) return; - const task = createTask(runnable); + const task = await createTask(runnable, ctx.config); task.group = vscode.TaskGroup.Build; task.presentationOptions = { reveal: vscode.TaskRevealKind.Always, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 9591d4fe32..fc95a7de6b 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -110,6 +110,10 @@ export class Config { }; } + get cargoRunner() { + return this.get("cargoRunner"); + } + get debug() { // "/rustc/" used by suggestions only. const { ["/rustc/"]: _, ...sourceFileMap } = this.get>("debug.sourceFileMap"); diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 12b4d05108..5b4f453c86 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -115,7 +115,7 @@ export async function activate(context: vscode.ExtensionContext) { ctx.registerCommand('applyActionGroup', commands.applyActionGroup); ctx.registerCommand('gotoLocation', commands.gotoLocation); - ctx.pushCleanup(activateTaskProvider(workspaceFolder)); + ctx.pushCleanup(activateTaskProvider(workspaceFolder, ctx.config)); activateStatusDisplay(ctx); diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index bb060cfe15..7ecdeeeaf7 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts @@ -1,10 +1,11 @@ import * as vscode from 'vscode'; import * as lc from 'vscode-languageclient'; import * as ra from './lsp_ext'; -import * as toolchain from "./toolchain"; +import * as tasks from './tasks'; import { Ctx } from './ctx'; import { makeDebugConfig } from './debug'; +import { Config } from './config'; const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; @@ -95,52 +96,28 @@ export class RunnableQuickPick implements vscode.QuickPickItem { } } -interface CargoTaskDefinition extends vscode.TaskDefinition { - type: 'cargo'; - label: string; - command: string; - args: string[]; - env?: { [key: string]: string }; -} +export async function createTask(runnable: ra.Runnable, config: Config): Promise { + if (runnable.kind !== "cargo") { + // rust-analyzer supports only one kind, "cargo" + // do not use tasks.TASK_TYPE here, these are completely different meanings. -export function createTask(runnable: ra.Runnable): vscode.Task { - const TASK_SOURCE = 'Rust'; - - let command; - switch (runnable.kind) { - case "cargo": command = toolchain.getPathForExecutable("cargo"); + throw `Unexpected runnable kind: ${runnable.kind}`; } + const args = [...runnable.args.cargoArgs]; // should be a copy! if (runnable.args.executableArgs.length > 0) { args.push('--', ...runnable.args.executableArgs); } - const definition: CargoTaskDefinition = { - type: 'cargo', - label: runnable.label, - command, - args, + const definition: tasks.CargoTaskDefinition = { + type: tasks.TASK_TYPE, + command: args[0], // run, test, etc... + args: args.slice(1), + cwd: runnable.args.workspaceRoot, env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }), }; - const execOption: vscode.ShellExecutionOptions = { - cwd: runnable.args.workspaceRoot || '.', - env: definition.env, - }; - const exec = new vscode.ShellExecution( - definition.command, - definition.args, - execOption, - ); + const cargoTask = await tasks.buildCargoTask(definition, runnable.label, args, config.cargoRunner); + cargoTask.presentationOptions.clear = true; - const f = vscode.workspace.workspaceFolders![0]; - const t = new vscode.Task( - definition, - f, - definition.label, - TASK_SOURCE, - exec, - ['$rustc'], - ); - t.presentationOptions.clear = true; - return t; + return cargoTask; } diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 9748824df3..e2c43fdd41 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts @@ -1,11 +1,14 @@ import * as vscode from 'vscode'; import * as toolchain from "./toolchain"; +import { Config } from './config'; +import { log } from './util'; // 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. -const TASK_TYPE = 'cargo'; +export const TASK_TYPE = 'cargo'; +export const TASK_SOURCE = 'rust'; -interface CargoTaskDefinition extends vscode.TaskDefinition { +export interface CargoTaskDefinition extends vscode.TaskDefinition { command?: string; args?: string[]; cwd?: string; @@ -14,9 +17,11 @@ interface CargoTaskDefinition extends vscode.TaskDefinition { class CargoTaskProvider implements vscode.TaskProvider { private readonly target: vscode.WorkspaceFolder; + private readonly config: Config; - constructor(target: vscode.WorkspaceFolder) { + constructor(target: vscode.WorkspaceFolder, config: Config) { this.target = target; + this.config = config; } provideTasks(): vscode.Task[] { @@ -58,29 +63,52 @@ class CargoTaskProvider implements vscode.TaskProvider { }); } - resolveTask(task: vscode.Task): vscode.Task | undefined { + async resolveTask(task: vscode.Task): Promise { // 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 CargoTaskDefinition; - if (definition.type === 'cargo' && definition.command) { + if (definition.type === TASK_TYPE && definition.command) { const args = [definition.command].concat(definition.args ?? []); - return new vscode.Task( - definition, - task.name, - 'rust', - new vscode.ShellExecution('cargo', args, definition), - ); + return await buildCargoTask(definition, task.name, args, this.config.cargoRunner); } return undefined; } } -export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { - const provider = new CargoTaskProvider(target); +export async function buildCargoTask(definition: CargoTaskDefinition, name: string, args: string[], customRunner?: string): Promise { + if (customRunner) { + const runnerCommand = `${customRunner}.createCargoTask`; + try { + const runnerArgs = { name, args, cwd: definition.cwd, env: definition.env, source: TASK_SOURCE }; + const task = await vscode.commands.executeCommand(runnerCommand, runnerArgs); + + if (task instanceof vscode.Task) { + return task; + } else if (task) { + log.debug("Invalid cargo task", task); + throw `Invalid task!`; + } + // fallback to default processing + + } catch (e) { + throw `Cargo runner '${customRunner}' failed! ${e}`; + } + } + + return new vscode.Task( + definition, + name, + TASK_SOURCE, + new vscode.ShellExecution(toolchain.cargoPath(), args, definition), + ); +} + +export function activateTaskProvider(target: vscode.WorkspaceFolder, config: Config): vscode.Disposable { + const provider = new CargoTaskProvider(target, config); return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); }