@@ -30,7 +30,7 @@ use syntax::{
30
30
ast:: { self , NameOwner } ,
31
31
lex_single_syntax_kind, AstNode , SyntaxKind , TextRange , T ,
32
32
} ;
33
- use text_edit:: TextEdit ;
33
+ use text_edit:: { TextEdit , TextEditBuilder } ;
34
34
35
35
use crate :: {
36
36
defs:: Definition ,
@@ -303,141 +303,187 @@ pub fn source_edit_from_references(
303
303
) -> TextEdit {
304
304
let mut edit = TextEdit :: builder ( ) ;
305
305
for reference in references {
306
- let ( range , replacement ) = match & reference. name {
306
+ let has_emitted_edit = match & reference. name {
307
307
// if the ranges differ then the node is inside a macro call, we can't really attempt
308
308
// to make special rewrites like shorthand syntax and such, so just rename the node in
309
309
// the macro input
310
310
ast:: NameLike :: NameRef ( name_ref)
311
311
if name_ref. syntax ( ) . text_range ( ) == reference. range =>
312
312
{
313
- source_edit_from_name_ref ( name_ref, new_name, def)
313
+ source_edit_from_name_ref ( & mut edit , name_ref, new_name, def)
314
314
}
315
315
ast:: NameLike :: Name ( name) if name. syntax ( ) . text_range ( ) == reference. range => {
316
- source_edit_from_name ( name, new_name)
316
+ source_edit_from_name ( & mut edit , name, new_name)
317
317
}
318
- _ => None ,
318
+ _ => false ,
319
+ } ;
320
+ if !has_emitted_edit {
321
+ edit. replace ( reference. range , new_name. to_string ( ) ) ;
319
322
}
320
- . unwrap_or_else ( || ( reference. range , new_name. to_string ( ) ) ) ;
321
- edit. replace ( range, replacement) ;
322
323
}
324
+
323
325
edit. finish ( )
324
326
}
325
327
326
- fn source_edit_from_name ( name : & ast:: Name , new_name : & str ) -> Option < ( TextRange , String ) > {
328
+ fn source_edit_from_name ( edit : & mut TextEditBuilder , name : & ast:: Name , new_name : & str ) -> bool {
327
329
if let Some ( _) = ast:: RecordPatField :: for_field_name ( name) {
328
- // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
329
- // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
330
330
if let Some ( ident_pat) = name. syntax ( ) . parent ( ) . and_then ( ast:: IdentPat :: cast) {
331
- return Some ( (
332
- TextRange :: empty ( ident_pat. syntax ( ) . text_range ( ) . start ( ) ) ,
333
- [ new_name, ": " ] . concat ( ) ,
334
- ) ) ;
331
+ cov_mark:: hit!( rename_record_pat_field_name_split) ;
332
+ // Foo { ref mut field } -> Foo { new_name: ref mut field }
333
+ // ^ insert `new_name: `
334
+
335
+ // FIXME: instead of splitting the shorthand, recursively trigger a rename of the
336
+ // other name https://github.com/rust-analyzer/rust-analyzer/issues/6547
337
+ edit. insert ( ident_pat. syntax ( ) . text_range ( ) . start ( ) , format ! ( "{}: " , new_name) ) ;
338
+ return true ;
335
339
}
336
340
}
337
- None
341
+
342
+ false
338
343
}
339
344
340
345
fn source_edit_from_name_ref (
346
+ edit : & mut TextEditBuilder ,
341
347
name_ref : & ast:: NameRef ,
342
348
new_name : & str ,
343
349
def : Definition ,
344
- ) -> Option < ( TextRange , String ) > {
350
+ ) -> bool {
345
351
if let Some ( record_field) = ast:: RecordExprField :: for_name_ref ( name_ref) {
346
352
let rcf_name_ref = record_field. name_ref ( ) ;
347
353
let rcf_expr = record_field. expr ( ) ;
348
- match ( rcf_name_ref, rcf_expr. and_then ( |it| it. name_ref ( ) ) ) {
354
+ match & ( rcf_name_ref, rcf_expr. and_then ( |it| it. name_ref ( ) ) ) {
349
355
// field: init-expr, check if we can use a field init shorthand
350
356
( Some ( field_name) , Some ( init) ) => {
351
- if field_name == * name_ref {
357
+ if field_name == name_ref {
352
358
if init. text ( ) == new_name {
353
359
cov_mark:: hit!( test_rename_field_put_init_shorthand) ;
360
+ // Foo { field: local } -> Foo { local }
361
+ // ^^^^^^^ delete this
362
+
354
363
// same names, we can use a shorthand here instead.
355
364
// we do not want to erase attributes hence this range start
356
365
let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
357
- let e = record_field. syntax ( ) . text_range ( ) . end ( ) ;
358
- return Some ( ( TextRange :: new ( s, e) , new_name. to_owned ( ) ) ) ;
366
+ let e = init. syntax ( ) . text_range ( ) . start ( ) ;
367
+ edit. delete ( TextRange :: new ( s, e) ) ;
368
+ return true ;
359
369
}
360
- } else if init == * name_ref {
370
+ } else if init == name_ref {
361
371
if field_name. text ( ) == new_name {
362
372
cov_mark:: hit!( test_rename_local_put_init_shorthand) ;
373
+ // Foo { field: local } -> Foo { field }
374
+ // ^^^^^^^ delete this
375
+
363
376
// same names, we can use a shorthand here instead.
364
377
// we do not want to erase attributes hence this range start
365
- let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
366
- let e = record_field. syntax ( ) . text_range ( ) . end ( ) ;
367
- return Some ( ( TextRange :: new ( s, e) , new_name. to_owned ( ) ) ) ;
378
+ let s = field_name. syntax ( ) . text_range ( ) . end ( ) ;
379
+ let e = init. syntax ( ) . text_range ( ) . end ( ) ;
380
+ edit. delete ( TextRange :: new ( s, e) ) ;
381
+ return true ;
368
382
}
369
383
}
370
- None
371
384
}
372
385
// init shorthand
373
386
( None , Some ( _) ) if matches ! ( def, Definition :: Field ( _) ) => {
374
387
cov_mark:: hit!( test_rename_field_in_field_shorthand) ;
375
- let s = name_ref. syntax ( ) . text_range ( ) . start ( ) ;
376
- Some ( ( TextRange :: empty ( s) , format ! ( "{}: " , new_name) ) )
388
+ // Foo { field } -> Foo { new_name: field }
389
+ // ^ insert `new_name: `
390
+ let offset = name_ref. syntax ( ) . text_range ( ) . start ( ) ;
391
+ edit. insert ( offset, format ! ( "{}: " , new_name) ) ;
392
+ return true ;
377
393
}
378
394
( None , Some ( _) ) if matches ! ( def, Definition :: Local ( _) ) => {
379
395
cov_mark:: hit!( test_rename_local_in_field_shorthand) ;
380
- let s = name_ref. syntax ( ) . text_range ( ) . end ( ) ;
381
- Some ( ( TextRange :: empty ( s) , format ! ( ": {}" , new_name) ) )
396
+ // Foo { field } -> Foo { field: new_name }
397
+ // ^ insert `: new_name`
398
+ let offset = name_ref. syntax ( ) . text_range ( ) . end ( ) ;
399
+ edit. insert ( offset, format ! ( ": {}" , new_name) ) ;
400
+ return true ;
382
401
}
383
- _ => None ,
402
+ _ => ( ) ,
384
403
}
385
404
} else if let Some ( record_field) = ast:: RecordPatField :: for_field_name_ref ( name_ref) {
386
405
let rcf_name_ref = record_field. name_ref ( ) ;
387
406
let rcf_pat = record_field. pat ( ) ;
388
407
match ( rcf_name_ref, rcf_pat) {
389
408
// field: rename
390
- ( Some ( field_name) , Some ( ast:: Pat :: IdentPat ( pat) ) ) if field_name == * name_ref => {
409
+ ( Some ( field_name) , Some ( ast:: Pat :: IdentPat ( pat) ) )
410
+ if field_name == * name_ref && pat. at_token ( ) . is_none ( ) =>
411
+ {
391
412
// field name is being renamed
392
- if pat. name ( ) . map_or ( false , |it| it. text ( ) == new_name) {
393
- cov_mark:: hit!( test_rename_field_put_init_shorthand_pat) ;
394
- // same names, we can use a shorthand here instead/
395
- // we do not want to erase attributes hence this range start
396
- let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
397
- let e = record_field. syntax ( ) . text_range ( ) . end ( ) ;
398
- Some ( ( TextRange :: new ( s, e) , pat. to_string ( ) ) )
399
- } else {
400
- None
413
+ if let Some ( name) = pat. name ( ) {
414
+ if name. text ( ) == new_name {
415
+ cov_mark:: hit!( test_rename_field_put_init_shorthand_pat) ;
416
+ // Foo { field: ref mut local } -> Foo { ref mut field }
417
+ // ^^^^^^^ delete this
418
+ // ^^^^^ replace this with `field`
419
+
420
+ // same names, we can use a shorthand here instead/
421
+ // we do not want to erase attributes hence this range start
422
+ let s = field_name. syntax ( ) . text_range ( ) . start ( ) ;
423
+ let e = pat. syntax ( ) . text_range ( ) . start ( ) ;
424
+ edit. delete ( TextRange :: new ( s, e) ) ;
425
+ edit. replace ( name. syntax ( ) . text_range ( ) , new_name. to_string ( ) ) ;
426
+ return true ;
427
+ }
401
428
}
402
429
}
403
- _ => None ,
430
+ _ => ( ) ,
404
431
}
405
- } else {
406
- None
407
432
}
433
+ false
408
434
}
409
435
410
436
fn source_edit_from_def (
411
437
sema : & Semantics < RootDatabase > ,
412
438
def : Definition ,
413
439
new_name : & str ,
414
440
) -> Result < ( FileId , TextEdit ) > {
415
- let frange = def
441
+ let FileRange { file_id , range } = def
416
442
. range_for_rename ( sema)
417
443
. ok_or_else ( || format_err ! ( "No identifier available to rename" ) ) ?;
418
444
419
- let mut replacement_text = String :: new ( ) ;
420
- let mut repl_range = frange. range ;
445
+ let mut edit = TextEdit :: builder ( ) ;
421
446
if let Definition :: Local ( local) = def {
422
447
if let Either :: Left ( pat) = local. source ( sema. db ) . value {
423
- if matches ! (
424
- pat. syntax( ) . parent( ) . and_then( ast:: RecordPatField :: cast) ,
425
- Some ( pat_field) if pat_field. name_ref( ) . is_none( )
426
- ) {
427
- replacement_text. push_str ( ": " ) ;
428
- replacement_text. push_str ( new_name) ;
429
- repl_range = TextRange :: new (
430
- pat. syntax ( ) . text_range ( ) . end ( ) ,
431
- pat. syntax ( ) . text_range ( ) . end ( ) ,
432
- ) ;
448
+ // special cases required for renaming fields/locals in Record patterns
449
+ if let Some ( pat_field) = pat. syntax ( ) . parent ( ) . and_then ( ast:: RecordPatField :: cast) {
450
+ let name_range = pat. name ( ) . unwrap ( ) . syntax ( ) . text_range ( ) ;
451
+ if let Some ( name_ref) = pat_field. name_ref ( ) {
452
+ if new_name == name_ref. text ( ) && pat. at_token ( ) . is_none ( ) {
453
+ // Foo { field: ref mut local } -> Foo { ref mut field }
454
+ // ^^^^^^ delete this
455
+ // ^^^^^ replace this with `field`
456
+ cov_mark:: hit!( test_rename_local_put_init_shorthand_pat) ;
457
+ edit. delete (
458
+ name_ref
459
+ . syntax ( )
460
+ . text_range ( )
461
+ . cover_offset ( pat. syntax ( ) . text_range ( ) . start ( ) ) ,
462
+ ) ;
463
+ edit. replace ( name_range, name_ref. text ( ) . to_string ( ) ) ;
464
+ } else {
465
+ // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 }
466
+ // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
467
+ // ^^^^^ replace this with `new_name`
468
+ edit. replace ( name_range, new_name. to_string ( ) ) ;
469
+ }
470
+ } else {
471
+ // Foo { ref mut field } -> Foo { field: ref mut new_name }
472
+ // ^ insert `field: `
473
+ // ^^^^^ replace this with `new_name`
474
+ edit. insert (
475
+ pat. syntax ( ) . text_range ( ) . start ( ) ,
476
+ format ! ( "{}: " , pat_field. field_name( ) . unwrap( ) ) ,
477
+ ) ;
478
+ edit. replace ( name_range, new_name. to_string ( ) ) ;
479
+ }
433
480
}
434
481
}
435
482
}
436
- if replacement_text . is_empty ( ) {
437
- replacement_text . push_str ( new_name) ;
483
+ if edit . is_empty ( ) {
484
+ edit . replace ( range , new_name. to_string ( ) ) ;
438
485
}
439
- let edit = TextEdit :: replace ( repl_range, replacement_text) ;
440
- Ok ( ( frange. file_id , edit) )
486
+ Ok ( ( file_id, edit. finish ( ) ) )
441
487
}
442
488
443
489
#[ derive( Copy , Clone , Debug , PartialEq ) ]
0 commit comments