1- use std:: iter ;
1+ use std:: ops :: RangeInclusive ;
22
3- use either:: Either ;
43use hir:: { HasSource , ModuleSource } ;
54use ide_db:: {
65 FileId , FxHashMap , FxHashSet ,
@@ -82,7 +81,15 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
8281 curr_parent_module = ast:: Module :: cast ( mod_syn_opt) ;
8382 }
8483
85- let mut module = extract_target ( & node, ctx. selection_trimmed ( ) ) ?;
84+ let selection_range = ctx. selection_trimmed ( ) ;
85+ let ( mut module, module_text_range) = if let Some ( item) = ast:: Item :: cast ( node. clone ( ) ) {
86+ let module = extract_single_target ( & item) ;
87+ ( module, node. text_range ( ) )
88+ } else {
89+ let ( module, range) = extract_child_target ( & node, selection_range) ?;
90+ let module_text_range = range. start ( ) . text_range ( ) . cover ( range. end ( ) . text_range ( ) ) ;
91+ ( module, module_text_range)
92+ } ;
8693 if module. body_items . is_empty ( ) {
8794 return None ;
8895 }
@@ -92,7 +99,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
9299 acc. add (
93100 AssistId :: refactor_extract ( "extract_module" ) ,
94101 "Extract Module" ,
95- module . text_range ,
102+ module_text_range ,
96103 |builder| {
97104 //This takes place in three steps:
98105 //
@@ -110,17 +117,17 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
110117 //for change_visibility and usages for first point mentioned above in the process
111118
112119 let ( usages_to_be_processed, record_fields, use_stmts_to_be_inserted) =
113- module. get_usages_and_record_fields ( ctx) ;
120+ module. get_usages_and_record_fields ( ctx, module_text_range ) ;
114121
115122 builder. edit_file ( ctx. vfs_file_id ( ) ) ;
116123 use_stmts_to_be_inserted. into_iter ( ) . for_each ( |( _, use_stmt) | {
117124 builder. insert ( ctx. selection_trimmed ( ) . end ( ) , format ! ( "\n {use_stmt}" ) ) ;
118125 } ) ;
119126
120- let import_paths_to_be_removed = module. resolve_imports ( curr_parent_module, ctx) ;
127+ let import_items = module. resolve_imports ( curr_parent_module, ctx) ;
121128 module. change_visibility ( record_fields) ;
122129
123- let module_def = generate_module_def ( & impl_parent, & mut module, old_item_indent) ;
130+ let module_def = generate_module_def ( & impl_parent, module, old_item_indent) . to_string ( ) ;
124131
125132 let mut usages_to_be_processed_for_cur_file = vec ! [ ] ;
126133 for ( file_id, usages) in usages_to_be_processed {
@@ -157,54 +164,63 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
157164
158165 builder. insert ( impl_. syntax ( ) . text_range ( ) . end ( ) , format ! ( "\n \n {module_def}" ) ) ;
159166 } else {
160- for import_path_text_range in import_paths_to_be_removed {
161- if module. text_range . intersect ( import_path_text_range) . is_some ( ) {
162- module. text_range = module. text_range . cover ( import_path_text_range) ;
163- } else {
164- builder. delete ( import_path_text_range) ;
167+ for import_item in import_items {
168+ if !module_text_range. contains_range ( import_item) {
169+ builder. delete ( import_item) ;
165170 }
166171 }
167-
168- builder. replace ( module. text_range , module_def)
172+ builder. replace ( module_text_range, module_def)
169173 }
170174 } ,
171175 )
172176}
173177
174178fn generate_module_def (
175179 parent_impl : & Option < ast:: Impl > ,
176- module : & mut Module ,
180+ module : Module ,
177181 old_indent : IndentLevel ,
178- ) -> String {
179- let ( items_to_be_processed, new_item_indent) = if parent_impl. is_some ( ) {
180- ( Either :: Left ( module. body_items . iter ( ) ) , old_indent + 2 )
182+ ) -> ast:: Module {
183+ let Module { name, body_items, use_items } = module;
184+ let items = if let Some ( self_ty) = parent_impl. as_ref ( ) . and_then ( |imp| imp. self_ty ( ) ) {
185+ let assoc_items = body_items
186+ . into_iter ( )
187+ . map ( |item| item. syntax ( ) . clone ( ) )
188+ . filter_map ( ast:: AssocItem :: cast)
189+ . map ( |it| it. indent ( IndentLevel ( 1 ) ) )
190+ . collect_vec ( ) ;
191+ let assoc_item_list = make:: assoc_item_list ( Some ( assoc_items) ) ;
192+ let impl_ = make:: impl_ ( None , None , None , self_ty. clone ( ) , None , Some ( assoc_item_list) ) ;
193+ // Add the import for enum/struct corresponding to given impl block
194+ let use_impl = make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) ;
195+ let mut module_body_items = use_items;
196+ module_body_items. insert ( 0 , use_impl) ;
197+ module_body_items. push ( ast:: Item :: Impl ( impl_) ) ;
198+ module_body_items
181199 } else {
182- ( Either :: Right ( module . use_items . iter ( ) . chain ( module . body_items . iter ( ) ) ) , old_indent + 1 )
200+ [ use_items, body_items] . concat ( )
183201 } ;
184202
185- let mut body = items_to_be_processed
186- . map ( |item| item. indent ( IndentLevel ( 1 ) ) )
187- . map ( |item| format ! ( "{new_item_indent}{item}" ) )
188- . join ( "\n \n " ) ;
203+ let items = items. into_iter ( ) . map ( |it| it. reset_indent ( ) . indent ( IndentLevel ( 1 ) ) ) . collect_vec ( ) ;
204+ let module_body = make:: item_list ( Some ( items) ) ;
189205
190- if let Some ( self_ty ) = parent_impl . as_ref ( ) . and_then ( |imp| imp . self_ty ( ) ) {
191- let impl_indent = old_indent + 1 ;
192- body = format ! ( "{impl_indent}impl {self_ty} {{ \n {body} \n {impl_indent}}}" ) ;
206+ let module_name = make :: name ( name ) ;
207+ make :: mod_ ( module_name , Some ( module_body ) ) . indent ( old_indent)
208+ }
193209
194- // Add the import for enum/struct corresponding to given impl block
195- module. make_use_stmt_of_node_with_super ( self_ty. syntax ( ) ) ;
196- for item in module. use_items . iter ( ) {
197- body = format ! ( "{impl_indent}{item}\n \n {body}" ) ;
198- }
199- }
210+ fn make_use_stmt_of_node_with_super ( node_syntax : & SyntaxNode ) -> ast:: Item {
211+ let super_path = make:: ext:: ident_path ( "super" ) ;
212+ let node_path = make:: ext:: ident_path ( & node_syntax. to_string ( ) ) ;
213+ let use_ = make:: use_ (
214+ None ,
215+ None ,
216+ make:: use_tree ( make:: join_paths ( vec ! [ super_path, node_path] ) , None , None , false ) ,
217+ ) ;
200218
201- let module_name = module. name ;
202- format ! ( "mod {module_name} {{\n {body}\n {old_indent}}}" )
219+ ast:: Item :: from ( use_)
203220}
204221
205222#[ derive( Debug ) ]
206223struct Module {
207- text_range : TextRange ,
208224 name : & ' static str ,
209225 /// All items except use items.
210226 body_items : Vec < ast:: Item > ,
@@ -214,22 +230,37 @@ struct Module {
214230 use_items : Vec < ast:: Item > ,
215231}
216232
217- fn extract_target ( node : & SyntaxNode , selection_range : TextRange ) -> Option < Module > {
233+ fn extract_single_target ( node : & ast:: Item ) -> Module {
234+ let ( body_items, use_items) = if matches ! ( node, ast:: Item :: Use ( _) ) {
235+ ( Vec :: new ( ) , vec ! [ node. clone( ) ] )
236+ } else {
237+ ( vec ! [ node. clone( ) ] , Vec :: new ( ) )
238+ } ;
239+ let name = "modname" ;
240+ Module { name, body_items, use_items }
241+ }
242+
243+ fn extract_child_target (
244+ node : & SyntaxNode ,
245+ selection_range : TextRange ,
246+ ) -> Option < ( Module , RangeInclusive < SyntaxNode > ) > {
218247 let selected_nodes = node
219248 . children ( )
220249 . filter ( |node| selection_range. contains_range ( node. text_range ( ) ) )
221- . chain ( iter:: once ( node. clone ( ) ) ) ;
222- let ( use_items, body_items) = selected_nodes
223250 . filter_map ( ast:: Item :: cast)
224- . partition ( |item| matches ! ( item, ast:: Item :: Use ( ..) ) ) ;
225-
226- Some ( Module { text_range : selection_range, name : "modname" , body_items, use_items } )
251+ . collect_vec ( ) ;
252+ let start = selected_nodes. first ( ) ?. syntax ( ) . clone ( ) ;
253+ let end = selected_nodes. last ( ) ?. syntax ( ) . clone ( ) ;
254+ let ( use_items, body_items) : ( Vec < ast:: Item > , Vec < ast:: Item > ) =
255+ selected_nodes. into_iter ( ) . partition ( |item| matches ! ( item, ast:: Item :: Use ( ..) ) ) ;
256+ Some ( ( Module { name : "modname" , body_items, use_items } , start..=end) )
227257}
228258
229259impl Module {
230260 fn get_usages_and_record_fields (
231261 & self ,
232262 ctx : & AssistContext < ' _ > ,
263+ replace_range : TextRange ,
233264 ) -> ( FxHashMap < FileId , Vec < ( TextRange , String ) > > , Vec < SyntaxNode > , FxHashMap < TextSize , ast:: Use > )
234265 {
235266 let mut adt_fields = Vec :: new ( ) ;
@@ -247,7 +278,7 @@ impl Module {
247278 ast:: Adt ( it) => {
248279 if let Some ( nod ) = ctx. sema. to_def( & it) {
249280 let node_def = Definition :: Adt ( nod) ;
250- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
281+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
251282
252283 //Enum Fields are not allowed to explicitly specify pub, it is implied
253284 match it {
@@ -281,30 +312,30 @@ impl Module {
281312 ast:: TypeAlias ( it) => {
282313 if let Some ( nod ) = ctx. sema. to_def( & it) {
283314 let node_def = Definition :: TypeAlias ( nod) ;
284- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
315+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
285316 }
286317 } ,
287318 ast:: Const ( it) => {
288319 if let Some ( nod ) = ctx. sema. to_def( & it) {
289320 let node_def = Definition :: Const ( nod) ;
290- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
321+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
291322 }
292323 } ,
293324 ast:: Static ( it) => {
294325 if let Some ( nod ) = ctx. sema. to_def( & it) {
295326 let node_def = Definition :: Static ( nod) ;
296- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
327+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
297328 }
298329 } ,
299330 ast:: Fn ( it) => {
300331 if let Some ( nod ) = ctx. sema. to_def( & it) {
301332 let node_def = Definition :: Function ( nod) ;
302- self . expand_and_group_usages_file_wise( ctx, node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
333+ self . expand_and_group_usages_file_wise( ctx, replace_range , node_def, & mut refs, & mut use_stmts_to_be_inserted) ;
303334 }
304335 } ,
305336 ast:: Macro ( it) => {
306337 if let Some ( nod) = ctx. sema. to_def( & it) {
307- self . expand_and_group_usages_file_wise( ctx, Definition :: Macro ( nod) , & mut refs, & mut use_stmts_to_be_inserted) ;
338+ self . expand_and_group_usages_file_wise( ctx, replace_range , Definition :: Macro ( nod) , & mut refs, & mut use_stmts_to_be_inserted) ;
308339 }
309340 } ,
310341 _ => ( ) ,
@@ -318,6 +349,7 @@ impl Module {
318349 fn expand_and_group_usages_file_wise (
319350 & self ,
320351 ctx : & AssistContext < ' _ > ,
352+ replace_range : TextRange ,
321353 node_def : Definition ,
322354 refs_in_files : & mut FxHashMap < FileId , Vec < ( TextRange , String ) > > ,
323355 use_stmts_to_be_inserted : & mut FxHashMap < TextSize , ast:: Use > ,
@@ -327,7 +359,7 @@ impl Module {
327359 syntax:: NodeOrToken :: Node ( node) => node,
328360 syntax:: NodeOrToken :: Token ( tok) => tok. parent ( ) . unwrap ( ) , // won't panic
329361 } ;
330- let out_of_sel = |node : & SyntaxNode | !self . text_range . contains_range ( node. text_range ( ) ) ;
362+ let out_of_sel = |node : & SyntaxNode | !replace_range . contains_range ( node. text_range ( ) ) ;
331363 let mut use_stmts_set = FxHashSet :: default ( ) ;
332364
333365 for ( file_id, refs) in node_def. usages ( & ctx. sema ) . all ( ) {
@@ -527,7 +559,8 @@ impl Module {
527559 // mod -> ust_stmt transversal
528560 // true | false -> super import insertion
529561 // true | true -> super import insertion
530- self . make_use_stmt_of_node_with_super ( use_node) ;
562+ let super_use_node = make_use_stmt_of_node_with_super ( use_node) ;
563+ self . use_items . insert ( 0 , super_use_node) ;
531564 }
532565 None => { }
533566 }
@@ -556,7 +589,8 @@ impl Module {
556589
557590 use_tree_paths = Some ( use_tree_str) ;
558591 } else if def_in_mod && def_out_sel {
559- self . make_use_stmt_of_node_with_super ( use_node) ;
592+ let super_use_node = make_use_stmt_of_node_with_super ( use_node) ;
593+ self . use_items . insert ( 0 , super_use_node) ;
560594 }
561595 }
562596
@@ -596,20 +630,6 @@ impl Module {
596630 import_path_to_be_removed
597631 }
598632
599- fn make_use_stmt_of_node_with_super ( & mut self , node_syntax : & SyntaxNode ) -> ast:: Item {
600- let super_path = make:: ext:: ident_path ( "super" ) ;
601- let node_path = make:: ext:: ident_path ( & node_syntax. to_string ( ) ) ;
602- let use_ = make:: use_ (
603- None ,
604- None ,
605- make:: use_tree ( make:: join_paths ( vec ! [ super_path, node_path] ) , None , None , false ) ,
606- ) ;
607-
608- let item = ast:: Item :: from ( use_) ;
609- self . use_items . insert ( 0 , item. clone ( ) ) ;
610- item
611- }
612-
613633 fn process_use_stmt_for_import_resolve (
614634 & self ,
615635 use_stmt : Option < ast:: Use > ,
@@ -1424,10 +1444,10 @@ $0fn foo(x: B) {}$0
14241444 struct B {}
14251445
14261446mod modname {
1427- use super::B;
1428-
14291447 use super::A;
14301448
1449+ use super::B;
1450+
14311451 impl A {
14321452 pub(crate) fn foo(x: B) {}
14331453 }
@@ -1739,4 +1759,27 @@ fn main() {
17391759"# ,
17401760 ) ;
17411761 }
1762+
1763+ #[ test]
1764+ fn test_miss_select_item ( ) {
1765+ check_assist (
1766+ extract_module,
1767+ r#"
1768+ mod foo {
1769+ mod $0bar {
1770+ fn foo(){}$0
1771+ }
1772+ }
1773+ "# ,
1774+ r#"
1775+ mod foo {
1776+ mod modname {
1777+ pub(crate) mod bar {
1778+ fn foo(){}
1779+ }
1780+ }
1781+ }
1782+ "# ,
1783+ )
1784+ }
17421785}
0 commit comments