aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-06-08 12:29:35 +0100
committerGitHub <[email protected]>2020-06-08 12:29:35 +0100
commit6e4fca5882bdd6d00dfcc384473c1f612e00baa2 (patch)
tree07fd36fde432cc8d42d23408a67133422a94fedb /crates/ra_ide
parenta5f427ced3ee0ec27041f0b7632b4baae6a736b2 (diff)
parentb7db9f058ad51b7ba47db02b581a76b6756d74e8 (diff)
Merge #4773
4773: Run|Debug hover actions. r=matklad a=vsrs ![hover_actions_run](https://user-images.githubusercontent.com/62505555/83335644-dfc1f780-a2b6-11ea-820b-ccaa82290e7d.gif) This hover actions work exactly like corresponding lenses. Co-authored-by: vsrs <[email protected]>
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/hover.rs142
-rw-r--r--crates/ra_ide/src/runnables.rs92
2 files changed, 225 insertions, 9 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 846d8c69b..ad78b7671 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -14,34 +14,43 @@ use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffs
14 14
15use crate::{ 15use crate::{
16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, 16 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav},
17 FilePosition, NavigationTarget, RangeInfo, 17 runnables::runnable,
18 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
18}; 19};
20use test_utils::mark;
19 21
20#[derive(Clone, Debug, PartialEq, Eq)] 22#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct HoverConfig { 23pub struct HoverConfig {
22 pub implementations: bool, 24 pub implementations: bool,
25 pub run: bool,
26 pub debug: bool,
23} 27}
24 28
25impl Default for HoverConfig { 29impl Default for HoverConfig {
26 fn default() -> Self { 30 fn default() -> Self {
27 Self { implementations: true } 31 Self { implementations: true, run: true, debug: true }
28 } 32 }
29} 33}
30 34
31impl HoverConfig { 35impl HoverConfig {
32 pub const NO_ACTIONS: Self = Self { implementations: false }; 36 pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false };
33 37
34 pub fn any(&self) -> bool { 38 pub fn any(&self) -> bool {
35 self.implementations 39 self.implementations || self.runnable()
36 } 40 }
37 41
38 pub fn none(&self) -> bool { 42 pub fn none(&self) -> bool {
39 !self.any() 43 !self.any()
40 } 44 }
45
46 pub fn runnable(&self) -> bool {
47 self.run || self.debug
48 }
41} 49}
42 50
43#[derive(Debug, Clone)] 51#[derive(Debug, Clone)]
44pub enum HoverAction { 52pub enum HoverAction {
53 Runnable(Runnable),
45 Implementaion(FilePosition), 54 Implementaion(FilePosition),
46} 55}
47 56
@@ -125,6 +134,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
125 res.push_action(action); 134 res.push_action(action);
126 } 135 }
127 136
137 if let Some(action) = runnable_action(&sema, name_kind, position.file_id) {
138 res.push_action(action);
139 }
140
128 return Some(RangeInfo::new(range, res)); 141 return Some(RangeInfo::new(range, res));
129 } 142 }
130 } 143 }
@@ -175,6 +188,36 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
175 } 188 }
176} 189}
177 190
191fn runnable_action(
192 sema: &Semantics<RootDatabase>,
193 def: Definition,
194 file_id: FileId,
195) -> Option<HoverAction> {
196 match def {
197 Definition::ModuleDef(it) => match it {
198 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
199 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
200 .map(|it| HoverAction::Runnable(it)),
201 _ => None,
202 },
203 ModuleDef::Function(it) => {
204 let src = it.source(sema.db);
205 if src.file_id != file_id.into() {
206 mark::hit!(hover_macro_generated_struct_fn_doc_comment);
207 mark::hit!(hover_macro_generated_struct_fn_doc_attr);
208
209 return None;
210 }
211
212 runnable(&sema, src.value.syntax().clone(), file_id)
213 .map(|it| HoverAction::Runnable(it))
214 }
215 _ => None,
216 },
217 _ => None,
218 }
219}
220
178fn hover_text( 221fn hover_text(
179 docs: Option<String>, 222 docs: Option<String>,
180 desc: Option<String>, 223 desc: Option<String>,
@@ -292,6 +335,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
292#[cfg(test)] 335#[cfg(test)]
293mod tests { 336mod tests {
294 use super::*; 337 use super::*;
338 use insta::assert_debug_snapshot;
295 339
296 use ra_db::FileLoader; 340 use ra_db::FileLoader;
297 use ra_syntax::TextRange; 341 use ra_syntax::TextRange;
@@ -309,6 +353,7 @@ mod tests {
309 fn assert_impl_action(action: &HoverAction, position: u32) { 353 fn assert_impl_action(action: &HoverAction, position: u32) {
310 let offset = match action { 354 let offset = match action {
311 HoverAction::Implementaion(pos) => pos.offset, 355 HoverAction::Implementaion(pos) => pos.offset,
356 it => panic!("Unexpected hover action: {:#?}", it),
312 }; 357 };
313 assert_eq!(offset, position.into()); 358 assert_eq!(offset, position.into());
314 } 359 }
@@ -1076,6 +1121,8 @@ fn func(foo: i32) { if true { <|>foo; }; }
1076 1121
1077 #[test] 1122 #[test]
1078 fn test_hover_macro_generated_struct_fn_doc_comment() { 1123 fn test_hover_macro_generated_struct_fn_doc_comment() {
1124 mark::check!(hover_macro_generated_struct_fn_doc_comment);
1125
1079 check_hover_result( 1126 check_hover_result(
1080 r#" 1127 r#"
1081 //- /lib.rs 1128 //- /lib.rs
@@ -1102,6 +1149,8 @@ fn func(foo: i32) { if true { <|>foo; }; }
1102 1149
1103 #[test] 1150 #[test]
1104 fn test_hover_macro_generated_struct_fn_doc_attr() { 1151 fn test_hover_macro_generated_struct_fn_doc_attr() {
1152 mark::check!(hover_macro_generated_struct_fn_doc_attr);
1153
1105 check_hover_result( 1154 check_hover_result(
1106 r#" 1155 r#"
1107 //- /lib.rs 1156 //- /lib.rs
@@ -1176,4 +1225,89 @@ fn func(foo: i32) { if true { <|>foo; }; }
1176 ); 1225 );
1177 assert_impl_action(&actions[0], 5); 1226 assert_impl_action(&actions[0], 5);
1178 } 1227 }
1228
1229 #[test]
1230 fn test_hover_test_has_action() {
1231 let (_, actions) = check_hover_result(
1232 "
1233 //- /lib.rs
1234 #[test]
1235 fn foo_<|>test() {}
1236 ",
1237 &["fn foo_test()"],
1238 );
1239 assert_debug_snapshot!(actions,
1240 @r###"
1241 [
1242 Runnable(
1243 Runnable {
1244 nav: NavigationTarget {
1245 file_id: FileId(
1246 1,
1247 ),
1248 full_range: 0..24,
1249 name: "foo_test",
1250 kind: FN_DEF,
1251 focus_range: Some(
1252 11..19,
1253 ),
1254 container_name: None,
1255 description: None,
1256 docs: None,
1257 },
1258 kind: Test {
1259 test_id: Path(
1260 "foo_test",
1261 ),
1262 attr: TestAttr {
1263 ignore: false,
1264 },
1265 },
1266 cfg_exprs: [],
1267 },
1268 ),
1269 ]
1270 "###);
1271 }
1272
1273 #[test]
1274 fn test_hover_test_mod_has_action() {
1275 let (_, actions) = check_hover_result(
1276 "
1277 //- /lib.rs
1278 mod tests<|> {
1279 #[test]
1280 fn foo_test() {}
1281 }
1282 ",
1283 &["mod tests"],
1284 );
1285 assert_debug_snapshot!(actions,
1286 @r###"
1287 [
1288 Runnable(
1289 Runnable {
1290 nav: NavigationTarget {
1291 file_id: FileId(
1292 1,
1293 ),
1294 full_range: 0..46,
1295 name: "tests",
1296 kind: MODULE,
1297 focus_range: Some(
1298 4..9,
1299 ),
1300 container_name: None,
1301 description: None,
1302 docs: None,
1303 },
1304 kind: TestMod {
1305 path: "tests",
1306 },
1307 cfg_exprs: [],
1308 },
1309 ),
1310 ]
1311 "###);
1312 }
1179} 1313}
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index f32ce0d22..fc57dc33d 100644
--- a/crates/ra_ide/src/runnables.rs
+++ b/crates/ra_ide/src/runnables.rs
@@ -11,14 +11,14 @@ use ra_syntax::{
11 11
12use crate::{display::ToNav, FileId, NavigationTarget}; 12use crate::{display::ToNav, FileId, NavigationTarget};
13 13
14#[derive(Debug)] 14#[derive(Debug, Clone)]
15pub struct Runnable { 15pub struct Runnable {
16 pub nav: NavigationTarget, 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),
@@ -33,7 +33,7 @@ impl fmt::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),
@@ -135,7 +175,7 @@ fn runnable_fn(
135 Some(Runnable { nav, kind, cfg_exprs }) 175 Some(Runnable { nav, kind, cfg_exprs })
136} 176}
137 177
138#[derive(Debug)] 178#[derive(Debug, Copy, Clone)]
139pub struct TestAttr { 179pub struct TestAttr {
140 pub ignore: bool, 180 pub ignore: bool,
141} 181}
@@ -207,6 +247,15 @@ mod tests {
207 247
208 use crate::mock_analysis::analysis_and_position; 248 use crate::mock_analysis::analysis_and_position;
209 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
210 #[test] 259 #[test]
211 fn test_runnables() { 260 fn test_runnables() {
212 let (analysis, pos) = analysis_and_position( 261 let (analysis, pos) = analysis_and_position(
@@ -221,6 +270,9 @@ mod tests {
221 #[test] 270 #[test]
222 #[ignore] 271 #[ignore]
223 fn test_foo() {} 272 fn test_foo() {}
273
274 #[bench]
275 fn bench() {}
224 "#, 276 "#,
225 ); 277 );
226 let runnables = analysis.runnables(pos.file_id).unwrap(); 278 let runnables = analysis.runnables(pos.file_id).unwrap();
@@ -295,9 +347,32 @@ mod tests {
295 }, 347 },
296 cfg_exprs: [], 348 cfg_exprs: [],
297 }, 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 },
298 ] 372 ]
299 "### 373 "###
300 ); 374 );
375 assert_actions(&runnables, &[&BIN, &TEST, &TEST, &BENCH]);
301 } 376 }
302 377
303 #[test] 378 #[test]
@@ -361,6 +436,7 @@ mod tests {
361 ] 436 ]
362 "### 437 "###
363 ); 438 );
439 assert_actions(&runnables, &[&BIN, &DOCTEST]);
364 } 440 }
365 441
366 #[test] 442 #[test]
@@ -427,6 +503,7 @@ mod tests {
427 ] 503 ]
428 "### 504 "###
429 ); 505 );
506 assert_actions(&runnables, &[&BIN, &DOCTEST]);
430 } 507 }
431 508
432 #[test] 509 #[test]
@@ -493,6 +570,7 @@ mod tests {
493 ] 570 ]
494 "### 571 "###
495 ); 572 );
573 assert_actions(&runnables, &[&TEST, &TEST]);
496 } 574 }
497 575
498 #[test] 576 #[test]
@@ -561,6 +639,7 @@ mod tests {
561 ] 639 ]
562 "### 640 "###
563 ); 641 );
642 assert_actions(&runnables, &[&TEST, &TEST]);
564 } 643 }
565 644
566 #[test] 645 #[test]
@@ -631,6 +710,7 @@ mod tests {
631 ] 710 ]
632 "### 711 "###
633 ); 712 );
713 assert_actions(&runnables, &[&TEST, &TEST]);
634 } 714 }
635 715
636 #[test] 716 #[test]
@@ -681,6 +761,7 @@ mod tests {
681 ] 761 ]
682 "### 762 "###
683 ); 763 );
764 assert_actions(&runnables, &[&TEST]);
684 } 765 }
685 766
686 #[test] 767 #[test]
@@ -739,6 +820,7 @@ mod tests {
739 ] 820 ]
740 "### 821 "###
741 ); 822 );
823 assert_actions(&runnables, &[&TEST]);
742 } 824 }
743 825
744 #[test] 826 #[test]