diff options
-rw-r--r-- | Cargo.lock | 10 | ||||
-rw-r--r-- | crates/expect/Cargo.toml | 10 | ||||
-rw-r--r-- | crates/expect/src/lib.rs | 293 | ||||
-rw-r--r-- | crates/ra_ide/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_ide/src/goto_definition.rs | 41 | ||||
-rw-r--r-- | crates/rust-analyzer/src/handlers.rs | 25 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lsp_ext.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 1 | ||||
-rw-r--r-- | editors/code/src/lsp_ext.ts | 1 | ||||
-rw-r--r-- | editors/code/src/run.ts | 6 |
10 files changed, 377 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock index ca3d14a09..c10803645 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -352,6 +352,15 @@ dependencies = [ | |||
352 | ] | 352 | ] |
353 | 353 | ||
354 | [[package]] | 354 | [[package]] |
355 | name = "expect" | ||
356 | version = "0.1.0" | ||
357 | dependencies = [ | ||
358 | "difference", | ||
359 | "once_cell", | ||
360 | "stdx", | ||
361 | ] | ||
362 | |||
363 | [[package]] | ||
355 | name = "filetime" | 364 | name = "filetime" |
356 | version = "0.2.10" | 365 | version = "0.2.10" |
357 | source = "registry+https://github.com/rust-lang/crates.io-index" | 366 | source = "registry+https://github.com/rust-lang/crates.io-index" |
@@ -1134,6 +1143,7 @@ name = "ra_ide" | |||
1134 | version = "0.1.0" | 1143 | version = "0.1.0" |
1135 | dependencies = [ | 1144 | dependencies = [ |
1136 | "either", | 1145 | "either", |
1146 | "expect", | ||
1137 | "indexmap", | 1147 | "indexmap", |
1138 | "insta", | 1148 | "insta", |
1139 | "itertools", | 1149 | "itertools", |
diff --git a/crates/expect/Cargo.toml b/crates/expect/Cargo.toml new file mode 100644 index 000000000..caee43106 --- /dev/null +++ b/crates/expect/Cargo.toml | |||
@@ -0,0 +1,10 @@ | |||
1 | [package] | ||
2 | name = "expect" | ||
3 | version = "0.1.0" | ||
4 | authors = ["rust-analyzer developers"] | ||
5 | edition = "2018" | ||
6 | |||
7 | [dependencies] | ||
8 | once_cell = "1" | ||
9 | difference = "2" | ||
10 | stdx = { path = "../stdx" } | ||
diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs new file mode 100644 index 000000000..dd7b96aab --- /dev/null +++ b/crates/expect/src/lib.rs | |||
@@ -0,0 +1,293 @@ | |||
1 | //! Snapshot testing library, see | ||
2 | //! https://github.com/rust-analyzer/rust-analyzer/pull/5101 | ||
3 | use std::{ | ||
4 | collections::HashMap, | ||
5 | env, fmt, fs, | ||
6 | ops::Range, | ||
7 | panic, | ||
8 | path::{Path, PathBuf}, | ||
9 | sync::Mutex, | ||
10 | }; | ||
11 | |||
12 | use difference::Changeset; | ||
13 | use once_cell::sync::Lazy; | ||
14 | use stdx::{lines_with_ends, trim_indent}; | ||
15 | |||
16 | const HELP: &str = " | ||
17 | You can update all `expect![[]]` tests by: | ||
18 | |||
19 | env UPDATE_EXPECT=1 cargo test | ||
20 | |||
21 | To update a single test, place the cursor on `expect` token and use `run` feature of rust-analyzer. | ||
22 | "; | ||
23 | |||
24 | fn update_expect() -> bool { | ||
25 | env::var("UPDATE_EXPECT").is_ok() | ||
26 | } | ||
27 | |||
28 | /// expect![[""]] | ||
29 | #[macro_export] | ||
30 | macro_rules! expect { | ||
31 | [[$lit:literal]] => {$crate::Expect { | ||
32 | file: file!(), | ||
33 | line: line!(), | ||
34 | column: column!(), | ||
35 | data: $lit, | ||
36 | }}; | ||
37 | [[]] => { $crate::expect![[""]] }; | ||
38 | } | ||
39 | |||
40 | #[derive(Debug)] | ||
41 | pub struct Expect { | ||
42 | pub file: &'static str, | ||
43 | pub line: u32, | ||
44 | pub column: u32, | ||
45 | pub data: &'static str, | ||
46 | } | ||
47 | |||
48 | impl Expect { | ||
49 | pub fn assert_eq(&self, actual: &str) { | ||
50 | let trimmed = self.trimmed(); | ||
51 | if &trimmed == actual { | ||
52 | return; | ||
53 | } | ||
54 | Runtime::fail(self, &trimmed, actual); | ||
55 | } | ||
56 | pub fn assert_debug_eq(&self, actual: &impl fmt::Debug) { | ||
57 | let actual = format!("{:#?}\n", actual); | ||
58 | self.assert_eq(&actual) | ||
59 | } | ||
60 | |||
61 | fn trimmed(&self) -> String { | ||
62 | if !self.data.contains('\n') { | ||
63 | return self.data.to_string(); | ||
64 | } | ||
65 | trim_indent(self.data) | ||
66 | } | ||
67 | |||
68 | fn locate(&self, file: &str) -> Location { | ||
69 | let mut target_line = None; | ||
70 | let mut line_start = 0; | ||
71 | for (i, line) in lines_with_ends(file).enumerate() { | ||
72 | if i == self.line as usize - 1 { | ||
73 | let pat = "expect![["; | ||
74 | let offset = line.find(pat).unwrap(); | ||
75 | let literal_start = line_start + offset + pat.len(); | ||
76 | let indent = line.chars().take_while(|&it| it == ' ').count(); | ||
77 | target_line = Some((literal_start, indent)); | ||
78 | break; | ||
79 | } | ||
80 | line_start += line.len(); | ||
81 | } | ||
82 | let (literal_start, line_indent) = target_line.unwrap(); | ||
83 | let literal_length = | ||
84 | file[literal_start..].find("]]").expect("Couldn't find matching `]]` for `expect![[`."); | ||
85 | let literal_range = literal_start..literal_start + literal_length; | ||
86 | Location { line_indent, literal_range } | ||
87 | } | ||
88 | } | ||
89 | |||
90 | #[derive(Default)] | ||
91 | struct Runtime { | ||
92 | help_printed: bool, | ||
93 | per_file: HashMap<&'static str, FileRuntime>, | ||
94 | } | ||
95 | static RT: Lazy<Mutex<Runtime>> = Lazy::new(Default::default); | ||
96 | |||
97 | impl Runtime { | ||
98 | fn fail(expect: &Expect, expected: &str, actual: &str) { | ||
99 | let mut rt = RT.lock().unwrap_or_else(|poisoned| poisoned.into_inner()); | ||
100 | let mut updated = ""; | ||
101 | if update_expect() { | ||
102 | updated = " (updated)"; | ||
103 | rt.per_file | ||
104 | .entry(expect.file) | ||
105 | .or_insert_with(|| FileRuntime::new(expect)) | ||
106 | .update(expect, actual); | ||
107 | } | ||
108 | let print_help = !rt.help_printed && !update_expect(); | ||
109 | rt.help_printed = true; | ||
110 | |||
111 | let help = if print_help { HELP } else { "" }; | ||
112 | |||
113 | let diff = Changeset::new(actual, expected, "\n"); | ||
114 | |||
115 | println!( | ||
116 | "\n | ||
117 | \x1b[1m\x1b[91merror\x1b[97m: expect test failed\x1b[0m{} | ||
118 | \x1b[1m\x1b[34m-->\x1b[0m {}:{}:{} | ||
119 | {} | ||
120 | \x1b[1mExpect\x1b[0m: | ||
121 | ---- | ||
122 | {} | ||
123 | ---- | ||
124 | |||
125 | \x1b[1mActual\x1b[0m: | ||
126 | ---- | ||
127 | {} | ||
128 | ---- | ||
129 | |||
130 | \x1b[1mDiff\x1b[0m: | ||
131 | ---- | ||
132 | {} | ||
133 | ---- | ||
134 | ", | ||
135 | updated, expect.file, expect.line, expect.column, help, expected, actual, diff | ||
136 | ); | ||
137 | // Use resume_unwind instead of panic!() to prevent a backtrace, which is unnecessary noise. | ||
138 | panic::resume_unwind(Box::new(())); | ||
139 | } | ||
140 | } | ||
141 | |||
142 | struct FileRuntime { | ||
143 | path: PathBuf, | ||
144 | original_text: String, | ||
145 | patchwork: Patchwork, | ||
146 | } | ||
147 | |||
148 | impl FileRuntime { | ||
149 | fn new(expect: &Expect) -> FileRuntime { | ||
150 | let path = workspace_root().join(expect.file); | ||
151 | let original_text = fs::read_to_string(&path).unwrap(); | ||
152 | let patchwork = Patchwork::new(original_text.clone()); | ||
153 | FileRuntime { path, original_text, patchwork } | ||
154 | } | ||
155 | fn update(&mut self, expect: &Expect, actual: &str) { | ||
156 | let loc = expect.locate(&self.original_text); | ||
157 | let patch = format_patch(loc.line_indent.clone(), actual); | ||
158 | self.patchwork.patch(loc.literal_range, &patch); | ||
159 | fs::write(&self.path, &self.patchwork.text).unwrap() | ||
160 | } | ||
161 | } | ||
162 | |||
163 | #[derive(Debug)] | ||
164 | struct Location { | ||
165 | line_indent: usize, | ||
166 | literal_range: Range<usize>, | ||
167 | } | ||
168 | |||
169 | #[derive(Debug)] | ||
170 | struct Patchwork { | ||
171 | text: String, | ||
172 | indels: Vec<(Range<usize>, usize)>, | ||
173 | } | ||
174 | |||
175 | impl Patchwork { | ||
176 | fn new(text: String) -> Patchwork { | ||
177 | Patchwork { text, indels: Vec::new() } | ||
178 | } | ||
179 | fn patch(&mut self, mut range: Range<usize>, patch: &str) { | ||
180 | self.indels.push((range.clone(), patch.len())); | ||
181 | self.indels.sort_by_key(|(delete, _insert)| delete.start); | ||
182 | |||
183 | let (delete, insert) = self | ||
184 | .indels | ||
185 | .iter() | ||
186 | .take_while(|(delete, _)| delete.start < range.start) | ||
187 | .map(|(delete, insert)| (delete.end - delete.start, insert)) | ||
188 | .fold((0usize, 0usize), |(x1, y1), (x2, y2)| (x1 + x2, y1 + y2)); | ||
189 | |||
190 | for pos in &mut [&mut range.start, &mut range.end] { | ||
191 | **pos -= delete; | ||
192 | **pos += insert; | ||
193 | } | ||
194 | |||
195 | self.text.replace_range(range, &patch); | ||
196 | } | ||
197 | } | ||
198 | |||
199 | fn format_patch(line_indent: usize, patch: &str) -> String { | ||
200 | let mut max_hashes = 0; | ||
201 | let mut cur_hashes = 0; | ||
202 | for byte in patch.bytes() { | ||
203 | if byte != b'#' { | ||
204 | cur_hashes = 0; | ||
205 | continue; | ||
206 | } | ||
207 | cur_hashes += 1; | ||
208 | max_hashes = max_hashes.max(cur_hashes); | ||
209 | } | ||
210 | let hashes = &"#".repeat(max_hashes + 1); | ||
211 | let indent = &" ".repeat(line_indent); | ||
212 | let is_multiline = patch.contains('\n'); | ||
213 | |||
214 | let mut buf = String::new(); | ||
215 | buf.push('r'); | ||
216 | buf.push_str(hashes); | ||
217 | buf.push('"'); | ||
218 | if is_multiline { | ||
219 | buf.push('\n'); | ||
220 | } | ||
221 | let mut final_newline = false; | ||
222 | for line in lines_with_ends(patch) { | ||
223 | if is_multiline { | ||
224 | buf.push_str(indent); | ||
225 | buf.push_str(" "); | ||
226 | } | ||
227 | buf.push_str(line); | ||
228 | final_newline = line.ends_with('\n'); | ||
229 | } | ||
230 | if final_newline { | ||
231 | buf.push_str(indent); | ||
232 | } | ||
233 | buf.push('"'); | ||
234 | buf.push_str(hashes); | ||
235 | buf | ||
236 | } | ||
237 | |||
238 | fn workspace_root() -> PathBuf { | ||
239 | Path::new( | ||
240 | &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()), | ||
241 | ) | ||
242 | .ancestors() | ||
243 | .nth(2) | ||
244 | .unwrap() | ||
245 | .to_path_buf() | ||
246 | } | ||
247 | |||
248 | #[cfg(test)] | ||
249 | mod tests { | ||
250 | use super::*; | ||
251 | |||
252 | #[test] | ||
253 | fn test_format_patch() { | ||
254 | let patch = format_patch(0, "hello\nworld\n"); | ||
255 | expect![[r##" | ||
256 | r#" | ||
257 | hello | ||
258 | world | ||
259 | "#"##]] | ||
260 | .assert_eq(&patch); | ||
261 | |||
262 | let patch = format_patch(4, "single line"); | ||
263 | expect![[r##"r#"single line"#"##]].assert_eq(&patch); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn test_patchwork() { | ||
268 | let mut patchwork = Patchwork::new("one two three".to_string()); | ||
269 | patchwork.patch(4..7, "zwei"); | ||
270 | patchwork.patch(0..3, "один"); | ||
271 | patchwork.patch(8..13, "3"); | ||
272 | expect![[r#" | ||
273 | Patchwork { | ||
274 | text: "один zwei 3", | ||
275 | indels: [ | ||
276 | ( | ||
277 | 0..3, | ||
278 | 8, | ||
279 | ), | ||
280 | ( | ||
281 | 4..7, | ||
282 | 4, | ||
283 | ), | ||
284 | ( | ||
285 | 8..13, | ||
286 | 1, | ||
287 | ), | ||
288 | ], | ||
289 | } | ||
290 | "#]] | ||
291 | .assert_debug_eq(&patchwork); | ||
292 | } | ||
293 | } | ||
diff --git a/crates/ra_ide/Cargo.toml b/crates/ra_ide/Cargo.toml index bbc6a5c9b..8e8892309 100644 --- a/crates/ra_ide/Cargo.toml +++ b/crates/ra_ide/Cargo.toml | |||
@@ -28,6 +28,7 @@ ra_cfg = { path = "../ra_cfg" } | |||
28 | ra_fmt = { path = "../ra_fmt" } | 28 | ra_fmt = { path = "../ra_fmt" } |
29 | ra_prof = { path = "../ra_prof" } | 29 | ra_prof = { path = "../ra_prof" } |
30 | test_utils = { path = "../test_utils" } | 30 | test_utils = { path = "../test_utils" } |
31 | expect = { path = "../expect" } | ||
31 | ra_assists = { path = "../ra_assists" } | 32 | ra_assists = { path = "../ra_assists" } |
32 | ra_ssr = { path = "../ra_ssr" } | 33 | ra_ssr = { path = "../ra_ssr" } |
33 | 34 | ||
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index bea7fbfa7..969d5e0ff 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -103,6 +103,7 @@ pub(crate) fn reference_definition( | |||
103 | 103 | ||
104 | #[cfg(test)] | 104 | #[cfg(test)] |
105 | mod tests { | 105 | mod tests { |
106 | use expect::{expect, Expect}; | ||
106 | use test_utils::assert_eq_text; | 107 | use test_utils::assert_eq_text; |
107 | 108 | ||
108 | use crate::mock_analysis::analysis_and_position; | 109 | use crate::mock_analysis::analysis_and_position; |
@@ -142,16 +143,40 @@ mod tests { | |||
142 | nav.assert_match(expected); | 143 | nav.assert_match(expected); |
143 | } | 144 | } |
144 | 145 | ||
146 | fn check(ra_fixture: &str, expect: Expect) { | ||
147 | let (analysis, pos) = analysis_and_position(ra_fixture); | ||
148 | |||
149 | let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; | ||
150 | if navs.len() == 0 { | ||
151 | panic!("unresolved reference") | ||
152 | } | ||
153 | assert_eq!(navs.len(), 1); | ||
154 | |||
155 | let nav = navs.pop().unwrap(); | ||
156 | let file_text = analysis.file_text(nav.file_id()).unwrap(); | ||
157 | |||
158 | let mut actual = nav.debug_render(); | ||
159 | actual += "\n"; | ||
160 | actual += &file_text[nav.full_range()].to_string(); | ||
161 | if let Some(focus) = nav.focus_range() { | ||
162 | actual += "|"; | ||
163 | actual += &file_text[focus]; | ||
164 | actual += "\n"; | ||
165 | } | ||
166 | expect.assert_eq(&actual); | ||
167 | } | ||
168 | |||
145 | #[test] | 169 | #[test] |
146 | fn goto_def_in_items() { | 170 | fn goto_def_in_items() { |
147 | check_goto( | 171 | check( |
148 | " | 172 | r#" |
149 | //- /lib.rs | 173 | struct Foo; |
150 | struct Foo; | 174 | enum E { X(Foo<|>) } |
151 | enum E { X(Foo<|>) } | 175 | "#, |
152 | ", | 176 | expect![[r#" |
153 | "Foo STRUCT_DEF FileId(1) 0..11 7..10", | 177 | Foo STRUCT_DEF FileId(1) 0..11 7..10 |
154 | "struct Foo;|Foo", | 178 | struct Foo;|Foo |
179 | "#]], | ||
155 | ); | 180 | ); |
156 | } | 181 | } |
157 | 182 | ||
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index e35a5e846..0940fcc28 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs | |||
@@ -23,7 +23,7 @@ use ra_ide::{ | |||
23 | }; | 23 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 25 | use ra_project_model::TargetKind; |
26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize}; | 26 | use ra_syntax::{algo, ast, AstNode, SyntaxKind, TextRange, TextSize}; |
27 | use serde::{Deserialize, Serialize}; | 27 | use serde::{Deserialize, Serialize}; |
28 | use serde_json::to_value; | 28 | use serde_json::to_value; |
29 | use stdx::{format_to, split_delim}; | 29 | use stdx::{format_to, split_delim}; |
@@ -407,8 +407,19 @@ pub(crate) fn handle_runnables( | |||
407 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; | 407 | let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; |
408 | let line_index = snap.analysis.file_line_index(file_id)?; | 408 | let line_index = snap.analysis.file_line_index(file_id)?; |
409 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); | 409 | let offset = params.position.map(|it| from_proto::offset(&line_index, it)); |
410 | let mut res = Vec::new(); | ||
411 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; | 410 | let cargo_spec = CargoTargetSpec::for_file(&snap, file_id)?; |
411 | |||
412 | let expect_test = match offset { | ||
413 | Some(offset) => { | ||
414 | let source_file = snap.analysis.parse(file_id)?; | ||
415 | algo::find_node_at_offset::<ast::MacroCall>(source_file.syntax(), offset) | ||
416 | .and_then(|it| it.path()?.segment()?.name_ref()) | ||
417 | .map_or(false, |it| it.text() == "expect") | ||
418 | } | ||
419 | None => false, | ||
420 | }; | ||
421 | |||
422 | let mut res = Vec::new(); | ||
412 | for runnable in snap.analysis.runnables(file_id)? { | 423 | for runnable in snap.analysis.runnables(file_id)? { |
413 | if let Some(offset) = offset { | 424 | if let Some(offset) = offset { |
414 | if !runnable.nav.full_range().contains_inclusive(offset) { | 425 | if !runnable.nav.full_range().contains_inclusive(offset) { |
@@ -418,8 +429,12 @@ pub(crate) fn handle_runnables( | |||
418 | if should_skip_target(&runnable, cargo_spec.as_ref()) { | 429 | if should_skip_target(&runnable, cargo_spec.as_ref()) { |
419 | continue; | 430 | continue; |
420 | } | 431 | } |
421 | 432 | let mut runnable = to_proto::runnable(&snap, file_id, runnable)?; | |
422 | res.push(to_proto::runnable(&snap, file_id, runnable)?); | 433 | if expect_test { |
434 | runnable.label = format!("{} + expect", runnable.label); | ||
435 | runnable.args.expect_test = Some(true); | ||
436 | } | ||
437 | res.push(runnable); | ||
423 | } | 438 | } |
424 | 439 | ||
425 | // Add `cargo check` and `cargo test` for the whole package | 440 | // Add `cargo check` and `cargo test` for the whole package |
@@ -438,6 +453,7 @@ pub(crate) fn handle_runnables( | |||
438 | spec.package.clone(), | 453 | spec.package.clone(), |
439 | ], | 454 | ], |
440 | executable_args: Vec::new(), | 455 | executable_args: Vec::new(), |
456 | expect_test: None, | ||
441 | }, | 457 | }, |
442 | }) | 458 | }) |
443 | } | 459 | } |
@@ -451,6 +467,7 @@ pub(crate) fn handle_runnables( | |||
451 | workspace_root: None, | 467 | workspace_root: None, |
452 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], | 468 | cargo_args: vec!["check".to_string(), "--workspace".to_string()], |
453 | executable_args: Vec::new(), | 469 | executable_args: Vec::new(), |
470 | expect_test: None, | ||
454 | }, | 471 | }, |
455 | }); | 472 | }); |
456 | } | 473 | } |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 1371f6cb4..1befe678c 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -161,6 +161,8 @@ pub struct CargoRunnable { | |||
161 | pub cargo_args: Vec<String>, | 161 | pub cargo_args: Vec<String>, |
162 | // stuff after -- | 162 | // stuff after -- |
163 | pub executable_args: Vec<String>, | 163 | pub executable_args: Vec<String>, |
164 | #[serde(skip_serializing_if = "Option::is_none")] | ||
165 | pub expect_test: Option<bool>, | ||
164 | } | 166 | } |
165 | 167 | ||
166 | pub enum InlayHints {} | 168 | pub enum InlayHints {} |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index f6cb8e4bb..a03222ae9 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -666,6 +666,7 @@ pub(crate) fn runnable( | |||
666 | workspace_root: workspace_root.map(|it| it.into()), | 666 | workspace_root: workspace_root.map(|it| it.into()), |
667 | cargo_args, | 667 | cargo_args, |
668 | executable_args, | 668 | executable_args, |
669 | expect_test: None, | ||
669 | }, | 670 | }, |
670 | }) | 671 | }) |
671 | } | 672 | } |
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index e16ea799c..fdb99956b 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts | |||
@@ -60,6 +60,7 @@ export interface Runnable { | |||
60 | workspaceRoot?: string; | 60 | workspaceRoot?: string; |
61 | cargoArgs: string[]; | 61 | cargoArgs: string[]; |
62 | executableArgs: string[]; | 62 | executableArgs: string[]; |
63 | expectTest?: boolean; | ||
63 | }; | 64 | }; |
64 | } | 65 | } |
65 | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables"); | 66 | export const runnables = new lc.RequestType<RunnablesParams, Runnable[], void>("experimental/runnables"); |
diff --git a/editors/code/src/run.ts b/editors/code/src/run.ts index 766b05112..e1430e31f 100644 --- a/editors/code/src/run.ts +++ b/editors/code/src/run.ts | |||
@@ -108,12 +108,16 @@ export async function createTask(runnable: ra.Runnable, config: Config): Promise | |||
108 | if (runnable.args.executableArgs.length > 0) { | 108 | if (runnable.args.executableArgs.length > 0) { |
109 | args.push('--', ...runnable.args.executableArgs); | 109 | args.push('--', ...runnable.args.executableArgs); |
110 | } | 110 | } |
111 | const env: { [key: string]: string } = { "RUST_BACKTRACE": "short" }; | ||
112 | if (runnable.args.expectTest) { | ||
113 | env["UPDATE_EXPECT"] = "1"; | ||
114 | } | ||
111 | const definition: tasks.CargoTaskDefinition = { | 115 | const definition: tasks.CargoTaskDefinition = { |
112 | type: tasks.TASK_TYPE, | 116 | type: tasks.TASK_TYPE, |
113 | command: args[0], // run, test, etc... | 117 | command: args[0], // run, test, etc... |
114 | args: args.slice(1), | 118 | args: args.slice(1), |
115 | cwd: runnable.args.workspaceRoot, | 119 | cwd: runnable.args.workspaceRoot, |
116 | env: Object.assign({}, process.env as { [key: string]: string }, { "RUST_BACKTRACE": "short" }), | 120 | env: Object.assign({}, process.env as { [key: string]: string }, env), |
117 | }; | 121 | }; |
118 | 122 | ||
119 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() | 123 | const target = vscode.workspace.workspaceFolders![0]; // safe, see main activate() |