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:: { RelativePath , 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,58 +97,59 @@ 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_path = if mod_path. file_stem ( ) == Some ( "mod" ) {
96
- mod_path
97
- . parent ( )
98
- . and_then ( |p| p. parent ( ) )
99
- . or_else ( || Some ( RelativePath :: new ( "" ) ) )
100
- . map ( |p| p. join ( new_name) . join ( "mod.rs" ) )
101
- } else {
102
- Some ( mod_path. with_file_name ( new_name) . with_extension ( "rs" ) )
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_path = if mod_path. file_stem ( ) == Some ( "mod" ) {
115
+ mod_path
116
+ . parent ( )
117
+ . and_then ( |p| p. parent ( ) )
118
+ . or_else ( || Some ( RelativePath :: new ( "" ) ) )
119
+ . map ( |p| p. join ( new_name) . join ( "mod.rs" ) )
120
+ } else {
121
+ Some ( mod_path. with_file_name ( new_name) . with_extension ( "rs" ) )
122
+ } ;
123
+ if let Some ( path) = dst_path {
124
+ let move_file = FileSystemEdit :: MoveFile {
125
+ src : file_id,
126
+ dst_source_root : db. file_source_root ( position. file_id ) ,
127
+ dst_path : path,
103
128
} ;
104
- if let Some ( path) = dst_path {
105
- let move_file = FileSystemEdit :: MoveFile {
106
- src : file_id,
107
- dst_source_root : sema. db . file_source_root ( position. file_id ) ,
108
- dst_path : path,
109
- } ;
110
- file_system_edits. push ( move_file) ;
111
- }
129
+ file_system_edits. push ( move_file) ;
112
130
}
113
- ModuleSource :: Module ( ..) => { }
114
131
}
132
+ ModuleSource :: Module ( ..) => { }
115
133
}
116
134
117
- let edit = SourceFileEdit {
118
- file_id : position. file_id ,
119
- edit : TextEdit :: replace ( ast_name. syntax ( ) . text_range ( ) , new_name. into ( ) ) ,
120
- } ;
121
- source_file_edits. push ( edit) ;
122
-
123
- if let Some ( RangeInfo { range : _, info : refs } ) = find_all_refs ( sema. db , position, None ) {
124
- let ref_edits = refs
125
- . references
126
- . into_iter ( )
127
- . map ( |reference| source_edit_from_reference ( reference, new_name) ) ;
128
- source_file_edits. extend ( ref_edits) ;
135
+ if let Some ( src) = module. declaration_source ( db) {
136
+ let file_id = src. file_id . original_file ( db) ;
137
+ let name = src. value . name ( ) ?;
138
+ let edit = SourceFileEdit {
139
+ file_id : file_id,
140
+ edit : TextEdit :: replace ( name. syntax ( ) . text_range ( ) , new_name. into ( ) ) ,
141
+ } ;
142
+ source_file_edits. push ( edit) ;
129
143
}
130
144
131
- Some ( SourceChange :: from_edits ( source_file_edits, file_system_edits) )
145
+ let RangeInfo { range, info : refs } = find_all_refs ( db, position, None ) ?;
146
+ let ref_edits = refs
147
+ . references
148
+ . into_iter ( )
149
+ . map ( |reference| source_edit_from_reference ( reference, new_name) ) ;
150
+ source_file_edits. extend ( ref_edits) ;
151
+
152
+ Some ( RangeInfo :: new ( range, SourceChange :: from_edits ( source_file_edits, file_system_edits) ) )
132
153
}
133
154
134
155
fn rename_to_self ( db : & RootDatabase , position : FilePosition ) -> Option < RangeInfo < SourceChange > > {
@@ -675,6 +696,76 @@ mod tests {
675
696
"### ) ;
676
697
}
677
698
699
+ #[ test]
700
+ fn test_rename_mod_in_use_tree ( ) {
701
+ let ( analysis, position) = analysis_and_position (
702
+ "
703
+ //- /main.rs
704
+ pub mod foo;
705
+ pub mod bar;
706
+ fn main() {}
707
+
708
+ //- /foo.rs
709
+ pub struct FooContent;
710
+
711
+ //- /bar.rs
712
+ use crate::foo<|>::FooContent;
713
+ " ,
714
+ ) ;
715
+ let new_name = "qux" ;
716
+ let source_change = analysis. rename ( position, new_name) . unwrap ( ) ;
717
+ assert_debug_snapshot ! ( & source_change,
718
+ @r###"
719
+ Some(
720
+ RangeInfo {
721
+ range: 11..14,
722
+ info: SourceChange {
723
+ source_file_edits: [
724
+ SourceFileEdit {
725
+ file_id: FileId(
726
+ 1,
727
+ ),
728
+ edit: TextEdit {
729
+ indels: [
730
+ Indel {
731
+ insert: "qux",
732
+ delete: 8..11,
733
+ },
734
+ ],
735
+ },
736
+ },
737
+ SourceFileEdit {
738
+ file_id: FileId(
739
+ 3,
740
+ ),
741
+ edit: TextEdit {
742
+ indels: [
743
+ Indel {
744
+ insert: "qux",
745
+ delete: 11..14,
746
+ },
747
+ ],
748
+ },
749
+ },
750
+ ],
751
+ file_system_edits: [
752
+ MoveFile {
753
+ src: FileId(
754
+ 2,
755
+ ),
756
+ dst_source_root: SourceRootId(
757
+ 0,
758
+ ),
759
+ dst_path: "qux.rs",
760
+ },
761
+ ],
762
+ is_snippet: false,
763
+ },
764
+ },
765
+ )
766
+ "### ) ;
767
+ }
768
+
678
769
#[ test]
679
770
fn test_rename_mod_in_dir ( ) {
680
771
let ( analysis, position) = analysis_and_position (
0 commit comments