1
1
//! FIXME: write short doc here
2
2
3
- use hir:: { ModuleSource , Semantics } ;
3
+ use hir:: { Module , ModuleDef , ModuleSource , Semantics } ;
4
4
use ra_db:: { RelativePathBuf , SourceDatabaseExt } ;
5
- use ra_ide_db:: RootDatabase ;
5
+ use ra_ide_db:: {
6
+ defs:: { classify_name, classify_name_ref, Definition , NameClass , NameRefClass } ,
7
+ RootDatabase ,
8
+ } ;
6
9
use ra_syntax:: {
7
- algo:: find_node_at_offset, ast, ast:: TypeAscriptionOwner , lex_single_valid_syntax_kind ,
8
- AstNode , SyntaxKind , SyntaxNode , SyntaxToken ,
10
+ algo:: find_node_at_offset, ast, ast:: NameOwner , ast :: TypeAscriptionOwner ,
11
+ lex_single_valid_syntax_kind , match_ast , AstNode , SyntaxKind , SyntaxNode , SyntaxToken ,
9
12
} ;
10
13
use ra_text_edit:: TextEdit ;
11
14
use std:: convert:: TryInto ;
@@ -30,10 +33,8 @@ pub(crate) fn rename(
30
33
let sema = Semantics :: new ( db) ;
31
34
let source_file = sema. parse ( position. file_id ) ;
32
35
let syntax = source_file. syntax ( ) ;
33
- if let Some ( ( ast_name, ast_module) ) = find_name_and_module_at_offset ( syntax, position) {
34
- let range = ast_name. syntax ( ) . text_range ( ) ;
35
- rename_mod ( & sema, & ast_name, & ast_module, position, new_name)
36
- . map ( |info| RangeInfo :: new ( range, info) )
36
+ if let Some ( module) = find_module_at_offset ( & sema, position, syntax) {
37
+ rename_mod ( db, position, module, new_name)
37
38
} else if let Some ( self_token) =
38
39
syntax. token_at_offset ( position. offset ) . find ( |t| t. kind ( ) == SyntaxKind :: SELF_KW )
39
40
{
@@ -43,13 +44,32 @@ pub(crate) fn rename(
43
44
}
44
45
}
45
46
46
- fn find_name_and_module_at_offset (
47
- syntax : & SyntaxNode ,
47
+ fn find_module_at_offset (
48
+ sema : & Semantics < RootDatabase > ,
48
49
position : FilePosition ,
49
- ) -> Option < ( ast:: Name , ast:: Module ) > {
50
- let ast_name = find_node_at_offset :: < ast:: Name > ( syntax, position. offset ) ?;
51
- let ast_module = ast:: Module :: cast ( ast_name. syntax ( ) . parent ( ) ?) ?;
52
- Some ( ( ast_name, ast_module) )
50
+ syntax : & SyntaxNode ,
51
+ ) -> Option < Module > {
52
+ let ident = syntax. token_at_offset ( position. offset ) . find ( |t| t. kind ( ) == SyntaxKind :: IDENT ) ?;
53
+
54
+ let module = match_ast ! {
55
+ match ( ident. parent( ) ) {
56
+ ast:: NameRef ( name_ref) => {
57
+ match classify_name_ref( sema, & name_ref) ? {
58
+ NameRefClass :: Definition ( Definition :: ModuleDef ( ModuleDef :: Module ( module) ) ) => module,
59
+ _ => return None ,
60
+ }
61
+ } ,
62
+ ast:: Name ( name) => {
63
+ match classify_name( & sema, & name) ? {
64
+ NameClass :: Definition ( Definition :: ModuleDef ( ModuleDef :: Module ( module) ) ) => module,
65
+ _ => return None ,
66
+ }
67
+ } ,
68
+ _ => return None ,
69
+ }
70
+ } ;
71
+
72
+ Some ( module)
53
73
}
54
74
55
75
fn source_edit_from_reference ( reference : Reference , new_name : & str ) -> SourceFileEdit {
@@ -77,49 +97,50 @@ fn source_edit_from_reference(reference: Reference, new_name: &str) -> SourceFil
77
97
}
78
98
79
99
fn rename_mod (
80
- sema : & Semantics < RootDatabase > ,
81
- ast_name : & ast:: Name ,
82
- ast_module : & ast:: Module ,
100
+ db : & RootDatabase ,
83
101
position : FilePosition ,
102
+ module : Module ,
84
103
new_name : & str ,
85
- ) -> Option < SourceChange > {
104
+ ) -> Option < RangeInfo < SourceChange > > {
86
105
let mut source_file_edits = Vec :: new ( ) ;
87
106
let mut file_system_edits = Vec :: new ( ) ;
88
- if let Some ( module) = sema. to_def ( ast_module) {
89
- let src = module. definition_source ( sema. db ) ;
90
- let file_id = src. file_id . original_file ( sema. db ) ;
91
- match src. value {
92
- ModuleSource :: SourceFile ( ..) => {
93
- let mod_path: RelativePathBuf = sema. db . file_relative_path ( file_id) ;
94
- // mod is defined in path/to/dir/mod.rs
95
- let dst = if mod_path. file_stem ( ) == Some ( "mod" ) {
96
- format ! ( "../{}/mod.rs" , new_name)
97
- } else {
98
- format ! ( "{}.rs" , new_name)
99
- } ;
100
- let move_file =
101
- FileSystemEdit :: MoveFile { src : file_id, anchor : position. file_id , dst } ;
102
- file_system_edits. push ( move_file) ;
103
- }
104
- ModuleSource :: Module ( ..) => { }
107
+
108
+ let src = module. definition_source ( db) ;
109
+ let file_id = src. file_id . original_file ( db) ;
110
+ match src. value {
111
+ ModuleSource :: SourceFile ( ..) => {
112
+ let mod_path: RelativePathBuf = db. file_relative_path ( file_id) ;
113
+ // mod is defined in path/to/dir/mod.rs
114
+ let dst = if mod_path. file_stem ( ) == Some ( "mod" ) {
115
+ format ! ( "../{}/mod.rs" , new_name)
116
+ } else {
117
+ format ! ( "{}.rs" , new_name)
118
+ } ;
119
+ let move_file =
120
+ FileSystemEdit :: MoveFile { src : file_id, anchor : position. file_id , dst } ;
121
+ file_system_edits. push ( move_file) ;
105
122
}
123
+ ModuleSource :: Module ( ..) => { }
106
124
}
107
125
108
- let edit = SourceFileEdit {
109
- file_id : position. file_id ,
110
- edit : TextEdit :: replace ( ast_name. syntax ( ) . text_range ( ) , new_name. into ( ) ) ,
111
- } ;
112
- source_file_edits. push ( edit) ;
113
-
114
- if let Some ( RangeInfo { range : _, info : refs } ) = find_all_refs ( sema. db , position, None ) {
115
- let ref_edits = refs
116
- . references
117
- . into_iter ( )
118
- . map ( |reference| source_edit_from_reference ( reference, new_name) ) ;
119
- source_file_edits. extend ( ref_edits) ;
126
+ if let Some ( src) = module. declaration_source ( db) {
127
+ let file_id = src. file_id . original_file ( db) ;
128
+ let name = src. value . name ( ) ?;
129
+ let edit = SourceFileEdit {
130
+ file_id : file_id,
131
+ edit : TextEdit :: replace ( name. syntax ( ) . text_range ( ) , new_name. into ( ) ) ,
132
+ } ;
133
+ source_file_edits. push ( edit) ;
120
134
}
121
135
122
- Some ( SourceChange :: from_edits ( source_file_edits, file_system_edits) )
136
+ let RangeInfo { range, info : refs } = find_all_refs ( db, position, None ) ?;
137
+ let ref_edits = refs
138
+ . references
139
+ . into_iter ( )
140
+ . map ( |reference| source_edit_from_reference ( reference, new_name) ) ;
141
+ source_file_edits. extend ( ref_edits) ;
142
+
143
+ Some ( RangeInfo :: new ( range, SourceChange :: from_edits ( source_file_edits, file_system_edits) ) )
123
144
}
124
145
125
146
fn rename_to_self ( db : & RootDatabase , position : FilePosition ) -> Option < RangeInfo < SourceChange > > {
@@ -666,6 +687,76 @@ mod foo<|>;
666
687
"### ) ;
667
688
}
668
689
690
+ #[ test]
691
+ fn test_rename_mod_in_use_tree ( ) {
692
+ let ( analysis, position) = analysis_and_position (
693
+ r#"
694
+ //- /main.rs
695
+ pub mod foo;
696
+ pub mod bar;
697
+ fn main() {}
698
+
699
+ //- /foo.rs
700
+ pub struct FooContent;
701
+
702
+ //- /bar.rs
703
+ use crate::foo<|>::FooContent;
704
+ "# ,
705
+ ) ;
706
+ let new_name = "qux" ;
707
+ let source_change = analysis. rename ( position, new_name) . unwrap ( ) ;
708
+ assert_debug_snapshot ! ( & source_change,
709
+ @r###"
710
+ Some(
711
+ RangeInfo {
712
+ range: 11..14,
713
+ info: SourceChange {
714
+ source_file_edits: [
715
+ SourceFileEdit {
716
+ file_id: FileId(
717
+ 1,
718
+ ),
719
+ edit: TextEdit {
720
+ indels: [
721
+ Indel {
722
+ insert: "qux",
723
+ delete: 8..11,
724
+ },
725
+ ],
726
+ },
727
+ },
728
+ SourceFileEdit {
729
+ file_id: FileId(
730
+ 3,
731
+ ),
732
+ edit: TextEdit {
733
+ indels: [
734
+ Indel {
735
+ insert: "qux",
736
+ delete: 11..14,
737
+ },
738
+ ],
739
+ },
740
+ },
741
+ ],
742
+ file_system_edits: [
743
+ MoveFile {
744
+ src: FileId(
745
+ 2,
746
+ ),
747
+ anchor: FileId(
748
+ 3,
749
+ ),
750
+ dst: "qux.rs",
751
+ },
752
+ ],
753
+ is_snippet: false,
754
+ },
755
+ },
756
+ )
757
+ "### ) ;
758
+ }
759
+
669
760
#[ test]
670
761
fn test_rename_mod_in_dir ( ) {
671
762
let ( analysis, position) = analysis_and_position (
0 commit comments