aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_ide/src/display/navigation_target.rs66
-rw-r--r--crates/ra_ide/src/runnables.rs250
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs2
-rw-r--r--crates/rust-analyzer/src/caps.rs3
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs85
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs31
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs165
-rw-r--r--crates/rust-analyzer/src/to_proto.rs38
-rw-r--r--crates/rust-analyzer/tests/heavy_tests/main.rs113
10 files changed, 471 insertions, 284 deletions
diff --git a/crates/ra_ide/src/display/navigation_target.rs b/crates/ra_ide/src/display/navigation_target.rs
index 5da28edd2..c7bb1e69f 100644
--- a/crates/ra_ide/src/display/navigation_target.rs
+++ b/crates/ra_ide/src/display/navigation_target.rs
@@ -92,15 +92,16 @@ impl NavigationTarget {
92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default(); 92 let name = module.name(db).map(|it| it.to_string().into()).unwrap_or_default();
93 if let Some(src) = module.declaration_source(db) { 93 if let Some(src) = module.declaration_source(db) {
94 let frange = original_range(db, src.as_ref().map(|it| it.syntax())); 94 let frange = original_range(db, src.as_ref().map(|it| it.syntax()));
95 return NavigationTarget::from_syntax( 95 let mut res = NavigationTarget::from_syntax(
96 frange.file_id, 96 frange.file_id,
97 name, 97 name,
98 None, 98 None,
99 frange.range, 99 frange.range,
100 src.value.syntax().kind(), 100 src.value.syntax().kind(),
101 src.value.doc_comment_text(),
102 src.value.short_label(),
103 ); 101 );
102 res.docs = src.value.doc_comment_text();
103 res.description = src.value.short_label();
104 return res;
104 } 105 }
105 module.to_nav(db) 106 module.to_nav(db)
106 } 107 }
@@ -130,11 +131,9 @@ impl NavigationTarget {
130 } 131 }
131 132
132 /// Allows `NavigationTarget` to be created from a `NameOwner` 133 /// Allows `NavigationTarget` to be created from a `NameOwner`
133 fn from_named( 134 pub(crate) fn from_named(
134 db: &RootDatabase, 135 db: &RootDatabase,
135 node: InFile<&dyn ast::NameOwner>, 136 node: InFile<&dyn ast::NameOwner>,
136 docs: Option<String>,
137 description: Option<String>,
138 ) -> NavigationTarget { 137 ) -> NavigationTarget {
139 //FIXME: use `_` instead of empty string 138 //FIXME: use `_` instead of empty string
140 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default(); 139 let name = node.value.name().map(|it| it.text().clone()).unwrap_or_default();
@@ -148,8 +147,6 @@ impl NavigationTarget {
148 focus_range, 147 focus_range,
149 frange.range, 148 frange.range,
150 node.value.syntax().kind(), 149 node.value.syntax().kind(),
151 docs,
152 description,
153 ) 150 )
154 } 151 }
155 152
@@ -159,8 +156,6 @@ impl NavigationTarget {
159 focus_range: Option<TextRange>, 156 focus_range: Option<TextRange>,
160 full_range: TextRange, 157 full_range: TextRange,
161 kind: SyntaxKind, 158 kind: SyntaxKind,
162 docs: Option<String>,
163 description: Option<String>,
164 ) -> NavigationTarget { 159 ) -> NavigationTarget {
165 NavigationTarget { 160 NavigationTarget {
166 file_id, 161 file_id,
@@ -169,8 +164,8 @@ impl NavigationTarget {
169 full_range, 164 full_range,
170 focus_range, 165 focus_range,
171 container_name: None, 166 container_name: None,
172 description, 167 description: None,
173 docs, 168 docs: None,
174 } 169 }
175 } 170 }
176} 171}
@@ -238,12 +233,11 @@ where
238{ 233{
239 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 234 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
240 let src = self.source(db); 235 let src = self.source(db);
241 NavigationTarget::from_named( 236 let mut res =
242 db, 237 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
243 src.as_ref().map(|it| it as &dyn ast::NameOwner), 238 res.docs = src.value.doc_comment_text();
244 src.value.doc_comment_text(), 239 res.description = src.value.short_label();
245 src.value.short_label(), 240 res
246 )
247 } 241 }
248} 242}
249 243
@@ -258,15 +252,7 @@ impl ToNav for hir::Module {
258 } 252 }
259 }; 253 };
260 let frange = original_range(db, src.with_value(syntax)); 254 let frange = original_range(db, src.with_value(syntax));
261 NavigationTarget::from_syntax( 255 NavigationTarget::from_syntax(frange.file_id, name, focus, frange.range, syntax.kind())
262 frange.file_id,
263 name,
264 focus,
265 frange.range,
266 syntax.kind(),
267 None,
268 None,
269 )
270 } 256 }
271} 257}
272 258
@@ -285,8 +271,6 @@ impl ToNav for hir::ImplDef {
285 None, 271 None,
286 frange.range, 272 frange.range,
287 src.value.syntax().kind(), 273 src.value.syntax().kind(),
288 None,
289 None,
290 ) 274 )
291 } 275 }
292} 276}
@@ -296,12 +280,12 @@ impl ToNav for hir::Field {
296 let src = self.source(db); 280 let src = self.source(db);
297 281
298 match &src.value { 282 match &src.value {
299 FieldSource::Named(it) => NavigationTarget::from_named( 283 FieldSource::Named(it) => {
300 db, 284 let mut res = NavigationTarget::from_named(db, src.with_value(it));
301 src.with_value(it), 285 res.docs = it.doc_comment_text();
302 it.doc_comment_text(), 286 res.description = it.short_label();
303 it.short_label(), 287 res
304 ), 288 }
305 FieldSource::Pos(it) => { 289 FieldSource::Pos(it) => {
306 let frange = original_range(db, src.with_value(it.syntax())); 290 let frange = original_range(db, src.with_value(it.syntax()));
307 NavigationTarget::from_syntax( 291 NavigationTarget::from_syntax(
@@ -310,8 +294,6 @@ impl ToNav for hir::Field {
310 None, 294 None,
311 frange.range, 295 frange.range,
312 it.syntax().kind(), 296 it.syntax().kind(),
313 None,
314 None,
315 ) 297 )
316 } 298 }
317 } 299 }
@@ -322,12 +304,10 @@ impl ToNav for hir::MacroDef {
322 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { 304 fn to_nav(&self, db: &RootDatabase) -> NavigationTarget {
323 let src = self.source(db); 305 let src = self.source(db);
324 log::debug!("nav target {:#?}", src.value.syntax()); 306 log::debug!("nav target {:#?}", src.value.syntax());
325 NavigationTarget::from_named( 307 let mut res =
326 db, 308 NavigationTarget::from_named(db, src.as_ref().map(|it| it as &dyn ast::NameOwner));
327 src.as_ref().map(|it| it as &dyn ast::NameOwner), 309 res.docs = src.value.doc_comment_text();
328 src.value.doc_comment_text(), 310 res
329 None,
330 )
331 } 311 }
332} 312}
333 313
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..f32ce0d22 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,19 +1,19 @@
1use std::fmt;
2
1use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics}; 3use hir::{AsAssocItem, Attrs, HirFileId, InFile, Semantics};
2use itertools::Itertools; 4use itertools::Itertools;
5use ra_cfg::CfgExpr;
3use ra_ide_db::RootDatabase; 6use ra_ide_db::RootDatabase;
4use ra_syntax::{ 7use ra_syntax::{
5 ast::{self, AstNode, AttrsOwner, ModuleItemOwner, NameOwner}, 8 ast::{self, AstNode, AttrsOwner, DocCommentsOwner, ModuleItemOwner, NameOwner},
6 match_ast, SyntaxNode, TextRange, 9 match_ast, SyntaxNode,
7}; 10};
8 11
9use crate::FileId; 12use crate::{display::ToNav, FileId, NavigationTarget};
10use ast::DocCommentsOwner;
11use ra_cfg::CfgExpr;
12use std::fmt::Display;
13 13
14#[derive(Debug)] 14#[derive(Debug)]
15pub struct Runnable { 15pub struct Runnable {
16 pub range: TextRange, 16 pub nav: NavigationTarget,
17 pub kind: RunnableKind, 17 pub kind: RunnableKind,
18 pub cfg_exprs: Vec<CfgExpr>, 18 pub cfg_exprs: Vec<CfgExpr>,
19} 19}
@@ -24,8 +24,8 @@ pub enum TestId {
24 Path(String), 24 Path(String),
25} 25}
26 26
27impl Display for TestId { 27impl fmt::Display for TestId {
28 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 28 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
29 match self { 29 match self {
30 TestId::Name(name) => write!(f, "{}", name), 30 TestId::Name(name) => write!(f, "{}", name),
31 TestId::Path(path) => write!(f, "{}", path), 31 TestId::Path(path) => write!(f, "{}", path),
@@ -131,7 +131,8 @@ fn runnable_fn(
131 let cfg_exprs = 131 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 133
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 134 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def));
135 Some(Runnable { nav, kind, cfg_exprs })
135} 136}
136 137
137#[derive(Debug)] 138#[derive(Debug)]
@@ -183,7 +184,6 @@ fn runnable_mod(
183 if !has_test_function { 184 if !has_test_function {
184 return None; 185 return None;
185 } 186 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 187 let module_def = sema.to_def(&module)?;
188 188
189 let path = module_def 189 let path = module_def
@@ -197,7 +197,8 @@ fn runnable_mod(
197 let cfg_exprs = 197 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 199
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 200 let nav = module_def.to_nav(sema.db);
201 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 202}
202 203
203#[cfg(test)] 204#[cfg(test)]
@@ -227,12 +228,38 @@ mod tests {
227 @r###" 228 @r###"
228 [ 229 [
229 Runnable { 230 Runnable {
230 range: 1..21, 231 nav: NavigationTarget {
232 file_id: FileId(
233 1,
234 ),
235 full_range: 1..21,
236 name: "main",
237 kind: FN_DEF,
238 focus_range: Some(
239 12..16,
240 ),
241 container_name: None,
242 description: None,
243 docs: None,
244 },
231 kind: Bin, 245 kind: Bin,
232 cfg_exprs: [], 246 cfg_exprs: [],
233 }, 247 },
234 Runnable { 248 Runnable {
235 range: 22..46, 249 nav: NavigationTarget {
250 file_id: FileId(
251 1,
252 ),
253 full_range: 22..46,
254 name: "test_foo",
255 kind: FN_DEF,
256 focus_range: Some(
257 33..41,
258 ),
259 container_name: None,
260 description: None,
261 docs: None,
262 },
236 kind: Test { 263 kind: Test {
237 test_id: Path( 264 test_id: Path(
238 "test_foo", 265 "test_foo",
@@ -244,7 +271,20 @@ mod tests {
244 cfg_exprs: [], 271 cfg_exprs: [],
245 }, 272 },
246 Runnable { 273 Runnable {
247 range: 47..81, 274 nav: NavigationTarget {
275 file_id: FileId(
276 1,
277 ),
278 full_range: 47..81,
279 name: "test_foo",
280 kind: FN_DEF,
281 focus_range: Some(
282 68..76,
283 ),
284 container_name: None,
285 description: None,
286 docs: None,
287 },
248 kind: Test { 288 kind: Test {
249 test_id: Path( 289 test_id: Path(
250 "test_foo", 290 "test_foo",
@@ -279,12 +319,38 @@ mod tests {
279 @r###" 319 @r###"
280 [ 320 [
281 Runnable { 321 Runnable {
282 range: 1..21, 322 nav: NavigationTarget {
323 file_id: FileId(
324 1,
325 ),
326 full_range: 1..21,
327 name: "main",
328 kind: FN_DEF,
329 focus_range: Some(
330 12..16,
331 ),
332 container_name: None,
333 description: None,
334 docs: None,
335 },
283 kind: Bin, 336 kind: Bin,
284 cfg_exprs: [], 337 cfg_exprs: [],
285 }, 338 },
286 Runnable { 339 Runnable {
287 range: 22..64, 340 nav: NavigationTarget {
341 file_id: FileId(
342 1,
343 ),
344 full_range: 22..64,
345 name: "foo",
346 kind: FN_DEF,
347 focus_range: Some(
348 56..59,
349 ),
350 container_name: None,
351 description: None,
352 docs: None,
353 },
288 kind: DocTest { 354 kind: DocTest {
289 test_id: Path( 355 test_id: Path(
290 "foo", 356 "foo",
@@ -319,12 +385,38 @@ mod tests {
319 @r###" 385 @r###"
320 [ 386 [
321 Runnable { 387 Runnable {
322 range: 1..21, 388 nav: NavigationTarget {
389 file_id: FileId(
390 1,
391 ),
392 full_range: 1..21,
393 name: "main",
394 kind: FN_DEF,
395 focus_range: Some(
396 12..16,
397 ),
398 container_name: None,
399 description: None,
400 docs: None,
401 },
323 kind: Bin, 402 kind: Bin,
324 cfg_exprs: [], 403 cfg_exprs: [],
325 }, 404 },
326 Runnable { 405 Runnable {
327 range: 51..105, 406 nav: NavigationTarget {
407 file_id: FileId(
408 1,
409 ),
410 full_range: 51..105,
411 name: "foo",
412 kind: FN_DEF,
413 focus_range: Some(
414 97..100,
415 ),
416 container_name: None,
417 description: None,
418 docs: None,
419 },
328 kind: DocTest { 420 kind: DocTest {
329 test_id: Path( 421 test_id: Path(
330 "Data::foo", 422 "Data::foo",
@@ -354,14 +446,40 @@ mod tests {
354 @r###" 446 @r###"
355 [ 447 [
356 Runnable { 448 Runnable {
357 range: 1..59, 449 nav: NavigationTarget {
450 file_id: FileId(
451 1,
452 ),
453 full_range: 1..59,
454 name: "test_mod",
455 kind: MODULE,
456 focus_range: Some(
457 13..21,
458 ),
459 container_name: None,
460 description: None,
461 docs: None,
462 },
358 kind: TestMod { 463 kind: TestMod {
359 path: "test_mod", 464 path: "test_mod",
360 }, 465 },
361 cfg_exprs: [], 466 cfg_exprs: [],
362 }, 467 },
363 Runnable { 468 Runnable {
364 range: 28..57, 469 nav: NavigationTarget {
470 file_id: FileId(
471 1,
472 ),
473 full_range: 28..57,
474 name: "test_foo1",
475 kind: FN_DEF,
476 focus_range: Some(
477 43..52,
478 ),
479 container_name: None,
480 description: None,
481 docs: None,
482 },
365 kind: Test { 483 kind: Test {
366 test_id: Path( 484 test_id: Path(
367 "test_mod::test_foo1", 485 "test_mod::test_foo1",
@@ -396,14 +514,40 @@ mod tests {
396 @r###" 514 @r###"
397 [ 515 [
398 Runnable { 516 Runnable {
399 range: 23..85, 517 nav: NavigationTarget {
518 file_id: FileId(
519 1,
520 ),
521 full_range: 23..85,
522 name: "test_mod",
523 kind: MODULE,
524 focus_range: Some(
525 27..35,
526 ),
527 container_name: None,
528 description: None,
529 docs: None,
530 },
400 kind: TestMod { 531 kind: TestMod {
401 path: "foo::test_mod", 532 path: "foo::test_mod",
402 }, 533 },
403 cfg_exprs: [], 534 cfg_exprs: [],
404 }, 535 },
405 Runnable { 536 Runnable {
406 range: 46..79, 537 nav: NavigationTarget {
538 file_id: FileId(
539 1,
540 ),
541 full_range: 46..79,
542 name: "test_foo1",
543 kind: FN_DEF,
544 focus_range: Some(
545 65..74,
546 ),
547 container_name: None,
548 description: None,
549 docs: None,
550 },
407 kind: Test { 551 kind: Test {
408 test_id: Path( 552 test_id: Path(
409 "foo::test_mod::test_foo1", 553 "foo::test_mod::test_foo1",
@@ -440,14 +584,40 @@ mod tests {
440 @r###" 584 @r###"
441 [ 585 [
442 Runnable { 586 Runnable {
443 range: 41..115, 587 nav: NavigationTarget {
588 file_id: FileId(
589 1,
590 ),
591 full_range: 41..115,
592 name: "test_mod",
593 kind: MODULE,
594 focus_range: Some(
595 45..53,
596 ),
597 container_name: None,
598 description: None,
599 docs: None,
600 },
444 kind: TestMod { 601 kind: TestMod {
445 path: "foo::bar::test_mod", 602 path: "foo::bar::test_mod",
446 }, 603 },
447 cfg_exprs: [], 604 cfg_exprs: [],
448 }, 605 },
449 Runnable { 606 Runnable {
450 range: 68..105, 607 nav: NavigationTarget {
608 file_id: FileId(
609 1,
610 ),
611 full_range: 68..105,
612 name: "test_foo1",
613 kind: FN_DEF,
614 focus_range: Some(
615 91..100,
616 ),
617 container_name: None,
618 description: None,
619 docs: None,
620 },
451 kind: Test { 621 kind: Test {
452 test_id: Path( 622 test_id: Path(
453 "foo::bar::test_mod::test_foo1", 623 "foo::bar::test_mod::test_foo1",
@@ -479,7 +649,20 @@ mod tests {
479 @r###" 649 @r###"
480 [ 650 [
481 Runnable { 651 Runnable {
482 range: 1..58, 652 nav: NavigationTarget {
653 file_id: FileId(
654 1,
655 ),
656 full_range: 1..58,
657 name: "test_foo1",
658 kind: FN_DEF,
659 focus_range: Some(
660 44..53,
661 ),
662 container_name: None,
663 description: None,
664 docs: None,
665 },
483 kind: Test { 666 kind: Test {
484 test_id: Path( 667 test_id: Path(
485 "test_foo1", 668 "test_foo1",
@@ -516,7 +699,20 @@ mod tests {
516 @r###" 699 @r###"
517 [ 700 [
518 Runnable { 701 Runnable {
519 range: 1..80, 702 nav: NavigationTarget {
703 file_id: FileId(
704 1,
705 ),
706 full_range: 1..80,
707 name: "test_foo1",
708 kind: FN_DEF,
709 focus_range: Some(
710 66..75,
711 ),
712 container_name: None,
713 description: None,
714 docs: None,
715 },
520 kind: Test { 716 kind: Test {
521 test_id: Path( 717 test_id: Path(
522 "test_foo1", 718 "test_foo1",
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index a306ce95f..4b7444039 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -64,7 +64,7 @@ impl Default for CargoConfig {
64 fn default() -> Self { 64 fn default() -> Self {
65 CargoConfig { 65 CargoConfig {
66 no_default_features: false, 66 no_default_features: false,
67 all_features: true, 67 all_features: false,
68 features: Vec::new(), 68 features: Vec::new(),
69 load_out_dirs_from_check: false, 69 load_out_dirs_from_check: false,
70 target: None, 70 target: None,
diff --git a/crates/rust-analyzer/src/caps.rs b/crates/rust-analyzer/src/caps.rs
index 345693524..673795e78 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -87,6 +87,9 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti
87 "ssr": true, 87 "ssr": true,
88 "onEnter": true, 88 "onEnter": true,
89 "parentModule": true, 89 "parentModule": true,
90 "runnables": {
91 "kinds": [ "cargo" ],
92 },
90 })), 93 })),
91 } 94 }
92} 95}
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
3use ra_cfg::CfgExpr;
3use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
4use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, ProjectWorkspace, TargetKind};
5 6
6use crate::{world::WorldSnapshot, Result}; 7use crate::{world::WorldSnapshot, Result};
7use 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
149fn 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)]
169mod 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/config.rs b/crates/rust-analyzer/src/config.rs
index 9e8e7ab82..3337078ac 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -123,7 +123,7 @@ impl Default for Config {
123 check: Some(FlycheckConfig::CargoCommand { 123 check: Some(FlycheckConfig::CargoCommand {
124 command: "check".to_string(), 124 command: "check".to_string(),
125 all_targets: true, 125 all_targets: true,
126 all_features: true, 126 all_features: false,
127 extra_args: Vec::new(), 127 extra_args: Vec::new(),
128 }), 128 }),
129 129
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 05b76e7c8..4b436c301 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -4,7 +4,6 @@ use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Position, Range, TextDocumentIdentifier}; 6use lsp_types::{Position, Range, TextDocumentIdentifier};
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
9 8
10pub enum AnalyzerStatus {} 9pub enum AnalyzerStatus {}
@@ -128,7 +127,7 @@ pub enum Runnables {}
128impl Request for Runnables { 127impl Request for Runnables {
129 type Params = RunnablesParams; 128 type Params = RunnablesParams;
130 type Result = Vec<Runnable>; 129 type Result = Vec<Runnable>;
131 const METHOD: &'static str = "rust-analyzer/runnables"; 130 const METHOD: &'static str = "experimental/runnables";
132} 131}
133 132
134#[derive(Serialize, Deserialize, Debug)] 133#[derive(Serialize, Deserialize, Debug)]
@@ -138,25 +137,31 @@ pub struct RunnablesParams {
138 pub position: Option<Position>, 137 pub position: Option<Position>,
139} 138}
140 139
141// Must strictly correspond to the executable name 140#[derive(Deserialize, Serialize, Debug)]
141#[serde(rename_all = "camelCase")]
142pub struct Runnable {
143 pub label: String,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub location: Option<lsp_types::LocationLink>,
146 pub kind: RunnableKind,
147 pub args: CargoRunnable,
148}
149
142#[derive(Serialize, Deserialize, Debug)] 150#[derive(Serialize, Deserialize, Debug)]
143#[serde(rename_all = "lowercase")] 151#[serde(rename_all = "lowercase")]
144pub enum RunnableKind { 152pub enum RunnableKind {
145 Cargo, 153 Cargo,
146 Rustc,
147 Rustup,
148} 154}
149 155
150#[derive(Deserialize, Serialize, Debug)] 156#[derive(Deserialize, Serialize, Debug)]
151#[serde(rename_all = "camelCase")] 157#[serde(rename_all = "camelCase")]
152pub struct Runnable { 158pub struct CargoRunnable {
153 pub range: Range, 159 #[serde(skip_serializing_if = "Option::is_none")]
154 pub label: String, 160 pub workspace_root: Option<PathBuf>,
155 pub kind: RunnableKind, 161 // command, --package and --lib stuff
156 pub args: Vec<String>, 162 pub cargo_args: Vec<String>,
157 pub extra_args: Vec<String>, 163 // stuff after --
158 pub env: FxHashMap<String, String>, 164 pub executable_args: Vec<String>,
159 pub cwd: Option<PathBuf>,
160} 165}
161 166
162pub enum InlayHints {} 167pub enum InlayHints {}
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index b342f4bb7..3c4064441 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -17,15 +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};
20use ra_cfg::CfgExpr;
21use ra_ide::{ 20use ra_ide::{
22 FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, Query, RangeInfo, RunnableKind, SearchScope, TextEdit,
23 TextEdit,
24}; 22};
25use ra_prof::profile; 23use ra_prof::profile;
26use ra_project_model::TargetKind; 24use ra_project_model::TargetKind;
27use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize}; 25use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
28use rustc_hash::FxHashMap;
29use serde::{Deserialize, Serialize}; 26use serde::{Deserialize, Serialize};
30use serde_json::to_value; 27use serde_json::to_value;
31use stdx::format_to; 28use stdx::format_to;
@@ -403,7 +400,7 @@ pub fn handle_runnables(
403 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?; 400 let cargo_spec = CargoTargetSpec::for_file(&world, file_id)?;
404 for runnable in world.analysis().runnables(file_id)? { 401 for runnable in world.analysis().runnables(file_id)? {
405 if let Some(offset) = offset { 402 if let Some(offset) = offset {
406 if !runnable.range.contains_inclusive(offset) { 403 if !runnable.nav.full_range().contains_inclusive(offset) {
407 continue; 404 continue;
408 } 405 }
409 } 406 }
@@ -416,7 +413,7 @@ pub fn handle_runnables(
416 } 413 }
417 } 414 }
418 } 415 }
419 res.push(to_lsp_runnable(&world, file_id, runnable)?); 416 res.push(to_proto::runnable(&world, file_id, runnable)?);
420 } 417 }
421 418
422 // Add `cargo check` and `cargo test` for the whole package 419 // Add `cargo check` and `cargo test` for the whole package
@@ -424,25 +421,31 @@ pub fn handle_runnables(
424 Some(spec) => { 421 Some(spec) => {
425 for &cmd in ["check", "test"].iter() { 422 for &cmd in ["check", "test"].iter() {
426 res.push(lsp_ext::Runnable { 423 res.push(lsp_ext::Runnable {
427 range: Default::default(),
428 label: format!("cargo {} -p {}", cmd, spec.package), 424 label: format!("cargo {} -p {}", cmd, spec.package),
425 location: None,
429 kind: lsp_ext::RunnableKind::Cargo, 426 kind: lsp_ext::RunnableKind::Cargo,
430 args: vec![cmd.to_string(), "--package".to_string(), spec.package.clone()], 427 args: lsp_ext::CargoRunnable {
431 extra_args: Vec::new(), 428 workspace_root: workspace_root.map(|root| root.to_owned()),
432 env: FxHashMap::default(), 429 cargo_args: vec![
433 cwd: workspace_root.map(|root| root.to_owned()), 430 cmd.to_string(),
431 "--package".to_string(),
432 spec.package.clone(),
433 ],
434 executable_args: Vec::new(),
435 },
434 }) 436 })
435 } 437 }
436 } 438 }
437 None => { 439 None => {
438 res.push(lsp_ext::Runnable { 440 res.push(lsp_ext::Runnable {
439 range: Default::default(),
440 label: "cargo check --workspace".to_string(), 441 label: "cargo check --workspace".to_string(),
442 location: None,
441 kind: lsp_ext::RunnableKind::Cargo, 443 kind: lsp_ext::RunnableKind::Cargo,
442 args: vec!["check".to_string(), "--workspace".to_string()], 444 args: lsp_ext::CargoRunnable {
443 extra_args: Vec::new(), 445 workspace_root: workspace_root.map(|root| root.to_owned()),
444 env: FxHashMap::default(), 446 cargo_args: vec!["check".to_string(), "--workspace".to_string()],
445 cwd: workspace_root.map(|root| root.to_owned()), 447 executable_args: Vec::new(),
448 },
446 }); 449 });
447 } 450 }
448 } 451 }
@@ -755,11 +758,11 @@ pub fn handle_code_action(
755 if world.config.client_caps.resolve_code_action { 758 if world.config.client_caps.resolve_code_action {
756 for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter() 759 for assist in world.analysis().unresolved_assists(&world.config.assist, frange)?.into_iter()
757 { 760 {
758 res.push(to_proto::unresolved_code_action(&world, assist)?.into()); 761 res.push(to_proto::unresolved_code_action(&world, assist)?);
759 } 762 }
760 } else { 763 } else {
761 for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { 764 for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() {
762 res.push(to_proto::resolved_code_action(&world, assist)?.into()); 765 res.push(to_proto::resolved_code_action(&world, assist)?);
763 } 766 }
764 } 767 }
765 768
@@ -782,7 +785,7 @@ pub fn handle_resolve_code_action(
782 let mut res: Vec<lsp_ext::CodeAction> = Vec::new(); 785 let mut res: Vec<lsp_ext::CodeAction> = Vec::new();
783 786
784 for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() { 787 for assist in world.analysis().resolved_assists(&world.config.assist, frange)?.into_iter() {
785 res.push(to_proto::resolved_code_action(&world, assist)?.into()); 788 res.push(to_proto::resolved_code_action(&world, assist)?);
786 } 789 }
787 Ok(res 790 Ok(res
788 .into_iter() 791 .into_iter()
@@ -833,10 +836,11 @@ pub fn handle_code_lens(
833 } 836 }
834 }; 837 };
835 838
836 let mut r = to_lsp_runnable(&world, file_id, runnable)?; 839 let range = to_proto::range(&line_index, runnable.nav.range());
840 let r = to_proto::runnable(&world, file_id, runnable)?;
837 if world.config.lens.run { 841 if world.config.lens.run {
838 let lens = CodeLens { 842 let lens = CodeLens {
839 range: r.range, 843 range,
840 command: Some(Command { 844 command: Some(Command {
841 title: run_title.to_string(), 845 title: run_title.to_string(),
842 command: "rust-analyzer.runSingle".into(), 846 command: "rust-analyzer.runSingle".into(),
@@ -848,13 +852,8 @@ pub fn handle_code_lens(
848 } 852 }
849 853
850 if debugee && world.config.lens.debug { 854 if debugee && world.config.lens.debug {
851 if r.args[0] == "run" {
852 r.args[0] = "build".into();
853 } else {
854 r.args.push("--no-run".into());
855 }
856 let debug_lens = CodeLens { 855 let debug_lens = CodeLens {
857 range: r.range, 856 range,
858 command: Some(Command { 857 command: Some(Command {
859 title: "Debug".into(), 858 title: "Debug".into(),
860 command: "rust-analyzer.debugSingle".into(), 859 command: "rust-analyzer.debugSingle".into(),
@@ -1008,65 +1007,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
1008 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 1007 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
1009} 1008}
1010 1009
1011fn to_lsp_runnable(
1012 world: &WorldSnapshot,
1013 file_id: FileId,
1014 runnable: Runnable,
1015) -> Result<lsp_ext::Runnable> {
1016 let spec = CargoTargetSpec::for_file(world, file_id)?;
1017 let target = spec.as_ref().map(|s| s.target.clone());
1018 let mut features_needed = vec![];
1019 for cfg_expr in &runnable.cfg_exprs {
1020 collect_minimal_features_needed(cfg_expr, &mut features_needed);
1021 }
1022 let (args, extra_args) =
1023 CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
1024 let line_index = world.analysis().file_line_index(file_id)?;
1025 let label = match &runnable.kind {
1026 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
1027 RunnableKind::TestMod { path } => format!("test-mod {}", path),
1028 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
1029 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
1030 RunnableKind::Bin => {
1031 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
1032 }
1033 };
1034
1035 Ok(lsp_ext::Runnable {
1036 range: to_proto::range(&line_index, runnable.range),
1037 label,
1038 kind: lsp_ext::RunnableKind::Cargo,
1039 args,
1040 extra_args,
1041 env: {
1042 let mut m = FxHashMap::default();
1043 m.insert("RUST_BACKTRACE".to_string(), "short".to_string());
1044 m
1045 },
1046 cwd: world.workspace_root_for(file_id).map(|root| root.to_owned()),
1047 })
1048}
1049
1050/// Fill minimal features needed
1051fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
1052 match cfg_expr {
1053 CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
1054 CfgExpr::All(preds) => {
1055 preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
1056 }
1057 CfgExpr::Any(preds) => {
1058 for cfg in preds {
1059 let len_features = features.len();
1060 collect_minimal_features_needed(cfg, features);
1061 if len_features != features.len() {
1062 break;
1063 }
1064 }
1065 }
1066 _ => {}
1067 }
1068}
1069
1070pub fn handle_inlay_hints( 1010pub fn handle_inlay_hints(
1071 world: WorldSnapshot, 1011 world: WorldSnapshot,
1072 params: InlayHintsParams, 1012 params: InlayHintsParams,
@@ -1203,54 +1143,3 @@ pub fn handle_semantic_tokens_range(
1203 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights); 1143 let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
1204 Ok(Some(semantic_tokens.into())) 1144 Ok(Some(semantic_tokens.into()))
1205} 1145}
1206
1207#[cfg(test)]
1208mod tests {
1209 use super::*;
1210
1211 use mbe::{ast_to_token_tree, TokenMap};
1212 use ra_cfg::parse_cfg;
1213 use ra_syntax::{
1214 ast::{self, AstNode},
1215 SmolStr,
1216 };
1217
1218 fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
1219 let source_file = ast::SourceFile::parse(input).ok().unwrap();
1220 let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
1221 ast_to_token_tree(&tt).unwrap()
1222 }
1223
1224 #[test]
1225 fn test_cfg_expr_minimal_features_needed() {
1226 let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
1227 let cfg_expr = parse_cfg(&subtree);
1228 let mut min_features = vec![];
1229 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1230
1231 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1232
1233 let (subtree, _) =
1234 get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
1235 let cfg_expr = parse_cfg(&subtree);
1236
1237 let mut min_features = vec![];
1238 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1239 assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
1240
1241 let (subtree, _) =
1242 get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
1243 let cfg_expr = parse_cfg(&subtree);
1244
1245 let mut min_features = vec![];
1246 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1247 assert_eq!(min_features, vec![SmolStr::new("baz")]);
1248
1249 let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
1250 let cfg_expr = parse_cfg(&subtree);
1251
1252 let mut min_features = vec![];
1253 collect_minimal_features_needed(&cfg_expr, &mut min_features);
1254 assert!(min_features.is_empty());
1255 }
1256}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index db78c4b5c..3672b1a26 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -4,12 +4,14 @@ 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, 6 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
7 ResolvedAssist, Severity, SourceChange, SourceFileEdit, TextEdit, 7 ResolvedAssist, Runnable, RunnableKind, Severity, SourceChange, SourceFileEdit, TextEdit,
8}; 8};
9use ra_syntax::{SyntaxKind, TextRange, TextSize}; 9use ra_syntax::{SyntaxKind, TextRange, TextSize};
10use ra_vfs::LineEndings; 10use ra_vfs::LineEndings;
11 11
12use crate::{lsp_ext, semantic_tokens, world::WorldSnapshot, Result}; 12use crate::{
13 cargo_target_spec::CargoTargetSpec, lsp_ext, semantic_tokens, world::WorldSnapshot, Result,
14};
13 15
14pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { 16pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position {
15 let line_col = line_index.line_col(offset); 17 let line_col = line_index.line_col(offset);
@@ -658,3 +660,35 @@ pub(crate) fn resolved_code_action(
658 }; 660 };
659 Ok(res) 661 Ok(res)
660} 662}
663
664pub(crate) fn runnable(
665 world: &WorldSnapshot,
666 file_id: FileId,
667 runnable: Runnable,
668) -> Result<lsp_ext::Runnable> {
669 let spec = CargoTargetSpec::for_file(world, file_id)?;
670 let target = spec.as_ref().map(|s| s.target.clone());
671 let (cargo_args, executable_args) =
672 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
673 let label = match &runnable.kind {
674 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
675 RunnableKind::TestMod { path } => format!("test-mod {}", path),
676 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
677 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
678 RunnableKind::Bin => {
679 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
680 }
681 };
682 let location = location_link(world, None, runnable.nav)?;
683
684 Ok(lsp_ext::Runnable {
685 label,
686 location: Some(location),
687 kind: lsp_ext::RunnableKind::Cargo,
688 args: lsp_ext::CargoRunnable {
689 workspace_root: world.workspace_root_for(file_id).map(|root| root.to_owned()),
690 cargo_args,
691 executable_args,
692 },
693 })
694}
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs
index 8b473ff74..e18f973b8 100644
--- a/crates/rust-analyzer/tests/heavy_tests/main.rs
+++ b/crates/rust-analyzer/tests/heavy_tests/main.rs
@@ -76,30 +76,33 @@ fn foo() {
76 server.request::<Runnables>( 76 server.request::<Runnables>(
77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None }, 77 RunnablesParams { text_document: server.doc_id("lib.rs"), position: None },
78 json!([ 78 json!([
79 { 79 {
80 "args": [ "test" ], 80 "args": {
81 "extraArgs": [ "foo", "--nocapture" ], 81 "cargoArgs": ["test"],
82 "kind": "cargo", 82 "executableArgs": ["foo", "--nocapture"],
83 "env": { "RUST_BACKTRACE": "short" }, 83 },
84 "cwd": null, 84 "kind": "cargo",
85 "label": "test foo", 85 "label": "test foo",
86 "range": { 86 "location": {
87 "end": { "character": 1, "line": 2 }, 87 "targetRange": {
88 "start": { "character": 0, "line": 0 } 88 "end": { "character": 1, "line": 2 },
89 } 89 "start": { "character": 0, "line": 0 }
90 }, 90 },
91 { 91 "targetSelectionRange": {
92 "args": ["check", "--workspace"], 92 "end": { "character": 6, "line": 1 },
93 "extraArgs": [], 93 "start": { "character": 3, "line": 1 }
94 "kind": "cargo", 94 },
95 "env": {}, 95 "targetUri": "file:///[..]/lib.rs"
96 "cwd": null, 96 }
97 "label": "cargo check --workspace", 97 },
98 "range": { 98 {
99 "end": { "character": 0, "line": 0 }, 99 "args": {
100 "start": { "character": 0, "line": 0 } 100 "cargoArgs": ["check", "--workspace"],
101 "executableArgs": [],
102 },
103 "kind": "cargo",
104 "label": "cargo check --workspace"
101 } 105 }
102 }
103 ]), 106 ]),
104 ); 107 );
105} 108}
@@ -138,42 +141,44 @@ fn main() {}
138 server.request::<Runnables>( 141 server.request::<Runnables>(
139 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None }, 142 RunnablesParams { text_document: server.doc_id("foo/tests/spam.rs"), position: None },
140 json!([ 143 json!([
141 { 144 {
142 "args": [ "test", "--package", "foo", "--test", "spam" ], 145 "args": {
143 "extraArgs": [ "test_eggs", "--exact", "--nocapture" ], 146 "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
144 "kind": "cargo", 147 "executableArgs": ["test_eggs", "--exact", "--nocapture"],
145 "env": { "RUST_BACKTRACE": "short" }, 148 "workspaceRoot": server.path().join("foo")
146 "label": "test test_eggs",
147 "range": {
148 "end": { "character": 17, "line": 1 },
149 "start": { "character": 0, "line": 0 }
150 },
151 "cwd": server.path().join("foo")
152 }, 149 },
153 { 150 "kind": "cargo",
154 "args": [ "check", "--package", "foo" ], 151 "label": "test test_eggs",
155 "extraArgs": [], 152 "location": {
156 "kind": "cargo", 153 "targetRange": {
157 "env": {}, 154 "end": { "character": 17, "line": 1 },
158 "label": "cargo check -p foo",
159 "range": {
160 "end": { "character": 0, "line": 0 },
161 "start": { "character": 0, "line": 0 } 155 "start": { "character": 0, "line": 0 }
162 }, 156 },
163 "cwd": server.path().join("foo") 157 "targetSelectionRange": {
164 }, 158 "end": { "character": 12, "line": 1 },
165 { 159 "start": { "character": 3, "line": 1 }
166 "args": [ "test", "--package", "foo" ],
167 "extraArgs": [],
168 "kind": "cargo",
169 "env": {},
170 "label": "cargo test -p foo",
171 "range": {
172 "end": { "character": 0, "line": 0 },
173 "start": { "character": 0, "line": 0 }
174 }, 160 },
175 "cwd": server.path().join("foo") 161 "targetUri": "file:///[..]/tests/spam.rs"
176 } 162 }
163 },
164 {
165 "args": {
166 "cargoArgs": ["check", "--package", "foo"],
167 "executableArgs": [],
168 "workspaceRoot": server.path().join("foo")
169 },
170 "kind": "cargo",
171 "label": "cargo check -p foo"
172 },
173 {
174 "args": {
175 "cargoArgs": ["test", "--package", "foo"],
176 "executableArgs": [],
177 "workspaceRoot": server.path().join("foo")
178 },
179 "kind": "cargo",
180 "label": "cargo test -p foo"
181 }
177 ]), 182 ]),
178 ); 183 );
179} 184}