]> git.lizzy.rs Git - rust.git/blob - editors/code/src/tasks.ts
Drop extensionUri copy
[rust.git] / editors / code / src / tasks.ts
1 import * as vscode from 'vscode';
2 import * as toolchain from "./toolchain";
3 import { Config } from './config';
4 import { log } from './util';
5
6 // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
7 // our configuration should be compatible with it so use the same key.
8 export const TASK_TYPE = 'cargo';
9 export const TASK_SOURCE = 'rust';
10
11 export interface CargoTaskDefinition extends vscode.TaskDefinition {
12     command?: string;
13     args?: string[];
14     cwd?: string;
15     env?: { [key: string]: string };
16     overrideCargo?: string;
17 }
18
19 class CargoTaskProvider implements vscode.TaskProvider {
20     private readonly config: Config;
21
22     constructor(config: Config) {
23         this.config = config;
24     }
25
26     async provideTasks(): Promise<vscode.Task[]> {
27         // Detect Rust tasks. Currently we do not do any actual detection
28         // of tasks (e.g. aliases in .cargo/config) and just return a fixed
29         // set of tasks that always exist. These tasks cannot be removed in
30         // tasks.json - only tweaked.
31
32         const defs = [
33             { command: 'build', group: vscode.TaskGroup.Build },
34             { command: 'check', group: vscode.TaskGroup.Build },
35             { command: 'test', group: vscode.TaskGroup.Test },
36             { command: 'clean', group: vscode.TaskGroup.Clean },
37             { command: 'run', group: undefined },
38         ];
39
40         const tasks: vscode.Task[] = [];
41         for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
42             for (const def of defs) {
43                 const vscodeTask = await buildCargoTask(workspaceTarget, { type: TASK_TYPE, command: def.command }, `cargo ${def.command}`, [def.command], this.config.cargoRunner);
44                 vscodeTask.group = def.group;
45                 tasks.push(vscodeTask);
46             }
47         }
48
49         return tasks;
50     }
51
52     async resolveTask(task: vscode.Task): Promise<vscode.Task | undefined> {
53         // VSCode calls this for every cargo task in the user's tasks.json,
54         // we need to inform VSCode how to execute that command by creating
55         // a ShellExecution for it.
56
57         const definition = task.definition as CargoTaskDefinition;
58
59         if (definition.type === TASK_TYPE && definition.command) {
60             const args = [definition.command].concat(definition.args ?? []);
61             return await buildCargoTask(task.scope, definition, task.name, args, this.config.cargoRunner);
62         }
63
64         return undefined;
65     }
66 }
67
68 export async function buildCargoTask(
69     scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined,
70     definition: CargoTaskDefinition,
71     name: string,
72     args: string[],
73     customRunner?: string,
74     throwOnError: boolean = false
75 ): Promise<vscode.Task> {
76
77     let exec: vscode.ProcessExecution | vscode.ShellExecution | undefined = undefined;
78
79     if (customRunner) {
80         const runnerCommand = `${customRunner}.buildShellExecution`;
81         try {
82             const runnerArgs = { kind: TASK_TYPE, args, cwd: definition.cwd, env: definition.env };
83             const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs);
84             if (customExec) {
85                 if (customExec instanceof vscode.ShellExecution) {
86                     exec = customExec;
87                 } else {
88                     log.debug("Invalid cargo ShellExecution", customExec);
89                     throw "Invalid cargo ShellExecution.";
90                 }
91             }
92             // fallback to default processing
93
94         } catch (e) {
95             if (throwOnError) throw `Cargo runner '${customRunner}' failed! ${e}`;
96             // fallback to default processing
97         }
98     }
99
100     if (!exec) {
101         // Check whether we must use a user-defined substitute for cargo.
102         // Split on spaces to allow overrides like "wrapper cargo".
103         const overrideCargo = definition.overrideCargo ?? definition.overrideCargo;
104         const cargoPath = await toolchain.cargoPath();
105         const cargoCommand = overrideCargo?.split(" ") ?? [cargoPath];
106
107         const fullCommand = [...cargoCommand, ...args];
108
109         exec = new vscode.ProcessExecution(fullCommand[0], fullCommand.slice(1), definition);
110     }
111
112     return new vscode.Task(
113         definition,
114         // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
115         // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
116         scope ?? vscode.TaskScope.Workspace,
117         name,
118         TASK_SOURCE,
119         exec,
120         ['$rustc']
121     );
122 }
123
124 export function activateTaskProvider(config: Config): vscode.Disposable {
125     const provider = new CargoTaskProvider(config);
126     return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
127 }