Skip to content

Commit f6e2895

Browse files
committed
Auto merge of rust-lang#16839 - Wilfred:extension_refactor_for_shell, r=Veykril
Refactor extension to support arbitrary shell command runnables Currently, the extension assumes that all runnables invoke cargo. Arguments are sometimes full CLI arguments, and sometimes arguments passed to a cargo subcommand. Refactor the extension so that tasks are just a `program` and a list of strings `args`, and rename `CargoTask` to `RustTask` to make it generic. (This was factored out of rust-lang#16135 and tidied.)
2 parents a71a032 + 4422a90 commit f6e2895

File tree

2 files changed

+40
-38
lines changed

2 files changed

+40
-38
lines changed

editors/code/src/run.ts

+19-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
22
import type * as lc from "vscode-languageclient";
33
import * as ra from "./lsp_ext";
44
import * as tasks from "./tasks";
5+
import * as toolchain from "./toolchain";
56

67
import type { CtxInit } from "./ctx";
78
import { makeDebugConfig } from "./debug";
@@ -111,35 +112,44 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
111112
throw `Unexpected runnable kind: ${runnable.kind}`;
112113
}
113114

114-
const args = createArgs(runnable);
115+
let program: string;
116+
let args = createArgs(runnable);
117+
if (runnable.args.overrideCargo) {
118+
// Split on spaces to allow overrides like "wrapper cargo".
119+
const cargoParts = runnable.args.overrideCargo.split(" ");
115120

116-
const definition: tasks.CargoTaskDefinition = {
121+
program = unwrapUndefinable(cargoParts[0]);
122+
args = [...cargoParts.slice(1), ...args];
123+
} else {
124+
program = await toolchain.cargoPath();
125+
}
126+
127+
const definition: tasks.RustTargetDefinition = {
117128
type: tasks.TASK_TYPE,
118-
command: args[0], // run, test, etc...
119-
args: args.slice(1),
129+
program,
130+
args,
120131
cwd: runnable.args.workspaceRoot || ".",
121132
env: prepareEnv(runnable, config.runnablesExtraEnv),
122133
overrideCargo: runnable.args.overrideCargo,
123134
};
124135

125136
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
126137
const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
127-
const cargoTask = await tasks.buildCargoTask(
138+
const task = await tasks.buildRustTask(
128139
target,
129140
definition,
130141
runnable.label,
131-
args,
132142
config.problemMatcher,
133143
config.cargoRunner,
134144
true,
135145
);
136146

137-
cargoTask.presentationOptions.clear = true;
147+
task.presentationOptions.clear = true;
138148
// Sadly, this doesn't prevent focus stealing if the terminal is currently
139149
// hidden, and will become revealed due to task execution.
140-
cargoTask.presentationOptions.focus = false;
150+
task.presentationOptions.focus = false;
141151

142-
return cargoTask;
152+
return task;
143153
}
144154

145155
export function createArgs(runnable: ra.Runnable): string[] {

editors/code/src/tasks.ts

+21-29
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,20 @@ import * as vscode from "vscode";
22
import * as toolchain from "./toolchain";
33
import type { Config } from "./config";
44
import { log } from "./util";
5-
import { unwrapUndefinable } from "./undefinable";
65

76
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
87
// our configuration should be compatible with it so use the same key.
98
export const TASK_TYPE = "cargo";
109
export const TASK_SOURCE = "rust";
1110

12-
export interface CargoTaskDefinition extends vscode.TaskDefinition {
13-
command?: string;
14-
args?: string[];
11+
export interface RustTargetDefinition extends vscode.TaskDefinition {
12+
program: string;
13+
args: string[];
1514
cwd?: string;
1615
env?: { [key: string]: string };
17-
overrideCargo?: string;
1816
}
1917

20-
class CargoTaskProvider implements vscode.TaskProvider {
18+
class RustTaskProvider implements vscode.TaskProvider {
2119
private readonly config: Config;
2220

2321
constructor(config: Config) {
@@ -39,14 +37,15 @@ class CargoTaskProvider implements vscode.TaskProvider {
3937
{ command: "run", group: undefined },
4038
];
4139

40+
const cargoPath = await toolchain.cargoPath();
41+
4242
const tasks: vscode.Task[] = [];
4343
for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
4444
for (const def of defs) {
45-
const vscodeTask = await buildCargoTask(
45+
const vscodeTask = await buildRustTask(
4646
workspaceTarget,
47-
{ type: TASK_TYPE, command: def.command },
47+
{ type: TASK_TYPE, program: cargoPath, args: [def.command] },
4848
`cargo ${def.command}`,
49-
[def.command],
5049
this.config.problemMatcher,
5150
this.config.cargoRunner,
5251
);
@@ -63,15 +62,13 @@ class CargoTaskProvider implements vscode.TaskProvider {
6362
// we need to inform VSCode how to execute that command by creating
6463
// a ShellExecution for it.
6564

66-
const definition = task.definition as CargoTaskDefinition;
65+
const definition = task.definition as RustTargetDefinition;
6766

68-
if (definition.type === TASK_TYPE && definition.command) {
69-
const args = [definition.command].concat(definition.args ?? []);
70-
return await buildCargoTask(
67+
if (definition.type === TASK_TYPE) {
68+
return await buildRustTask(
7169
task.scope,
7270
definition,
7371
task.name,
74-
args,
7572
this.config.problemMatcher,
7673
this.config.cargoRunner,
7774
);
@@ -81,11 +78,10 @@ class CargoTaskProvider implements vscode.TaskProvider {
8178
}
8279
}
8380

84-
export async function buildCargoTask(
81+
export async function buildRustTask(
8582
scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined,
86-
definition: CargoTaskDefinition,
83+
definition: RustTargetDefinition,
8784
name: string,
88-
args: string[],
8985
problemMatcher: string[],
9086
customRunner?: string,
9187
throwOnError: boolean = false,
@@ -95,7 +91,12 @@ export async function buildCargoTask(
9591
if (customRunner) {
9692
const runnerCommand = `${customRunner}.buildShellExecution`;
9793
try {
98-
const runnerArgs = { kind: TASK_TYPE, args, cwd: definition.cwd, env: definition.env };
94+
const runnerArgs = {
95+
kind: TASK_TYPE,
96+
args: definition.args,
97+
cwd: definition.cwd,
98+
env: definition.env,
99+
};
99100
const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs);
100101
if (customExec) {
101102
if (customExec instanceof vscode.ShellExecution) {
@@ -113,16 +114,7 @@ export async function buildCargoTask(
113114
}
114115

115116
if (!exec) {
116-
// Check whether we must use a user-defined substitute for cargo.
117-
// Split on spaces to allow overrides like "wrapper cargo".
118-
const overrideCargo = definition.overrideCargo ?? definition.overrideCargo;
119-
const cargoPath = await toolchain.cargoPath();
120-
const cargoCommand = overrideCargo?.split(" ") ?? [cargoPath];
121-
122-
const fullCommand = [...cargoCommand, ...args];
123-
124-
const processName = unwrapUndefinable(fullCommand[0]);
125-
exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition);
117+
exec = new vscode.ProcessExecution(definition.program, definition.args, definition);
126118
}
127119

128120
return new vscode.Task(
@@ -138,6 +130,6 @@ export async function buildCargoTask(
138130
}
139131

140132
export function activateTaskProvider(config: Config): vscode.Disposable {
141-
const provider = new CargoTaskProvider(config);
133+
const provider = new RustTaskProvider(config);
142134
return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
143135
}

0 commit comments

Comments
 (0)