@@ -9,21 +9,22 @@ use ra_syntax::{
9
9
SyntaxNode , SyntaxToken , TextRange , TextUnit , TokenAtOffset , T ,
10
10
} ;
11
11
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 ;
14
14
use itertools:: Itertools ;
15
15
16
16
pub ( crate ) fn extend_selection ( db : & RootDatabase , frange : FileRange ) -> TextRange {
17
17
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 )
20
19
}
21
20
22
21
fn try_extend_selection (
23
22
db : & RootDatabase ,
24
- root : InFile < & SyntaxNode > ,
25
- range : TextRange ,
23
+ root : & SyntaxNode ,
24
+ frange : FileRange ,
26
25
) -> Option < TextRange > {
26
+ let range = frange. range ;
27
+
27
28
let string_kinds = [ COMMENT , STRING , RAW_STRING , BYTE_STRING , RAW_BYTE_STRING ] ;
28
29
let list_kinds = [
29
30
RECORD_FIELD_PAT_LIST ,
@@ -46,9 +47,9 @@ fn try_extend_selection(
46
47
47
48
if range. is_empty ( ) {
48
49
let offset = range. start ( ) ;
49
- let mut leaves = root. value . token_at_offset ( offset) ;
50
+ let mut leaves = root. token_at_offset ( offset) ;
50
51
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) ) ;
52
53
}
53
54
let leaf_range = match leaves {
54
55
TokenAtOffset :: None => return None ,
@@ -64,7 +65,7 @@ fn try_extend_selection(
64
65
} ;
65
66
return Some ( leaf_range) ;
66
67
} ;
67
- let node = match find_covering_element ( root. value , range) {
68
+ let node = match find_covering_element ( root, range) {
68
69
NodeOrToken :: Token ( token) => {
69
70
if token. text_range ( ) != range {
70
71
return Some ( token. text_range ( ) ) ;
@@ -82,7 +83,7 @@ fn try_extend_selection(
82
83
// if we are in single token_tree, we maybe live in macro or attr
83
84
if node. kind ( ) == TOKEN_TREE {
84
85
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) {
86
87
return Some ( range) ;
87
88
}
88
89
}
@@ -105,48 +106,52 @@ fn try_extend_selection(
105
106
106
107
fn extend_tokens_from_range (
107
108
db : & RootDatabase ,
108
- root : & InFile < & SyntaxNode > ,
109
+ file_id : FileId ,
109
110
macro_call : ast:: MacroCall ,
110
111
original_range : TextRange ,
111
112
) -> Option < TextRange > {
112
- let analyzer = hir:: SourceAnalyzer :: new ( db, root. clone ( ) , None ) ;
113
- let expansion = analyzer. expand ( db, root. with_value ( & macro_call) ) ?;
114
-
115
113
// compute original mapped token range
114
+ let mut expanded = None ;
116
115
let range = macro_call
117
116
. syntax ( )
118
117
. descendants_with_tokens ( )
119
118
. filter_map ( |n| match n {
120
119
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
+ }
124
129
}
125
130
_ => None ,
126
131
} )
127
132
. fold1 ( |x, y| union_range ( x, y) ) ?;
128
133
129
- let src = db. parse_or_expand ( expansion. file_id ( ) ) ?;
134
+ let expanded = expanded?;
135
+ let src = db. parse_or_expand ( expanded) ?;
130
136
let parent = shallowest_node ( & find_covering_element ( & src, range) ) ?. parent ( ) ?;
131
-
132
137
// compute parent mapped token range
133
138
let range = macro_call
134
139
. syntax ( )
135
140
. descendants_with_tokens ( )
136
141
. filter_map ( |n| match n {
137
142
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
+ }
145
151
}
146
152
_ => None ,
147
153
} )
148
154
. fold1 ( |x, y| union_range ( x, y) ) ?;
149
-
150
155
if original_range. is_subrange ( & range) && original_range != range {
151
156
Some ( range)
152
157
} else {
@@ -597,4 +602,21 @@ fn main() { let var = (
597
602
] ,
598
603
) ;
599
604
}
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
+ }
600
622
}
0 commit comments