diff options
Diffstat (limited to 'crates/ide/src/goto_definition.rs')
-rw-r--r-- | crates/ide/src/goto_definition.rs | 105 |
1 files changed, 32 insertions, 73 deletions
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index a1d2bce1d..abed1969e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -2,16 +2,14 @@ use either::Either; | |||
2 | use hir::{HasAttrs, ModuleDef, Semantics}; | 2 | use hir::{HasAttrs, ModuleDef, Semantics}; |
3 | use ide_db::{ | 3 | use ide_db::{ |
4 | defs::{Definition, NameClass, NameRefClass}, | 4 | defs::{Definition, NameClass, NameRefClass}, |
5 | symbol_index, RootDatabase, | 5 | RootDatabase, |
6 | }; | 6 | }; |
7 | use syntax::{ | 7 | use syntax::{ |
8 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, | 8 | ast, match_ast, AstNode, AstToken, SyntaxKind::*, SyntaxToken, TextSize, TokenAtOffset, T, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | display::{ToNav, TryToNav}, | 12 | display::TryToNav, doc_links::extract_definitions_from_markdown, runnables::doc_owner_to_def, |
13 | doc_links::extract_definitions_from_markdown, | ||
14 | runnables::doc_owner_to_def, | ||
15 | FilePosition, NavigationTarget, RangeInfo, | 13 | FilePosition, NavigationTarget, RangeInfo, |
16 | }; | 14 | }; |
17 | 15 | ||
@@ -33,33 +31,31 @@ pub(crate) fn goto_definition( | |||
33 | let original_token = pick_best(file.token_at_offset(position.offset))?; | 31 | let original_token = pick_best(file.token_at_offset(position.offset))?; |
34 | let token = sema.descend_into_macros(original_token.clone()); | 32 | let token = sema.descend_into_macros(original_token.clone()); |
35 | let parent = token.parent(); | 33 | let parent = token.parent(); |
36 | if let Some(comment) = ast::Comment::cast(token.clone()) { | 34 | if let Some(comment) = ast::Comment::cast(token) { |
37 | let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; | 35 | let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; |
38 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); | 36 | return Some(RangeInfo::new(original_token.text_range(), vec![nav])); |
39 | } | 37 | } |
40 | 38 | ||
41 | let nav_targets = match_ast! { | 39 | let nav = match_ast! { |
42 | match parent { | 40 | match parent { |
43 | ast::NameRef(name_ref) => { | 41 | ast::NameRef(name_ref) => { |
44 | reference_definition(&sema, Either::Right(&name_ref)).to_vec() | 42 | reference_definition(&sema, Either::Right(&name_ref)) |
45 | }, | 43 | }, |
46 | ast::Name(name) => { | 44 | ast::Name(name) => { |
47 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); | 45 | let def = NameClass::classify(&sema, &name)?.referenced_or_defined(sema.db); |
48 | let nav = def.try_to_nav(sema.db)?; | 46 | def.try_to_nav(sema.db) |
49 | vec![nav] | ||
50 | }, | 47 | }, |
51 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { | 48 | ast::Lifetime(lt) => if let Some(name_class) = NameClass::classify_lifetime(&sema, <) { |
52 | let def = name_class.referenced_or_defined(sema.db); | 49 | let def = name_class.referenced_or_defined(sema.db); |
53 | let nav = def.try_to_nav(sema.db)?; | 50 | def.try_to_nav(sema.db) |
54 | vec![nav] | ||
55 | } else { | 51 | } else { |
56 | reference_definition(&sema, Either::Left(<)).to_vec() | 52 | reference_definition(&sema, Either::Left(<)) |
57 | }, | 53 | }, |
58 | _ => return None, | 54 | _ => return None, |
59 | } | 55 | } |
60 | }; | 56 | }; |
61 | 57 | ||
62 | Some(RangeInfo::new(original_token.text_range(), nav_targets)) | 58 | Some(RangeInfo::new(original_token.text_range(), nav.into_iter().collect())) |
63 | } | 59 | } |
64 | 60 | ||
65 | fn def_for_doc_comment( | 61 | fn def_for_doc_comment( |
@@ -120,63 +116,26 @@ fn pick_best(tokens: TokenAtOffset<SyntaxToken>) -> Option<SyntaxToken> { | |||
120 | } | 116 | } |
121 | } | 117 | } |
122 | 118 | ||
123 | #[derive(Debug)] | ||
124 | pub(crate) enum ReferenceResult { | ||
125 | Exact(NavigationTarget), | ||
126 | Approximate(Vec<NavigationTarget>), | ||
127 | } | ||
128 | |||
129 | impl ReferenceResult { | ||
130 | fn to_vec(self) -> Vec<NavigationTarget> { | ||
131 | match self { | ||
132 | ReferenceResult::Exact(target) => vec![target], | ||
133 | ReferenceResult::Approximate(vec) => vec, | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | pub(crate) fn reference_definition( | 119 | pub(crate) fn reference_definition( |
139 | sema: &Semantics<RootDatabase>, | 120 | sema: &Semantics<RootDatabase>, |
140 | name_ref: Either<&ast::Lifetime, &ast::NameRef>, | 121 | name_ref: Either<&ast::Lifetime, &ast::NameRef>, |
141 | ) -> ReferenceResult { | 122 | ) -> Option<NavigationTarget> { |
142 | let name_kind = name_ref.either( | 123 | let name_kind = name_ref.either( |
143 | |lifetime| NameRefClass::classify_lifetime(sema, lifetime), | 124 | |lifetime| NameRefClass::classify_lifetime(sema, lifetime), |
144 | |name_ref| NameRefClass::classify(sema, name_ref), | 125 | |name_ref| NameRefClass::classify(sema, name_ref), |
145 | ); | 126 | )?; |
146 | if let Some(def) = name_kind { | 127 | let def = name_kind.referenced(sema.db); |
147 | let def = def.referenced(sema.db); | 128 | def.try_to_nav(sema.db) |
148 | return match def.try_to_nav(sema.db) { | ||
149 | Some(nav) => ReferenceResult::Exact(nav), | ||
150 | None => ReferenceResult::Approximate(Vec::new()), | ||
151 | }; | ||
152 | } | ||
153 | |||
154 | // Fallback index based approach: | ||
155 | let name = name_ref.either(ast::Lifetime::text, ast::NameRef::text); | ||
156 | let navs = | ||
157 | symbol_index::index_resolve(sema.db, name).into_iter().map(|s| s.to_nav(sema.db)).collect(); | ||
158 | ReferenceResult::Approximate(navs) | ||
159 | } | 129 | } |
160 | 130 | ||
161 | #[cfg(test)] | 131 | #[cfg(test)] |
162 | mod tests { | 132 | mod tests { |
163 | use ide_db::base_db::FileRange; | 133 | use ide_db::base_db::FileRange; |
164 | use syntax::{TextRange, TextSize}; | ||
165 | 134 | ||
166 | use crate::fixture; | 135 | use crate::fixture; |
167 | 136 | ||
168 | fn check(ra_fixture: &str) { | 137 | fn check(ra_fixture: &str) { |
169 | let (analysis, position, mut annotations) = fixture::annotations(ra_fixture); | 138 | let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture); |
170 | let (mut expected, data) = annotations.pop().unwrap(); | ||
171 | match data.as_str() { | ||
172 | "" => (), | ||
173 | "file" => { | ||
174 | expected.range = | ||
175 | TextRange::up_to(TextSize::of(&*analysis.file_text(expected.file_id).unwrap())) | ||
176 | } | ||
177 | data => panic!("bad data: {}", data), | ||
178 | } | ||
179 | |||
180 | let mut navs = | 139 | let mut navs = |
181 | analysis.goto_definition(position).unwrap().expect("no definition found").info; | 140 | analysis.goto_definition(position).unwrap().expect("no definition found").info; |
182 | if navs.len() == 0 { | 141 | if navs.len() == 0 { |
@@ -192,12 +151,12 @@ mod tests { | |||
192 | fn goto_def_for_extern_crate() { | 151 | fn goto_def_for_extern_crate() { |
193 | check( | 152 | check( |
194 | r#" | 153 | r#" |
195 | //- /main.rs crate:main deps:std | 154 | //- /main.rs crate:main deps:std |
196 | extern crate std$0; | 155 | extern crate std$0; |
197 | //- /std/lib.rs crate:std | 156 | //- /std/lib.rs crate:std |
198 | // empty | 157 | // empty |
199 | //^ file | 158 | //^ file |
200 | "#, | 159 | "#, |
201 | ) | 160 | ) |
202 | } | 161 | } |
203 | 162 | ||
@@ -205,12 +164,12 @@ mod tests { | |||
205 | fn goto_def_for_renamed_extern_crate() { | 164 | fn goto_def_for_renamed_extern_crate() { |
206 | check( | 165 | check( |
207 | r#" | 166 | r#" |
208 | //- /main.rs crate:main deps:std | 167 | //- /main.rs crate:main deps:std |
209 | extern crate std as abc$0; | 168 | extern crate std as abc$0; |
210 | //- /std/lib.rs crate:std | 169 | //- /std/lib.rs crate:std |
211 | // empty | 170 | // empty |
212 | //^ file | 171 | //^ file |
213 | "#, | 172 | "#, |
214 | ) | 173 | ) |
215 | } | 174 | } |
216 | 175 | ||
@@ -297,13 +256,13 @@ fn bar() { | |||
297 | fn goto_def_for_macros_from_other_crates() { | 256 | fn goto_def_for_macros_from_other_crates() { |
298 | check( | 257 | check( |
299 | r#" | 258 | r#" |
300 | //- /lib.rs | 259 | //- /lib.rs crate:main deps:foo |
301 | use foo::foo; | 260 | use foo::foo; |
302 | fn bar() { | 261 | fn bar() { |
303 | $0foo!(); | 262 | $0foo!(); |
304 | } | 263 | } |
305 | 264 | ||
306 | //- /foo/lib.rs | 265 | //- /foo/lib.rs crate:foo |
307 | #[macro_export] | 266 | #[macro_export] |
308 | macro_rules! foo { () => { () } } | 267 | macro_rules! foo { () => { () } } |
309 | //^^^ | 268 | //^^^ |
@@ -315,10 +274,10 @@ macro_rules! foo { () => { () } } | |||
315 | fn goto_def_for_macros_in_use_tree() { | 274 | fn goto_def_for_macros_in_use_tree() { |
316 | check( | 275 | check( |
317 | r#" | 276 | r#" |
318 | //- /lib.rs | 277 | //- /lib.rs crate:main deps:foo |
319 | use foo::foo$0; | 278 | use foo::foo$0; |
320 | 279 | ||
321 | //- /foo/lib.rs | 280 | //- /foo/lib.rs crate:foo |
322 | #[macro_export] | 281 | #[macro_export] |
323 | macro_rules! foo { () => { () } } | 282 | macro_rules! foo { () => { () } } |
324 | //^^^ | 283 | //^^^ |
@@ -976,10 +935,10 @@ type Alias<T> = T$0; | |||
976 | fn goto_def_for_macro_container() { | 935 | fn goto_def_for_macro_container() { |
977 | check( | 936 | check( |
978 | r#" | 937 | r#" |
979 | //- /lib.rs | 938 | //- /lib.rs crate:main deps:foo |
980 | foo::module$0::mac!(); | 939 | foo::module$0::mac!(); |
981 | 940 | ||
982 | //- /foo/lib.rs | 941 | //- /foo/lib.rs crate:foo |
983 | pub mod module { | 942 | pub mod module { |
984 | //^^^^^^ | 943 | //^^^^^^ |
985 | #[macro_export] | 944 | #[macro_export] |