aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/runnables.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/runnables.rs')
-rw-r--r--crates/ra_ide/src/runnables.rs342
1 files changed, 310 insertions, 32 deletions
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 286d45eee..fc57dc33d 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -1,31 +1,31 @@
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, Clone)]
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}
20 20
21#[derive(Debug)] 21#[derive(Debug, Clone)]
22pub enum TestId { 22pub enum TestId {
23 Name(String), 23 Name(String),
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),
@@ -33,7 +33,7 @@ impl Display for TestId {
33 } 33 }
34} 34}
35 35
36#[derive(Debug)] 36#[derive(Debug, Clone)]
37pub enum RunnableKind { 37pub enum RunnableKind {
38 Test { test_id: TestId, attr: TestAttr }, 38 Test { test_id: TestId, attr: TestAttr },
39 TestMod { path: String }, 39 TestMod { path: String },
@@ -42,6 +42,42 @@ pub enum RunnableKind {
42 Bin, 42 Bin,
43} 43}
44 44
45#[derive(Debug, Eq, PartialEq)]
46pub struct RunnableAction {
47 pub run_title: &'static str,
48 pub debugee: bool,
49}
50
51const TEST: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Test", debugee: true };
52const DOCTEST: RunnableAction =
53 RunnableAction { run_title: "▶\u{fe0e} Run Doctest", debugee: false };
54const BENCH: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run Bench", debugee: true };
55const BIN: RunnableAction = RunnableAction { run_title: "▶\u{fe0e} Run", debugee: true };
56
57impl Runnable {
58 // test package::module::testname
59 pub fn label(&self, target: Option<String>) -> String {
60 match &self.kind {
61 RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
62 RunnableKind::TestMod { path } => format!("test-mod {}", path),
63 RunnableKind::Bench { test_id } => format!("bench {}", test_id),
64 RunnableKind::DocTest { test_id, .. } => format!("doctest {}", test_id),
65 RunnableKind::Bin => {
66 target.map_or_else(|| "run binary".to_string(), |t| format!("run {}", t))
67 }
68 }
69 }
70
71 pub fn action(&self) -> &'static RunnableAction {
72 match &self.kind {
73 RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => &TEST,
74 RunnableKind::DocTest { .. } => &DOCTEST,
75 RunnableKind::Bench { .. } => &BENCH,
76 RunnableKind::Bin => &BIN,
77 }
78 }
79}
80
45// Feature: Run 81// Feature: Run
46// 82//
47// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor 83// Shows a popup suggesting to run a test/benchmark/binary **at the current cursor
@@ -59,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
59 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect() 95 source_file.syntax().descendants().filter_map(|i| runnable(&sema, i, file_id)).collect()
60} 96}
61 97
62fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { 98pub(crate) fn runnable(
99 sema: &Semantics<RootDatabase>,
100 item: SyntaxNode,
101 file_id: FileId,
102) -> Option<Runnable> {
63 match_ast! { 103 match_ast! {
64 match item { 104 match item {
65 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::FnDef(it) => runnable_fn(sema, it, file_id),
@@ -131,10 +171,11 @@ fn runnable_fn(
131 let cfg_exprs = 171 let cfg_exprs =
132 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 172 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
133 173
134 Some(Runnable { range: fn_def.syntax().text_range(), kind, cfg_exprs }) 174 let nav = NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def));
175 Some(Runnable { nav, kind, cfg_exprs })
135} 176}
136 177
137#[derive(Debug)] 178#[derive(Debug, Copy, Clone)]
138pub struct TestAttr { 179pub struct TestAttr {
139 pub ignore: bool, 180 pub ignore: bool,
140} 181}
@@ -183,7 +224,6 @@ fn runnable_mod(
183 if !has_test_function { 224 if !has_test_function {
184 return None; 225 return None;
185 } 226 }
186 let range = module.syntax().text_range();
187 let module_def = sema.to_def(&module)?; 227 let module_def = sema.to_def(&module)?;
188 228
189 let path = module_def 229 let path = module_def
@@ -197,7 +237,8 @@ fn runnable_mod(
197 let cfg_exprs = 237 let cfg_exprs =
198 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect(); 238 attrs.by_key("cfg").tt_values().map(|subtree| ra_cfg::parse_cfg(subtree)).collect();
199 239
200 Some(Runnable { range, kind: RunnableKind::TestMod { path }, cfg_exprs }) 240 let nav = module_def.to_nav(sema.db);
241 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
201} 242}
202 243
203#[cfg(test)] 244#[cfg(test)]
@@ -206,6 +247,15 @@ mod tests {
206 247
207 use crate::mock_analysis::analysis_and_position; 248 use crate::mock_analysis::analysis_and_position;
208 249
250 use super::{Runnable, RunnableAction, BENCH, BIN, DOCTEST, TEST};
251
252 fn assert_actions(runnables: &[Runnable], actions: &[&RunnableAction]) {
253 assert_eq!(
254 actions,
255 runnables.into_iter().map(|it| it.action()).collect::<Vec<_>>().as_slice()
256 );
257 }
258
209 #[test] 259 #[test]
210 fn test_runnables() { 260 fn test_runnables() {
211 let (analysis, pos) = analysis_and_position( 261 let (analysis, pos) = analysis_and_position(
@@ -220,6 +270,9 @@ mod tests {
220 #[test] 270 #[test]
221 #[ignore] 271 #[ignore]
222 fn test_foo() {} 272 fn test_foo() {}
273
274 #[bench]
275 fn bench() {}
223 "#, 276 "#,
224 ); 277 );
225 let runnables = analysis.runnables(pos.file_id).unwrap(); 278 let runnables = analysis.runnables(pos.file_id).unwrap();
@@ -227,12 +280,38 @@ mod tests {
227 @r###" 280 @r###"
228 [ 281 [
229 Runnable { 282 Runnable {
230 range: 1..21, 283 nav: NavigationTarget {
284 file_id: FileId(
285 1,
286 ),
287 full_range: 1..21,
288 name: "main",
289 kind: FN_DEF,
290 focus_range: Some(
291 12..16,
292 ),
293 container_name: None,
294 description: None,
295 docs: None,
296 },
231 kind: Bin, 297 kind: Bin,
232 cfg_exprs: [], 298 cfg_exprs: [],
233 }, 299 },
234 Runnable { 300 Runnable {
235 range: 22..46, 301 nav: NavigationTarget {
302 file_id: FileId(
303 1,
304 ),
305 full_range: 22..46,
306 name: "test_foo",
307 kind: FN_DEF,
308 focus_range: Some(
309 33..41,
310 ),
311 container_name: None,
312 description: None,
313 docs: None,
314 },
236 kind: Test { 315 kind: Test {
237 test_id: Path( 316 test_id: Path(
238 "test_foo", 317 "test_foo",
@@ -244,7 +323,20 @@ mod tests {
244 cfg_exprs: [], 323 cfg_exprs: [],
245 }, 324 },
246 Runnable { 325 Runnable {
247 range: 47..81, 326 nav: NavigationTarget {
327 file_id: FileId(
328 1,
329 ),
330 full_range: 47..81,
331 name: "test_foo",
332 kind: FN_DEF,
333 focus_range: Some(
334 68..76,
335 ),
336 container_name: None,
337 description: None,
338 docs: None,
339 },
248 kind: Test { 340 kind: Test {
249 test_id: Path( 341 test_id: Path(
250 "test_foo", 342 "test_foo",
@@ -255,9 +347,32 @@ mod tests {
255 }, 347 },
256 cfg_exprs: [], 348 cfg_exprs: [],
257 }, 349 },
350 Runnable {
351 nav: NavigationTarget {
352 file_id: FileId(
353 1,
354 ),
355 full_range: 82..104,
356 name: "bench",
357 kind: FN_DEF,
358 focus_range: Some(
359 94..99,
360 ),
361 container_name: None,
362 description: None,
363 docs: None,
364 },
365 kind: Bench {
366 test_id: Path(
367 "bench",
368 ),
369 },
370 cfg_exprs: [],
371 },
258 ] 372 ]
259 "### 373 "###
260 ); 374 );
375 assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]);
261 } 376 }
262 377
263 #[test] 378 #[test]
@@ -279,12 +394,38 @@ mod tests {
279 @r###" 394 @r###"
280 [ 395 [
281 Runnable { 396 Runnable {
282 range: 1..21, 397 nav: NavigationTarget {
398 file_id: FileId(
399 1,
400 ),
401 full_range: 1..21,
402 name: "main",
403 kind: FN_DEF,
404 focus_range: Some(
405 12..16,
406 ),
407 container_name: None,
408 description: None,
409 docs: None,
410 },
283 kind: Bin, 411 kind: Bin,
284 cfg_exprs: [], 412 cfg_exprs: [],
285 }, 413 },
286 Runnable { 414 Runnable {
287 range: 22..64, 415 nav: NavigationTarget {
416 file_id: FileId(
417 1,
418 ),
419 full_range: 22..64,
420 name: "foo",
421 kind: FN_DEF,
422 focus_range: Some(
423 56..59,
424 ),
425 container_name: None,
426 description: None,
427 docs: None,
428 },
288 kind: DocTest { 429 kind: DocTest {
289 test_id: Path( 430 test_id: Path(
290 "foo", 431 "foo",
@@ -295,6 +436,7 @@ mod tests {
295 ] 436 ]
296 "### 437 "###
297 ); 438 );
439 assert_actions(&runnables, &[&BIN, &DOCTEST]);
298 } 440 }
299 441
300 #[test] 442 #[test]
@@ -319,12 +461,38 @@ mod tests {
319 @r###" 461 @r###"
320 [ 462 [
321 Runnable { 463 Runnable {
322 range: 1..21, 464 nav: NavigationTarget {
465 file_id: FileId(
466 1,
467 ),
468 full_range: 1..21,
469 name: "main",
470 kind: FN_DEF,
471 focus_range: Some(
472 12..16,
473 ),
474 container_name: None,
475 description: None,
476 docs: None,
477 },
323 kind: Bin, 478 kind: Bin,
324 cfg_exprs: [], 479 cfg_exprs: [],
325 }, 480 },
326 Runnable { 481 Runnable {
327 range: 51..105, 482 nav: NavigationTarget {
483 file_id: FileId(
484 1,
485 ),
486 full_range: 51..105,
487 name: "foo",
488 kind: FN_DEF,
489 focus_range: Some(
490 97..100,
491 ),
492 container_name: None,
493 description: None,
494 docs: None,
495 },
328 kind: DocTest { 496 kind: DocTest {
329 test_id: Path( 497 test_id: Path(
330 "Data::foo", 498 "Data::foo",
@@ -335,6 +503,7 @@ mod tests {
335 ] 503 ]
336 "### 504 "###
337 ); 505 );
506 assert_actions(&runnables, &[&BIN, &DOCTEST]);
338 } 507 }
339 508
340 #[test] 509 #[test]
@@ -354,14 +523,40 @@ mod tests {
354 @r###" 523 @r###"
355 [ 524 [
356 Runnable { 525 Runnable {
357 range: 1..59, 526 nav: NavigationTarget {
527 file_id: FileId(
528 1,
529 ),
530 full_range: 1..59,
531 name: "test_mod",
532 kind: MODULE,
533 focus_range: Some(
534 13..21,
535 ),
536 container_name: None,
537 description: None,
538 docs: None,
539 },
358 kind: TestMod { 540 kind: TestMod {
359 path: "test_mod", 541 path: "test_mod",
360 }, 542 },
361 cfg_exprs: [], 543 cfg_exprs: [],
362 }, 544 },
363 Runnable { 545 Runnable {
364 range: 28..57, 546 nav: NavigationTarget {
547 file_id: FileId(
548 1,
549 ),
550 full_range: 28..57,
551 name: "test_foo1",
552 kind: FN_DEF,
553 focus_range: Some(
554 43..52,
555 ),
556 container_name: None,
557 description: None,
558 docs: None,
559 },
365 kind: Test { 560 kind: Test {
366 test_id: Path( 561 test_id: Path(
367 "test_mod::test_foo1", 562 "test_mod::test_foo1",
@@ -375,6 +570,7 @@ mod tests {
375 ] 570 ]
376 "### 571 "###
377 ); 572 );
573 assert_actions(&runnables, &[&TEST, &TEST]);
378 } 574 }
379 575
380 #[test] 576 #[test]
@@ -396,14 +592,40 @@ mod tests {
396 @r###" 592 @r###"
397 [ 593 [
398 Runnable { 594 Runnable {
399 range: 23..85, 595 nav: NavigationTarget {
596 file_id: FileId(
597 1,
598 ),
599 full_range: 23..85,
600 name: "test_mod",
601 kind: MODULE,
602 focus_range: Some(
603 27..35,
604 ),
605 container_name: None,
606 description: None,
607 docs: None,
608 },
400 kind: TestMod { 609 kind: TestMod {
401 path: "foo::test_mod", 610 path: "foo::test_mod",
402 }, 611 },
403 cfg_exprs: [], 612 cfg_exprs: [],
404 }, 613 },
405 Runnable { 614 Runnable {
406 range: 46..79, 615 nav: NavigationTarget {
616 file_id: FileId(
617 1,
618 ),
619 full_range: 46..79,
620 name: "test_foo1",
621 kind: FN_DEF,
622 focus_range: Some(
623 65..74,
624 ),
625 container_name: None,
626 description: None,
627 docs: None,
628 },
407 kind: Test { 629 kind: Test {
408 test_id: Path( 630 test_id: Path(
409 "foo::test_mod::test_foo1", 631 "foo::test_mod::test_foo1",
@@ -417,6 +639,7 @@ mod tests {
417 ] 639 ]
418 "### 640 "###
419 ); 641 );
642 assert_actions(&runnables, &[&TEST, &TEST]);
420 } 643 }
421 644
422 #[test] 645 #[test]
@@ -440,14 +663,40 @@ mod tests {
440 @r###" 663 @r###"
441 [ 664 [
442 Runnable { 665 Runnable {
443 range: 41..115, 666 nav: NavigationTarget {
667 file_id: FileId(
668 1,
669 ),
670 full_range: 41..115,
671 name: "test_mod",
672 kind: MODULE,
673 focus_range: Some(
674 45..53,
675 ),
676 container_name: None,
677 description: None,
678 docs: None,
679 },
444 kind: TestMod { 680 kind: TestMod {
445 path: "foo::bar::test_mod", 681 path: "foo::bar::test_mod",
446 }, 682 },
447 cfg_exprs: [], 683 cfg_exprs: [],
448 }, 684 },
449 Runnable { 685 Runnable {
450 range: 68..105, 686 nav: NavigationTarget {
687 file_id: FileId(
688 1,
689 ),
690 full_range: 68..105,
691 name: "test_foo1",
692 kind: FN_DEF,
693 focus_range: Some(
694 91..100,
695 ),
696 container_name: None,
697 description: None,
698 docs: None,
699 },
451 kind: Test { 700 kind: Test {
452 test_id: Path( 701 test_id: Path(
453 "foo::bar::test_mod::test_foo1", 702 "foo::bar::test_mod::test_foo1",
@@ -461,6 +710,7 @@ mod tests {
461 ] 710 ]
462 "### 711 "###
463 ); 712 );
713 assert_actions(&runnables, &[&TEST, &TEST]);
464 } 714 }
465 715
466 #[test] 716 #[test]
@@ -479,7 +729,20 @@ mod tests {
479 @r###" 729 @r###"
480 [ 730 [
481 Runnable { 731 Runnable {
482 range: 1..58, 732 nav: NavigationTarget {
733 file_id: FileId(
734 1,
735 ),
736 full_range: 1..58,
737 name: "test_foo1",
738 kind: FN_DEF,
739 focus_range: Some(
740 44..53,
741 ),
742 container_name: None,
743 description: None,
744 docs: None,
745 },
483 kind: Test { 746 kind: Test {
484 test_id: Path( 747 test_id: Path(
485 "test_foo1", 748 "test_foo1",
@@ -498,6 +761,7 @@ mod tests {
498 ] 761 ]
499 "### 762 "###
500 ); 763 );
764 assert_actions(&runnables, &[&TEST]);
501 } 765 }
502 766
503 #[test] 767 #[test]
@@ -516,7 +780,20 @@ mod tests {
516 @r###" 780 @r###"
517 [ 781 [
518 Runnable { 782 Runnable {
519 range: 1..80, 783 nav: NavigationTarget {
784 file_id: FileId(
785 1,
786 ),
787 full_range: 1..80,
788 name: "test_foo1",
789 kind: FN_DEF,
790 focus_range: Some(
791 66..75,
792 ),
793 container_name: None,
794 description: None,
795 docs: None,
796 },
520 kind: Test { 797 kind: Test {
521 test_id: Path( 798 test_id: Path(
522 "test_foo1", 799 "test_foo1",
@@ -543,6 +820,7 @@ mod tests {
543 ] 820 ]
544 "### 821 "###
545 ); 822 );
823 assert_actions(&runnables, &[&TEST]);
546 } 824 }
547 825
548 #[test] 826 #[test]