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.rs116
1 files changed, 59 insertions, 57 deletions
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index 3e721dcca..d96cb5596 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -1,10 +1,10 @@
1//! Logic for computing info that is displayed when the user hovers over any 1use std::iter::once;
2//! source code items (e.g. function call, struct field, variable symbol...)
3 2
4use hir::{ 3use hir::{
5 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef, 4 Adt, AsAssocItem, AssocItemContainer, FieldSource, HasSource, HirDisplay, ModuleDef,
6 ModuleSource, Semantics, 5 ModuleSource, Semantics,
7}; 6};
7use itertools::Itertools;
8use ra_db::SourceDatabase; 8use ra_db::SourceDatabase;
9use ra_ide_db::{ 9use ra_ide_db::{
10 defs::{classify_name, classify_name_ref, Definition}, 10 defs::{classify_name, classify_name_ref, Definition},
@@ -21,8 +21,6 @@ use crate::{
21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel}, 21 display::{macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel},
22 FilePosition, RangeInfo, 22 FilePosition, RangeInfo,
23}; 23};
24use itertools::Itertools;
25use std::iter::once;
26 24
27/// Contains the results when hovering over an item 25/// Contains the results when hovering over an item
28#[derive(Debug, Default)] 26#[derive(Debug, Default)]
@@ -62,6 +60,63 @@ impl HoverResult {
62 } 60 }
63} 61}
64 62
63// Feature: Hover
64//
65// Shows additional information, like type of an expression or documentation for definition when "focusing" code.
66// Focusing is usually hovering with a mouse, but can also be triggered with a shortcut.
67pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
68 let sema = Semantics::new(db);
69 let file = sema.parse(position.file_id).syntax().clone();
70 let token = pick_best(file.token_at_offset(position.offset))?;
71 let token = sema.descend_into_macros(token);
72
73 let mut res = HoverResult::new();
74
75 if let Some((node, name_kind)) = match_ast! {
76 match (token.parent()) {
77 ast::NameRef(name_ref) => {
78 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
79 },
80 ast::Name(name) => {
81 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
82 },
83 _ => None,
84 }
85 } {
86 let range = sema.original_range(&node).range;
87 res.extend(hover_text_from_name_kind(db, name_kind));
88
89 if !res.is_empty() {
90 return Some(RangeInfo::new(range, res));
91 }
92 }
93
94 let node = token
95 .ancestors()
96 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
97
98 let ty = match_ast! {
99 match node {
100 ast::MacroCall(_it) => {
101 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
102 // (e.g expanding a builtin macro). So we give up here.
103 return None;
104 },
105 ast::Expr(it) => {
106 sema.type_of_expr(&it)
107 },
108 ast::Pat(it) => {
109 sema.type_of_pat(&it)
110 },
111 _ => None,
112 }
113 }?;
114
115 res.extend(Some(rust_code_markup(&ty.display(db))));
116 let range = sema.original_range(&node).range;
117 Some(RangeInfo::new(range, res))
118}
119
65fn hover_text( 120fn hover_text(
66 docs: Option<String>, 121 docs: Option<String>,
67 desc: Option<String>, 122 desc: Option<String>,
@@ -160,59 +215,6 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
160 } 215 }
161} 216}
162 217
163pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
164 let sema = Semantics::new(db);
165 let file = sema.parse(position.file_id).syntax().clone();
166 let token = pick_best(file.token_at_offset(position.offset))?;
167 let token = sema.descend_into_macros(token);
168
169 let mut res = HoverResult::new();
170
171 if let Some((node, name_kind)) = match_ast! {
172 match (token.parent()) {
173 ast::NameRef(name_ref) => {
174 classify_name_ref(&sema, &name_ref).map(|d| (name_ref.syntax().clone(), d.definition()))
175 },
176 ast::Name(name) => {
177 classify_name(&sema, &name).map(|d| (name.syntax().clone(), d.definition()))
178 },
179 _ => None,
180 }
181 } {
182 let range = sema.original_range(&node).range;
183 res.extend(hover_text_from_name_kind(db, name_kind));
184
185 if !res.is_empty() {
186 return Some(RangeInfo::new(range, res));
187 }
188 }
189
190 let node = token
191 .ancestors()
192 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())?;
193
194 let ty = match_ast! {
195 match node {
196 ast::MacroCall(_it) => {
197 // If this node is a MACRO_CALL, it means that `descend_into_macros` failed to resolve.
198 // (e.g expanding a builtin macro). So we give up here.
199 return None;
200 },
201 ast::Expr(it) => {
202 sema.type_of_expr(&it)
203 },
204 ast::Pat(it) => {
205 sema.type_of_pat(&it)
206 },
207 _ => None,
208 }
209 }?;
210
211 res.extend(Some(rust_code_markup(&ty.display(db))));
212 let range = sema.original_range(&node).range;
213 Some(RangeInfo::new(range, res))
214}
215
216fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { 218fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> {
217 return tokens.max_by_key(priority); 219 return tokens.max_by_key(priority);
218 fn priority(n: &SyntaxToken) -> usize { 220 fn priority(n: &SyntaxToken) -> usize {