11import * as vscode from "vscode" ;
22import type { Config } from "./config" ;
33import { log } from "./util" ;
4- import { expectNotUndefined , unwrapUndefinable } from "./undefinable" ;
4+ import { unwrapUndefinable } from "./undefinable" ;
5+ import * as toolchain from "./toolchain" ;
56
67// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
78// our configuration should be compatible with it so use the same key.
8- export const TASK_TYPE = "cargo" ;
9+ export const CARGO_TASK_TYPE = "cargo" ;
10+ export const SHELL_TASK_TYPE = "shell" ;
911
10- export const TASK_SOURCE = "rust" ;
12+ export const RUST_TASK_SOURCE = "rust" ;
1113
12- export interface RustTargetDefinition extends vscode . TaskDefinition {
13- // The cargo command, such as "run" or "check".
14+ export type RustTargetDefinition = {
15+ readonly type : typeof CARGO_TASK_TYPE | typeof SHELL_TASK_TYPE ;
16+ } & vscode . TaskDefinition &
17+ RustTarget ;
18+ export type RustTarget = {
19+ // The command to run, usually `cargo`.
1420 command : string ;
15- // Additional arguments passed to the cargo command.
21+ // Additional arguments passed to the command.
1622 args ?: string [ ] ;
17- // The working directory to run the cargo command in.
23+ // The working directory to run the command in.
1824 cwd ?: string ;
1925 // The shell environment.
2026 env ?: { [ key : string ] : string } ;
21- // Override the cargo executable name, such as
22- // "my_custom_cargo_bin".
23- overrideCargo ?: string ;
24- }
27+ } ;
2528
2629class RustTaskProvider implements vscode . TaskProvider {
2730 private readonly config : Config ;
@@ -31,6 +34,10 @@ class RustTaskProvider implements vscode.TaskProvider {
3134 }
3235
3336 async provideTasks ( ) : Promise < vscode . Task [ ] > {
37+ if ( ! vscode . workspace . workspaceFolders ) {
38+ return [ ] ;
39+ }
40+
3441 // Detect Rust tasks. Currently we do not do any actual detection
3542 // of tasks (e.g. aliases in .cargo/config) and just return a fixed
3643 // set of tasks that always exist. These tasks cannot be removed in
@@ -45,15 +52,23 @@ class RustTaskProvider implements vscode.TaskProvider {
4552 { command : "run" , group : undefined } ,
4653 ] ;
4754
55+ // FIXME: The server should provide this
56+ const cargo = await toolchain . cargoPath ( ) ;
57+
4858 const tasks : vscode . Task [ ] = [ ] ;
49- for ( const workspaceTarget of vscode . workspace . workspaceFolders || [ ] ) {
59+ for ( const workspaceTarget of vscode . workspace . workspaceFolders ) {
5060 for ( const def of defs ) {
61+ const definition = {
62+ command : cargo ,
63+ args : [ def . command ] ,
64+ } ;
65+ const exec = await targetToExecution ( definition , this . config . cargoRunner ) ;
5166 const vscodeTask = await buildRustTask (
5267 workspaceTarget ,
53- { type : TASK_TYPE , command : def . command } ,
68+ { ... definition , type : CARGO_TASK_TYPE } ,
5469 `cargo ${ def . command } ` ,
5570 this . config . problemMatcher ,
56- this . config . cargoRunner ,
71+ exec ,
5772 ) ;
5873 vscodeTask . group = def . group ;
5974 tasks . push ( vscodeTask ) ;
@@ -67,16 +82,24 @@ class RustTaskProvider implements vscode.TaskProvider {
6782 // VSCode calls this for every cargo task in the user's tasks.json,
6883 // we need to inform VSCode how to execute that command by creating
6984 // a ShellExecution for it.
70-
71- const definition = task . definition as RustTargetDefinition ;
72-
73- if ( definition . type === TASK_TYPE ) {
85+ if ( task . definition . type === CARGO_TASK_TYPE ) {
86+ const taskDefinition = task . definition as RustTargetDefinition ;
87+ const cargo = await toolchain . cargoPath ( ) ;
88+ const exec = await targetToExecution (
89+ {
90+ command : cargo ,
91+ args : [ taskDefinition . command ] . concat ( taskDefinition . args || [ ] ) ,
92+ cwd : taskDefinition . cwd ,
93+ env : taskDefinition . env ,
94+ } ,
95+ this . config . cargoRunner ,
96+ ) ;
7497 return await buildRustTask (
7598 task . scope ,
76- definition ,
99+ taskDefinition ,
77100 task . name ,
78101 this . config . problemMatcher ,
79- this . config . cargoRunner ,
102+ exec ,
80103 ) ;
81104 }
82105
@@ -89,34 +112,31 @@ export async function buildRustTask(
89112 definition : RustTargetDefinition ,
90113 name : string ,
91114 problemMatcher : string [ ] ,
92- customRunner ?: string ,
93- throwOnError : boolean = false ,
115+ exec : vscode . ProcessExecution | vscode . ShellExecution ,
94116) : Promise < vscode . Task > {
95- const exec = await cargoToExecution ( definition , customRunner , throwOnError ) ;
96-
97117 return new vscode . Task (
98118 definition ,
99119 // scope can sometimes be undefined. in these situations we default to the workspace taskscope as
100120 // recommended by the official docs: https://code.visualstudio.com/api/extension-guides/task-provider#task-provider)
101121 scope ?? vscode . TaskScope . Workspace ,
102122 name ,
103- TASK_SOURCE ,
123+ RUST_TASK_SOURCE ,
104124 exec ,
105125 problemMatcher ,
106126 ) ;
107127}
108128
109- async function cargoToExecution (
110- definition : RustTargetDefinition ,
111- customRunner : string | undefined ,
112- throwOnError : boolean ,
129+ export async function targetToExecution (
130+ definition : RustTarget ,
131+ customRunner ? : string ,
132+ throwOnError : boolean = false ,
113133) : Promise < vscode . ProcessExecution | vscode . ShellExecution > {
114134 if ( customRunner ) {
115135 const runnerCommand = `${ customRunner } .buildShellExecution` ;
116136
117137 try {
118138 const runnerArgs = {
119- kind : TASK_TYPE ,
139+ kind : CARGO_TASK_TYPE ,
120140 args : definition . args ,
121141 cwd : definition . cwd ,
122142 env : definition . env ,
@@ -136,37 +156,14 @@ async function cargoToExecution(
136156 // fallback to default processing
137157 }
138158 }
139-
140- // this is a cargo task; do Cargo-esque processing
141- if ( definition . type === TASK_TYPE ) {
142- // Check whether we must use a user-defined substitute for cargo.
143- // Split on spaces to allow overrides like "wrapper cargo".
144- const cargoCommand = definition . overrideCargo ?. split ( " " ) ?? [ definition . command ] ;
145-
146- const definitionArgs = expectNotUndefined (
147- definition . args ,
148- "args were not provided via runnables; this is a bug." ,
149- ) ;
150- const args = [ ...cargoCommand . slice ( 1 ) , ...definitionArgs ] ;
151- const processName = unwrapUndefinable ( cargoCommand [ 0 ] ) ;
152-
153- return new vscode . ProcessExecution ( processName , args , {
154- cwd : definition . cwd ,
155- env : definition . env ,
156- } ) ;
157- } else {
158- // we've been handed a process definition by rust-analyzer, trust all its inputs
159- // and make a shell execution.
160- const args = unwrapUndefinable ( definition . args ) ;
161-
162- return new vscode . ProcessExecution ( definition . command , args , {
163- cwd : definition . cwd ,
164- env : definition . env ,
165- } ) ;
166- }
159+ const args = unwrapUndefinable ( definition . args ) ;
160+ return new vscode . ProcessExecution ( definition . command , args , {
161+ cwd : definition . cwd ,
162+ env : definition . env ,
163+ } ) ;
167164}
168165
169166export function activateTaskProvider ( config : Config ) : vscode . Disposable {
170167 const provider = new RustTaskProvider ( config ) ;
171- return vscode . tasks . registerTaskProvider ( TASK_TYPE , provider ) ;
168+ return vscode . tasks . registerTaskProvider ( CARGO_TASK_TYPE , provider ) ;
172169}
0 commit comments