@@ -4,20 +4,27 @@ use ra_db::SourceDatabase;
44use ra_syntax:: {
55 algo:: find_covering_element,
66 ast:: { self , AstNode , AstToken } ,
7- Direction , NodeOrToken ,
7+ Direction , NodeOrToken , SyntaxElement ,
88 SyntaxKind :: { self , * } ,
99 SyntaxNode , SyntaxToken , TextRange , TextUnit , TokenAtOffset , T ,
1010} ;
1111
12- use crate :: { db:: RootDatabase , FileRange } ;
12+ use crate :: { db:: RootDatabase , expand:: descend_into_macros, FileId , FileRange } ;
13+ use hir:: db:: AstDatabase ;
14+ use std:: iter:: successors;
1315
14- // FIXME: restore macro support
1516pub ( crate ) fn extend_selection ( db : & RootDatabase , frange : FileRange ) -> TextRange {
16- let parse = db. parse ( frange. file_id ) ;
17- try_extend_selection ( parse . tree ( ) . syntax ( ) , frange. range ) . unwrap_or ( frange. range )
17+ let src = db. parse ( frange. file_id ) . tree ( ) ;
18+ try_extend_selection ( db , src . syntax ( ) , frange) . unwrap_or ( frange. range )
1819}
1920
20- fn try_extend_selection ( root : & SyntaxNode , range : TextRange ) -> Option < TextRange > {
21+ fn try_extend_selection (
22+ db : & RootDatabase ,
23+ root : & SyntaxNode ,
24+ frange : FileRange ,
25+ ) -> Option < TextRange > {
26+ let range = frange. range ;
27+
2128 let string_kinds = [ COMMENT , STRING , RAW_STRING , BYTE_STRING , RAW_BYTE_STRING ] ;
2229 let list_kinds = [
2330 RECORD_FIELD_PAT_LIST ,
@@ -72,12 +79,21 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
7279 }
7380 NodeOrToken :: Node ( node) => node,
7481 } ;
82+
83+ // if we are in single token_tree, we maybe live in macro or attr
84+ if node. kind ( ) == TOKEN_TREE {
85+ if let Some ( macro_call) = node. ancestors ( ) . find_map ( ast:: MacroCall :: cast) {
86+ if let Some ( range) = extend_tokens_from_range ( db, frange. file_id , macro_call, range) {
87+ return Some ( range) ;
88+ }
89+ }
90+ }
91+
7592 if node. text_range ( ) != range {
7693 return Some ( node. text_range ( ) ) ;
7794 }
7895
79- // Using shallowest node with same range allows us to traverse siblings.
80- let node = node. ancestors ( ) . take_while ( |n| n. text_range ( ) == node. text_range ( ) ) . last ( ) . unwrap ( ) ;
96+ let node = shallowest_node ( & node. into ( ) ) . unwrap ( ) ;
8197
8298 if node. parent ( ) . map ( |n| list_kinds. contains ( & n. kind ( ) ) ) == Some ( true ) {
8399 if let Some ( range) = extend_list_item ( & node) {
@@ -88,6 +104,94 @@ fn try_extend_selection(root: &SyntaxNode, range: TextRange) -> Option<TextRange
88104 node. parent ( ) . map ( |it| it. text_range ( ) )
89105}
90106
107+ fn extend_tokens_from_range (
108+ db : & RootDatabase ,
109+ file_id : FileId ,
110+ macro_call : ast:: MacroCall ,
111+ original_range : TextRange ,
112+ ) -> Option < TextRange > {
113+ let src = find_covering_element ( & macro_call. syntax ( ) , original_range) ;
114+ let ( first_token, last_token) = match src {
115+ NodeOrToken :: Node ( it) => ( it. first_token ( ) ?, it. last_token ( ) ?) ,
116+ NodeOrToken :: Token ( it) => ( it. clone ( ) , it) ,
117+ } ;
118+
119+ let mut first_token = skip_whitespace ( first_token, Direction :: Next ) ?;
120+ let mut last_token = skip_whitespace ( last_token, Direction :: Prev ) ?;
121+
122+ while !first_token. text_range ( ) . is_subrange ( & original_range) {
123+ first_token = skip_whitespace ( first_token. next_token ( ) ?, Direction :: Next ) ?;
124+ }
125+ while !last_token. text_range ( ) . is_subrange ( & original_range) {
126+ last_token = skip_whitespace ( last_token. prev_token ( ) ?, Direction :: Prev ) ?;
127+ }
128+
129+ // compute original mapped token range
130+ let expanded = {
131+ let first_node = descend_into_macros ( db, file_id, first_token. clone ( ) ) ;
132+ let first_node = first_node. map ( |it| it. text_range ( ) ) ;
133+
134+ let last_node = descend_into_macros ( db, file_id, last_token. clone ( ) ) ;
135+ if last_node. file_id == file_id. into ( ) || first_node. file_id != last_node. file_id {
136+ return None ;
137+ }
138+ first_node. map ( |it| union_range ( it, last_node. value . text_range ( ) ) )
139+ } ;
140+
141+ // Compute parent node range
142+ let src = db. parse_or_expand ( expanded. file_id ) ?;
143+ let parent = shallowest_node ( & find_covering_element ( & src, expanded. value ) ) ?. parent ( ) ?;
144+
145+ let validate = |token : SyntaxToken | {
146+ let node = descend_into_macros ( db, file_id, token. clone ( ) ) ;
147+ if node. file_id == expanded. file_id
148+ && node. value . text_range ( ) . is_subrange ( & parent. text_range ( ) )
149+ {
150+ Some ( token)
151+ } else {
152+ None
153+ }
154+ } ;
155+
156+ // Find the first and last text range under expanded parent
157+ let first = successors ( Some ( first_token) , |token| {
158+ validate ( skip_whitespace ( token. prev_token ( ) ?, Direction :: Prev ) ?)
159+ } )
160+ . last ( ) ?;
161+ let last = successors ( Some ( last_token) , |token| {
162+ validate ( skip_whitespace ( token. next_token ( ) ?, Direction :: Next ) ?)
163+ } )
164+ . last ( ) ?;
165+
166+ let range = union_range ( first. text_range ( ) , last. text_range ( ) ) ;
167+ if original_range. is_subrange ( & range) && original_range != range {
168+ Some ( range)
169+ } else {
170+ None
171+ }
172+ }
173+
174+ fn skip_whitespace ( mut token : SyntaxToken , direction : Direction ) -> Option < SyntaxToken > {
175+ while token. kind ( ) == WHITESPACE {
176+ token = match direction {
177+ Direction :: Next => token. next_token ( ) ?,
178+ Direction :: Prev => token. prev_token ( ) ?,
179+ }
180+ }
181+ Some ( token)
182+ }
183+
184+ fn union_range ( range : TextRange , r : TextRange ) -> TextRange {
185+ let start = range. start ( ) . min ( r. start ( ) ) ;
186+ let end = range. end ( ) . max ( r. end ( ) ) ;
187+ TextRange :: from_to ( start, end)
188+ }
189+
190+ /// Find the shallowest node with same range, which allows us to traverse siblings.
191+ fn shallowest_node ( node : & SyntaxElement ) -> Option < SyntaxNode > {
192+ node. ancestors ( ) . take_while ( |n| n. text_range ( ) == node. text_range ( ) ) . last ( )
193+ }
194+
91195fn extend_single_word_in_comment_or_string (
92196 leaf : & SyntaxToken ,
93197 offset : TextUnit ,
@@ -227,18 +331,19 @@ fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment {
227331
228332#[ cfg( test) ]
229333mod tests {
230- use ra_syntax:: { AstNode , SourceFile } ;
231- use test_utils:: extract_offset;
232-
233334 use super :: * ;
335+ use crate :: mock_analysis:: single_file;
336+ use test_utils:: extract_offset;
234337
235338 fn do_check ( before : & str , afters : & [ & str ] ) {
236339 let ( cursor, before) = extract_offset ( before) ;
237- let parse = SourceFile :: parse ( & before) ;
238- let mut range = TextRange :: offset_len ( cursor, 0 . into ( ) ) ;
340+ let ( analysis, file_id) = single_file ( & before) ;
341+ let range = TextRange :: offset_len ( cursor, 0 . into ( ) ) ;
342+ let mut frange = FileRange { file_id : file_id, range } ;
343+
239344 for & after in afters {
240- range = try_extend_selection ( parse . tree ( ) . syntax ( ) , range ) . unwrap ( ) ;
241- let actual = & before[ range] ;
345+ frange . range = analysis . extend_selection ( frange ) . unwrap ( ) ;
346+ let actual = & before[ frange . range ] ;
242347 assert_eq ! ( after, actual) ;
243348 }
244349 }
@@ -503,4 +608,37 @@ fn main() { let var = (
503608 ] ,
504609 ) ;
505610 }
611+
612+ #[ test]
613+ fn extend_selection_inside_macros ( ) {
614+ do_check (
615+ r#"macro_rules! foo { ($item:item) => {$item} }
616+ foo!{fn hello(na<|>me:usize){}}"# ,
617+ & [
618+ "name" ,
619+ "name:usize" ,
620+ "(name:usize)" ,
621+ "fn hello(name:usize){}" ,
622+ "{fn hello(name:usize){}}" ,
623+ "foo!{fn hello(name:usize){}}" ,
624+ ] ,
625+ ) ;
626+ }
627+
628+ #[ test]
629+ fn extend_selection_inside_recur_macros ( ) {
630+ do_check (
631+ r#" macro_rules! foo2 { ($item:item) => {$item} }
632+ macro_rules! foo { ($item:item) => {foo2!($item);} }
633+ foo!{fn hello(na<|>me:usize){}}"# ,
634+ & [
635+ "name" ,
636+ "name:usize" ,
637+ "(name:usize)" ,
638+ "fn hello(name:usize){}" ,
639+ "{fn hello(name:usize){}}" ,
640+ "foo!{fn hello(name:usize){}}" ,
641+ ] ,
642+ ) ;
643+ }
506644}
0 commit comments