diff options
Diffstat (limited to 'crates/ra_ide/src/expand.rs')
-rw-r--r-- | crates/ra_ide/src/expand.rs | 79 |
1 files changed, 42 insertions, 37 deletions
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs index 661628ae4..327393dbb 100644 --- a/crates/ra_ide/src/expand.rs +++ b/crates/ra_ide/src/expand.rs | |||
@@ -1,57 +1,62 @@ | |||
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::{ExpansionOrigin, InFile}; |
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 | #[derive(Debug, PartialEq, Eq)] |
11 | let expansion = match node.file_id.expansion_info(db) { | 11 | pub(crate) enum OriginalRangeKind { |
12 | None => { | 12 | /// Return range if any token is matched |
13 | return FileRange { | 13 | #[allow(dead_code)] |
14 | file_id: node.file_id.original_file(db), | 14 | Any, |
15 | range: node.value.text_range(), | 15 | /// Return range if token is inside macro_call |
16 | } | 16 | CallToken, |
17 | } | 17 | /// Return whole macro call range if matched |
18 | Some(it) => it, | 18 | WholeCall, |
19 | }; | 19 | } |
20 | |||
21 | pub(crate) fn original_range_by_kind( | ||
22 | db: &RootDatabase, | ||
23 | node: InFile<&SyntaxNode>, | ||
24 | kind: OriginalRangeKind, | ||
25 | ) -> Option<FileRange> { | ||
26 | let expansion = node.file_id.expansion_info(db)?; | ||
27 | |||
28 | // the input node has only one token ? | ||
29 | let single = node.value.first_token()? == node.value.last_token()?; | ||
30 | |||
20 | // FIXME: We should handle recurside macro expansions | 31 | // FIXME: We should handle recurside macro expansions |
32 | let range = match kind { | ||
33 | OriginalRangeKind::WholeCall => expansion.call_node()?.map(|node| node.text_range()), | ||
34 | _ => node.value.descendants().find_map(|it| { | ||
35 | let first = it.first_token()?; | ||
36 | let last = it.last_token()?; | ||
21 | 37 | ||
22 | let range = node.value.descendants_with_tokens().find_map(|it| { | 38 | if !single && first == last { |
23 | match it.as_token() { | 39 | 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 | } | 40 | } |
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 | 41 | ||
36 | // FIXME: Is is possible ? | 42 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens |
37 | if first.file_id != last.file_id { | 43 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; |
38 | return None; | 44 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; |
39 | } | ||
40 | 45 | ||
41 | // FIXME: Add union method in TextRange | 46 | if first.file_id != last.file_id |
42 | let range = union_range(first.value.text_range(), last.value.text_range()); | 47 | || first_origin != last_origin |
43 | Some(first.with_value(range)) | 48 | || (kind == OriginalRangeKind::CallToken && first_origin != ExpansionOrigin::Call) |
49 | { | ||
50 | return None; | ||
44 | } | 51 | } |
45 | } | ||
46 | }); | ||
47 | 52 | ||
48 | return match range { | 53 | // FIXME: Add union method in TextRange |
49 | Some(it) => FileRange { file_id: it.file_id.original_file(db), range: it.value }, | 54 | Some(first.with_value(union_range(first.value.text_range(), last.value.text_range()))) |
50 | None => { | 55 | })?, |
51 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | ||
52 | } | ||
53 | }; | 56 | }; |
54 | 57 | ||
58 | return Some(FileRange { file_id: range.file_id.original_file(db), range: range.value }); | ||
59 | |||
55 | fn union_range(a: TextRange, b: TextRange) -> TextRange { | 60 | fn union_range(a: TextRange, b: TextRange) -> TextRange { |
56 | let start = a.start().min(b.start()); | 61 | let start = a.start().min(b.start()); |
57 | let end = a.end().max(b.end()); | 62 | let end = a.end().max(b.end()); |