aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/hover.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/hover.rs')
-rw-r--r--crates/ra_ide/src/hover.rs284
1 files changed, 271 insertions, 13 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 9636cd0d6..ad78b7671 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -13,14 +13,52 @@ use ra_ide_db::{
13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset}; 13use ra_syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset};
14 14
15use crate::{ 15use 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 runnables::runnable,
18 FileId, FilePosition, NavigationTarget, RangeInfo, Runnable,
18}; 19};
20use test_utils::mark;
21
22#[derive(Clone, Debug, PartialEq, Eq)]
23pub struct HoverConfig {
24 pub implementations: bool,
25 pub run: bool,
26 pub debug: bool,
27}
28
29impl Default for HoverConfig {
30 fn default() -> Self {
31 Self { implementations: true, run: true, debug: true }
32 }
33}
34
35impl HoverConfig {
36 pub const NO_ACTIONS: Self = Self { implementations: false, run: false, debug: false };
37
38 pub fn any(&self) -> bool {
39 self.implementations || self.runnable()
40 }
41
42 pub fn none(&self) -> bool {
43 !self.any()
44 }
45
46 pub fn runnable(&self) -> bool {
47 self.run || self.debug
48 }
49}
50
51#[derive(Debug, Clone)]
52pub enum HoverAction {
53 Runnable(Runnable),
54 Implementaion(FilePosition),
55}
19 56
20/// Contains the results when hovering over an item 57/// Contains the results when hovering over an item
21#[derive(Debug, Default)] 58#[derive(Debug, Default)]
22pub struct HoverResult { 59pub struct HoverResult {
23 results: Vec<String>, 60 results: Vec<String>,
61 actions: Vec<HoverAction>,
24} 62}
25 63
26impl HoverResult { 64impl HoverResult {
@@ -48,10 +86,20 @@ impl HoverResult {
48 &self.results 86 &self.results
49 } 87 }
50 88
89 pub fn actions(&self) -> &[HoverAction] {
90 &self.actions
91 }
92
93 pub fn push_action(&mut self, action: HoverAction) {
94 self.actions.push(action);
95 }
96
51 /// Returns the results converted into markup 97 /// Returns the results converted into markup
52 /// for displaying in a UI 98 /// for displaying in a UI
99 ///
100 /// Does not process actions!
53 pub fn to_markup(&self) -> String { 101 pub fn to_markup(&self) -> String {
54 self.results.join("\n\n---\n") 102 self.results.join("\n\n___\n")
55 } 103 }
56} 104}
57 105
@@ -82,6 +130,14 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
82 res.extend(hover_text_from_name_kind(db, name_kind)); 130 res.extend(hover_text_from_name_kind(db, name_kind));
83 131
84 if !res.is_empty() { 132 if !res.is_empty() {
133 if let Some(action) = show_implementations_action(db, name_kind) {
134 res.push_action(action);
135 }
136
137 if let Some(action) = runnable_action(&sema, name_kind, position.file_id) {
138 res.push_action(action);
139 }
140
85 return Some(RangeInfo::new(range, res)); 141 return Some(RangeInfo::new(range, res));
86 } 142 }
87 } 143 }
@@ -112,6 +168,56 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeIn
112 Some(RangeInfo::new(range, res)) 168 Some(RangeInfo::new(range, res))
113} 169}
114 170
171fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> {
172 fn to_action(nav_target: NavigationTarget) -> HoverAction {
173 HoverAction::Implementaion(FilePosition {
174 file_id: nav_target.file_id(),
175 offset: nav_target.range().start(),
176 })
177 }
178
179 match def {
180 Definition::ModuleDef(it) => match it {
181 ModuleDef::Adt(Adt::Struct(it)) => Some(to_action(it.to_nav(db))),
182 ModuleDef::Adt(Adt::Union(it)) => Some(to_action(it.to_nav(db))),
183 ModuleDef::Adt(Adt::Enum(it)) => Some(to_action(it.to_nav(db))),
184 ModuleDef::Trait(it) => Some(to_action(it.to_nav(db))),
185 _ => None,
186 },
187 _ => None,
188 }
189}
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
115fn hover_text( 221fn hover_text(
116 docs: Option<String>, 222 docs: Option<String>,
117 desc: Option<String>, 223 desc: Option<String>,
@@ -228,6 +334,9 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
228 334
229#[cfg(test)] 335#[cfg(test)]
230mod tests { 336mod tests {
337 use super::*;
338 use insta::assert_debug_snapshot;
339
231 use ra_db::FileLoader; 340 use ra_db::FileLoader;
232 use ra_syntax::TextRange; 341 use ra_syntax::TextRange;
233 342
@@ -241,7 +350,15 @@ mod tests {
241 s.map(trim_markup) 350 s.map(trim_markup)
242 } 351 }
243 352
244 fn check_hover_result(fixture: &str, expected: &[&str]) -> String { 353 fn assert_impl_action(action: &HoverAction, position: u32) {
354 let offset = match action {
355 HoverAction::Implementaion(pos) => pos.offset,
356 it => panic!("Unexpected hover action: {:#?}", it),
357 };
358 assert_eq!(offset, position.into());
359 }
360
361 fn check_hover_result(fixture: &str, expected: &[&str]) -> (String, Vec<HoverAction>) {
245 let (analysis, position) = analysis_and_position(fixture); 362 let (analysis, position) = analysis_and_position(fixture);
246 let hover = analysis.hover(position).unwrap().unwrap(); 363 let hover = analysis.hover(position).unwrap().unwrap();
247 let mut results = Vec::from(hover.info.results()); 364 let mut results = Vec::from(hover.info.results());
@@ -256,7 +373,7 @@ mod tests {
256 assert_eq!(hover.info.len(), expected.len()); 373 assert_eq!(hover.info.len(), expected.len());
257 374
258 let content = analysis.db.file_text(position.file_id); 375 let content = analysis.db.file_text(position.file_id);
259 content[hover.range].to_string() 376 (content[hover.range].to_string(), hover.info.actions().to_vec())
260 } 377 }
261 378
262 fn check_hover_no_result(fixture: &str) { 379 fn check_hover_no_result(fixture: &str) {
@@ -457,7 +574,7 @@ struct Test<K, T = u8> {
457} 574}
458 575
459fn main() { 576fn main() {
460 let zz<|> = Test { t: 23, k: 33 }; 577 let zz<|> = Test { t: 23u8, k: 33 };
461}"#, 578}"#,
462 &["Test<i32, u8>"], 579 &["Test<i32, u8>"],
463 ); 580 );
@@ -746,7 +863,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
746 863
747 #[test] 864 #[test]
748 fn test_hover_through_macro() { 865 fn test_hover_through_macro() {
749 let hover_on = check_hover_result( 866 let (hover_on, _) = check_hover_result(
750 " 867 "
751 //- /lib.rs 868 //- /lib.rs
752 macro_rules! id { 869 macro_rules! id {
@@ -767,7 +884,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
767 884
768 #[test] 885 #[test]
769 fn test_hover_through_expr_in_macro() { 886 fn test_hover_through_expr_in_macro() {
770 let hover_on = check_hover_result( 887 let (hover_on, _) = check_hover_result(
771 " 888 "
772 //- /lib.rs 889 //- /lib.rs
773 macro_rules! id { 890 macro_rules! id {
@@ -785,7 +902,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
785 902
786 #[test] 903 #[test]
787 fn test_hover_through_expr_in_macro_recursive() { 904 fn test_hover_through_expr_in_macro_recursive() {
788 let hover_on = check_hover_result( 905 let (hover_on, _) = check_hover_result(
789 " 906 "
790 //- /lib.rs 907 //- /lib.rs
791 macro_rules! id_deep { 908 macro_rules! id_deep {
@@ -806,7 +923,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
806 923
807 #[test] 924 #[test]
808 fn test_hover_through_func_in_macro_recursive() { 925 fn test_hover_through_func_in_macro_recursive() {
809 let hover_on = check_hover_result( 926 let (hover_on, _) = check_hover_result(
810 " 927 "
811 //- /lib.rs 928 //- /lib.rs
812 macro_rules! id_deep { 929 macro_rules! id_deep {
@@ -830,7 +947,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
830 947
831 #[test] 948 #[test]
832 fn test_hover_through_literal_string_in_macro() { 949 fn test_hover_through_literal_string_in_macro() {
833 let hover_on = check_hover_result( 950 let (hover_on, _) = check_hover_result(
834 r#" 951 r#"
835 //- /lib.rs 952 //- /lib.rs
836 macro_rules! arr { 953 macro_rules! arr {
@@ -849,7 +966,7 @@ fn func(foo: i32) { if true { <|>foo; }; }
849 966
850 #[test] 967 #[test]
851 fn test_hover_through_assert_macro() { 968 fn test_hover_through_assert_macro() {
852 let hover_on = check_hover_result( 969 let (hover_on, _) = check_hover_result(
853 r#" 970 r#"
854 //- /lib.rs 971 //- /lib.rs
855 #[rustc_builtin_macro] 972 #[rustc_builtin_macro]
@@ -925,13 +1042,14 @@ fn func(foo: i32) { if true { <|>foo; }; }
925 1042
926 #[test] 1043 #[test]
927 fn test_hover_trait_show_qualifiers() { 1044 fn test_hover_trait_show_qualifiers() {
928 check_hover_result( 1045 let (_, actions) = check_hover_result(
929 " 1046 "
930 //- /lib.rs 1047 //- /lib.rs
931 unsafe trait foo<|>() {} 1048 unsafe trait foo<|>() {}
932 ", 1049 ",
933 &["unsafe trait foo"], 1050 &["unsafe trait foo"],
934 ); 1051 );
1052 assert_impl_action(&actions[0], 13);
935 } 1053 }
936 1054
937 #[test] 1055 #[test]
@@ -1003,6 +1121,8 @@ fn func(foo: i32) { if true { <|>foo; }; }
1003 1121
1004 #[test] 1122 #[test]
1005 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
1006 check_hover_result( 1126 check_hover_result(
1007 r#" 1127 r#"
1008 //- /lib.rs 1128 //- /lib.rs
@@ -1029,6 +1149,8 @@ fn func(foo: i32) { if true { <|>foo; }; }
1029 1149
1030 #[test] 1150 #[test]
1031 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
1032 check_hover_result( 1154 check_hover_result(
1033 r#" 1155 r#"
1034 //- /lib.rs 1156 //- /lib.rs
@@ -1052,4 +1174,140 @@ fn func(foo: i32) { if true { <|>foo; }; }
1052 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"], 1174 &["Bar\n```\n\n```rust\nfn foo(&self)\n```\n___\n\nDo the foo"],
1053 ); 1175 );
1054 } 1176 }
1177
1178 #[test]
1179 fn test_hover_trait_has_impl_action() {
1180 let (_, actions) = check_hover_result(
1181 "
1182 //- /lib.rs
1183 trait foo<|>() {}
1184 ",
1185 &["trait foo"],
1186 );
1187 assert_impl_action(&actions[0], 6);
1188 }
1189
1190 #[test]
1191 fn test_hover_struct_has_impl_action() {
1192 let (_, actions) = check_hover_result(
1193 "
1194 //- /lib.rs
1195 struct foo<|>() {}
1196 ",
1197 &["struct foo"],
1198 );
1199 assert_impl_action(&actions[0], 7);
1200 }
1201
1202 #[test]
1203 fn test_hover_union_has_impl_action() {
1204 let (_, actions) = check_hover_result(
1205 "
1206 //- /lib.rs
1207 union foo<|>() {}
1208 ",
1209 &["union foo"],
1210 );
1211 assert_impl_action(&actions[0], 6);
1212 }
1213
1214 #[test]
1215 fn test_hover_enum_has_impl_action() {
1216 let (_, actions) = check_hover_result(
1217 "
1218 //- /lib.rs
1219 enum foo<|>() {
1220 A,
1221 B
1222 }
1223 ",
1224 &["enum foo"],
1225 );
1226 assert_impl_action(&actions[0], 5);
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 }
1055} 1313}