11//! FIXME: write short doc here
22
3- use hir:: { ModuleSource , Semantics } ;
3+ use hir:: { Module , ModuleDef , ModuleSource , Semantics } ;
44use 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+ } ;
69use 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 ,
912} ;
1013use ra_text_edit:: TextEdit ;
1114use std:: convert:: TryInto ;
@@ -30,10 +33,8 @@ pub(crate) fn rename(
3033 let sema = Semantics :: new ( db) ;
3134 let source_file = sema. parse ( position. file_id ) ;
3235 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)
3738 } else if let Some ( self_token) =
3839 syntax. token_at_offset ( position. offset ) . find ( |t| t. kind ( ) == SyntaxKind :: SELF_KW )
3940 {
@@ -43,13 +44,32 @@ pub(crate) fn rename(
4344 }
4445}
4546
46- fn find_name_and_module_at_offset (
47- syntax : & SyntaxNode ,
47+ fn find_module_at_offset (
48+ sema : & Semantics < RootDatabase > ,
4849 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)
5373}
5474
5575fn 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
7797}
7898
7999fn rename_mod (
80- sema : & Semantics < RootDatabase > ,
81- ast_name : & ast:: Name ,
82- ast_module : & ast:: Module ,
100+ db : & RootDatabase ,
83101 position : FilePosition ,
102+ module : Module ,
84103 new_name : & str ,
85- ) -> Option < SourceChange > {
104+ ) -> Option < RangeInfo < SourceChange > > {
86105 let mut source_file_edits = Vec :: new ( ) ;
87106 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,
103128 } ;
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) ;
112130 }
113- ModuleSource :: Module ( ..) => { }
114131 }
132+ ModuleSource :: Module ( ..) => { }
115133 }
116134
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) ;
129143 }
130144
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) ) )
132153}
133154
134155fn rename_to_self ( db : & RootDatabase , position : FilePosition ) -> Option < RangeInfo < SourceChange > > {
@@ -675,6 +696,76 @@ mod tests {
675696 "### ) ;
676697 }
677698
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+
678769 #[ test]
679770 fn test_rename_mod_in_dir ( ) {
680771 let ( analysis, position) = analysis_and_position (
0 commit comments