diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 11 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/main.rs | 14 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 2 | ||||
-rw-r--r-- | editors/code/src/debug.ts | 2 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 5 | ||||
-rw-r--r-- | editors/code/src/run.ts | 3 | ||||
-rw-r--r-- | editors/code/src/tasks.ts | 4 | ||||
-rw-r--r-- | editors/code/src/toolchain.ts (renamed from editors/code/src/cargo.ts) | 117 | ||||
-rw-r--r-- | editors/code/src/util.ts | 18 | ||||
-rw-r--r-- | editors/code/tests/unit/launch_config.test.ts | 14 |
13 files changed, 123 insertions, 83 deletions
diff --git a/Cargo.lock b/Cargo.lock index e1da335f5..07f18c760 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1365,7 +1365,6 @@ dependencies = [ | |||
1365 | "ra_syntax", | 1365 | "ra_syntax", |
1366 | "ra_text_edit", | 1366 | "ra_text_edit", |
1367 | "ra_tt", | 1367 | "ra_tt", |
1368 | "ra_toolchain", | ||
1369 | "ra_vfs", | 1368 | "ra_vfs", |
1370 | "rand", | 1369 | "rand", |
1371 | "relative-path", | 1370 | "relative-path", |
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 2e49448cc..65b487db3 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml | |||
@@ -48,7 +48,6 @@ hir = { path = "../ra_hir", package = "ra_hir" } | |||
48 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } | 48 | hir_def = { path = "../ra_hir_def", package = "ra_hir_def" } |
49 | hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } | 49 | hir_ty = { path = "../ra_hir_ty", package = "ra_hir_ty" } |
50 | ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } | 50 | ra_proc_macro_srv = { path = "../ra_proc_macro_srv" } |
51 | ra_toolchain = { path = "../ra_toolchain" } | ||
52 | 51 | ||
53 | [target.'cfg(windows)'.dependencies] | 52 | [target.'cfg(windows)'.dependencies] |
54 | winapi = "0.3.8" | 53 | winapi = "0.3.8" |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index acb1dacb6..173c23b9e 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -121,12 +121,21 @@ pub struct RunnablesParams { | |||
121 | pub position: Option<Position>, | 121 | pub position: Option<Position>, |
122 | } | 122 | } |
123 | 123 | ||
124 | // Must strictly correspond to the executable name | ||
125 | #[derive(Serialize, Deserialize, Debug)] | ||
126 | #[serde(rename_all = "lowercase")] | ||
127 | pub enum RunnableKind { | ||
128 | Cargo, | ||
129 | Rustc, | ||
130 | Rustup, | ||
131 | } | ||
132 | |||
124 | #[derive(Deserialize, Serialize, Debug)] | 133 | #[derive(Deserialize, Serialize, Debug)] |
125 | #[serde(rename_all = "camelCase")] | 134 | #[serde(rename_all = "camelCase")] |
126 | pub struct Runnable { | 135 | pub struct Runnable { |
127 | pub range: Range, | 136 | pub range: Range, |
128 | pub label: String, | 137 | pub label: String, |
129 | pub bin: String, | 138 | pub kind: RunnableKind, |
130 | pub args: Vec<String>, | 139 | pub args: Vec<String>, |
131 | pub extra_args: Vec<String>, | 140 | pub extra_args: Vec<String>, |
132 | pub env: FxHashMap<String, String>, | 141 | pub env: FxHashMap<String, String>, |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index d42cfa300..bc7c7f1ef 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -40,7 +40,6 @@ use crate::{ | |||
40 | world::WorldSnapshot, | 40 | world::WorldSnapshot, |
41 | LspError, Result, | 41 | LspError, Result, |
42 | }; | 42 | }; |
43 | use anyhow::Context; | ||
44 | 43 | ||
45 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { | 44 | pub fn handle_analyzer_status(world: WorldSnapshot, _: ()) -> Result<String> { |
46 | let _p = profile("handle_analyzer_status"); | 45 | let _p = profile("handle_analyzer_status"); |
@@ -427,7 +426,7 @@ pub fn handle_runnables( | |||
427 | res.push(lsp_ext::Runnable { | 426 | res.push(lsp_ext::Runnable { |
428 | range: Default::default(), | 427 | range: Default::default(), |
429 | label: format!("cargo {} -p {}", cmd, spec.package), | 428 | label: format!("cargo {} -p {}", cmd, spec.package), |
430 | bin: cargo_path()?, | 429 | kind: lsp_ext::RunnableKind::Cargo, |
431 | args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], | 430 | args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], |
432 | extra_args: Vec::new(), | 431 | extra_args: Vec::new(), |
433 | env: FxHashMap::default(), | 432 | env: FxHashMap::default(), |
@@ -439,7 +438,7 @@ pub fn handle_runnables( | |||
439 | res.push(lsp_ext::Runnable { | 438 | res.push(lsp_ext::Runnable { |
440 | range: Default::default(), | 439 | range: Default::default(), |
441 | label: "cargo check --workspace".to_string(), | 440 | label: "cargo check --workspace".to_string(), |
442 | bin: cargo_path()?, | 441 | kind: lsp_ext::RunnableKind::Cargo, |
443 | args: vec!["check".to_string(), "--workspace".to_string()], | 442 | args: vec!["check".to_string(), "--workspace".to_string()], |
444 | extra_args: Vec::new(), | 443 | extra_args: Vec::new(), |
445 | env: FxHashMap::default(), | 444 | env: FxHashMap::default(), |
@@ -450,13 +449,6 @@ pub fn handle_runnables( | |||
450 | Ok(res) | 449 | Ok(res) |
451 | } | 450 | } |
452 | 451 | ||
453 | fn cargo_path() -> Result<String> { | ||
454 | Ok(ra_toolchain::cargo() | ||
455 | .to_str() | ||
456 | .context("Path to `cargo` executable contains invalid UTF8 characters")? | ||
457 | .to_owned()) | ||
458 | } | ||
459 | |||
460 | pub fn handle_completion( | 452 | pub fn handle_completion( |
461 | world: WorldSnapshot, | 453 | world: WorldSnapshot, |
462 | params: lsp_types::CompletionParams, | 454 | params: lsp_types::CompletionParams, |
@@ -994,7 +986,7 @@ fn to_lsp_runnable( | |||
994 | Ok(lsp_ext::Runnable { | 986 | Ok(lsp_ext::Runnable { |
995 | range: to_proto::range(&line_index, runnable.range), | 987 | range: to_proto::range(&line_index, runnable.range), |
996 | label, | 988 | label, |
997 | bin: cargo_path()?, | 989 | kind: lsp_ext::RunnableKind::Cargo, |
998 | args, | 990 | args, |
999 | extra_args, | 991 | extra_args, |
1000 | env: { | 992 | env: { |
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index a31580c86..8b473ff74 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -58,10 +58,6 @@ use std::collections::Spam; | |||
58 | eprintln!("completion took {:?}", completion_start.elapsed()); | 58 | eprintln!("completion took {:?}", completion_start.elapsed()); |
59 | } | 59 | } |
60 | 60 | ||
61 | fn cargo_path() -> String { | ||
62 | ra_toolchain::cargo().to_str().unwrap().to_owned() | ||
63 | } | ||
64 | |||
65 | #[test] | 61 | #[test] |
66 | fn test_runnables_no_project() { | 62 | fn test_runnables_no_project() { |
67 | if skip_slow_tests() { | 63 | if skip_slow_tests() { |
@@ -83,7 +79,7 @@ fn foo() { | |||
83 | { | 79 | { |
84 | "args": [ "test" ], | 80 | "args": [ "test" ], |
85 | "extraArgs": [ "foo", "--nocapture" ], | 81 | "extraArgs": [ "foo", "--nocapture" ], |
86 | "bin": cargo_path(), | 82 | "kind": "cargo", |
87 | "env": { "RUST_BACKTRACE": "short" }, | 83 | "env": { "RUST_BACKTRACE": "short" }, |
88 | "cwd": null, | 84 | "cwd": null, |
89 | "label": "test foo", | 85 | "label": "test foo", |
@@ -95,7 +91,7 @@ fn foo() { | |||
95 | { | 91 | { |
96 | "args": ["check", "--workspace"], | 92 | "args": ["check", "--workspace"], |
97 | "extraArgs": [], | 93 | "extraArgs": [], |
98 | "bin": cargo_path(), | 94 | "kind": "cargo", |
99 | "env": {}, | 95 | "env": {}, |
100 | "cwd": null, | 96 | "cwd": null, |
101 | "label": "cargo check --workspace", | 97 | "label": "cargo check --workspace", |
@@ -145,7 +141,7 @@ fn main() {} | |||
145 | { | 141 | { |
146 | "args": [ "test", "--package", "foo", "--test", "spam" ], | 142 | "args": [ "test", "--package", "foo", "--test", "spam" ], |
147 | "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], | 143 | "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], |
148 | "bin": cargo_path(), | 144 | "kind": "cargo", |
149 | "env": { "RUST_BACKTRACE": "short" }, | 145 | "env": { "RUST_BACKTRACE": "short" }, |
150 | "label": "test test_eggs", | 146 | "label": "test test_eggs", |
151 | "range": { | 147 | "range": { |
@@ -157,7 +153,7 @@ fn main() {} | |||
157 | { | 153 | { |
158 | "args": [ "check", "--package", "foo" ], | 154 | "args": [ "check", "--package", "foo" ], |
159 | "extraArgs": [], | 155 | "extraArgs": [], |
160 | "bin": cargo_path(), | 156 | "kind": "cargo", |
161 | "env": {}, | 157 | "env": {}, |
162 | "label": "cargo check -p foo", | 158 | "label": "cargo check -p foo", |
163 | "range": { | 159 | "range": { |
@@ -169,7 +165,7 @@ fn main() {} | |||
169 | { | 165 | { |
170 | "args": [ "test", "--package", "foo" ], | 166 | "args": [ "test", "--package", "foo" ], |
171 | "extraArgs": [], | 167 | "extraArgs": [], |
172 | "bin": cargo_path(), | 168 | "kind": "cargo", |
173 | "env": {}, | 169 | "env": {}, |
174 | "label": "cargo test -p foo", | 170 | "label": "cargo test -p foo", |
175 | "range": { | 171 | "range": { |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index dbc95be38..d06da355d 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -417,7 +417,7 @@ interface Runnable { | |||
417 | /// The label to show in the UI. | 417 | /// The label to show in the UI. |
418 | label: string; | 418 | label: string; |
419 | /// The following fields describe a process to spawn. | 419 | /// The following fields describe a process to spawn. |
420 | bin: string; | 420 | kind: "cargo" | "rustc" | "rustup"; |
421 | args: string[]; | 421 | args: string[]; |
422 | /// Args for cargo after `--`. | 422 | /// Args for cargo after `--`. |
423 | extraArgs: string[]; | 423 | extraArgs: string[]; |
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 027504ecd..bdec5b735 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts | |||
@@ -3,7 +3,7 @@ import * as vscode from 'vscode'; | |||
3 | import * as path from 'path'; | 3 | import * as path from 'path'; |
4 | import * as ra from './lsp_ext'; | 4 | import * as ra from './lsp_ext'; |
5 | 5 | ||
6 | import { Cargo } from './cargo'; | 6 | import { Cargo } from './toolchain'; |
7 | import { Ctx } from "./ctx"; | 7 | import { Ctx } from "./ctx"; |
8 | 8 | ||
9 | const debugOutput = vscode.window.createOutputChannel("Debug"); | 9 | const debugOutput = vscode.window.createOutputChannel("Debug"); |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 4da12eb30..3e0b60699 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -45,10 +45,13 @@ export interface RunnablesParams { | |||
45 | textDocument: lc.TextDocumentIdentifier; | 45 | textDocument: lc.TextDocumentIdentifier; |
46 | position: lc.Position | null; | 46 | position: lc.Position | null; |
47 | } | 47 | } |
48 | |||
49 | export type RunnableKind = "cargo" | "rustc" | "rustup"; | ||
50 | |||
48 | export interface Runnable { | 51 | export interface Runnable { |
49 | range: lc.Range; | 52 | range: lc.Range; |
50 | label: string; | 53 | label: string; |
51 | bin: string; | 54 | kind: RunnableKind; |
52 | args: string[]; | 55 | args: string[]; |
53 | extraArgs: string[]; | 56 | extraArgs: string[]; |
54 | env: { [key: string]: string }; | 57 | env: { [key: string]: string }; |
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 2a7a429cf..401cb76af 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as lc from 'vscode-languageclient'; | 2 | import * as lc from 'vscode-languageclient'; |
3 | import * as ra from './lsp_ext'; | 3 | import * as ra from './lsp_ext'; |
4 | import * as toolchain from "./toolchain"; | ||
4 | 5 | ||
5 | import { Ctx, Cmd } from './ctx'; | 6 | import { Ctx, Cmd } from './ctx'; |
6 | import { startDebugSession, getDebugConfiguration } from './debug'; | 7 | import { startDebugSession, getDebugConfiguration } from './debug'; |
@@ -175,7 +176,7 @@ export function createTask(spec: ra.Runnable): vscode.Task { | |||
175 | const definition: CargoTaskDefinition = { | 176 | const definition: CargoTaskDefinition = { |
176 | type: 'cargo', | 177 | type: 'cargo', |
177 | label: spec.label, | 178 | label: spec.label, |
178 | command: spec.bin, | 179 | command: toolchain.getPathForExecutable(spec.kind), |
179 | args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, | 180 | args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, |
180 | env: spec.env, | 181 | env: spec.env, |
181 | }; | 182 | }; |
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index c22d69362..9748824df 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import { getCargoPathOrFail } from "./cargo"; | 2 | import * as toolchain from "./toolchain"; |
3 | 3 | ||
4 | // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and | 4 | // This ends up as the `type` key in tasks.json. RLS also uses `cargo` and |
5 | // our configuration should be compatible with it so use the same key. | 5 | // our configuration should be compatible with it so use the same key. |
@@ -25,7 +25,7 @@ class CargoTaskProvider implements vscode.TaskProvider { | |||
25 | // set of tasks that always exist. These tasks cannot be removed in | 25 | // set of tasks that always exist. These tasks cannot be removed in |
26 | // tasks.json - only tweaked. | 26 | // tasks.json - only tweaked. |
27 | 27 | ||
28 | const cargoPath = getCargoPathOrFail(); | 28 | const cargoPath = toolchain.cargoPath(); |
29 | 29 | ||
30 | return [ | 30 | return [ |
31 | { command: 'build', group: vscode.TaskGroup.Build }, | 31 | { command: 'build', group: vscode.TaskGroup.Build }, |
diff --git a/editors/code/src/cargo.ts b/editors/code/src/toolchain.ts index 46cd3d777..80a7915e9 100644 --- a/editors/code/src/cargo.ts +++ b/editors/code/src/toolchain.ts | |||
@@ -1,9 +1,10 @@ | |||
1 | import * as cp from 'child_process'; | 1 | import * as cp from 'child_process'; |
2 | import * as os from 'os'; | 2 | import * as os from 'os'; |
3 | import * as path from 'path'; | 3 | import * as path from 'path'; |
4 | import * as fs from 'fs'; | ||
4 | import * as readline from 'readline'; | 5 | import * as readline from 'readline'; |
5 | import { OutputChannel } from 'vscode'; | 6 | import { OutputChannel } from 'vscode'; |
6 | import { isValidExecutable } from './util'; | 7 | import { log, memoize } from './util'; |
7 | 8 | ||
8 | interface CompilationArtifact { | 9 | interface CompilationArtifact { |
9 | fileName: string; | 10 | fileName: string; |
@@ -17,33 +18,34 @@ export interface ArtifactSpec { | |||
17 | filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; | 18 | filter?: (artifacts: CompilationArtifact[]) => CompilationArtifact[]; |
18 | } | 19 | } |
19 | 20 | ||
20 | export function artifactSpec(args: readonly string[]): ArtifactSpec { | 21 | export class Cargo { |
21 | const cargoArgs = [...args, "--message-format=json"]; | 22 | constructor(readonly rootFolder: string, readonly output: OutputChannel) { } |
22 | 23 | ||
23 | // arguments for a runnable from the quick pick should be updated. | 24 | // Made public for testing purposes |
24 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens | 25 | static artifactSpec(args: readonly string[]): ArtifactSpec { |
25 | switch (cargoArgs[0]) { | 26 | const cargoArgs = [...args, "--message-format=json"]; |
26 | case "run": cargoArgs[0] = "build"; break; | 27 | |
27 | case "test": { | 28 | // arguments for a runnable from the quick pick should be updated. |
28 | if (!cargoArgs.includes("--no-run")) { | 29 | // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens |
29 | cargoArgs.push("--no-run"); | 30 | switch (cargoArgs[0]) { |
31 | case "run": cargoArgs[0] = "build"; break; | ||
32 | case "test": { | ||
33 | if (!cargoArgs.includes("--no-run")) { | ||
34 | cargoArgs.push("--no-run"); | ||
35 | } | ||
36 | break; | ||
30 | } | 37 | } |
31 | break; | ||
32 | } | 38 | } |
33 | } | ||
34 | 39 | ||
35 | const result: ArtifactSpec = { cargoArgs: cargoArgs }; | 40 | const result: ArtifactSpec = { cargoArgs: cargoArgs }; |
36 | if (cargoArgs[0] === "test") { | 41 | if (cargoArgs[0] === "test") { |
37 | // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests | 42 | // for instance, `crates\rust-analyzer\tests\heavy_tests\main.rs` tests |
38 | // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} | 43 | // produce 2 artifacts: {"kind": "bin"} and {"kind": "test"} |
39 | result.filter = (artifacts) => artifacts.filter(it => it.isTest); | 44 | result.filter = (artifacts) => artifacts.filter(it => it.isTest); |
40 | } | 45 | } |
41 | |||
42 | return result; | ||
43 | } | ||
44 | 46 | ||
45 | export class Cargo { | 47 | return result; |
46 | constructor(readonly rootFolder: string, readonly output: OutputChannel) { } | 48 | } |
47 | 49 | ||
48 | private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> { | 50 | private async getArtifacts(spec: ArtifactSpec): Promise<CompilationArtifact[]> { |
49 | const artifacts: CompilationArtifact[] = []; | 51 | const artifacts: CompilationArtifact[] = []; |
@@ -77,7 +79,7 @@ export class Cargo { | |||
77 | } | 79 | } |
78 | 80 | ||
79 | async executableFromArgs(args: readonly string[]): Promise<string> { | 81 | async executableFromArgs(args: readonly string[]): Promise<string> { |
80 | const artifacts = await this.getArtifacts(artifactSpec(args)); | 82 | const artifacts = await this.getArtifacts(Cargo.artifactSpec(args)); |
81 | 83 | ||
82 | if (artifacts.length === 0) { | 84 | if (artifacts.length === 0) { |
83 | throw new Error('No compilation artifacts'); | 85 | throw new Error('No compilation artifacts'); |
@@ -94,14 +96,7 @@ export class Cargo { | |||
94 | onStderrString: (data: string) => void | 96 | onStderrString: (data: string) => void |
95 | ): Promise<number> { | 97 | ): Promise<number> { |
96 | return new Promise((resolve, reject) => { | 98 | return new Promise((resolve, reject) => { |
97 | let cargoPath; | 99 | const cargo = cp.spawn(cargoPath(), cargoArgs, { |
98 | try { | ||
99 | cargoPath = getCargoPathOrFail(); | ||
100 | } catch (err) { | ||
101 | return reject(err); | ||
102 | } | ||
103 | |||
104 | const cargo = cp.spawn(cargoPath, cargoArgs, { | ||
105 | stdio: ['ignore', 'pipe', 'pipe'], | 100 | stdio: ['ignore', 'pipe', 'pipe'], |
106 | cwd: this.rootFolder | 101 | cwd: this.rootFolder |
107 | }); | 102 | }); |
@@ -126,26 +121,54 @@ export class Cargo { | |||
126 | } | 121 | } |
127 | } | 122 | } |
128 | 123 | ||
129 | // Mirrors `ra_toolchain::cargo()` implementation | 124 | /** Mirrors `ra_toolchain::cargo()` implementation */ |
130 | export function getCargoPathOrFail(): string { | 125 | export function cargoPath(): string { |
131 | const envVar = process.env.CARGO; | 126 | return getPathForExecutable("cargo"); |
132 | const executableName = "cargo"; | 127 | } |
128 | |||
129 | /** Mirrors `ra_toolchain::get_path_for_executable()` implementation */ | ||
130 | export const getPathForExecutable = memoize( | ||
131 | // We apply caching to decrease file-system interactions | ||
132 | (executableName: "cargo" | "rustc" | "rustup"): string => { | ||
133 | { | ||
134 | const envVar = process.env[executableName.toUpperCase()]; | ||
135 | if (envVar) return envVar; | ||
136 | } | ||
137 | |||
138 | if (lookupInPath(executableName)) return executableName; | ||
133 | 139 | ||
134 | if (envVar) { | 140 | try { |
135 | if (isValidExecutable(envVar)) return envVar; | 141 | // hmm, `os.homedir()` seems to be infallible |
142 | // it is not mentioned in docs and cannot be infered by the type signature... | ||
143 | const standardPath = path.join(os.homedir(), ".cargo", "bin", executableName); | ||
136 | 144 | ||
137 | throw new Error(`\`${envVar}\` environment variable points to something that's not a valid executable`); | 145 | if (isFile(standardPath)) return standardPath; |
146 | } catch (err) { | ||
147 | log.error("Failed to read the fs info", err); | ||
148 | } | ||
149 | return executableName; | ||
138 | } | 150 | } |
151 | ); | ||
139 | 152 | ||
140 | if (isValidExecutable(executableName)) return executableName; | 153 | function lookupInPath(exec: string): boolean { |
154 | const paths = process.env.PATH ?? "";; | ||
141 | 155 | ||
142 | const standardLocation = path.join(os.homedir(), '.cargo', 'bin', executableName); | 156 | const candidates = paths.split(path.delimiter).flatMap(dirInPath => { |
157 | const candidate = path.join(dirInPath, exec); | ||
158 | return os.type() === "Windows_NT" | ||
159 | ? [candidate, `${candidate}.exe`] | ||
160 | : [candidate]; | ||
161 | }); | ||
143 | 162 | ||
144 | if (isValidExecutable(standardLocation)) return standardLocation; | 163 | return candidates.some(isFile); |
164 | } | ||
145 | 165 | ||
146 | throw new Error( | 166 | function isFile(suspectPath: string): boolean { |
147 | `Failed to find \`${executableName}\` executable. ` + | 167 | // It is not mentionned in docs, but `statSync()` throws an error when |
148 | `Make sure \`${executableName}\` is in \`$PATH\`, ` + | 168 | // the path doesn't exist |
149 | `or set \`${envVar}\` to point to a valid executable.` | 169 | try { |
150 | ); | 170 | return fs.statSync(suspectPath).isFile(); |
171 | } catch { | ||
172 | return false; | ||
173 | } | ||
151 | } | 174 | } |
diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 352ef9162..fe3fb71cd 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts | |||
@@ -99,3 +99,21 @@ export function isValidExecutable(path: string): boolean { | |||
99 | export function setContextValue(key: string, value: any): Thenable<void> { | 99 | export function setContextValue(key: string, value: any): Thenable<void> { |
100 | return vscode.commands.executeCommand('setContext', key, value); | 100 | return vscode.commands.executeCommand('setContext', key, value); |
101 | } | 101 | } |
102 | |||
103 | /** | ||
104 | * Returns a higher-order function that caches the results of invoking the | ||
105 | * underlying function. | ||
106 | */ | ||
107 | export function memoize<Ret, TThis, Param extends string>(func: (this: TThis, arg: Param) => Ret) { | ||
108 | const cache = new Map<string, Ret>(); | ||
109 | |||
110 | return function(this: TThis, arg: Param) { | ||
111 | const cached = cache.get(arg); | ||
112 | if (cached) return cached; | ||
113 | |||
114 | const result = func.call(this, arg); | ||
115 | cache.set(arg, result); | ||
116 | |||
117 | return result; | ||
118 | }; | ||
119 | } | ||
diff --git a/editors/code/tests/unit/launch_config.test.ts b/editors/code/tests/unit/launch_config.test.ts index d5cf1b74e..68794d53e 100644 --- a/editors/code/tests/unit/launch_config.test.ts +++ b/editors/code/tests/unit/launch_config.test.ts | |||
@@ -1,25 +1,25 @@ | |||
1 | import * as assert from 'assert'; | 1 | import * as assert from 'assert'; |
2 | import * as cargo from '../../src/cargo'; | 2 | import { Cargo } from '../../src/toolchain'; |
3 | 3 | ||
4 | suite('Launch configuration', () => { | 4 | suite('Launch configuration', () => { |
5 | 5 | ||
6 | suite('Lens', () => { | 6 | suite('Lens', () => { |
7 | test('A binary', async () => { | 7 | test('A binary', async () => { |
8 | const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]); | 8 | const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "pkg_name"]); |
9 | 9 | ||
10 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); | 10 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); |
11 | assert.deepEqual(args.filter, undefined); | 11 | assert.deepEqual(args.filter, undefined); |
12 | }); | 12 | }); |
13 | 13 | ||
14 | test('One of Multiple Binaries', async () => { | 14 | test('One of Multiple Binaries', async () => { |
15 | const args = cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); | 15 | const args = Cargo.artifactSpec(["build", "--package", "pkg_name", "--bin", "bin1"]); |
16 | 16 | ||
17 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]); | 17 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin1", "--message-format=json"]); |
18 | assert.deepEqual(args.filter, undefined); | 18 | assert.deepEqual(args.filter, undefined); |
19 | }); | 19 | }); |
20 | 20 | ||
21 | test('A test', async () => { | 21 | test('A test', async () => { |
22 | const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); | 22 | const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib", "--no-run"]); |
23 | 23 | ||
24 | assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]); | 24 | assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--no-run", "--message-format=json"]); |
25 | assert.notDeepEqual(args.filter, undefined); | 25 | assert.notDeepEqual(args.filter, undefined); |
@@ -28,7 +28,7 @@ suite('Launch configuration', () => { | |||
28 | 28 | ||
29 | suite('QuickPick', () => { | 29 | suite('QuickPick', () => { |
30 | test('A binary', async () => { | 30 | test('A binary', async () => { |
31 | const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); | 31 | const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "pkg_name"]); |
32 | 32 | ||
33 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); | 33 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "pkg_name", "--message-format=json"]); |
34 | assert.deepEqual(args.filter, undefined); | 34 | assert.deepEqual(args.filter, undefined); |
@@ -36,14 +36,14 @@ suite('Launch configuration', () => { | |||
36 | 36 | ||
37 | 37 | ||
38 | test('One of Multiple Binaries', async () => { | 38 | test('One of Multiple Binaries', async () => { |
39 | const args = cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); | 39 | const args = Cargo.artifactSpec(["run", "--package", "pkg_name", "--bin", "bin2"]); |
40 | 40 | ||
41 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]); | 41 | assert.deepEqual(args.cargoArgs, ["build", "--package", "pkg_name", "--bin", "bin2", "--message-format=json"]); |
42 | assert.deepEqual(args.filter, undefined); | 42 | assert.deepEqual(args.filter, undefined); |
43 | }); | 43 | }); |
44 | 44 | ||
45 | test('A test', async () => { | 45 | test('A test', async () => { |
46 | const args = cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); | 46 | const args = Cargo.artifactSpec(["test", "--package", "pkg_name", "--lib"]); |
47 | 47 | ||
48 | assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]); | 48 | assert.deepEqual(args.cargoArgs, ["test", "--package", "pkg_name", "--lib", "--message-format=json", "--no-run"]); |
49 | assert.notDeepEqual(args.filter, undefined); | 49 | assert.notDeepEqual(args.filter, undefined); |