aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide_api
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide_api')
-rw-r--r--crates/ra_ide_api/src/goto_type_definition.rs36
-rw-r--r--crates/ra_ide_api/src/hover.rs104
2 files changed, 91 insertions, 49 deletions
diff --git a/crates/ra_ide_api/src/goto_type_definition.rs b/crates/ra_ide_api/src/goto_type_definition.rs
index 2327cb1e7..7d694e1f6 100644
--- a/crates/ra_ide_api/src/goto_type_definition.rs
+++ b/crates/ra_ide_api/src/goto_type_definition.rs
@@ -1,25 +1,28 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_db::SourceDatabase; 3use hir::db::AstDatabase;
4use ra_syntax::{ast, AstNode}; 4use ra_syntax::{ast, AstNode};
5 5
6use crate::{db::RootDatabase, display::ToNav, FilePosition, NavigationTarget, RangeInfo}; 6use crate::{
7 db::RootDatabase, display::ToNav, expand::descend_into_macros, FilePosition, NavigationTarget,
8 RangeInfo,
9};
7 10
8pub(crate) fn goto_type_definition( 11pub(crate) fn goto_type_definition(
9 db: &RootDatabase, 12 db: &RootDatabase,
10 position: FilePosition, 13 position: FilePosition,
11) -> Option<RangeInfo<Vec<NavigationTarget>>> { 14) -> Option<RangeInfo<Vec<NavigationTarget>>> {
12 let parse = db.parse(position.file_id); 15 let file = db.parse_or_expand(position.file_id.into())?;
16 let token = file.token_at_offset(position.offset).filter(|it| !it.kind().is_trivia()).next()?;
17 let token = descend_into_macros(db, position.file_id, token);
13 18
14 let node = parse.tree().syntax().token_at_offset(position.offset).find_map(|token| { 19 let node = token.ast.ancestors().find_map(|token| {
15 token 20 token
16 .parent()
17 .ancestors() 21 .ancestors()
18 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some()) 22 .find(|n| ast::Expr::cast(n.clone()).is_some() || ast::Pat::cast(n.clone()).is_some())
19 })?; 23 })?;
20 24
21 let analyzer = 25 let analyzer = hir::SourceAnalyzer::new(db, token.with_ast(&node), None);
22 hir::SourceAnalyzer::new(db, hir::Source::new(position.file_id.into(), &node), None);
23 26
24 let ty: hir::Ty = if let Some(ty) = 27 let ty: hir::Ty = if let Some(ty) =
25 ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e)) 28 ast::Expr::cast(node.clone()).and_then(|e| analyzer.type_of(db, &e))
@@ -80,4 +83,23 @@ mod tests {
80 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)", 83 "Foo STRUCT_DEF FileId(1) [0; 11) [7; 10)",
81 ); 84 );
82 } 85 }
86
87 #[test]
88 fn goto_type_definition_works_through_macro() {
89 check_goto(
90 "
91 //- /lib.rs
92 macro_rules! id {
93 ($($tt:tt)*) => { $($tt)* }
94 }
95 struct Foo {}
96 id! {
97 fn bar() {
98 let f<|> = Foo {};
99 }
100 }
101 ",
102 "Foo STRUCT_DEF FileId(1) [52; 65) [59; 62)",
103 );
104 }
83} 105}
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}