diff options
author | veetaha <[email protected]> | 2020-05-31 03:13:08 +0100 |
---|---|---|
committer | veetaha <[email protected]> | 2020-05-31 03:21:45 +0100 |
commit | d605ec9c321392d9c7ee4b440c560e1e405d92e6 (patch) | |
tree | 58d16996d1d1a05733dcc85ae4efddc563b3d3b1 | |
parent | a419cedb1cc661349a022262c8b03993e063252f (diff) |
Change Runnable.bin -> Runnable.kind
As per matklad, we now pass the responsibility for finding the binary to the frontend.
Also, added caching for finding the binary path to reduce
the amount of filesystem interactions.
-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); |