diff options
Diffstat (limited to 'crates/ra_ide/src/hover.rs')
-rw-r--r-- | crates/ra_ide/src/hover.rs | 148 |
1 files changed, 136 insertions, 12 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 9636cd0d6..62df07459 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -13,14 +13,43 @@ use ra_ide_db::{ | |||
13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; | 13 | use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; |
14 | 14 | ||
15 | use crate::{ | 15 | use crate::{ |
16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, | 16 | display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, ToNav}, |
17 | FilePosition, RangeInfo, | 17 | FilePosition, NavigationTarget, RangeInfo, |
18 | }; | 18 | }; |
19 | 19 | ||
20 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
21 | pub struct HoverConfig { | ||
22 | pub implementations: bool, | ||
23 | } | ||
24 | |||
25 | impl Default for HoverConfig { | ||
26 | fn default() -> Self { | ||
27 | Self { implementations: true } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | impl HoverConfig { | ||
32 | pub const NO_ACTIONS: Self = Self { implementations: false }; | ||
33 | |||
34 | pub fn any(&self) -> bool { | ||
35 | self.implementations | ||
36 | } | ||
37 | |||
38 | pub fn none(&self) -> bool { | ||
39 | !self.any() | ||
40 | } | ||
41 | } | ||
42 | |||
43 | #[derive(Debug, Clone)] | ||
44 | pub enum HoverAction { | ||
45 | Implementaion(FilePosition), | ||
46 | } | ||
47 | |||
20 | /// Contains the results when hovering over an item | 48 | /// Contains the results when hovering over an item |
21 | #[derive(Debug, Default)] | 49 | #[derive(Debug, Default)] |
22 | pub struct HoverResult { | 50 | pub struct HoverResult { |
23 | results: Vec<String>, | 51 | results: Vec<String>, |
52 | actions: Vec<HoverAction>, | ||
24 | } | 53 | } |
25 | 54 | ||
26 | impl HoverResult { | 55 | impl HoverResult { |
@@ -48,10 +77,20 @@ impl HoverResult { | |||
48 | &self.results | 77 | &self.results |
49 | } | 78 | } |
50 | 79 | ||
80 | pub fn actions(&self) -> &[HoverAction] { | ||
81 | &self.actions | ||
82 | } | ||
83 | |||
84 | pub fn push_action(&mut self, action: HoverAction) { | ||
85 | self.actions.push(action); | ||
86 | } | ||
87 | |||
51 | /// Returns the results converted into markup | 88 | /// Returns the results converted into markup |
52 | /// for displaying in a UI | 89 | /// for displaying in a UI |
90 | /// | ||
91 | /// Does not process actions! | ||
53 | pub fn to_markup(&self) -> String { | 92 | pub fn to_markup(&self) -> String { |
54 | self.results.join("\n\n---\n") | 93 | self.results.join("\n\n___\n") |
55 | } | 94 | } |
56 | } | 95 | } |
57 | 96 | ||
@@ -82,6 +121,10 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
82 | res.extend(hover_text_from_name_kind(db, name_kind)); | 121 | res.extend(hover_text_from_name_kind(db, name_kind)); |
83 | 122 | ||
84 | if !res.is_empty() { | 123 | if !res.is_empty() { |
124 | if let Some(action) = show_implementations_action(db, name_kind) { | ||
125 | res.push_action(action); | ||
126 | } | ||
127 | |||
85 | return Some(RangeInfo::new(range, res)); | 128 | return Some(RangeInfo::new(range, res)); |
86 | } | 129 | } |
87 | } | 130 | } |
@@ -112,6 +155,26 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn | |||
112 | Some(RangeInfo::new(range, res)) | 155 | Some(RangeInfo::new(range, res)) |
113 | } | 156 | } |
114 | 157 | ||
158 | fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { | ||
159 | fn to_action(nav_target: NavigationTarget) -> HoverAction { | ||
160 | HoverAction::Implementaion(FilePosition { | ||
161 | file_id: nav_target.file_id(), | ||
162 | offset: nav_target.range().start(), | ||
163 | }) | ||
164 | } | ||
165 | |||
166 | match def { | ||
167 | Definition::ModuleDef(it) => match it { | ||
168 | ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))), | ||
169 | ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))), | ||
170 | ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))), | ||
171 | ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))), | ||
172 | _ => None, | ||
173 | }, | ||
174 | _ => None, | ||
175 | } | ||
176 | } | ||
177 | |||
115 | fn hover_text( | 178 | fn hover_text( |
116 | docs: Option<String>, | 179 | docs: Option<String>, |
117 | desc: Option<String>, | 180 | desc: Option<String>, |
@@ -228,6 +291,8 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
228 | 291 | ||
229 | #[cfg(test)] | 292 | #[cfg(test)] |
230 | mod tests { | 293 | mod tests { |
294 | use super::*; | ||
295 | |||
231 | use ra_db::FileLoader; | 296 | use ra_db::FileLoader; |
232 | use ra_syntax::TextRange; | 297 | use ra_syntax::TextRange; |
233 | 298 | ||
@@ -241,7 +306,14 @@ mod tests { | |||
241 | s.map(trim_markup) | 306 | s.map(trim_markup) |
242 | } | 307 | } |
243 | 308 | ||
244 | fn check_hover_result(fixture: &str, expected: &[&str]) -> String { | 309 | fn assert_impl_action(action: &HoverAction, position: u32) { |
310 | let offset = match action { | ||
311 | HoverAction::Implementaion(pos) => pos.offset, | ||
312 | }; | ||
313 | assert_eq!(offset, position.into()); | ||
314 | } | ||
315 | |||
316 | fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) { | ||
245 | let (analysis, position) = analysis_and_position(fixture); | 317 | let (analysis, position) = analysis_and_position(fixture); |
246 | let hover = analysis.hover(position).unwrap().unwrap(); | 318 | let hover = analysis.hover(position).unwrap().unwrap(); |
247 | let mut results = Vec::from(hover.info.results()); | 319 | let mut results = Vec::from(hover.info.results()); |
@@ -256,7 +328,7 @@ mod tests { | |||
256 | assert_eq!(hover.info.len(), expected.len()); | 328 | assert_eq!(hover.info.len(), expected.len()); |
257 | 329 | ||
258 | let content = analysis.db.file_text(position.file_id); | 330 | let content = analysis.db.file_text(position.file_id); |
259 | content[hover.range].to_string() | 331 | (content[hover.range].to_string(), hover.info.actions().to_vec()) |
260 | } | 332 | } |
261 | 333 | ||
262 | fn check_hover_no_result(fixture: &str) { | 334 | fn check_hover_no_result(fixture: &str) { |
@@ -746,7 +818,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
746 | 818 | ||
747 | #[test] | 819 | #[test] |
748 | fn test_hover_through_macro() { | 820 | fn test_hover_through_macro() { |
749 | let hover_on = check_hover_result( | 821 | let (hover_on, _) = check_hover_result( |
750 | " | 822 | " |
751 | //- /lib.rs | 823 | //- /lib.rs |
752 | macro_rules! id { | 824 | macro_rules! id { |
@@ -767,7 +839,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
767 | 839 | ||
768 | #[test] | 840 | #[test] |
769 | fn test_hover_through_expr_in_macro() { | 841 | fn test_hover_through_expr_in_macro() { |
770 | let hover_on = check_hover_result( | 842 | let (hover_on, _) = check_hover_result( |
771 | " | 843 | " |
772 | //- /lib.rs | 844 | //- /lib.rs |
773 | macro_rules! id { | 845 | macro_rules! id { |
@@ -785,7 +857,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
785 | 857 | ||
786 | #[test] | 858 | #[test] |
787 | fn test_hover_through_expr_in_macro_recursive() { | 859 | fn test_hover_through_expr_in_macro_recursive() { |
788 | let hover_on = check_hover_result( | 860 | let (hover_on, _) = check_hover_result( |
789 | " | 861 | " |
790 | //- /lib.rs | 862 | //- /lib.rs |
791 | macro_rules! id_deep { | 863 | macro_rules! id_deep { |
@@ -806,7 +878,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
806 | 878 | ||
807 | #[test] | 879 | #[test] |
808 | fn test_hover_through_func_in_macro_recursive() { | 880 | fn test_hover_through_func_in_macro_recursive() { |
809 | let hover_on = check_hover_result( | 881 | let (hover_on, _) = check_hover_result( |
810 | " | 882 | " |
811 | //- /lib.rs | 883 | //- /lib.rs |
812 | macro_rules! id_deep { | 884 | macro_rules! id_deep { |
@@ -830,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
830 | 902 | ||
831 | #[test] | 903 | #[test] |
832 | fn test_hover_through_literal_string_in_macro() { | 904 | fn test_hover_through_literal_string_in_macro() { |
833 | let hover_on = check_hover_result( | 905 | let (hover_on, _) = check_hover_result( |
834 | r#" | 906 | r#" |
835 | //- /lib.rs | 907 | //- /lib.rs |
836 | macro_rules! arr { | 908 | macro_rules! arr { |
@@ -849,7 +921,7 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
849 | 921 | ||
850 | #[test] | 922 | #[test] |
851 | fn test_hover_through_assert_macro() { | 923 | fn test_hover_through_assert_macro() { |
852 | let hover_on = check_hover_result( | 924 | let (hover_on, _) = check_hover_result( |
853 | r#" | 925 | r#" |
854 | //- /lib.rs | 926 | //- /lib.rs |
855 | #[rustc_builtin_macro] | 927 | #[rustc_builtin_macro] |
@@ -925,13 +997,14 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
925 | 997 | ||
926 | #[test] | 998 | #[test] |
927 | fn test_hover_trait_show_qualifiers() { | 999 | fn test_hover_trait_show_qualifiers() { |
928 | check_hover_result( | 1000 | let (_, actions) = check_hover_result( |
929 | " | 1001 | " |
930 | //- /lib.rs | 1002 | //- /lib.rs |
931 | unsafe trait foo<|>() {} | 1003 | unsafe trait foo<|>() {} |
932 | ", | 1004 | ", |
933 | &["unsafe trait foo"], | 1005 | &["unsafe trait foo"], |
934 | ); | 1006 | ); |
1007 | assert_impl_action(&actions[0], 13); | ||
935 | } | 1008 | } |
936 | 1009 | ||
937 | #[test] | 1010 | #[test] |
@@ -1052,4 +1125,55 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
1052 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], | 1125 | &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], |
1053 | ); | 1126 | ); |
1054 | } | 1127 | } |
1128 | |||
1129 | #[test] | ||
1130 | fn test_hover_trait_has_impl_action() { | ||
1131 | let (_, actions) = check_hover_result( | ||
1132 | " | ||
1133 | //- /lib.rs | ||
1134 | trait foo<|>() {} | ||
1135 | ", | ||
1136 | &["trait foo"], | ||
1137 | ); | ||
1138 | assert_impl_action(&actions[0], 6); | ||
1139 | } | ||
1140 | |||
1141 | #[test] | ||
1142 | fn test_hover_struct_has_impl_action() { | ||
1143 | let (_, actions) = check_hover_result( | ||
1144 | " | ||
1145 | //- /lib.rs | ||
1146 | struct foo<|>() {} | ||
1147 | ", | ||
1148 | &["struct foo"], | ||
1149 | ); | ||
1150 | assert_impl_action(&actions[0], 7); | ||
1151 | } | ||
1152 | |||
1153 | #[test] | ||
1154 | fn test_hover_union_has_impl_action() { | ||
1155 | let (_, actions) = check_hover_result( | ||
1156 | " | ||
1157 | //- /lib.rs | ||
1158 | union foo<|>() {} | ||
1159 | ", | ||
1160 | &["union foo"], | ||
1161 | ); | ||
1162 | assert_impl_action(&actions[0], 6); | ||
1163 | } | ||
1164 | |||
1165 | #[test] | ||
1166 | fn test_hover_enum_has_impl_action() { | ||
1167 | let (_, actions) = check_hover_result( | ||
1168 | " | ||
1169 | //- /lib.rs | ||
1170 | enum foo<|>() { | ||
1171 | A, | ||
1172 | B | ||
1173 | } | ||
1174 | ", | ||
1175 | &["enum foo"], | ||
1176 | ); | ||
1177 | assert_impl_action(&actions[0], 5); | ||
1178 | } | ||
1055 | } | 1179 | } |