diff options
-rw-r--r-- | crates/ra_ide/src/hover.rs | 129 | ||||
-rw-r--r-- | crates/ra_ide/src/runnables.rs | 14 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 49 | ||||
-rw-r--r-- | crates/rust-analyzer/src/to_proto.rs | 4 | ||||
-rw-r--r-- | editors/code/package.json | 10 |
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 | ||
15 | use crate::{ | 15 | use 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)] |
21 | pub struct HoverConfig { | 22 | pub struct HoverConfig { |
22 | pub implementations: bool, | 23 | pub implementations: bool, |
24 | pub run: bool, | ||
25 | pub debug: bool, | ||
23 | } | 26 | } |
24 | 27 | ||
25 | impl Default for HoverConfig { | 28 | impl 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 | ||
31 | impl HoverConfig { | 34 | impl 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)] |
44 | pub enum HoverAction { | 51 | pub 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 | ||
190 | fn 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 | |||
178 | fn hover_text( | 212 | fn 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)] |
293 | mod tests { | 327 | mod 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 | ||
12 | use crate::{display::ToNav, FileId, NavigationTarget}; | 12 | use crate::{display::ToNav, FileId, NavigationTarget}; |
13 | 13 | ||
14 | #[derive(Debug)] | 14 | #[derive(Debug, Clone)] |
15 | pub struct Runnable { | 15 | pub 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)] |
22 | pub enum TestId { | 22 | pub 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)] |
37 | pub enum RunnableKind { | 37 | pub 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 | ||
98 | fn runnable(sema: &Semantics<RootDatabase>, item: SyntaxNode, file_id: FileId) -> Option<Runnable> { | 98 | pub(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)] |
175 | pub struct TestAttr { | 179 | pub 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 | }; |
20 | use ra_ide::{ | 20 | use 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 | }; |
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | use ra_project_model::TargetKind; | 25 | use 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 | ||
1183 | fn 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 | |||
1186 | fn prepare_hover_actions( | 1207 | fn 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( | |||
656 | pub(crate) fn runnable( | 656 | pub(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.", |