aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvsrs <[email protected]>2020-06-06 12:30:29 +0100
committervsrs <[email protected]>2020-06-06 16:17:52 +0100
commit3434f1dd2c47fff3df159b9d62115c2df3fd6401 (patch)
treef821cbd28bc82b00e9c01727baeca65d8ef357ae
parentde74c0dcab2efd50d68f70d15de3fced718e8c7a (diff)
Add Run|Debug hover actions
-rw-r--r--crates/ra_ide/src/hover.rs129
-rw-r--r--crates/ra_ide/src/runnables.rs14
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs49
-rw-r--r--crates/rust-analyzer/src/to_proto.rs4
-rw-r--r--editors/code/package.json10
6 files changed, 184 insertions, 24 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 846d8c69b..138a7a7a9 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -14,34 +14,42 @@ 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};
19 20
20#[derive(Clone, Debug, PartialEq, Eq)] 21#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct HoverConfig { 22pub struct HoverConfig {
22 pub implementations: bool, 23 pub implementations: bool,
24 pub run: bool,
25 pub debug: bool,
23} 26}
24 27
25impl Default for HoverConfig { 28impl Default for HoverConfig {
26 fn default() -> Self { 29 fn default() -> Self {
27 Self { implementations: true } 30 Self { implementations: true, run: true, debug: true }
28 } 31 }
29} 32}
30 33
31impl HoverConfig { 34impl HoverConfig {
32 pub const NO_ACTIONS: Self = Self { implementations: false }; 35 pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false };
33 36
34 pub fn any(&self) -> bool { 37 pub fn any(&self) -> bool {
35 self.implementations 38 self.implementations || self.runnable()
36 } 39 }
37 40
38 pub fn none(&self) -> bool { 41 pub fn none(&self) -> bool {
39 !self.any() 42 !self.any()
40 } 43 }
44
45 pub fn runnable(&self) -> bool {
46 self.run || self.debug
47 }
41} 48}
42 49
43#[derive(Debug, Clone)] 50#[derive(Debug, Clone)]
44pub enum HoverAction { 51pub enum HoverAction {
52 Runnable(Runnable),
45 Implementaion(FilePosition), 53 Implementaion(FilePosition),
46} 54}
47 55
@@ -125,6 +133,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
125 res.push_action(action); 133 res.push_action(action);
126 } 134 }
127 135
136 if let Some(action) = runnable_action(&sema, name_kind, position.file_id) {
137 res.push_action(action);
138 }
139
128 return Some(RangeInfo::new(range, res)); 140 return Some(RangeInfo::new(range, res));
129 } 141 }
130 } 142 }
@@ -175,6 +187,28 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov
175 } 187 }
176} 188}
177 189
190fn runnable_action(
191 sema: &Semantics<RootDatabase>,
192 def: Definition,
193 file_id: FileId,
194) -> Option<HoverAction> {
195 match def {
196 Definition::ModuleDef(it) => match it {
197 ModuleDef::Module(it) => match it.definition_source(sema.db).value {
198 ModuleSource::Module(it) => runnable(&sema, it.syntax().clone(), file_id)
199 .map(|it| HoverAction::Runnable(it)),
200 _ => None,
201 },
202 ModuleDef::Function(it) => {
203 runnable(&sema, it.source(sema.db).value.syntax().clone(), file_id)
204 .map(|it| HoverAction::Runnable(it))
205 }
206 _ => None,
207 },
208 _ => None,
209 }
210}
211
178fn hover_text( 212fn hover_text(
179 docs: Option<String>, 213 docs: Option<String>,
180 desc: Option<String>, 214 desc: Option<String>,
@@ -292,6 +326,7 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
292#[cfg(test)] 326#[cfg(test)]
293mod tests { 327mod tests {
294 use super::*; 328 use super::*;
329 use insta::assert_debug_snapshot;
295 330
296 use ra_db::FileLoader; 331 use ra_db::FileLoader;
297 use ra_syntax::TextRange; 332 use ra_syntax::TextRange;
@@ -309,6 +344,7 @@ mod tests {
309 fn assert_impl_action(action: &HoverAction, position: u32) { 344 fn assert_impl_action(action: &HoverAction, position: u32) {
310 let offset = match action { 345 let offset = match action {
311 HoverAction::Implementaion(pos) => pos.offset, 346 HoverAction::Implementaion(pos) => pos.offset,
347 it => panic!("Unexpected hover action: {:#?}", it),
312 }; 348 };
313 assert_eq!(offset, position.into()); 349 assert_eq!(offset, position.into());
314 } 350 }
@@ -1176,4 +1212,89 @@ fn func(foo: i32) { if true { <|>foo; }; }
1176 ); 1212 );
1177 assert_impl_action(&actions[0], 5); 1213 assert_impl_action(&actions[0], 5);
1178 } 1214 }
1215
1216 #[test]
1217 fn test_hover_test_has_action() {
1218 let (_, actions) = check_hover_result(
1219 "
1220 //- /lib.rs
1221 #[test]
1222 fn foo_<|>test() {}
1223 ",
1224 &["fn foo_test()"],
1225 );
1226 assert_debug_snapshot!(actions,
1227 @r###"
1228 [
1229 Runnable(
1230 Runnable {
1231 nav: NavigationTarget {
1232 file_id: FileId(
1233 1,
1234 ),
1235 full_range: 0..24,
1236 name: "foo_test",
1237 kind: FN_DEF,
1238 focus_range: Some(
1239 11..19,
1240 ),
1241 container_name: None,
1242 description: None,
1243 docs: None,
1244 },
1245 kind: Test {
1246 test_id: Path(
1247 "foo_test",
1248 ),
1249 attr: TestAttr {
1250 ignore: false,
1251 },
1252 },
1253 cfg_exprs: [],
1254 },
1255 ),
1256 ]
1257 "###);
1258 }
1259
1260 #[test]
1261 fn test_hover_test_mod_has_action() {
1262 let (_, actions) = check_hover_result(
1263 "
1264 //- /lib.rs
1265 mod tests<|> {
1266 #[test]
1267 fn foo_test() {}
1268 }
1269 ",
1270 &["mod tests"],
1271 );
1272 assert_debug_snapshot!(actions,
1273 @r###"
1274 [
1275 Runnable(
1276 Runnable {
1277 nav: NavigationTarget {
1278 file_id: FileId(
1279 1,
1280 ),
1281 full_range: 0..46,
1282 name: "tests",
1283 kind: MODULE,
1284 focus_range: Some(
1285 4..9,
1286 ),
1287 container_name: None,
1288 description: None,
1289 docs: None,
1290 },
1291 kind: TestMod {
1292 path: "tests",
1293 },
1294 cfg_exprs: [],
1295 },
1296 ),
1297 ]
1298 "###);
1299 }
1179} 1300}
diff --git a/crates/ra_ide/src/runnables.rs b/crates/ra_ide/src/runnables.rs
index 9f7b5edfd..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 },
@@ -95,7 +95,11 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
95 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()
96} 96}
97 97
98fn 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> {
99 match_ast! { 103 match_ast! {
100 match item { 104 match item {
101 ast::FnDef(it) => runnable_fn(sema, it, file_id), 105 ast::FnDef(it) => runnable_fn(sema, it, file_id),
@@ -171,7 +175,7 @@ fn runnable_fn(
171 Some(Runnable { nav, kind, cfg_exprs }) 175 Some(Runnable { nav, kind, cfg_exprs })
172} 176}
173 177
174#[derive(Debug)] 178#[derive(Debug, Copy, Clone)]
175pub struct TestAttr { 179pub struct TestAttr {
176 pub ignore: bool, 180 pub ignore: bool,
177} 181}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 8d6efdbe8..17671f89e 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -285,6 +285,8 @@ impl Config {
285 set(value, "/hoverActions/enable", &mut use_hover_actions); 285 set(value, "/hoverActions/enable", &mut use_hover_actions);
286 if use_hover_actions { 286 if use_hover_actions {
287 set(value, "/hoverActions/implementations", &mut self.hover.implementations); 287 set(value, "/hoverActions/implementations", &mut self.hover.implementations);
288 set(value, "/hoverActions/run", &mut self.hover.run);
289 set(value, "/hoverActions/debug", &mut self.hover.debug);
288 } else { 290 } else {
289 self.hover = HoverConfig::NO_ACTIONS; 291 self.hover = HoverConfig::NO_ACTIONS;
290 } 292 }
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index da16976d3..cae447eea 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -18,8 +18,8 @@ use lsp_types::{
18 TextDocumentIdentifier, Url, WorkspaceEdit, 18 TextDocumentIdentifier, Url, WorkspaceEdit,
19}; 19};
20use ra_ide::{ 20use ra_ide::{
21 FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind, SearchScope, 21 FileId, FilePosition, FileRange, HoverAction, Query, RangeInfo, Runnable, RunnableKind,
22 TextEdit, 22 SearchScope, TextEdit,
23}; 23};
24use ra_prof::profile; 24use ra_prof::profile;
25use ra_project_model::TargetKind; 25use ra_project_model::TargetKind;
@@ -403,12 +403,12 @@ pub fn handle_runnables(
403 if !runnable.nav.full_range().contains_inclusive(offset) { 403 if !runnable.nav.full_range().contains_inclusive(offset) {
404 continue; 404 continue;
405 } 405 }
406 } 406 }
407 if is_lib_target(&runnable, cargo_spec.as_ref()) { 407 if is_lib_target(&runnable, cargo_spec.as_ref()) {
408 continue; 408 continue;
409 } 409 }
410 410
411 res.push(to_proto::runnable(&snap, file_id, runnable)?); 411 res.push(to_proto::runnable(&snap, file_id, &runnable)?);
412 } 412 }
413 413
414 // Add `cargo check` and `cargo test` for the whole package 414 // Add `cargo check` and `cargo test` for the whole package
@@ -550,7 +550,7 @@ pub fn handle_hover(
550 }), 550 }),
551 range: Some(range), 551 range: Some(range),
552 }, 552 },
553 actions: prepare_hover_actions(&snap, info.info.actions()), 553 actions: prepare_hover_actions(&snap, position.file_id, info.info.actions()),
554 }; 554 };
555 555
556 Ok(Some(hover)) 556 Ok(Some(hover))
@@ -818,7 +818,7 @@ pub fn handle_code_lens(
818 818
819 let action = runnable.action(); 819 let action = runnable.action();
820 let range = to_proto::range(&line_index, runnable.nav.range()); 820 let range = to_proto::range(&line_index, runnable.nav.range());
821 let r = to_proto::runnable(&snap, file_id, runnable)?; 821 let r = to_proto::runnable(&snap, file_id, &runnable)?;
822 if snap.config.lens.run { 822 if snap.config.lens.run {
823 let lens = CodeLens { 823 let lens = CodeLens {
824 range, 824 range,
@@ -829,11 +829,8 @@ pub fn handle_code_lens(
829 } 829 }
830 830
831 if action.debugee && snap.config.lens.debug { 831 if action.debugee && snap.config.lens.debug {
832 let debug_lens = CodeLens { 832 let debug_lens =
833 range, 833 CodeLens { range, command: Some(debug_single_command(r)), data: None };
834 command: Some(debug_single_command(r)),
835 data: None,
836 };
837 lenses.push(debug_lens); 834 lenses.push(debug_lens);
838 } 835 }
839 } 836 }
@@ -1183,8 +1180,33 @@ fn show_impl_command_link(
1183 None 1180 None
1184} 1181}
1185 1182
1183fn to_runnable_action(
1184 snap: &GlobalStateSnapshot,
1185 file_id: FileId,
1186 runnable: &Runnable,
1187) -> Option<lsp_ext::CommandLinkGroup> {
1188 to_proto::runnable(snap, file_id, runnable).ok().map(|r| {
1189 let mut group = lsp_ext::CommandLinkGroup::default();
1190
1191 let action = runnable.action();
1192 if snap.config.hover.run {
1193 let run_command = run_single_command(&r, action.run_title);
1194 group.commands.push(to_command_link(run_command, r.label.clone()));
1195 }
1196
1197 if snap.config.hover.debug {
1198 let hint = r.label.clone();
1199 let dbg_command = debug_single_command(r);
1200 group.commands.push(to_command_link(dbg_command, hint));
1201 }
1202
1203 group
1204 })
1205}
1206
1186fn prepare_hover_actions( 1207fn prepare_hover_actions(
1187 snap: &GlobalStateSnapshot, 1208 snap: &GlobalStateSnapshot,
1209 file_id: FileId,
1188 actions: &[HoverAction], 1210 actions: &[HoverAction],
1189) -> Vec<lsp_ext::CommandLinkGroup> { 1211) -> Vec<lsp_ext::CommandLinkGroup> {
1190 if snap.config.hover.none() || !snap.config.client_caps.hover_actions { 1212 if snap.config.hover.none() || !snap.config.client_caps.hover_actions {
@@ -1195,6 +1217,7 @@ fn prepare_hover_actions(
1195 .iter() 1217 .iter()
1196 .filter_map(|it| match it { 1218 .filter_map(|it| match it {
1197 HoverAction::Implementaion(position) => show_impl_command_link(snap, position), 1219 HoverAction::Implementaion(position) => show_impl_command_link(snap, position),
1220 HoverAction::Runnable(r) => to_runnable_action(snap, file_id, r),
1198 }) 1221 })
1199 .collect() 1222 .collect()
1200} 1223}
@@ -1205,10 +1228,10 @@ fn is_lib_target(runnable: &Runnable, cargo_spec: Option<&CargoTargetSpec>) -> b
1205 if let Some(spec) = cargo_spec { 1228 if let Some(spec) = cargo_spec {
1206 match spec.target_kind { 1229 match spec.target_kind {
1207 TargetKind::Bin => return true, 1230 TargetKind::Bin => return true,
1208 _ => () 1231 _ => (),
1209 } 1232 }
1210 } 1233 }
1211 } 1234 }
1212 1235
1213 false 1236 false
1214} \ No newline at end of file 1237}
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 710df1fbd..5daf037da 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -656,14 +656,14 @@ pub(crate) fn resolved_code_action(
656pub(crate) fn runnable( 656pub(crate) fn runnable(
657 snap: &GlobalStateSnapshot, 657 snap: &GlobalStateSnapshot,
658 file_id: FileId, 658 file_id: FileId,
659 runnable: Runnable, 659 runnable: &Runnable,
660) -> Result<lsp_ext::Runnable> { 660) -> Result<lsp_ext::Runnable> {
661 let spec = CargoTargetSpec::for_file(snap, file_id)?; 661 let spec = CargoTargetSpec::for_file(snap, file_id)?;
662 let target = spec.as_ref().map(|s| s.target.clone()); 662 let target = spec.as_ref().map(|s| s.target.clone());
663 let (cargo_args, executable_args) = 663 let (cargo_args, executable_args) =
664 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?; 664 CargoTargetSpec::runnable_args(spec, &runnable.kind, &runnable.cfg_exprs)?;
665 let label = runnable.label(target); 665 let label = runnable.label(target);
666 let location = location_link(snap, None, runnable.nav)?; 666 let location = location_link(snap, None, runnable.nav.clone())?;
667 667
668 Ok(lsp_ext::Runnable { 668 Ok(lsp_ext::Runnable {
669 label, 669 label,
diff --git a/editors/code/package.json b/editors/code/package.json
index b9c57db3b..7fdb5c27d 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -486,6 +486,16 @@
486 "type": "boolean", 486 "type": "boolean",
487 "default": true 487 "default": true
488 }, 488 },
489 "rust-analyzer.hoverActions.run": {
490 "markdownDescription": "Whether to show `Run` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
491 "type": "boolean",
492 "default": true
493 },
494 "rust-analyzer.hoverActions.debug": {
495 "markdownDescription": "Whether to show `Debug` action. Only applies when `#rust-analyzer.hoverActions.enable#` is set.",
496 "type": "boolean",
497 "default": true
498 },
489 "rust-analyzer.linkedProjects": { 499 "rust-analyzer.linkedProjects": {
490 "markdownDescription": [ 500 "markdownDescription": [
491 "Disable project auto-discovery in favor of explicitly specified set of projects.", 501 "Disable project auto-discovery in favor of explicitly specified set of projects.",