diff options
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/snapshots/highlight_strings.html | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/syntax_highlighting/tests.rs | 1 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_proc_macro_srv/src/tests/utils.rs | 3 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 5 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cargo_target_spec.rs | 85 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 11 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 123 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 44 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/main.rs | 10 | ||||
-rw-r--r-- | docs/dev/lsp-extensions.md | 2 | ||||
-rw-r--r-- | editors/code/src/commands.ts | 74 | ||||
-rw-r--r-- | editors/code/src/debug.ts | 121 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 5 | ||||
-rw-r--r-- | editors/code/src/run.ts | 77 | ||||
-rw-r--r-- | editors/code/src/tasks.ts | 7 | ||||
-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 |
20 files changed, 389 insertions, 331 deletions
diff --git a/Cargo.lock b/Cargo.lock index 9981a2e33..af27bfc85 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -1119,6 +1119,7 @@ dependencies = [ | |||
1119 | "memmap", | 1119 | "memmap", |
1120 | "ra_mbe", | 1120 | "ra_mbe", |
1121 | "ra_proc_macro", | 1121 | "ra_proc_macro", |
1122 | "ra_toolchain", | ||
1122 | "ra_tt", | 1123 | "ra_tt", |
1123 | "serde_derive", | 1124 | "serde_derive", |
1124 | "test_utils", | 1125 | "test_utils", |
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html index 326744361..41cddd0ff 100644 --- a/crates/ra_ide/src/snapshots/highlight_strings.html +++ b/crates/ra_ide/src/snapshots/highlight_strings.html | |||
@@ -52,6 +52,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> | 52 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal">"</span>, argument = <span class="string_literal">"test"</span>); <span class="comment">// => "test"</span> |
53 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> | 53 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">"</span>, <span class="numeric_literal">1</span>, name = <span class="numeric_literal">2</span>); <span class="comment">// => "2 1"</span> |
54 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> | 54 | <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal">"</span>, a=<span class="string_literal">"a"</span>, b=<span class="char_literal">'b'</span>, c=<span class="numeric_literal">3</span>); <span class="comment">// => "a 3 b"</span> |
55 | <span class="macro">println!</span>(<span class="string_literal">"{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">}}"</span>, <span class="numeric_literal">2</span>); <span class="comment">// => "{2}"</span> | ||
55 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); | 56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>); |
56 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); | 57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"x"</span>, <span class="numeric_literal">5</span>); |
57 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); | 58 | <span class="macro">println!</span>(<span class="string_literal">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="numeric_literal">5</span>, <span class="string_literal">"x"</span>); |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index eb43a23da..7dc229cab 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -218,6 +218,7 @@ fn main() { | |||
218 | println!("{argument}", argument = "test"); // => "test" | 218 | println!("{argument}", argument = "test"); // => "test" |
219 | println!("{name} {}", 1, name = 2); // => "2 1" | 219 | println!("{name} {}", 1, name = 2); // => "2 1" |
220 | println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" | 220 | println!("{a} {c} {b}", a="a", b='b', c=3); // => "a 3 b" |
221 | println!("{{{}}}", 2); // => "{2}" | ||
221 | println!("Hello {:5}!", "x"); | 222 | println!("Hello {:5}!", "x"); |
222 | println!("Hello {:1$}!", "x", 5); | 223 | println!("Hello {:1$}!", "x", 5); |
223 | println!("Hello {1:0$}!", 5, "x"); | 224 | println!("Hello {1:0$}!", 5, "x"); |
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml index bb3003278..582102945 100644 --- a/crates/ra_proc_macro_srv/Cargo.toml +++ b/crates/ra_proc_macro_srv/Cargo.toml | |||
@@ -22,3 +22,4 @@ cargo_metadata = "0.10.0" | |||
22 | difference = "2.0.0" | 22 | difference = "2.0.0" |
23 | # used as proc macro test target | 23 | # used as proc macro test target |
24 | serde_derive = "1.0.106" | 24 | serde_derive = "1.0.106" |
25 | ra_toolchain = { path = "../ra_toolchain" } | ||
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs index 84348b5de..8d85f2d8a 100644 --- a/crates/ra_proc_macro_srv/src/tests/utils.rs +++ b/crates/ra_proc_macro_srv/src/tests/utils.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | 2 | ||
3 | use crate::dylib; | 3 | use crate::dylib; |
4 | use crate::ProcMacroSrv; | 4 | use crate::ProcMacroSrv; |
5 | pub use difference::Changeset as __Changeset; | ||
6 | use ra_proc_macro::ListMacrosTask; | 5 | use ra_proc_macro::ListMacrosTask; |
7 | use std::str::FromStr; | 6 | use std::str::FromStr; |
8 | use test_utils::assert_eq_text; | 7 | use test_utils::assert_eq_text; |
@@ -13,7 +12,7 @@ mod fixtures { | |||
13 | 12 | ||
14 | // Use current project metadata to get the proc-macro dylib path | 13 | // Use current project metadata to get the proc-macro dylib path |
15 | pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { | 14 | pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf { |
16 | let command = Command::new("cargo") | 15 | let command = Command::new(ra_toolchain::cargo()) |
17 | .args(&["check", "--message-format", "json"]) | 16 | .args(&["check", "--message-format", "json"]) |
18 | .output() | 17 | .output() |
19 | .unwrap() | 18 | .unwrap() |
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 3cd6d99c3..04b0a4480 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -418,14 +418,9 @@ pub trait HasFormatSpecifier: AstToken { | |||
418 | 418 | ||
419 | let mut cloned = chars.clone().take(2); | 419 | let mut cloned = chars.clone().take(2); |
420 | let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); | 420 | let first = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); |
421 | let second = cloned.next().and_then(|next| next.1.as_ref().ok()).copied(); | ||
422 | if first != Some('}') { | 421 | if first != Some('}') { |
423 | continue; | 422 | continue; |
424 | } | 423 | } |
425 | if second == Some('}') { | ||
426 | // Escaped format end specifier, `}}` | ||
427 | continue; | ||
428 | } | ||
429 | skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); | 424 | skip_char_and_emit(&mut chars, FormatSpecifier::Close, &mut callback); |
430 | } | 425 | } |
431 | _ => { | 426 | _ => { |
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 441fb61df..008518a08 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs | |||
@@ -1,10 +1,10 @@ | |||
1 | //! See `CargoTargetSpec` | 1 | //! See `CargoTargetSpec` |
2 | 2 | ||
3 | use ra_cfg::CfgExpr; | ||
3 | use ra_ide::{FileId, RunnableKind, TestId}; | 4 | use ra_ide::{FileId, RunnableKind, TestId}; |
4 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; | 5 | use ra_project_model::{self, ProjectWorkspace, TargetKind}; |
5 | 6 | ||
6 | use crate::{world::WorldSnapshot, Result}; | 7 | use crate::{world::WorldSnapshot, Result}; |
7 | use ra_syntax::SmolStr; | ||
8 | 8 | ||
9 | /// Abstract representation of Cargo target. | 9 | /// Abstract representation of Cargo target. |
10 | /// | 10 | /// |
@@ -21,7 +21,7 @@ impl CargoTargetSpec { | |||
21 | pub(crate) fn runnable_args( | 21 | pub(crate) fn runnable_args( |
22 | spec: Option<CargoTargetSpec>, | 22 | spec: Option<CargoTargetSpec>, |
23 | kind: &RunnableKind, | 23 | kind: &RunnableKind, |
24 | features_needed: &Vec<SmolStr>, | 24 | cfgs: &[CfgExpr], |
25 | ) -> Result<(Vec<String>, Vec<String>)> { | 25 | ) -> Result<(Vec<String>, Vec<String>)> { |
26 | let mut args = Vec::new(); | 26 | let mut args = Vec::new(); |
27 | let mut extra_args = Vec::new(); | 27 | let mut extra_args = Vec::new(); |
@@ -76,10 +76,14 @@ impl CargoTargetSpec { | |||
76 | } | 76 | } |
77 | } | 77 | } |
78 | 78 | ||
79 | features_needed.iter().for_each(|feature| { | 79 | let mut features = Vec::new(); |
80 | for cfg in cfgs { | ||
81 | required_features(cfg, &mut features); | ||
82 | } | ||
83 | for feature in features { | ||
80 | args.push("--features".to_string()); | 84 | args.push("--features".to_string()); |
81 | args.push(feature.to_string()); | 85 | args.push(feature); |
82 | }); | 86 | } |
83 | 87 | ||
84 | Ok((args, extra_args)) | 88 | Ok((args, extra_args)) |
85 | } | 89 | } |
@@ -140,3 +144,74 @@ impl CargoTargetSpec { | |||
140 | } | 144 | } |
141 | } | 145 | } |
142 | } | 146 | } |
147 | |||
148 | /// Fill minimal features needed | ||
149 | fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) { | ||
150 | match cfg_expr { | ||
151 | CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()), | ||
152 | CfgExpr::All(preds) => { | ||
153 | preds.iter().for_each(|cfg| required_features(cfg, features)); | ||
154 | } | ||
155 | CfgExpr::Any(preds) => { | ||
156 | for cfg in preds { | ||
157 | let len_features = features.len(); | ||
158 | required_features(cfg, features); | ||
159 | if len_features != features.len() { | ||
160 | break; | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | _ => {} | ||
165 | } | ||
166 | } | ||
167 | |||
168 | #[cfg(test)] | ||
169 | mod tests { | ||
170 | use super::*; | ||
171 | |||
172 | use mbe::{ast_to_token_tree, TokenMap}; | ||
173 | use ra_cfg::parse_cfg; | ||
174 | use ra_syntax::{ | ||
175 | ast::{self, AstNode}, | ||
176 | SmolStr, | ||
177 | }; | ||
178 | |||
179 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { | ||
180 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
181 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
182 | ast_to_token_tree(&tt).unwrap() | ||
183 | } | ||
184 | |||
185 | #[test] | ||
186 | fn test_cfg_expr_minimal_features_needed() { | ||
187 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); | ||
188 | let cfg_expr = parse_cfg(&subtree); | ||
189 | let mut min_features = vec![]; | ||
190 | required_features(&cfg_expr, &mut min_features); | ||
191 | |||
192 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
193 | |||
194 | let (subtree, _) = | ||
195 | get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); | ||
196 | let cfg_expr = parse_cfg(&subtree); | ||
197 | |||
198 | let mut min_features = vec![]; | ||
199 | required_features(&cfg_expr, &mut min_features); | ||
200 | assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); | ||
201 | |||
202 | let (subtree, _) = | ||
203 | get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); | ||
204 | let cfg_expr = parse_cfg(&subtree); | ||
205 | |||
206 | let mut min_features = vec![]; | ||
207 | required_features(&cfg_expr, &mut min_features); | ||
208 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
209 | |||
210 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); | ||
211 | let cfg_expr = parse_cfg(&subtree); | ||
212 | |||
213 | let mut min_features = vec![]; | ||
214 | required_features(&cfg_expr, &mut min_features); | ||
215 | assert!(min_features.is_empty()); | ||
216 | } | ||
217 | } | ||
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 1f910ff82..410c654ab 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -17,14 +17,12 @@ use lsp_types::{ | |||
17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, | 17 | SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, |
18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, | 18 | SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit, |
19 | }; | 19 | }; |
20 | use ra_cfg::CfgExpr; | ||
21 | use ra_ide::{ | 20 | use ra_ide::{ |
22 | FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, | 21 | FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit, |
23 | TextEdit, | ||
24 | }; | 22 | }; |
25 | use ra_prof::profile; | 23 | use ra_prof::profile; |
26 | use ra_project_model::TargetKind; | 24 | use ra_project_model::TargetKind; |
27 | use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; | 25 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; |
28 | use rustc_hash::FxHashMap; | 26 | use rustc_hash::FxHashMap; |
29 | use serde::{Deserialize, Serialize}; | 27 | use serde::{Deserialize, Serialize}; |
30 | use serde_json::to_value; | 28 | use serde_json::to_value; |
@@ -416,7 +414,7 @@ pub fn handle_runnables( | |||
416 | } | 414 | } |
417 | } | 415 | } |
418 | } | 416 | } |
419 | res.push(to_lsp_runnable(&world, file_id, runnable)?); | 417 | res.push(to_proto::runnable(&world, file_id, runnable)?); |
420 | } | 418 | } |
421 | 419 | ||
422 | // Add `cargo check` and `cargo test` for the whole package | 420 | // Add `cargo check` and `cargo test` for the whole package |
@@ -426,7 +424,7 @@ pub fn handle_runnables( | |||
426 | res.push(lsp_ext::Runnable { | 424 | res.push(lsp_ext::Runnable { |
427 | range: Default::default(), | 425 | range: Default::default(), |
428 | label: format!("cargo {} -p {}", cmd, spec.package), | 426 | label: format!("cargo {} -p {}", cmd, spec.package), |
429 | bin: "cargo".to_string(), | 427 | kind: lsp_ext::RunnableKind::Cargo, |
430 | args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], | 428 | args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], |
431 | extra_args: Vec::new(), | 429 | extra_args: Vec::new(), |
432 | env: FxHashMap::default(), | 430 | env: FxHashMap::default(), |
@@ -438,7 +436,7 @@ pub fn handle_runnables( | |||
438 | res.push(lsp_ext::Runnable { | 436 | res.push(lsp_ext::Runnable { |
439 | range: Default::default(), | 437 | range: Default::default(), |
440 | label: "cargo check --workspace".to_string(), | 438 | label: "cargo check --workspace".to_string(), |
441 | bin: "cargo".to_string(), | 439 | kind: lsp_ext::RunnableKind::Cargo, |
442 | args: vec!["check".to_string(), "--workspace".to_string()], | 440 | args: vec!["check".to_string(), "--workspace".to_string()], |
443 | extra_args: Vec::new(), | 441 | extra_args: Vec::new(), |
444 | env: FxHashMap::default(), | 442 | env: FxHashMap::default(), |
@@ -784,7 +782,7 @@ pub fn handle_code_lens( | |||
784 | } | 782 | } |
785 | }; | 783 | }; |
786 | 784 | ||
787 | let mut r = to_lsp_runnable(&world, file_id, runnable)?; | 785 | let mut r = to_proto::runnable(&world, file_id, runnable)?; |
788 | if world.config.lens.run { | 786 | if world.config.lens.run { |
789 | let lens = CodeLens { | 787 | let lens = CodeLens { |
790 | range: r.range, | 788 | range: r.range, |
@@ -959,64 +957,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia | |||
959 | Ok(DiagnosticTask::SetNative(file_id, diagnostics)) | 957 | Ok(DiagnosticTask::SetNative(file_id, diagnostics)) |
960 | } | 958 | } |
961 | 959 | ||
962 | fn to_lsp_runnable( | ||
963 | world: &WorldSnapshot, | ||
964 | file_id: FileId, | ||
965 | runnable: Runnable, | ||
966 | ) -> Result<lsp_ext::Runnable> { | ||
967 | let spec = CargoTargetSpec::for_file(world, file_id)?; | ||
968 | let target = spec.as_ref().map(|s| s.target.clone()); | ||
969 | let mut features_needed = vec![]; | ||
970 | for cfg_expr in &runnable.cfg_exprs { | ||
971 | collect_minimal_features_needed(cfg_expr, &mut features_needed); | ||
972 | } | ||
973 | let (args, extra_args) = | ||
974 | CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?; | ||
975 | let line_index = world.analysis().file_line_index(file_id)?; | ||
976 | let label = match &runnable.kind { | ||
977 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | ||
978 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | ||
979 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | ||
980 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | ||
981 | RunnableKind::Bin => { | ||
982 | target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) | ||
983 | } | ||
984 | }; | ||
985 | Ok(lsp_ext::Runnable { | ||
986 | range: to_proto::range(&line_index, runnable.range), | ||
987 | label, | ||
988 | bin: "cargo".to_string(), | ||
989 | args, | ||
990 | extra_args, | ||
991 | env: { | ||
992 | let mut m = FxHashMap::default(); | ||
993 | m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); | ||
994 | m | ||
995 | }, | ||
996 | cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), | ||
997 | }) | ||
998 | } | ||
999 | |||
1000 | /// Fill minimal features needed | ||
1001 | fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) { | ||
1002 | match cfg_expr { | ||
1003 | CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()), | ||
1004 | CfgExpr::All(preds) => { | ||
1005 | preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features)); | ||
1006 | } | ||
1007 | CfgExpr::Any(preds) => { | ||
1008 | for cfg in preds { | ||
1009 | let len_features = features.len(); | ||
1010 | collect_minimal_features_needed(cfg, features); | ||
1011 | if len_features != features.len() { | ||
1012 | break; | ||
1013 | } | ||
1014 | } | ||
1015 | } | ||
1016 | _ => {} | ||
1017 | } | ||
1018 | } | ||
1019 | |||
1020 | pub fn handle_inlay_hints( | 960 | pub fn handle_inlay_hints( |
1021 | world: WorldSnapshot, | 961 | world: WorldSnapshot, |
1022 | params: InlayHintsParams, | 962 | params: InlayHintsParams, |
@@ -1153,54 +1093,3 @@ pub fn handle_semantic_tokens_range( | |||
1153 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); | 1093 | let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); |
1154 | Ok(Some(semantic_tokens.into())) | 1094 | Ok(Some(semantic_tokens.into())) |
1155 | } | 1095 | } |
1156 | |||
1157 | #[cfg(test)] | ||
1158 | mod tests { | ||
1159 | use super::*; | ||
1160 | |||
1161 | use mbe::{ast_to_token_tree, TokenMap}; | ||
1162 | use ra_cfg::parse_cfg; | ||
1163 | use ra_syntax::{ | ||
1164 | ast::{self, AstNode}, | ||
1165 | SmolStr, | ||
1166 | }; | ||
1167 | |||
1168 | fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) { | ||
1169 | let source_file = ast::SourceFile::parse(input).ok().unwrap(); | ||
1170 | let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
1171 | ast_to_token_tree(&tt).unwrap() | ||
1172 | } | ||
1173 | |||
1174 | #[test] | ||
1175 | fn test_cfg_expr_minimal_features_needed() { | ||
1176 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#); | ||
1177 | let cfg_expr = parse_cfg(&subtree); | ||
1178 | let mut min_features = vec![]; | ||
1179 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1180 | |||
1181 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
1182 | |||
1183 | let (subtree, _) = | ||
1184 | get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#); | ||
1185 | let cfg_expr = parse_cfg(&subtree); | ||
1186 | |||
1187 | let mut min_features = vec![]; | ||
1188 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1189 | assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]); | ||
1190 | |||
1191 | let (subtree, _) = | ||
1192 | get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#); | ||
1193 | let cfg_expr = parse_cfg(&subtree); | ||
1194 | |||
1195 | let mut min_features = vec![]; | ||
1196 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1197 | assert_eq!(min_features, vec![SmolStr::new("baz")]); | ||
1198 | |||
1199 | let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#); | ||
1200 | let cfg_expr = parse_cfg(&subtree); | ||
1201 | |||
1202 | let mut min_features = vec![]; | ||
1203 | collect_minimal_features_needed(&cfg_expr, &mut min_features); | ||
1204 | assert!(min_features.is_empty()); | ||
1205 | } | ||
1206 | } | ||
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 2fbbb4e63..66144fe24 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -3,13 +3,16 @@ use ra_db::{FileId, FileRange}; | |||
3 | use ra_ide::{ | 3 | use ra_ide::{ |
4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, | 4 | Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, |
5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, | 5 | FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, |
6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Severity, | 6 | InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, Runnable, |
7 | SourceChange, SourceFileEdit, TextEdit, | 7 | RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit, |
8 | }; | 8 | }; |
9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; | 9 | use ra_syntax::{SyntaxKind, TextRange, TextSize}; |
10 | use ra_vfs::LineEndings; | 10 | use ra_vfs::LineEndings; |
11 | use rustc_hash::FxHashMap; | ||
11 | 12 | ||
12 | use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; | 13 | use crate::{ |
14 | cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result, | ||
15 | }; | ||
13 | 16 | ||
14 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { | 17 | pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { |
15 | let line_col = line_index.line_col(offset); | 18 | let line_col = line_index.line_col(offset); |
@@ -627,3 +630,38 @@ pub(crate) fn code_action(world: &WorldSnapshot, assist: Assist) -> Result<lsp_e | |||
627 | }; | 630 | }; |
628 | Ok(res) | 631 | Ok(res) |
629 | } | 632 | } |
633 | |||
634 | pub(crate) fn runnable( | ||
635 | world: &WorldSnapshot, | ||
636 | file_id: FileId, | ||
637 | runnable: Runnable, | ||
638 | ) -> Result<lsp_ext::Runnable> { | ||
639 | let spec = CargoTargetSpec::for_file(world, file_id)?; | ||
640 | let target = spec.as_ref().map(|s| s.target.clone()); | ||
641 | let (args, extra_args) = | ||
642 | CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; | ||
643 | let line_index = world.analysis().file_line_index(file_id)?; | ||
644 | let label = match &runnable.kind { | ||
645 | RunnableKind::Test { test_id, .. } => format!("test {}", test_id), | ||
646 | RunnableKind::TestMod { path } => format!("test-mod {}", path), | ||
647 | RunnableKind::Bench { test_id } => format!("bench {}", test_id), | ||
648 | RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id), | ||
649 | RunnableKind::Bin => { | ||
650 | target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t)) | ||
651 | } | ||
652 | }; | ||
653 | |||
654 | Ok(lsp_ext::Runnable { | ||
655 | range: range(&line_index, runnable.range), | ||
656 | label, | ||
657 | kind: lsp_ext::RunnableKind::Cargo, | ||
658 | args, | ||
659 | extra_args, | ||
660 | env: { | ||
661 | let mut m = FxHashMap::default(); | ||
662 | m.insert("RUST_BACKTRACE".to_string(), "short".to_string()); | ||
663 | m | ||
664 | }, | ||
665 | cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()), | ||
666 | }) | ||
667 | } | ||
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 405ddb362..8b473ff74 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -79,7 +79,7 @@ fn foo() { | |||
79 | { | 79 | { |
80 | "args": [ "test" ], | 80 | "args": [ "test" ], |
81 | "extraArgs": [ "foo", "--nocapture" ], | 81 | "extraArgs": [ "foo", "--nocapture" ], |
82 | "bin": "cargo", | 82 | "kind": "cargo", |
83 | "env": { "RUST_BACKTRACE": "short" }, | 83 | "env": { "RUST_BACKTRACE": "short" }, |
84 | "cwd": null, | 84 | "cwd": null, |
85 | "label": "test foo", | 85 | "label": "test foo", |
@@ -91,7 +91,7 @@ fn foo() { | |||
91 | { | 91 | { |
92 | "args": ["check", "--workspace"], | 92 | "args": ["check", "--workspace"], |
93 | "extraArgs": [], | 93 | "extraArgs": [], |
94 | "bin": "cargo", | 94 | "kind": "cargo", |
95 | "env": {}, | 95 | "env": {}, |
96 | "cwd": null, | 96 | "cwd": null, |
97 | "label": "cargo check --workspace", | 97 | "label": "cargo check --workspace", |
@@ -141,7 +141,7 @@ fn main() {} | |||
141 | { | 141 | { |
142 | "args": [ "test", "--package", "foo", "--test", "spam" ], | 142 | "args": [ "test", "--package", "foo", "--test", "spam" ], |
143 | "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], | 143 | "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], |
144 | "bin": "cargo", | 144 | "kind": "cargo", |
145 | "env": { "RUST_BACKTRACE": "short" }, | 145 | "env": { "RUST_BACKTRACE": "short" }, |
146 | "label": "test test_eggs", | 146 | "label": "test test_eggs", |
147 | "range": { | 147 | "range": { |
@@ -153,7 +153,7 @@ fn main() {} | |||
153 | { | 153 | { |
154 | "args": [ "check", "--package", "foo" ], | 154 | "args": [ "check", "--package", "foo" ], |
155 | "extraArgs": [], | 155 | "extraArgs": [], |
156 | "bin": "cargo", | 156 | "kind": "cargo", |
157 | "env": {}, | 157 | "env": {}, |
158 | "label": "cargo check -p foo", | 158 | "label": "cargo check -p foo", |
159 | "range": { | 159 | "range": { |
@@ -165,7 +165,7 @@ fn main() {} | |||
165 | { | 165 | { |
166 | "args": [ "test", "--package", "foo" ], | 166 | "args": [ "test", "--package", "foo" ], |
167 | "extraArgs": [], | 167 | "extraArgs": [], |
168 | "bin": "cargo", | 168 | "kind": "cargo", |
169 | "env": {}, | 169 | "env": {}, |
170 | "label": "cargo test -p foo", | 170 | "label": "cargo test -p foo", |
171 | "range": { | 171 | "range": { |
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index c57a93f12..b7237ee90 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md | |||
@@ -427,7 +427,7 @@ interface Runnable { | |||
427 | /// The label to show in the UI. | 427 | /// The label to show in the UI. |
428 | label: string; | 428 | label: string; |
429 | /// The following fields describe a process to spawn. | 429 | /// The following fields describe a process to spawn. |
430 | bin: string; | 430 | kind: "cargo" | "rustc" | "rustup"; |
431 | args: string[]; | 431 | args: string[]; |
432 | /// Args for cargo after `--`. | 432 | /// Args for cargo after `--`. |
433 | extraArgs: string[]; | 433 | extraArgs: string[]; |
diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 86302db37..534d2a984 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts | |||
@@ -8,6 +8,7 @@ import { spawnSync } from 'child_process'; | |||
8 | import { RunnableQuickPick, selectRunnable, createTask } from './run'; | 8 | import { RunnableQuickPick, selectRunnable, createTask } from './run'; |
9 | import { AstInspector } from './ast_inspector'; | 9 | import { AstInspector } from './ast_inspector'; |
10 | import { isRustDocument, sleep, isRustEditor } from './util'; | 10 | import { isRustDocument, sleep, isRustEditor } from './util'; |
11 | import { startDebugSession, makeDebugConfig } from './debug'; | ||
11 | 12 | ||
12 | export * from './ast_inspector'; | 13 | export * from './ast_inspector'; |
13 | export * from './run'; | 14 | export * from './run'; |
@@ -197,20 +198,6 @@ export function toggleInlayHints(ctx: Ctx): Cmd { | |||
197 | }; | 198 | }; |
198 | } | 199 | } |
199 | 200 | ||
200 | export function run(ctx: Ctx): Cmd { | ||
201 | let prevRunnable: RunnableQuickPick | undefined; | ||
202 | |||
203 | return async () => { | ||
204 | const item = await selectRunnable(ctx, prevRunnable); | ||
205 | if (!item) return; | ||
206 | |||
207 | item.detail = 'rerun'; | ||
208 | prevRunnable = item; | ||
209 | const task = createTask(item.runnable); | ||
210 | return await vscode.tasks.executeTask(task); | ||
211 | }; | ||
212 | } | ||
213 | |||
214 | // Opens the virtual file that will show the syntax tree | 201 | // Opens the virtual file that will show the syntax tree |
215 | // | 202 | // |
216 | // The contents of the file come from the `TextDocumentContentProvider` | 203 | // The contents of the file come from the `TextDocumentContentProvider` |
@@ -368,3 +355,62 @@ export function applySnippetWorkspaceEditCommand(_ctx: Ctx): Cmd { | |||
368 | await applySnippetWorkspaceEdit(edit); | 355 | await applySnippetWorkspaceEdit(edit); |
369 | }; | 356 | }; |
370 | } | 357 | } |
358 | |||
359 | export function run(ctx: Ctx): Cmd { | ||
360 | let prevRunnable: RunnableQuickPick | undefined; | ||
361 | |||
362 | return async () => { | ||
363 | const item = await selectRunnable(ctx, prevRunnable); | ||
364 | if (!item) return; | ||
365 | |||
366 | item.detail = 'rerun'; | ||
367 | prevRunnable = item; | ||
368 | const task = createTask(item.runnable); | ||
369 | return await vscode.tasks.executeTask(task); | ||
370 | }; | ||
371 | } | ||
372 | |||
373 | export function runSingle(ctx: Ctx): Cmd { | ||
374 | return async (runnable: ra.Runnable) => { | ||
375 | const editor = ctx.activeRustEditor; | ||
376 | if (!editor) return; | ||
377 | |||
378 | const task = createTask(runnable); | ||
379 | task.group = vscode.TaskGroup.Build; | ||
380 | task.presentationOptions = { | ||
381 | reveal: vscode.TaskRevealKind.Always, | ||
382 | panel: vscode.TaskPanelKind.Dedicated, | ||
383 | clear: true, | ||
384 | }; | ||
385 | |||
386 | return vscode.tasks.executeTask(task); | ||
387 | }; | ||
388 | } | ||
389 | |||
390 | export function debug(ctx: Ctx): Cmd { | ||
391 | let prevDebuggee: RunnableQuickPick | undefined; | ||
392 | |||
393 | return async () => { | ||
394 | const item = await selectRunnable(ctx, prevDebuggee, true); | ||
395 | if (!item) return; | ||
396 | |||
397 | item.detail = 'restart'; | ||
398 | prevDebuggee = item; | ||
399 | return await startDebugSession(ctx, item.runnable); | ||
400 | }; | ||
401 | } | ||
402 | |||
403 | export function debugSingle(ctx: Ctx): Cmd { | ||
404 | return async (config: ra.Runnable) => { | ||
405 | await startDebugSession(ctx, config); | ||
406 | }; | ||
407 | } | ||
408 | |||
409 | export function newDebugConfig(ctx: Ctx): Cmd { | ||
410 | return async () => { | ||
411 | const item = await selectRunnable(ctx, undefined, true, false); | ||
412 | if (!item) return; | ||
413 | |||
414 | await makeDebugConfig(ctx, item.runnable); | ||
415 | }; | ||
416 | } | ||
diff --git a/editors/code/src/debug.ts b/editors/code/src/debug.ts index 027504ecd..1e421d407 100644 --- a/editors/code/src/debug.ts +++ b/editors/code/src/debug.ts | |||
@@ -3,46 +3,59 @@ 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"); |
10 | type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; | 10 | type DebugConfigProvider = (config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>) => vscode.DebugConfiguration; |
11 | 11 | ||
12 | function getLldbDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { | 12 | export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> { |
13 | return { | 13 | const scope = ctx.activeRustEditor?.document.uri; |
14 | type: "lldb", | 14 | if (!scope) return; |
15 | request: "launch", | ||
16 | name: config.label, | ||
17 | program: executable, | ||
18 | args: config.extraArgs, | ||
19 | cwd: config.cwd, | ||
20 | sourceMap: sourceFileMap, | ||
21 | sourceLanguages: ["rust"] | ||
22 | }; | ||
23 | } | ||
24 | 15 | ||
25 | function getCppvsDebugConfig(config: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { | 16 | const debugConfig = await getDebugConfiguration(ctx, runnable); |
26 | return { | 17 | if (!debugConfig) return; |
27 | type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", | 18 | |
28 | request: "launch", | 19 | const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); |
29 | name: config.label, | 20 | const configurations = wsLaunchSection.get<any[]>("configurations") || []; |
30 | program: executable, | 21 | |
31 | args: config.extraArgs, | 22 | const index = configurations.findIndex(c => c.name === debugConfig.name); |
32 | cwd: config.cwd, | 23 | if (index !== -1) { |
33 | sourceFileMap: sourceFileMap, | 24 | const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update'); |
34 | }; | 25 | if (answer === "Cancel") return; |
26 | |||
27 | configurations[index] = debugConfig; | ||
28 | } else { | ||
29 | configurations.push(debugConfig); | ||
30 | } | ||
31 | |||
32 | await wsLaunchSection.update("configurations", configurations); | ||
35 | } | 33 | } |
36 | 34 | ||
37 | async function getDebugExecutable(config: ra.Runnable): Promise<string> { | 35 | export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promise<boolean> { |
38 | const cargo = new Cargo(config.cwd || '.', debugOutput); | 36 | let debugConfig: vscode.DebugConfiguration | undefined = undefined; |
39 | const executable = await cargo.executableFromArgs(config.args); | 37 | let message = ""; |
40 | 38 | ||
41 | // if we are here, there were no compilation errors. | 39 | const wsLaunchSection = vscode.workspace.getConfiguration("launch"); |
42 | return executable; | 40 | const configurations = wsLaunchSection.get<any[]>("configurations") || []; |
41 | |||
42 | const index = configurations.findIndex(c => c.name === runnable.label); | ||
43 | if (-1 !== index) { | ||
44 | debugConfig = configurations[index]; | ||
45 | message = " (from launch.json)"; | ||
46 | debugOutput.clear(); | ||
47 | } else { | ||
48 | debugConfig = await getDebugConfiguration(ctx, runnable); | ||
49 | } | ||
50 | |||
51 | if (!debugConfig) return false; | ||
52 | |||
53 | debugOutput.appendLine(`Launching debug configuration${message}:`); | ||
54 | debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); | ||
55 | return vscode.debug.startDebugging(undefined, debugConfig); | ||
43 | } | 56 | } |
44 | 57 | ||
45 | export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> { | 58 | async function getDebugConfiguration(ctx: Ctx, runnable: ra.Runnable): Promise<vscode.DebugConfiguration | undefined> { |
46 | const editor = ctx.activeRustEditor; | 59 | const editor = ctx.activeRustEditor; |
47 | if (!editor) return; | 60 | if (!editor) return; |
48 | 61 | ||
@@ -78,8 +91,8 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom | |||
78 | return path.normalize(p).replace(wsFolder, '${workspaceRoot}'); | 91 | return path.normalize(p).replace(wsFolder, '${workspaceRoot}'); |
79 | } | 92 | } |
80 | 93 | ||
81 | const executable = await getDebugExecutable(config); | 94 | const executable = await getDebugExecutable(runnable); |
82 | const debugConfig = knownEngines[debugEngine.id](config, simplifyPath(executable), debugOptions.sourceFileMap); | 95 | const debugConfig = knownEngines[debugEngine.id](runnable, simplifyPath(executable), debugOptions.sourceFileMap); |
83 | if (debugConfig.type in debugOptions.engineSettings) { | 96 | if (debugConfig.type in debugOptions.engineSettings) { |
84 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; | 97 | const settingsMap = (debugOptions.engineSettings as any)[debugConfig.type]; |
85 | for (var key in settingsMap) { | 98 | for (var key in settingsMap) { |
@@ -100,25 +113,35 @@ export async function getDebugConfiguration(ctx: Ctx, config: ra.Runnable): Prom | |||
100 | return debugConfig; | 113 | return debugConfig; |
101 | } | 114 | } |
102 | 115 | ||
103 | export async function startDebugSession(ctx: Ctx, config: ra.Runnable): Promise<boolean> { | 116 | async function getDebugExecutable(runnable: ra.Runnable): Promise<string> { |
104 | let debugConfig: vscode.DebugConfiguration | undefined = undefined; | 117 | const cargo = new Cargo(runnable.cwd || '.', debugOutput); |
105 | let message = ""; | 118 | const executable = await cargo.executableFromArgs(runnable.args); |
106 | |||
107 | const wsLaunchSection = vscode.workspace.getConfiguration("launch"); | ||
108 | const configurations = wsLaunchSection.get<any[]>("configurations") || []; | ||
109 | 119 | ||
110 | const index = configurations.findIndex(c => c.name === config.label); | 120 | // if we are here, there were no compilation errors. |
111 | if (-1 !== index) { | 121 | return executable; |
112 | debugConfig = configurations[index]; | 122 | } |
113 | message = " (from launch.json)"; | ||
114 | debugOutput.clear(); | ||
115 | } else { | ||
116 | debugConfig = await getDebugConfiguration(ctx, config); | ||
117 | } | ||
118 | 123 | ||
119 | if (!debugConfig) return false; | 124 | function getLldbDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { |
125 | return { | ||
126 | type: "lldb", | ||
127 | request: "launch", | ||
128 | name: runnable.label, | ||
129 | program: executable, | ||
130 | args: runnable.extraArgs, | ||
131 | cwd: runnable.cwd, | ||
132 | sourceMap: sourceFileMap, | ||
133 | sourceLanguages: ["rust"] | ||
134 | }; | ||
135 | } | ||
120 | 136 | ||
121 | debugOutput.appendLine(`Launching debug configuration${message}:`); | 137 | function getCppvsDebugConfig(runnable: ra.Runnable, executable: string, sourceFileMap?: Record<string, string>): vscode.DebugConfiguration { |
122 | debugOutput.appendLine(JSON.stringify(debugConfig, null, 2)); | 138 | return { |
123 | return vscode.debug.startDebugging(undefined, debugConfig); | 139 | type: (os.platform() === "win32") ? "cppvsdbg" : "cppdbg", |
140 | request: "launch", | ||
141 | name: runnable.label, | ||
142 | program: executable, | ||
143 | args: runnable.extraArgs, | ||
144 | cwd: runnable.cwd, | ||
145 | sourceFileMap: sourceFileMap, | ||
146 | }; | ||
124 | } | 147 | } |
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 8e1ba83ed..5fc4f8e41 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts | |||
@@ -1,9 +1,10 @@ | |||
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 } from './ctx'; |
6 | import { startDebugSession, getDebugConfiguration } from './debug'; | 7 | import { makeDebugConfig } from './debug'; |
7 | 8 | ||
8 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; | 9 | const quickPickButtons = [{ iconPath: new vscode.ThemeIcon("save"), tooltip: "Save as a launch.json configurtation." }]; |
9 | 10 | ||
@@ -64,7 +65,7 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, | |||
64 | quickPick.onDidHide(() => close()), | 65 | quickPick.onDidHide(() => close()), |
65 | quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), | 66 | quickPick.onDidAccept(() => close(quickPick.selectedItems[0])), |
66 | quickPick.onDidTriggerButton((_button) => { | 67 | quickPick.onDidTriggerButton((_button) => { |
67 | (async () => await makeDebugConfig(ctx, quickPick.activeItems[0]))(); | 68 | (async () => await makeDebugConfig(ctx, quickPick.activeItems[0].runnable))(); |
68 | close(); | 69 | close(); |
69 | }), | 70 | }), |
70 | quickPick.onDidChangeActive((active) => { | 71 | quickPick.onDidChangeActive((active) => { |
@@ -83,74 +84,6 @@ export async function selectRunnable(ctx: Ctx, prevRunnable?: RunnableQuickPick, | |||
83 | }); | 84 | }); |
84 | } | 85 | } |
85 | 86 | ||
86 | export function runSingle(ctx: Ctx): Cmd { | ||
87 | return async (runnable: ra.Runnable) => { | ||
88 | const editor = ctx.activeRustEditor; | ||
89 | if (!editor) return; | ||
90 | |||
91 | const task = createTask(runnable); | ||
92 | task.group = vscode.TaskGroup.Build; | ||
93 | task.presentationOptions = { | ||
94 | reveal: vscode.TaskRevealKind.Always, | ||
95 | panel: vscode.TaskPanelKind.Dedicated, | ||
96 | clear: true, | ||
97 | }; | ||
98 | |||
99 | return vscode.tasks.executeTask(task); | ||
100 | }; | ||
101 | } | ||
102 | |||
103 | export function debug(ctx: Ctx): Cmd { | ||
104 | let prevDebuggee: RunnableQuickPick | undefined; | ||
105 | |||
106 | return async () => { | ||
107 | const item = await selectRunnable(ctx, prevDebuggee, true); | ||
108 | if (!item) return; | ||
109 | |||
110 | item.detail = 'restart'; | ||
111 | prevDebuggee = item; | ||
112 | return await startDebugSession(ctx, item.runnable); | ||
113 | }; | ||
114 | } | ||
115 | |||
116 | export function debugSingle(ctx: Ctx): Cmd { | ||
117 | return async (config: ra.Runnable) => { | ||
118 | await startDebugSession(ctx, config); | ||
119 | }; | ||
120 | } | ||
121 | |||
122 | async function makeDebugConfig(ctx: Ctx, item: RunnableQuickPick): Promise<void> { | ||
123 | const scope = ctx.activeRustEditor?.document.uri; | ||
124 | if (!scope) return; | ||
125 | |||
126 | const debugConfig = await getDebugConfiguration(ctx, item.runnable); | ||
127 | if (!debugConfig) return; | ||
128 | |||
129 | const wsLaunchSection = vscode.workspace.getConfiguration("launch", scope); | ||
130 | const configurations = wsLaunchSection.get<any[]>("configurations") || []; | ||
131 | |||
132 | const index = configurations.findIndex(c => c.name === debugConfig.name); | ||
133 | if (index !== -1) { | ||
134 | const answer = await vscode.window.showErrorMessage(`Launch configuration '${debugConfig.name}' already exists!`, 'Cancel', 'Update'); | ||
135 | if (answer === "Cancel") return; | ||
136 | |||
137 | configurations[index] = debugConfig; | ||
138 | } else { | ||
139 | configurations.push(debugConfig); | ||
140 | } | ||
141 | |||
142 | await wsLaunchSection.update("configurations", configurations); | ||
143 | } | ||
144 | |||
145 | export function newDebugConfig(ctx: Ctx): Cmd { | ||
146 | return async () => { | ||
147 | const item = await selectRunnable(ctx, undefined, true, false); | ||
148 | if (!item) return; | ||
149 | |||
150 | await makeDebugConfig(ctx, item); | ||
151 | }; | ||
152 | } | ||
153 | |||
154 | export class RunnableQuickPick implements vscode.QuickPickItem { | 87 | export class RunnableQuickPick implements vscode.QuickPickItem { |
155 | public label: string; | 88 | public label: string; |
156 | public description?: string | undefined; | 89 | public description?: string | undefined; |
@@ -175,7 +108,7 @@ export function createTask(spec: ra.Runnable): vscode.Task { | |||
175 | const definition: CargoTaskDefinition = { | 108 | const definition: CargoTaskDefinition = { |
176 | type: 'cargo', | 109 | type: 'cargo', |
177 | label: spec.label, | 110 | label: spec.label, |
178 | command: spec.bin, | 111 | command: toolchain.getPathForExecutable(spec.kind), |
179 | args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, | 112 | args: spec.extraArgs ? [...spec.args, '--', ...spec.extraArgs] : spec.args, |
180 | env: Object.assign({}, process.env, spec.env), | 113 | env: Object.assign({}, process.env, spec.env), |
181 | }; | 114 | }; |
diff --git a/editors/code/src/tasks.ts b/editors/code/src/tasks.ts index 1366c76d6..9748824df 100644 --- a/editors/code/src/tasks.ts +++ b/editors/code/src/tasks.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import * as vscode from 'vscode'; | 1 | import * as vscode from 'vscode'; |
2 | import * as toolchain from "./toolchain"; | ||
2 | 3 | ||
3 | // 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 |
4 | // 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. |
@@ -24,6 +25,8 @@ class CargoTaskProvider implements vscode.TaskProvider { | |||
24 | // 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 |
25 | // tasks.json - only tweaked. | 26 | // tasks.json - only tweaked. |
26 | 27 | ||
28 | const cargoPath = toolchain.cargoPath(); | ||
29 | |||
27 | return [ | 30 | return [ |
28 | { command: 'build', group: vscode.TaskGroup.Build }, | 31 | { command: 'build', group: vscode.TaskGroup.Build }, |
29 | { command: 'check', group: vscode.TaskGroup.Build }, | 32 | { command: 'check', group: vscode.TaskGroup.Build }, |
@@ -46,7 +49,7 @@ class CargoTaskProvider implements vscode.TaskProvider { | |||
46 | `cargo ${command}`, | 49 | `cargo ${command}`, |
47 | 'rust', | 50 | 'rust', |
48 | // What to do when this command is executed. | 51 | // What to do when this command is executed. |
49 | new vscode.ShellExecution('cargo', [command]), | 52 | new vscode.ShellExecution(cargoPath, [command]), |
50 | // Problem matchers. | 53 | // Problem matchers. |
51 | ['$rustc'], | 54 | ['$rustc'], |
52 | ); | 55 | ); |
@@ -80,4 +83,4 @@ class CargoTaskProvider implements vscode.TaskProvider { | |||
80 | export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { | 83 | export function activateTaskProvider(target: vscode.WorkspaceFolder): vscode.Disposable { |
81 | const provider = new CargoTaskProvider(target); | 84 | const provider = new CargoTaskProvider(target); |
82 | return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); | 85 | return vscode.tasks.registerTaskProvider(TASK_TYPE, provider); |
83 | } \ No newline at end of file | 86 | } |
diff --git a/editors/code/src/cargo.ts b/editors/code/src/toolchain.ts index a55b2f860..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_env::get_path_for_executable` implementation | 124 | /** Mirrors `ra_toolchain::cargo()` implementation */ |
130 | 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); |