3
3
use crate :: source_change:: SourceFileEdit ;
4
4
use ra_ide_db:: RootDatabase ;
5
5
use ra_syntax:: ast:: make:: expr_from_text;
6
- use ra_syntax:: AstNode ;
7
- use ra_syntax:: SyntaxElement ;
8
- use ra_syntax:: SyntaxNode ;
6
+ use ra_syntax:: ast:: { AstToken , Comment } ;
7
+ use ra_syntax:: { AstNode , SyntaxElement , SyntaxNode } ;
9
8
use ra_text_edit:: { TextEdit , TextEditBuilder } ;
10
9
use rustc_hash:: FxHashMap ;
11
10
use std:: collections:: HashMap ;
@@ -72,6 +71,7 @@ type Binding = HashMap<Var, SyntaxNode>;
72
71
struct Match {
73
72
place : SyntaxNode ,
74
73
binding : Binding ,
74
+ ignored_comments : Vec < Comment > ,
75
75
}
76
76
77
77
#[ derive( Debug ) ]
@@ -179,44 +179,61 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
179
179
pattern : & SyntaxElement ,
180
180
code : & SyntaxElement ,
181
181
placeholders : & [ Var ] ,
182
- match_ : & mut Match ,
183
- ) -> bool {
182
+ mut match_ : Match ,
183
+ ) -> Option < Match > {
184
184
match ( pattern, code) {
185
185
( SyntaxElement :: Token ( ref pattern) , SyntaxElement :: Token ( ref code) ) => {
186
- pattern. text ( ) == code. text ( )
186
+ if pattern. text ( ) == code. text ( ) {
187
+ Some ( match_)
188
+ } else {
189
+ None
190
+ }
187
191
}
188
192
( SyntaxElement :: Node ( ref pattern) , SyntaxElement :: Node ( ref code) ) => {
189
193
if placeholders. iter ( ) . any ( |n| n. 0 . as_str ( ) == pattern. text ( ) ) {
190
194
match_. binding . insert ( Var ( pattern. text ( ) . to_string ( ) ) , code. clone ( ) ) ;
191
- true
195
+ Some ( match_ )
192
196
} else {
193
- pattern. green ( ) . children ( ) . count ( ) == code. green ( ) . children ( ) . count ( )
194
- && pattern
195
- . children_with_tokens ( )
196
- . zip ( code. children_with_tokens ( ) )
197
- . all ( |( a, b) | check ( & a, & b, placeholders, match_) )
197
+ let mut pattern_children = pattern
198
+ . children_with_tokens ( )
199
+ . filter ( |element| !element. kind ( ) . is_trivia ( ) ) ;
200
+ let mut code_children =
201
+ code. children_with_tokens ( ) . filter ( |element| !element. kind ( ) . is_trivia ( ) ) ;
202
+ let new_ignored_comments = code. children_with_tokens ( ) . filter_map ( |element| {
203
+ element. as_token ( ) . and_then ( |token| Comment :: cast ( token. clone ( ) ) )
204
+ } ) ;
205
+ match_. ignored_comments . extend ( new_ignored_comments) ;
206
+ let match_from_children = pattern_children
207
+ . by_ref ( )
208
+ . zip ( code_children. by_ref ( ) )
209
+ . fold ( Some ( match_) , |accum, ( a, b) | {
210
+ accum. and_then ( |match_| check ( & a, & b, placeholders, match_) )
211
+ } ) ;
212
+ match_from_children. and_then ( |match_| {
213
+ if pattern_children. count ( ) == 0 && code_children. count ( ) == 0 {
214
+ Some ( match_)
215
+ } else {
216
+ None
217
+ }
218
+ } )
198
219
}
199
220
}
200
- _ => false ,
221
+ _ => None ,
201
222
}
202
223
}
203
224
let kind = pattern. pattern . kind ( ) ;
204
225
let matches = code
205
- . descendants_with_tokens ( )
226
+ . descendants ( )
206
227
. filter ( |n| n. kind ( ) == kind)
207
228
. filter_map ( |code| {
208
- let mut match_ =
209
- Match { place : code. as_node ( ) . unwrap ( ) . clone ( ) , binding : HashMap :: new ( ) } ;
210
- if check (
229
+ let match_ =
230
+ Match { place : code. clone ( ) , binding : HashMap :: new ( ) , ignored_comments : vec ! [ ] } ;
231
+ check (
211
232
& SyntaxElement :: from ( pattern. pattern . clone ( ) ) ,
212
- & code,
233
+ & SyntaxElement :: from ( code) ,
213
234
& pattern. vars ,
214
- & mut match_,
215
- ) {
216
- Some ( match_)
217
- } else {
218
- None
219
- }
235
+ match_,
236
+ )
220
237
} )
221
238
. collect ( ) ;
222
239
SsrMatches { matches }
@@ -225,18 +242,28 @@ fn find(pattern: &SsrPattern, code: &SyntaxNode) -> SsrMatches {
225
242
fn replace ( matches : & SsrMatches , template : & SsrTemplate ) -> TextEdit {
226
243
let mut builder = TextEditBuilder :: default ( ) ;
227
244
for match_ in & matches. matches {
228
- builder. replace ( match_. place . text_range ( ) , render_replace ( & match_. binding , template) ) ;
245
+ builder. replace (
246
+ match_. place . text_range ( ) ,
247
+ render_replace ( & match_. binding , & match_. ignored_comments , template) ,
248
+ ) ;
229
249
}
230
250
builder. finish ( )
231
251
}
232
252
233
- fn render_replace ( binding : & Binding , template : & SsrTemplate ) -> String {
253
+ fn render_replace (
254
+ binding : & Binding ,
255
+ ignored_comments : & Vec < Comment > ,
256
+ template : & SsrTemplate ,
257
+ ) -> String {
234
258
let mut builder = TextEditBuilder :: default ( ) ;
235
259
for element in template. template . descendants ( ) {
236
260
if let Some ( var) = template. placeholders . get ( & element) {
237
261
builder. replace ( element. text_range ( ) , binding[ var] . to_string ( ) )
238
262
}
239
263
}
264
+ for comment in ignored_comments {
265
+ builder. insert ( template. template . text_range ( ) . end ( ) , comment. syntax ( ) . to_string ( ) )
266
+ }
240
267
builder. finish ( ) . apply ( & template. template . text ( ) . to_string ( ) )
241
268
}
242
269
@@ -325,4 +352,66 @@ mod tests {
325
352
let edit = replace ( & matches, & query. template ) ;
326
353
assert_eq ! ( edit. apply( input) , "fn main() { bar(1+2); }" ) ;
327
354
}
355
+
356
+ fn assert_ssr_transform ( query : & str , input : & str , result : & str ) {
357
+ let query: SsrQuery = query. parse ( ) . unwrap ( ) ;
358
+ let code = SourceFile :: parse ( input) . tree ( ) ;
359
+ let matches = find ( & query. pattern , code. syntax ( ) ) ;
360
+ let edit = replace ( & matches, & query. template ) ;
361
+ assert_eq ! ( edit. apply( input) , result) ;
362
+ }
363
+
364
+ #[ test]
365
+ fn ssr_function_to_method ( ) {
366
+ assert_ssr_transform (
367
+ "my_function($a:expr, $b:expr) ==>> ($a).my_method($b)" ,
368
+ "loop { my_function( other_func(x, y), z + w) }" ,
369
+ "loop { (other_func(x, y)).my_method(z + w) }" ,
370
+ )
371
+ }
372
+
373
+ #[ test]
374
+ fn ssr_nested_function ( ) {
375
+ assert_ssr_transform (
376
+ "foo($a:expr, $b:expr, $c:expr) ==>> bar($c, baz($a, $b))" ,
377
+ "fn main { foo (x + value.method(b), x+y-z, true && false) }" ,
378
+ "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }" ,
379
+ )
380
+ }
381
+
382
+ #[ test]
383
+ fn ssr_expected_spacing ( ) {
384
+ assert_ssr_transform (
385
+ "foo($x:expr) + bar() ==>> bar($x)" ,
386
+ "fn main() { foo(5) + bar() }" ,
387
+ "fn main() { bar(5) }" ,
388
+ ) ;
389
+ }
390
+
391
+ #[ test]
392
+ fn ssr_with_extra_space ( ) {
393
+ assert_ssr_transform (
394
+ "foo($x:expr ) + bar() ==>> bar($x)" ,
395
+ "fn main() { foo( 5 ) +bar( ) }" ,
396
+ "fn main() { bar(5) }" ,
397
+ ) ;
398
+ }
399
+
400
+ #[ test]
401
+ fn ssr_keeps_nested_comment ( ) {
402
+ assert_ssr_transform (
403
+ "foo($x:expr) ==>> bar($x)" ,
404
+ "fn main() { foo(other(5 /* using 5 */)) }" ,
405
+ "fn main() { bar(other(5 /* using 5 */)) }" ,
406
+ )
407
+ }
408
+
409
+ #[ test]
410
+ fn ssr_keeps_comment ( ) {
411
+ assert_ssr_transform (
412
+ "foo($x:expr) ==>> bar($x)" ,
413
+ "fn main() { foo(5 /* using 5 */) }" ,
414
+ "fn main() { bar(5)/* using 5 */ }" ,
415
+ )
416
+ }
328
417
}
0 commit comments