1
1
import * as vscode from 'vscode' ;
2
2
import * as toolchain from "./toolchain" ;
3
+ import { Config } from './config' ;
4
+ import { log } from './util' ;
3
5
4
6
// This ends up as the `type` key in tasks.json. RLS also uses `cargo` and
5
7
// our configuration should be compatible with it so use the same key.
6
- const TASK_TYPE = 'cargo' ;
8
+ export const TASK_TYPE = 'cargo' ;
9
+ export const TASK_SOURCE = 'rust' ;
7
10
8
- interface CargoTaskDefinition extends vscode . TaskDefinition {
11
+ export interface CargoTaskDefinition extends vscode . TaskDefinition {
9
12
command ?: string ;
10
13
args ?: string [ ] ;
11
14
cwd ?: string ;
@@ -14,73 +17,101 @@ interface CargoTaskDefinition extends vscode.TaskDefinition {
14
17
15
18
class CargoTaskProvider implements vscode . TaskProvider {
16
19
private readonly target : vscode . WorkspaceFolder ;
20
+ private readonly config : Config ;
17
21
18
- constructor ( target : vscode . WorkspaceFolder ) {
22
+ constructor ( target : vscode . WorkspaceFolder , config : Config ) {
19
23
this . target = target ;
24
+ this . config = config ;
20
25
}
21
26
22
- provideTasks ( ) : vscode . Task [ ] {
27
+ async provideTasks ( ) : Promise < vscode . Task [ ] > {
23
28
// Detect Rust tasks. Currently we do not do any actual detection
24
29
// of tasks (e.g. aliases in .cargo/config) and just return a fixed
25
30
// set of tasks that always exist. These tasks cannot be removed in
26
31
// tasks.json - only tweaked.
27
32
28
- const cargoPath = toolchain . cargoPath ( ) ;
29
-
30
- return [
33
+ const defs = [
31
34
{ command : 'build' , group : vscode . TaskGroup . Build } ,
32
35
{ command : 'check' , group : vscode . TaskGroup . Build } ,
33
36
{ command : 'test' , group : vscode . TaskGroup . Test } ,
34
37
{ command : 'clean' , group : vscode . TaskGroup . Clean } ,
35
38
{ command : 'run' , group : undefined } ,
36
- ]
37
- . map ( ( { command, group } ) => {
38
- const vscodeTask = new vscode . Task (
39
- // The contents of this object end up in the tasks.json entries.
40
- {
41
- type : TASK_TYPE ,
42
- command,
43
- } ,
44
- // The scope of the task - workspace or specific folder (global
45
- // is not supported).
46
- this . target ,
47
- // The task name, and task source. These are shown in the UI as
48
- // `${source}: ${name}`, e.g. `rust: cargo build`.
49
- `cargo ${ command } ` ,
50
- 'rust' ,
51
- // What to do when this command is executed.
52
- new vscode . ShellExecution ( cargoPath , [ command ] ) ,
53
- // Problem matchers.
54
- [ '$rustc' ] ,
55
- ) ;
56
- vscodeTask . group = group ;
57
- return vscodeTask ;
58
- } ) ;
39
+ ] ;
40
+
41
+ const tasks : vscode . Task [ ] = [ ] ;
42
+ for ( const def of defs ) {
43
+ const vscodeTask = await buildCargoTask ( this . target , { 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
+ return tasks ;
59
49
}
60
50
61
- resolveTask ( task : vscode . Task ) : vscode . Task | undefined {
51
+ async resolveTask ( task : vscode . Task ) : Promise < vscode . Task | undefined > {
62
52
// VSCode calls this for every cargo task in the user's tasks.json,
63
53
// we need to inform VSCode how to execute that command by creating
64
54
// a ShellExecution for it.
65
55
66
56
const definition = task . definition as CargoTaskDefinition ;
67
57
68
- if ( definition . type === 'cargo' && definition . command ) {
58
+ if ( definition . type === TASK_TYPE && definition . command ) {
69
59
const args = [ definition . command ] . concat ( definition . args ?? [ ] ) ;
70
60
71
- return new vscode . Task (
72
- definition ,
73
- task . name ,
74
- 'rust' ,
75
- new vscode . ShellExecution ( 'cargo' , args , definition ) ,
76
- ) ;
61
+ return await buildCargoTask ( this . target , definition , task . name , args , this . config . cargoRunner ) ;
77
62
}
78
63
79
64
return undefined ;
80
65
}
81
66
}
82
67
83
- export function activateTaskProvider ( target : vscode . WorkspaceFolder ) : vscode . Disposable {
84
- const provider = new CargoTaskProvider ( target ) ;
68
+ export async function buildCargoTask (
69
+ target : vscode . WorkspaceFolder ,
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 . 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
+ exec = new vscode . ShellExecution ( toolchain . cargoPath ( ) , args , definition ) ;
102
+ }
103
+
104
+ return new vscode . Task (
105
+ definition ,
106
+ target ,
107
+ name ,
108
+ TASK_SOURCE ,
109
+ exec ,
110
+ [ '$rustc' ]
111
+ ) ;
112
+ }
113
+
114
+ export function activateTaskProvider ( target : vscode . WorkspaceFolder , config : Config ) : vscode . Disposable {
115
+ const provider = new CargoTaskProvider ( target , config ) ;
85
116
return vscode . tasks . registerTaskProvider ( TASK_TYPE , provider ) ;
86
117
}
0 commit comments