diff options
-rw-r--r-- | crates/ra_hir/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/lib.rs | 13 | ||||
-rw-r--r-- | crates/ra_ide/src/expand.rs | 84 | ||||
-rw-r--r-- | crates/ra_ide/src/goto_definition.rs | 34 | ||||
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 1 |
5 files changed, 86 insertions, 48 deletions
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 451b227a6..2e52a1f5c 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -58,6 +58,6 @@ pub use hir_def::{ | |||
58 | type_ref::Mutability, | 58 | type_ref::Mutability, |
59 | }; | 59 | }; |
60 | pub use hir_expand::{ | 60 | pub use hir_expand::{ |
61 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, | 61 | name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, MacroDefId, MacroFile, Origin, |
62 | }; | 62 | }; |
63 | pub use hir_ty::{display::HirDisplay, CallableDef}; | 63 | pub use hir_ty::{display::HirDisplay, CallableDef}; |
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 94e1e466a..cb4e1950b 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs | |||
@@ -214,7 +214,13 @@ pub struct ExpansionInfo { | |||
214 | exp_map: Arc<mbe::TokenMap>, | 214 | exp_map: Arc<mbe::TokenMap>, |
215 | } | 215 | } |
216 | 216 | ||
217 | pub use mbe::Origin; | ||
218 | |||
217 | impl ExpansionInfo { | 219 | impl ExpansionInfo { |
220 | pub fn call_node(&self) -> Option<InFile<SyntaxNode>> { | ||
221 | Some(self.arg.with_value(self.arg.value.parent()?)) | ||
222 | } | ||
223 | |||
218 | pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { | 224 | pub fn map_token_down(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { |
219 | assert_eq!(token.file_id, self.arg.file_id); | 225 | assert_eq!(token.file_id, self.arg.file_id); |
220 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; | 226 | let range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; |
@@ -228,7 +234,10 @@ impl ExpansionInfo { | |||
228 | Some(self.expanded.with_value(token)) | 234 | Some(self.expanded.with_value(token)) |
229 | } | 235 | } |
230 | 236 | ||
231 | pub fn map_token_up(&self, token: InFile<&SyntaxToken>) -> Option<InFile<SyntaxToken>> { | 237 | pub fn map_token_up( |
238 | &self, | ||
239 | token: InFile<&SyntaxToken>, | ||
240 | ) -> Option<(InFile<SyntaxToken>, Origin)> { | ||
232 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; | 241 | let token_id = self.exp_map.token_by_range(token.value.text_range())?; |
233 | 242 | ||
234 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); | 243 | let (token_id, origin) = self.macro_def.0.map_id_up(token_id); |
@@ -242,7 +251,7 @@ impl ExpansionInfo { | |||
242 | let range = token_map.range_by_token(token_id)?; | 251 | let range = token_map.range_by_token(token_id)?; |
243 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) | 252 | let token = algo::find_covering_element(&tt.value, range + tt.value.text_range().start()) |
244 | .into_token()?; | 253 | .into_token()?; |
245 | Some(tt.with_value(token)) | 254 | Some((tt.with_value(token), origin)) |
246 | } | 255 | } |
247 | } | 256 | } |
248 | 257 | ||
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs index 661628ae4..7a22bb0a4 100644 --- a/crates/ra_ide/src/expand.rs +++ b/crates/ra_ide/src/expand.rs | |||
@@ -1,56 +1,66 @@ | |||
1 | //! Utilities to work with files, produced by macros. | 1 | //! Utilities to work with files, produced by macros. |
2 | use std::iter::successors; | 2 | use std::iter::successors; |
3 | 3 | ||
4 | use hir::InFile; | 4 | use hir::{InFile, Origin}; |
5 | use ra_db::FileId; | 5 | use ra_db::FileId; |
6 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; | 6 | use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken, TextRange}; |
7 | 7 | ||
8 | use crate::{db::RootDatabase, FileRange}; | 8 | use crate::{db::RootDatabase, FileRange}; |
9 | 9 | ||
10 | pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { | 10 | pub(crate) fn original_range(db: &RootDatabase, node: InFile<&SyntaxNode>) -> FileRange { |
11 | let expansion = match node.file_id.expansion_info(db) { | 11 | if let Some((range, Origin::Call)) = original_range_and_origin(db, node) { |
12 | None => { | 12 | return range; |
13 | } | ||
14 | |||
15 | if let Some(expansion) = node.file_id.expansion_info(db) { | ||
16 | if let Some(call_node) = expansion.call_node() { | ||
13 | return FileRange { | 17 | return FileRange { |
14 | file_id: node.file_id.original_file(db), | 18 | file_id: call_node.file_id.original_file(db), |
15 | range: node.value.text_range(), | 19 | range: call_node.value.text_range(), |
16 | } | 20 | }; |
17 | } | 21 | } |
18 | Some(it) => it, | 22 | } |
19 | }; | 23 | |
24 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | ||
25 | } | ||
26 | |||
27 | fn original_range_and_origin( | ||
28 | db: &RootDatabase, | ||
29 | node: InFile<&SyntaxNode>, | ||
30 | ) -> Option<(FileRange, Origin)> { | ||
31 | let expansion = node.file_id.expansion_info(db)?; | ||
32 | |||
33 | // the input node has only one token ? | ||
34 | let single = node.value.first_token()? == node.value.last_token()?; | ||
35 | |||
20 | // FIXME: We should handle recurside macro expansions | 36 | // FIXME: We should handle recurside macro expansions |
37 | let (range, origin) = node.value.descendants().find_map(|it| { | ||
38 | let first = it.first_token()?; | ||
39 | let last = it.last_token()?; | ||
21 | 40 | ||
22 | let range = node.value.descendants_with_tokens().find_map(|it| { | 41 | if !single && first == last { |
23 | match it.as_token() { | 42 | return None; |
24 | // FIXME: Remove this branch after all `tt::TokenTree`s have a proper `TokenId`, | ||
25 | // and return the range of the overall macro expansions if mapping first and last tokens fails. | ||
26 | Some(token) => { | ||
27 | let token = expansion.map_token_up(node.with_value(&token))?; | ||
28 | Some(token.with_value(token.value.text_range())) | ||
29 | } | ||
30 | None => { | ||
31 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | ||
32 | let n = it.into_node()?; | ||
33 | let first = expansion.map_token_up(node.with_value(&n.first_token()?))?; | ||
34 | let last = expansion.map_token_up(node.with_value(&n.last_token()?))?; | ||
35 | |||
36 | // FIXME: Is is possible ? | ||
37 | if first.file_id != last.file_id { | ||
38 | return None; | ||
39 | } | ||
40 | |||
41 | // FIXME: Add union method in TextRange | ||
42 | let range = union_range(first.value.text_range(), last.value.text_range()); | ||
43 | Some(first.with_value(range)) | ||
44 | } | ||
45 | } | 43 | } |
46 | }); | ||
47 | 44 | ||
48 | return match range { | 45 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens |
49 | Some(it) => FileRange { file_id: it.file_id.original_file(db), range: it.value }, | 46 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; |
50 | None => { | 47 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; |
51 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | 48 | |
49 | if first.file_id != last.file_id || first_origin != last_origin { | ||
50 | return None; | ||
52 | } | 51 | } |
53 | }; | 52 | |
53 | // FIXME: Add union method in TextRange | ||
54 | Some(( | ||
55 | first.with_value(union_range(first.value.text_range(), last.value.text_range())), | ||
56 | first_origin, | ||
57 | )) | ||
58 | })?; | ||
59 | |||
60 | return Some(( | ||
61 | FileRange { file_id: range.file_id.original_file(db), range: range.value }, | ||
62 | origin, | ||
63 | )); | ||
54 | 64 | ||
55 | fn union_range(a: TextRange, b: TextRange) -> TextRange { | 65 | fn union_range(a: TextRange, b: TextRange) -> TextRange { |
56 | let start = a.start().min(b.start()); | 66 | let start = a.start().min(b.start()); |
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 27052d72b..bee8e9df2 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs | |||
@@ -221,7 +221,7 @@ fn named_target(db: &RootDatabase, node: InFile<&SyntaxNode>) -> Option<Navigati | |||
221 | 221 | ||
222 | #[cfg(test)] | 222 | #[cfg(test)] |
223 | mod tests { | 223 | mod tests { |
224 | use test_utils::covers; | 224 | use test_utils::{assert_eq_text, covers}; |
225 | 225 | ||
226 | use crate::mock_analysis::analysis_and_position; | 226 | use crate::mock_analysis::analysis_and_position; |
227 | 227 | ||
@@ -234,6 +234,24 @@ mod tests { | |||
234 | nav.assert_match(expected); | 234 | nav.assert_match(expected); |
235 | } | 235 | } |
236 | 236 | ||
237 | fn check_goto_with_range_content(fixture: &str, expected: &str, expected_range: &str) { | ||
238 | let (analysis, pos) = analysis_and_position(fixture); | ||
239 | |||
240 | let mut navs = analysis.goto_definition(pos).unwrap().unwrap().info; | ||
241 | assert_eq!(navs.len(), 1); | ||
242 | let nav = navs.pop().unwrap(); | ||
243 | let file_text = analysis.file_text(pos.file_id).unwrap(); | ||
244 | |||
245 | let actual_full_range = &file_text[nav.full_range()]; | ||
246 | let actual_range = &file_text[nav.range()]; | ||
247 | |||
248 | test_utils::assert_eq_text!( | ||
249 | &format!("{}|{}", actual_full_range, actual_range), | ||
250 | expected_range | ||
251 | ); | ||
252 | nav.assert_match(expected); | ||
253 | } | ||
254 | |||
237 | #[test] | 255 | #[test] |
238 | fn goto_definition_works_in_items() { | 256 | fn goto_definition_works_in_items() { |
239 | check_goto( | 257 | check_goto( |
@@ -363,28 +381,27 @@ mod tests { | |||
363 | 381 | ||
364 | #[test] | 382 | #[test] |
365 | fn goto_definition_works_for_macro_defined_fn_with_arg() { | 383 | fn goto_definition_works_for_macro_defined_fn_with_arg() { |
366 | check_goto( | 384 | check_goto_with_range_content( |
367 | " | 385 | " |
368 | //- /lib.rs | 386 | //- /lib.rs |
369 | macro_rules! define_fn { | 387 | macro_rules! define_fn { |
370 | ($name:ident) => (fn $name() {}) | 388 | ($name:ident) => (fn $name() {}) |
371 | } | 389 | } |
372 | 390 | ||
373 | define_fn!( | 391 | define_fn!(foo); |
374 | foo | ||
375 | ) | ||
376 | 392 | ||
377 | fn bar() { | 393 | fn bar() { |
378 | <|>foo(); | 394 | <|>foo(); |
379 | } | 395 | } |
380 | ", | 396 | ", |
381 | "foo FN_DEF FileId(1) [80; 83) [80; 83)", | 397 | "foo FN_DEF FileId(1) [64; 80) [75; 78)", |
398 | "define_fn!(foo);|foo", | ||
382 | ); | 399 | ); |
383 | } | 400 | } |
384 | 401 | ||
385 | #[test] | 402 | #[test] |
386 | fn goto_definition_works_for_macro_defined_fn_no_arg() { | 403 | fn goto_definition_works_for_macro_defined_fn_no_arg() { |
387 | check_goto( | 404 | check_goto_with_range_content( |
388 | " | 405 | " |
389 | //- /lib.rs | 406 | //- /lib.rs |
390 | macro_rules! define_fn { | 407 | macro_rules! define_fn { |
@@ -397,7 +414,8 @@ mod tests { | |||
397 | <|>foo(); | 414 | <|>foo(); |
398 | } | 415 | } |
399 | ", | 416 | ", |
400 | "foo FN_DEF FileId(1) [39; 42) [39; 42)", | 417 | "foo FN_DEF FileId(1) [51; 64) [51; 64)", |
418 | "define_fn!();|define_fn!();", | ||
401 | ); | 419 | ); |
402 | } | 420 | } |
403 | 421 | ||
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 0d2d43bef..ce2deadf6 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -104,6 +104,7 @@ impl Shift { | |||
104 | } | 104 | } |
105 | } | 105 | } |
106 | 106 | ||
107 | #[derive(Debug, Eq, PartialEq)] | ||
107 | pub enum Origin { | 108 | pub enum Origin { |
108 | Def, | 109 | Def, |
109 | Call, | 110 | Call, |