aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api/src
diff options
context:
space:
mode:
authorkjeremy <[email protected]>2019-11-18 16:58:42 +0000
committerkjeremy <[email protected]>2019-11-18 16:58:42 +0000
commitb2cc593381aa835b708dfc2df05809e7574be0d6 (patch)
tree61c352fa2ca5cdb1e2192187a9e29ac6d6e3a5ac /crates/ra_ide_api/src
parent7614439033bc8b68d3982d93595161ddfda80837 (diff)
Support hover through macro
Diffstat (limited to 'crates/ra_ide_api/src')
-rw-r--r--crates/ra_ide_api/src/hover.rs108
1 files changed, 66 insertions, 42 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index c6d678c0c..78210b085 100644
--- a/crates/ra_ide_api/src/hover.rs
+++ b/crates/ra_ide_api/src/hover.rs
@@ -1,11 +1,11 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{Adt, HasSource, HirDisplay, Source}; 3use hir::{db::AstDatabase, Adt, HasSource, HirDisplay};
4use ra_db::SourceDatabase; 4use ra_db::SourceDatabase;
5use ra_syntax::{ 5use ra_syntax::{
6 algo::{ancestors_at_offset, find_covering_element, find_node_at_offset}, 6 algo::find_covering_element,
7 ast::{self, DocCommentsOwner}, 7 ast::{self, DocCommentsOwner},
8 AstNode, 8 match_ast, AstNode,
9}; 9};
10 10
11use crate::{ 11use crate::{
@@ -14,6 +14,7 @@ use crate::{
14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, 14 description_from_symbol, docs_from_symbol, macro_label, rust_code_markup,
15 rust_code_markup_with_doc, ShortLabel, 15 rust_code_markup_with_doc, ShortLabel,
16 }, 16 },
17 expand::descend_into_macros,
17 references::{classify_name, classify_name_ref, NameKind, NameKind::*}, 18 references::{classify_name, classify_name_ref, NameKind, NameKind::*},
18 FilePosition, FileRange, RangeInfo, 19 FilePosition, FileRange, RangeInfo,
19}; 20};
@@ -162,55 +163,56 @@ fn hover_text_from_name_kind(
162} 163}
163 164
164pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> { 165pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option<RangeInfo<HoverResult>> {
165 let parse = db.parse(position.file_id); 166 let file = db.parse_or_expand(position.file_id.into())?;
166 let file = parse.tree(); 167 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
168 let token = descend_into_macros(db, position.file_id, token);
167 169
168 let mut res = HoverResult::new(); 170 let mut res = HoverResult::new();
169 171
170 let mut range = if let Some(name_ref) = 172 let mut range = match_ast! {
171 find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) 173 match (token.ast.parent()) {
172 { 174 ast::NameRef(name_ref) => {
173 let mut no_fallback = false; 175 let mut no_fallback = false;
174 if let Some(name_kind) = 176 if let Some(name_kind) =
175 classify_name_ref(db, Source::new(position.file_id.into(), &name_ref)).map(|d| d.kind) 177 classify_name_ref(db, token.with_ast(&name_ref)).map(|d| d.kind)
176 { 178 {
177 res.extend(hover_text_from_name_kind(db, name_kind, &mut no_fallback)) 179 res.extend(hover_text_from_name_kind(db, name_kind, &mut no_fallback))
178 } 180 }
179 181
180 if res.is_empty() && !no_fallback { 182 if res.is_empty() && !no_fallback {
181 // Fallback index based approach: 183 // Fallback index based approach:
182 let symbols = crate::symbol_index::index_resolve(db, &name_ref); 184 let symbols = crate::symbol_index::index_resolve(db, &name_ref);
183 for sym in symbols { 185 for sym in symbols {
184 let docs = docs_from_symbol(db, &sym); 186 let docs = docs_from_symbol(db, &sym);
185 let desc = description_from_symbol(db, &sym); 187 let desc = description_from_symbol(db, &sym);
186 res.extend(hover_text(docs, desc)); 188 res.extend(hover_text(docs, desc));
187 } 189 }
188 } 190 }
189 191
190 if !res.is_empty() { 192 if !res.is_empty() {
191 Some(name_ref.syntax().text_range()) 193 Some(name_ref.syntax().text_range())
192 } else { 194 } else {
193 None 195 None
194 } 196 }
195 } else if let Some(name) = find_node_at_offset::<ast::Name>(file.syntax(), position.offset) { 197 },
196 if let Some(name_kind) = 198 ast::Name(name) => {
197 classify_name(db, Source::new(position.file_id.into(), &name)).map(|d| d.kind) 199 if let Some(name_kind) = classify_name(db, token.with_ast(&name)).map(|d| d.kind) {
198 { 200 let mut _b: bool = true;
199 let mut _b: bool = true; 201 res.extend(hover_text_from_name_kind(db, name_kind, &mut _b));
200 res.extend(hover_text_from_name_kind(db, name_kind, &mut _b)); 202 }
201 }
202 203
203 if !res.is_empty() { 204 if !res.is_empty() {
204 Some(name.syntax().text_range()) 205 Some(name.syntax().text_range())
205 } else { 206 } else {
206 None 207 None
208 }
209 },
210 _ => None,
207 } 211 }
208 } else {
209 None
210 }; 212 };
211 213
212 if range.is_none() { 214 if range.is_none() {
213 let node = ancestors_at_offset(file.syntax(), position.offset).find(|n| { 215 let node = token.ast.ancestors().find(|n| {
214 ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some() 216 ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some()
215 })?; 217 })?;
216 let frange = FileRange { file_id: position.file_id, range: node.text_range() }; 218 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
@@ -716,4 +718,26 @@ fn func(foo: i32) { if true { <|>foo; }; }
716 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 718 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
717 assert_eq!(hover.info.is_exact(), true); 719 assert_eq!(hover.info.is_exact(), true);
718 } 720 }
721
722 #[test]
723 fn test_hover_through_macro() {
724 let (analysis, position) = single_file_with_position(
725 "
726 macro_rules! id {
727 ($($tt:$tt)*) => { $($tt)* }
728 }
729
730 fn foo() {}
731
732 id! {
733 fn bar() {
734 foo<|>();
735 }
736 }
737 ",
738 );
739 let hover = analysis.hover(position).unwrap().unwrap();
740 assert_eq!(trim_markup_opt(hover.info.first()), Some("fn foo()"));
741 assert_eq!(hover.info.is_exact(), true);
742 }
719} 743}