aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2019-11-18 19:57:06 +0000
committerGitHub <[email protected]>2019-11-18 19:57:06 +0000
commitc8fb5b491a172efed308ba1f15843fbc93815fc5 (patch)
tree0c6d2ea15471a4be005546df7065fb55948d7d23 /crates/ra_ide_api
parenta4f21801c54c65eafa337edc5e86de2c46b37544 (diff)
parent90f6f6080ea5f3f8588ed4a10056820e418e78d0 (diff)
Merge #2307
2307: Support hover through macro r=matklad a=kjeremy Allows hover to work through macros like `match_ast!`. Co-authored-by: kjeremy <[email protected]>
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/hover.rs104
1 files changed, 62 insertions, 42 deletions
diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs
index c6d678c0c..787b714b3 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,55 @@ 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 res.extend(hover_text_from_name_kind(db, name_kind, &mut true));
199 let mut _b: bool = true; 201 }
200 res.extend(hover_text_from_name_kind(db, name_kind, &mut _b));
201 }
202 202
203 if !res.is_empty() { 203 if !res.is_empty() {
204 Some(name.syntax().text_range()) 204 Some(name.syntax().text_range())
205 } else { 205 } else {
206 None 206 None
207 }
208 },
209 _ => None,
207 } 210 }
208 } else {
209 None
210 }; 211 };
211 212
212 if range.is_none() { 213 if range.is_none() {
213 let node = ancestors_at_offset(file.syntax(), position.offset).find(|n| { 214 let node = token.ast.ancestors().find(|n| {
214 ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some() 215 ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some()
215 })?; 216 })?;
216 let frange = FileRange { file_id: position.file_id, range: node.text_range() }; 217 let frange = FileRange { file_id: position.file_id, range: node.text_range() };
@@ -716,4 +717,23 @@ fn func(foo: i32) { if true { <|>foo; }; }
716 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32")); 717 assert_eq!(trim_markup_opt(hover.info.first()), Some("i32"));
717 assert_eq!(hover.info.is_exact(), true); 718 assert_eq!(hover.info.is_exact(), true);
718 } 719 }
720
721 #[test]
722 fn test_hover_through_macro() {
723 check_hover_result(
724 "
725 //- /lib.rs
726 macro_rules! id {
727 ($($tt:tt)*) => { $($tt)* }
728 }
729 fn foo() {}
730 id! {
731 fn bar() {
732 fo<|>o();
733 }
734 }
735 ",
736 &["fn foo()"],
737 );
738 }
719} 739}