From d472fd932b8292479547166ca83b2e2425f57ccc Mon Sep 17 00:00:00 2001
From: Wilfred Hughes <wilfred@meta.com>
Date: Wed, 13 Mar 2024 15:47:34 -0700
Subject: [PATCH 1/3] refactor: Rename CargoTask to RustTask in extension

---
 editors/code/src/run.ts   | 10 +++++-----
 editors/code/src/tasks.ts | 16 ++++++++--------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index fc3f1acce544..64a8a945f7f9 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -113,7 +113,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
 
     const args = createArgs(runnable);
 
-    const definition: tasks.CargoTaskDefinition = {
+    const definition: tasks.RustTargetDefinition = {
         type: tasks.TASK_TYPE,
         command: args[0], // run, test, etc...
         args: args.slice(1),
@@ -124,7 +124,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
 
     // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
     const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate()
-    const cargoTask = await tasks.buildCargoTask(
+    const task = await tasks.buildRustTask(
         target,
         definition,
         runnable.label,
@@ -134,12 +134,12 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
         true,
     );
 
-    cargoTask.presentationOptions.clear = true;
+    task.presentationOptions.clear = true;
     // Sadly, this doesn't prevent focus stealing if the terminal is currently
     // hidden, and will become revealed due to task execution.
-    cargoTask.presentationOptions.focus = false;
+    task.presentationOptions.focus = false;
 
-    return cargoTask;
+    return task;
 }
 
 export function createArgs(runnable: ra.Runnable): string[] {
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 1d5ab82aa04b..7525dd0ff77a 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -9,7 +9,7 @@ import { unwrapUndefinable } from "./undefinable";
 export const TASK_TYPE = "cargo";
 export const TASK_SOURCE = "rust";
 
-export interface CargoTaskDefinition extends vscode.TaskDefinition {
+export interface RustTargetDefinition extends vscode.TaskDefinition {
     command?: string;
     args?: string[];
     cwd?: string;
@@ -17,7 +17,7 @@ export interface CargoTaskDefinition extends vscode.TaskDefinition {
     overrideCargo?: string;
 }
 
-class CargoTaskProvider implements vscode.TaskProvider {
+class RustTaskProvider implements vscode.TaskProvider {
     private readonly config: Config;
 
     constructor(config: Config) {
@@ -42,7 +42,7 @@ class CargoTaskProvider implements vscode.TaskProvider {
         const tasks: vscode.Task[] = [];
         for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
             for (const def of defs) {
-                const vscodeTask = await buildCargoTask(
+                const vscodeTask = await buildRustTask(
                     workspaceTarget,
                     { type: TASK_TYPE, command: def.command },
                     `cargo ${def.command}`,
@@ -63,11 +63,11 @@ class CargoTaskProvider implements vscode.TaskProvider {
         // we need to inform VSCode how to execute that command by creating
         // a ShellExecution for it.
 
-        const definition = task.definition as CargoTaskDefinition;
+        const definition = task.definition as RustTargetDefinition;
 
         if (definition.type === TASK_TYPE && definition.command) {
             const args = [definition.command].concat(definition.args ?? []);
-            return await buildCargoTask(
+            return await buildRustTask(
                 task.scope,
                 definition,
                 task.name,
@@ -81,9 +81,9 @@ class CargoTaskProvider implements vscode.TaskProvider {
     }
 }
 
-export async function buildCargoTask(
+export async function buildRustTask(
     scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined,
-    definition: CargoTaskDefinition,
+    definition: RustTargetDefinition,
     name: string,
     args: string[],
     problemMatcher: string[],
@@ -138,6 +138,6 @@ export async function buildCargoTask(
 }
 
 export function activateTaskProvider(config: Config): vscode.Disposable {
-    const provider = new CargoTaskProvider(config);
+    const provider = new RustTaskProvider(config);
     return vscode.tasks.registerTaskProvider(TASK_TYPE, provider);
 }

From 2e109c7da8fb5b4adc809f30035472512e7ac7cd Mon Sep 17 00:00:00 2001
From: Wilfred Hughes <wilfred@meta.com>
Date: Wed, 13 Mar 2024 16:46:57 -0700
Subject: [PATCH 2/3] refactor: Use a single CLI args array rather than a
 separate subcommand field

---
 editors/code/src/run.ts   |  4 +---
 editors/code/src/tasks.ts | 20 ++++++++++----------
 2 files changed, 11 insertions(+), 13 deletions(-)

diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index 64a8a945f7f9..b6c730a4cd6a 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -115,8 +115,7 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
 
     const definition: tasks.RustTargetDefinition = {
         type: tasks.TASK_TYPE,
-        command: args[0], // run, test, etc...
-        args: args.slice(1),
+        args,
         cwd: runnable.args.workspaceRoot || ".",
         env: prepareEnv(runnable, config.runnablesExtraEnv),
         overrideCargo: runnable.args.overrideCargo,
@@ -128,7 +127,6 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
         target,
         definition,
         runnable.label,
-        args,
         config.problemMatcher,
         config.cargoRunner,
         true,
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 7525dd0ff77a..39684b4165ff 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -10,8 +10,7 @@ export const TASK_TYPE = "cargo";
 export const TASK_SOURCE = "rust";
 
 export interface RustTargetDefinition extends vscode.TaskDefinition {
-    command?: string;
-    args?: string[];
+    args: string[];
     cwd?: string;
     env?: { [key: string]: string };
     overrideCargo?: string;
@@ -44,9 +43,8 @@ class RustTaskProvider implements vscode.TaskProvider {
             for (const def of defs) {
                 const vscodeTask = await buildRustTask(
                     workspaceTarget,
-                    { type: TASK_TYPE, command: def.command },
+                    { type: TASK_TYPE, args: [def.command] },
                     `cargo ${def.command}`,
-                    [def.command],
                     this.config.problemMatcher,
                     this.config.cargoRunner,
                 );
@@ -65,13 +63,11 @@ class RustTaskProvider implements vscode.TaskProvider {
 
         const definition = task.definition as RustTargetDefinition;
 
-        if (definition.type === TASK_TYPE && definition.command) {
-            const args = [definition.command].concat(definition.args ?? []);
+        if (definition.type === TASK_TYPE) {
             return await buildRustTask(
                 task.scope,
                 definition,
                 task.name,
-                args,
                 this.config.problemMatcher,
                 this.config.cargoRunner,
             );
@@ -85,7 +81,6 @@ export async function buildRustTask(
     scope: vscode.WorkspaceFolder | vscode.TaskScope | undefined,
     definition: RustTargetDefinition,
     name: string,
-    args: string[],
     problemMatcher: string[],
     customRunner?: string,
     throwOnError: boolean = false,
@@ -95,7 +90,12 @@ export async function buildRustTask(
     if (customRunner) {
         const runnerCommand = `${customRunner}.buildShellExecution`;
         try {
-            const runnerArgs = { kind: TASK_TYPE, args, cwd: definition.cwd, env: definition.env };
+            const runnerArgs = {
+                kind: TASK_TYPE,
+                args: definition.args,
+                cwd: definition.cwd,
+                env: definition.env,
+            };
             const customExec = await vscode.commands.executeCommand(runnerCommand, runnerArgs);
             if (customExec) {
                 if (customExec instanceof vscode.ShellExecution) {
@@ -119,7 +119,7 @@ export async function buildRustTask(
         const cargoPath = await toolchain.cargoPath();
         const cargoCommand = overrideCargo?.split(" ") ?? [cargoPath];
 
-        const fullCommand = [...cargoCommand, ...args];
+        const fullCommand = [...cargoCommand, ...definition.args];
 
         const processName = unwrapUndefinable(fullCommand[0]);
         exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition);

From 4422a90b1106065146d0f3f6d0aa86c212753c10 Mon Sep 17 00:00:00 2001
From: Wilfred Hughes <wilfred@meta.com>
Date: Wed, 13 Mar 2024 17:13:26 -0700
Subject: [PATCH 3/3] refactor: Store the CLI command directly in
 RustTargetDefinition

---
 editors/code/src/run.ts   | 14 +++++++++++++-
 editors/code/src/tasks.ts | 18 +++++-------------
 2 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts
index b6c730a4cd6a..02ccbb6956a2 100644
--- a/editors/code/src/run.ts
+++ b/editors/code/src/run.ts
@@ -2,6 +2,7 @@ import * as vscode from "vscode";
 import type * as lc from "vscode-languageclient";
 import * as ra from "./lsp_ext";
 import * as tasks from "./tasks";
+import * as toolchain from "./toolchain";
 
 import type { CtxInit } from "./ctx";
 import { makeDebugConfig } from "./debug";
@@ -111,10 +112,21 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise
         throw `Unexpected runnable kind: ${runnable.kind}`;
     }
 
-    const args = createArgs(runnable);
+    let program: string;
+    let args = createArgs(runnable);
+    if (runnable.args.overrideCargo) {
+        // Split on spaces to allow overrides like "wrapper cargo".
+        const cargoParts = runnable.args.overrideCargo.split(" ");
+
+        program = unwrapUndefinable(cargoParts[0]);
+        args = [...cargoParts.slice(1), ...args];
+    } else {
+        program = await toolchain.cargoPath();
+    }
 
     const definition: tasks.RustTargetDefinition = {
         type: tasks.TASK_TYPE,
+        program,
         args,
         cwd: runnable.args.workspaceRoot || ".",
         env: prepareEnv(runnable, config.runnablesExtraEnv),
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts
index 39684b4165ff..89abb37b0eb4 100644
--- a/editors/code/src/tasks.ts
+++ b/editors/code/src/tasks.ts
@@ -2,7 +2,6 @@ import * as vscode from "vscode";
 import * as toolchain from "./toolchain";
 import type { Config } from "./config";
 import { log } from "./util";
-import { unwrapUndefinable } from "./undefinable";
 
 // 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.
@@ -10,10 +9,10 @@ export const TASK_TYPE = "cargo";
 export const TASK_SOURCE = "rust";
 
 export interface RustTargetDefinition extends vscode.TaskDefinition {
+    program: string;
     args: string[];
     cwd?: string;
     env?: { [key: string]: string };
-    overrideCargo?: string;
 }
 
 class RustTaskProvider implements vscode.TaskProvider {
@@ -38,12 +37,14 @@ class RustTaskProvider implements vscode.TaskProvider {
             { command: "run", group: undefined },
         ];
 
+        const cargoPath = await toolchain.cargoPath();
+
         const tasks: vscode.Task[] = [];
         for (const workspaceTarget of vscode.workspace.workspaceFolders || []) {
             for (const def of defs) {
                 const vscodeTask = await buildRustTask(
                     workspaceTarget,
-                    { type: TASK_TYPE, args: [def.command] },
+                    { type: TASK_TYPE, program: cargoPath, args: [def.command] },
                     `cargo ${def.command}`,
                     this.config.problemMatcher,
                     this.config.cargoRunner,
@@ -113,16 +114,7 @@ export async function buildRustTask(
     }
 
     if (!exec) {
-        // Check whether we must use a user-defined substitute for cargo.
-        // Split on spaces to allow overrides like "wrapper cargo".
-        const overrideCargo = definition.overrideCargo ?? definition.overrideCargo;
-        const cargoPath = await toolchain.cargoPath();
-        const cargoCommand = overrideCargo?.split(" ") ?? [cargoPath];
-
-        const fullCommand = [...cargoCommand, ...definition.args];
-
-        const processName = unwrapUndefinable(fullCommand[0]);
-        exec = new vscode.ProcessExecution(processName, fullCommand.slice(1), definition);
+        exec = new vscode.ProcessExecution(definition.program, definition.args, definition);
     }
 
     return new vscode.Task(