11//! FIXME: write short doc here
22
3- use hir:: { ModuleSource , Semantics } ;
3+ use hir:: { Module , ModuleDef , ModuleSource , Semantics } ;
44use 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+ } ;
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,49 +97,50 @@ 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 = 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) ;
105122 }
123+ ModuleSource :: Module ( ..) => { }
106124 }
107125
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) ;
120134 }
121135
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) ) )
123144}
124145
125146fn rename_to_self ( db : & RootDatabase , position : FilePosition ) -> Option < RangeInfo < SourceChange > > {
@@ -666,6 +687,76 @@ mod foo<|>;
666687 "### ) ;
667688 }
668689
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+
669760 #[ test]
670761 fn test_rename_mod_in_dir ( ) {
671762 let ( analysis, position) = analysis_and_position (
0 commit comments