Skip to content

Commit b30e6a7

Browse files
committed
Handle extend selection in recursive macro
1 parent 07f4171 commit b30e6a7

File tree

1 file changed

+49
-27
lines changed

1 file changed

+49
-27
lines changed

crates/ra_ide/src/extend_selection.rs

Lines changed: 49 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,22 @@ use ra_syntax::{
99
SyntaxNode, SyntaxToken, TextRange, TextUnit, TokenAtOffset, T,
1010
};
1111

12-
use crate::{db::RootDatabase, FileRange};
13-
use hir::{db::AstDatabase, InFile};
12+
use crate::{db::RootDatabase, expand::descend_into_macros, FileId, FileRange};
13+
use hir::db::AstDatabase;
1414
use itertools::Itertools;
1515

1616
pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
1717
let src = db.parse(frange.file_id).tree();
18-
let root = InFile::new(frange.file_id.into(), src.syntax());
19-
try_extend_selection(db, root, frange.range).unwrap_or(frange.range)
18+
try_extend_selection(db, src.syntax(), frange).unwrap_or(frange.range)
2019
}
2120

2221
fn try_extend_selection(
2322
db: &RootDatabase,
24-
root: InFile<&SyntaxNode>,
25-
range: TextRange,
23+
root: &SyntaxNode,
24+
frange: FileRange,
2625
) -> Option<TextRange> {
26+
let range = frange.range;
27+
2728
let string_kinds = [COMMENT, STRING, RAW_STRING, BYTE_STRING, RAW_BYTE_STRING];
2829
let list_kinds = [
2930
RECORD_FIELD_PAT_LIST,
@@ -46,9 +47,9 @@ fn try_extend_selection(
4647

4748
if range.is_empty() {
4849
let offset = range.start();
49-
let mut leaves = root.value.token_at_offset(offset);
50+
let mut leaves = root.token_at_offset(offset);
5051
if leaves.clone().all(|it| it.kind() == WHITESPACE) {
51-
return Some(extend_ws(root.value, leaves.next()?, offset));
52+
return Some(extend_ws(root, leaves.next()?, offset));
5253
}
5354
let leaf_range = match leaves {
5455
TokenAtOffset::None => return None,
@@ -64,7 +65,7 @@ fn try_extend_selection(
6465
};
6566
return Some(leaf_range);
6667
};
67-
let node = match find_covering_element(root.value, range) {
68+
let node = match find_covering_element(root, range) {
6869
NodeOrToken::Token(token) => {
6970
if token.text_range() != range {
7071
return Some(token.text_range());
@@ -82,7 +83,7 @@ fn try_extend_selection(
8283
// if we are in single token_tree, we maybe live in macro or attr
8384
if node.kind() == TOKEN_TREE {
8485
if let Some(macro_call) = node.ancestors().find_map(ast::MacroCall::cast) {
85-
if let Some(range) = extend_tokens_from_range(db, &root, macro_call, range) {
86+
if let Some(range) = extend_tokens_from_range(db, frange.file_id, macro_call, range) {
8687
return Some(range);
8788
}
8889
}
@@ -105,48 +106,52 @@ fn try_extend_selection(
105106

106107
fn extend_tokens_from_range(
107108
db: &RootDatabase,
108-
root: &InFile<&SyntaxNode>,
109+
file_id: FileId,
109110
macro_call: ast::MacroCall,
110111
original_range: TextRange,
111112
) -> Option<TextRange> {
112-
let analyzer = hir::SourceAnalyzer::new(db, root.clone(), None);
113-
let expansion = analyzer.expand(db, root.with_value(&macro_call))?;
114-
115113
// compute original mapped token range
114+
let mut expanded = None;
116115
let range = macro_call
117116
.syntax()
118117
.descendants_with_tokens()
119118
.filter_map(|n| match n {
120119
NodeOrToken::Token(token) if token.text_range().is_subrange(&original_range) => {
121-
expansion
122-
.map_token_down(db, root.with_value(&token))
123-
.map(|node| node.value.text_range())
120+
let node = descend_into_macros(db, file_id, token);
121+
match node.file_id {
122+
it if it == file_id.into() => None,
123+
it if expanded.is_none() || expanded == Some(it) => {
124+
expanded = Some(it.into());
125+
Some(node.value.text_range())
126+
}
127+
_ => None,
128+
}
124129
}
125130
_ => None,
126131
})
127132
.fold1(|x, y| union_range(x, y))?;
128133

129-
let src = db.parse_or_expand(expansion.file_id())?;
134+
let expanded = expanded?;
135+
let src = db.parse_or_expand(expanded)?;
130136
let parent = shallowest_node(&find_covering_element(&src, range))?.parent()?;
131-
132137
// compute parent mapped token range
133138
let range = macro_call
134139
.syntax()
135140
.descendants_with_tokens()
136141
.filter_map(|n| match n {
137142
NodeOrToken::Token(token) => {
138-
expansion.map_token_down(db, root.with_value(&token)).and_then(|node| {
139-
if node.value.text_range().is_subrange(&parent.text_range()) {
140-
Some(token.text_range())
141-
} else {
142-
None
143-
}
144-
})
143+
let node = descend_into_macros(db, file_id, token.clone());
144+
if node.file_id == expanded
145+
&& node.value.text_range().is_subrange(&parent.text_range())
146+
{
147+
Some(token.text_range())
148+
} else {
149+
None
150+
}
145151
}
146152
_ => None,
147153
})
148154
.fold1(|x, y| union_range(x, y))?;
149-
150155
if original_range.is_subrange(&range) && original_range != range {
151156
Some(range)
152157
} else {
@@ -597,4 +602,21 @@ fn main() { let var = (
597602
],
598603
);
599604
}
605+
606+
#[test]
607+
fn extend_selection_inside_recur_macros() {
608+
do_check(
609+
r#" macro_rules! foo2 { ($item:item) => {$item} }
610+
macro_rules! foo { ($item:item) => {foo2!($item);} }
611+
foo!{fn hello(na<|>me:usize){}}"#,
612+
&[
613+
"name",
614+
"name:usize",
615+
"(name:usize)",
616+
"fn hello(name:usize){}",
617+
"{fn hello(name:usize){}}",
618+
"foo!{fn hello(name:usize){}}",
619+
],
620+
);
621+
}
600622
}

0 commit comments

Comments
 (0)