aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/expand.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/expand.rs')
-rw-r--r--crates/ra_ide/src/expand.rs63
1 files changed, 63 insertions, 0 deletions
diff --git a/crates/ra_ide/src/expand.rs b/crates/ra_ide/src/expand.rs
new file mode 100644
index 000000000..2f1abf509
--- /dev/null
+++ b/crates/ra_ide/src/expand.rs
@@ -0,0 +1,63 @@
1//! Utilities to work with files, produced by macros.
2use std::iter::successors;
3
4use hir::Source;
5use ra_db::FileId;
6use ra_syntax::{ast, AstNode, SyntaxNode, SyntaxToken};
7
8use crate::{db::RootDatabase, FileRange};
9
10pub(crate) fn original_range(db: &RootDatabase, node: Source<&SyntaxNode>) -> FileRange {
11 let expansion = match node.file_id.expansion_info(db) {
12 None => {
13 return FileRange {
14 file_id: node.file_id.original_file(db),
15 range: node.value.text_range(),
16 }
17 }
18 Some(it) => it,
19 };
20 // FIXME: the following completely wrong.
21 //
22 // *First*, we should try to map first and last tokens of node, and, if that
23 // fails, return the range of the overall macro expansions.
24 //
25 // *Second*, we should handle recurside macro expansions
26
27 let token = node
28 .value
29 .descendants_with_tokens()
30 .filter_map(|it| it.into_token())
31 .find_map(|it| expansion.map_token_up(node.with_value(&it)));
32
33 match token {
34 Some(it) => {
35 FileRange { file_id: it.file_id.original_file(db), range: it.value.text_range() }
36 }
37 None => {
38 FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
39 }
40 }
41}
42
43pub(crate) fn descend_into_macros(
44 db: &RootDatabase,
45 file_id: FileId,
46 token: SyntaxToken,
47) -> Source<SyntaxToken> {
48 let src = Source::new(file_id.into(), token);
49
50 successors(Some(src), |token| {
51 let macro_call = token.value.ancestors().find_map(ast::MacroCall::cast)?;
52 let tt = macro_call.token_tree()?;
53 if !token.value.text_range().is_subrange(&tt.syntax().text_range()) {
54 return None;
55 }
56 let source_analyzer =
57 hir::SourceAnalyzer::new(db, token.with_value(token.value.parent()).as_ref(), None);
58 let exp = source_analyzer.expand(db, token.with_value(&macro_call))?;
59 exp.map_token_down(db, token.as_ref())
60 })
61 .last()
62 .unwrap()
63}