Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 5b2809f

Browse files
committed
fix: simplification on extract_module
1 parent 6248b45 commit 5b2809f

File tree

1 file changed

+96
-115
lines changed

1 file changed

+96
-115
lines changed

crates/ide-assists/src/handlers/extract_module.rs

Lines changed: 96 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::iter;
22

3+
use either::Either;
34
use hir::{HasSource, HirFileIdExt, ModuleSource};
45
use ide_db::{
56
assists::{AssistId, AssistKind},
@@ -10,15 +11,14 @@ use ide_db::{
1011
};
1112
use itertools::Itertools;
1213
use smallvec::SmallVec;
13-
use stdx::format_to;
1414
use syntax::{
1515
algo::find_node_at_range,
1616
ast::{
1717
self,
1818
edit::{AstNodeEdit, IndentLevel},
1919
make, HasVisibility,
2020
},
21-
match_ast, ted, AstNode, SourceFile,
21+
match_ast, ted, AstNode,
2222
SyntaxKind::{self, WHITESPACE},
2323
SyntaxNode, TextRange,
2424
};
@@ -114,63 +114,23 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
114114
let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
115115
module.change_visibility(record_fields);
116116

117-
let mut body_items: Vec<String> = Vec::new();
118-
let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
117+
let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent);
119118

120-
let new_item_indent = if impl_parent.is_some() {
121-
old_item_indent + 2
122-
} else {
123-
items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat();
124-
old_item_indent + 1
125-
};
126-
127-
for item in items_to_be_processed {
128-
let item = item.indent(IndentLevel(1));
129-
let mut indented_item = String::new();
130-
format_to!(indented_item, "{new_item_indent}{item}");
131-
body_items.push(indented_item);
132-
}
133-
134-
let mut body = body_items.join("\n\n");
135-
136-
if let Some(impl_) = &impl_parent {
137-
if let Some(self_ty) = impl_.self_ty() {
138-
let impl_indent = old_item_indent + 1;
139-
let mut impl_body_def = String::new();
140-
format_to!(
141-
impl_body_def,
142-
"{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}",
143-
);
144-
body = impl_body_def;
145-
146-
// Add the import for enum/struct corresponding to given impl block
147-
module.make_use_stmt_of_node_with_super(self_ty.syntax());
148-
for item in module.use_items {
149-
let item_indent = old_item_indent + 1;
150-
body = format!("{item_indent}{item}\n\n{body}");
151-
}
152-
}
153-
}
154-
155-
let mut module_def = String::new();
156-
let module_name = module.name;
157-
format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}");
158-
159-
let mut usages_to_be_updated_for_curr_file = vec![];
160-
for usages_to_be_updated_for_file in usages_to_be_processed {
161-
if usages_to_be_updated_for_file.0 == ctx.file_id() {
162-
usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
119+
let mut usages_to_be_processed_for_cur_file = vec![];
120+
for (file_id, usages) in usages_to_be_processed {
121+
if file_id == ctx.file_id() {
122+
usages_to_be_processed_for_cur_file = usages;
163123
continue;
164124
}
165-
builder.edit_file(usages_to_be_updated_for_file.0);
166-
for usage_to_be_processed in usages_to_be_updated_for_file.1 {
167-
builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
125+
builder.edit_file(file_id);
126+
for (text_range, usage) in usages {
127+
builder.replace(text_range, usage)
168128
}
169129
}
170130

171131
builder.edit_file(ctx.file_id());
172-
for usage_to_be_processed in usages_to_be_updated_for_curr_file {
173-
builder.replace(usage_to_be_processed.0, usage_to_be_processed.1)
132+
for (text_range, usage) in usages_to_be_processed_for_cur_file {
133+
builder.replace(text_range, usage);
174134
}
175135

176136
if let Some(impl_) = impl_parent {
@@ -205,6 +165,37 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
205165
)
206166
}
207167

168+
fn generate_module_def(
169+
parent_impl: &Option<ast::Impl>,
170+
module: &mut Module,
171+
old_indent: IndentLevel,
172+
) -> String {
173+
let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() {
174+
(Either::Left(module.body_items.iter()), old_indent + 2)
175+
} else {
176+
(Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1)
177+
};
178+
179+
let mut body = items_to_be_processed
180+
.map(|item| item.indent(IndentLevel(1)))
181+
.map(|item| format!("{new_item_indent}{item}"))
182+
.join("\n\n");
183+
184+
if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) {
185+
let impl_indent = old_indent + 1;
186+
body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}");
187+
188+
// Add the import for enum/struct corresponding to given impl block
189+
module.make_use_stmt_of_node_with_super(self_ty.syntax());
190+
for item in module.use_items.iter() {
191+
body = format!("{impl_indent}{item}\n\n{body}");
192+
}
193+
}
194+
195+
let module_name = module.name;
196+
format!("mod {module_name} {{\n{body}\n{old_indent}}}")
197+
}
198+
208199
#[derive(Debug)]
209200
struct Module {
210201
text_range: TextRange,
@@ -240,6 +231,7 @@ impl Module {
240231
//Here impl is not included as each item inside impl will be tied to the parent of
241232
//implementing block(a struct, enum, etc), if the parent is in selected module, it will
242233
//get updated by ADT section given below or if it is not, then we dont need to do any operation
234+
243235
for item in &self.body_items {
244236
match_ast! {
245237
match (item.syntax()) {
@@ -320,48 +312,38 @@ impl Module {
320312
node_def: Definition,
321313
refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>,
322314
) {
323-
for (file_id, references) in node_def.usages(&ctx.sema).all() {
315+
let mod_name = self.name;
316+
let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range());
317+
for (file_id, refs) in node_def.usages(&ctx.sema).all() {
324318
let source_file = ctx.sema.parse(file_id);
325-
let usages_in_file = references
326-
.into_iter()
327-
.filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage));
328-
refs_in_files.entry(file_id).or_default().extend(usages_in_file);
329-
}
330-
}
331-
332-
fn get_usage_to_be_processed(
333-
&self,
334-
source_file: &SourceFile,
335-
FileReference { range, name, .. }: FileReference,
336-
) -> Option<(TextRange, String)> {
337-
let path: ast::Path = find_node_at_range(source_file.syntax(), range)?;
338-
339-
for desc in path.syntax().descendants() {
340-
if desc.to_string() == name.syntax().to_string()
341-
&& !self.text_range.contains_range(desc.text_range())
342-
{
343-
if let Some(name_ref) = ast::NameRef::cast(desc) {
344-
let mod_name = self.name;
345-
return Some((
346-
name_ref.syntax().text_range(),
347-
format!("{mod_name}::{name_ref}"),
348-
));
319+
let usages = refs.into_iter().filter_map(|FileReference { range, name, .. }| {
320+
let path: ast::Path = find_node_at_range(source_file.syntax(), range)?;
321+
let name = name.syntax().to_string();
322+
323+
for desc in path.syntax().descendants() {
324+
if desc.to_string() == name && out_of_sel(&desc) {
325+
if let Some(name_ref) = ast::NameRef::cast(desc) {
326+
let new_ref = format!("{mod_name}::{name_ref}");
327+
return Some((name_ref.syntax().text_range(), new_ref));
328+
}
329+
}
349330
}
350-
}
351-
}
352331

353-
None
332+
None
333+
});
334+
refs_in_files.entry(file_id).or_default().extend(usages);
335+
}
354336
}
355337

356338
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
357339
let (mut replacements, record_field_parents, impls) =
358340
get_replacements_for_visibility_change(&mut self.body_items, false);
359341

360-
let mut impl_items: Vec<ast::Item> = impls
342+
let mut impl_items = impls
361343
.into_iter()
362344
.flat_map(|impl_| impl_.syntax().descendants())
363345
.filter_map(ast::Item::cast)
364-
.collect();
346+
.collect_vec();
365347

366348
let (mut impl_item_replacements, _, _) =
367349
get_replacements_for_visibility_change(&mut impl_items, true);
@@ -444,19 +426,19 @@ impl Module {
444426
let file = ctx.sema.parse(file_id);
445427

446428
// track uses which does not exists in `Use`
447-
let mut exists_inside_sel = false;
448-
let mut exists_outside_sel = false;
429+
let mut uses_exist_in_sel = false;
430+
let mut uses_exist_out_sel = false;
449431
'outside: for (_, refs) in usage_res.iter() {
450432
for x in refs
451433
.iter()
452434
.filter(|x| find_node_at_range::<ast::Use>(file.syntax(), x.range).is_none())
453435
.filter_map(|x| find_node_at_range::<ast::Path>(file.syntax(), x.range))
454436
{
455437
let in_selectin = selection_range.contains_range(x.syntax().text_range());
456-
exists_inside_sel |= in_selectin;
457-
exists_outside_sel |= !in_selectin;
438+
uses_exist_in_sel |= in_selectin;
439+
uses_exist_out_sel |= !in_selectin;
458440

459-
if exists_inside_sel && exists_outside_sel {
441+
if uses_exist_in_sel && uses_exist_out_sel {
460442
break 'outside;
461443
}
462444
}
@@ -475,7 +457,7 @@ impl Module {
475457
!selection_range.contains_range(use_stmt.syntax().text_range())
476458
});
477459

478-
let mut use_tree_str_opt: Option<Vec<ast::Path>> = None;
460+
let mut use_tree_paths: Option<Vec<ast::Path>> = None;
479461
//Exists inside and outside selection
480462
// - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
481463
// module
@@ -489,13 +471,13 @@ impl Module {
489471
//get the use_tree_str, reconstruct the use stmt in new module
490472

491473
let mut import_path_to_be_removed: Option<TextRange> = None;
492-
if exists_inside_sel && exists_outside_sel {
474+
if uses_exist_in_sel && uses_exist_out_sel {
493475
//Changes to be made only inside new module
494476

495477
//If use_stmt exists, find the use_tree_str, reconstruct it inside new module
496478
//If not, insert a use stmt with super and the given nameref
497479
match self.process_use_stmt_for_import_resolve(use_stmt, use_node) {
498-
Some((use_tree_str, _)) => use_tree_str_opt = Some(use_tree_str),
480+
Some((use_tree_str, _)) => use_tree_paths = Some(use_tree_str),
499481
None if def_in_mod && def_out_sel => {
500482
//Considered only after use_stmt is not present
501483
//def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel =
@@ -509,7 +491,7 @@ impl Module {
509491
}
510492
None => {}
511493
}
512-
} else if exists_inside_sel && !exists_outside_sel {
494+
} else if uses_exist_in_sel && !uses_exist_out_sel {
513495
//Changes to be made inside new module, and remove import from outside
514496

515497
if let Some((mut use_tree_str, text_range_opt)) =
@@ -531,43 +513,42 @@ impl Module {
531513
}
532514
}
533515

534-
use_tree_str_opt = Some(use_tree_str);
516+
use_tree_paths = Some(use_tree_str);
535517
} else if def_in_mod && def_out_sel {
536518
self.make_use_stmt_of_node_with_super(use_node);
537519
}
538520
}
539521

540-
if let Some(use_tree_str) = use_tree_str_opt {
541-
let mut use_tree_str = use_tree_str;
542-
use_tree_str.reverse();
522+
if let Some(mut use_tree_paths) = use_tree_paths {
523+
use_tree_paths.reverse();
543524

544-
if exists_outside_sel || !exists_inside_sel || !def_in_mod || !def_out_sel {
545-
if let Some(first_path_in_use_tree) = use_tree_str.first() {
525+
if uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel {
526+
if let Some(first_path_in_use_tree) = use_tree_paths.first() {
546527
if first_path_in_use_tree.to_string().contains("super") {
547-
use_tree_str.insert(0, make::ext::ident_path("super"));
528+
use_tree_paths.insert(0, make::ext::ident_path("super"));
548529
}
549530
}
550531
}
551532

552-
let use_ =
553-
make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false));
554-
let item = ast::Item::from(use_);
555-
556-
let is_item = match def {
557-
Definition::Macro(_) => true,
558-
Definition::Module(_) => true,
559-
Definition::Function(_) => true,
560-
Definition::Adt(_) => true,
561-
Definition::Const(_) => true,
562-
Definition::Static(_) => true,
563-
Definition::Trait(_) => true,
564-
Definition::TraitAlias(_) => true,
565-
Definition::TypeAlias(_) => true,
566-
_ => false,
567-
};
533+
let is_item = matches!(
534+
def,
535+
Definition::Macro(_)
536+
| Definition::Module(_)
537+
| Definition::Function(_)
538+
| Definition::Adt(_)
539+
| Definition::Const(_)
540+
| Definition::Static(_)
541+
| Definition::Trait(_)
542+
| Definition::TraitAlias(_)
543+
| Definition::TypeAlias(_)
544+
);
568545

569546
if (def_out_sel || !is_item) && use_stmt_not_in_sel {
570-
self.use_items.insert(0, item.clone());
547+
let use_ = make::use_(
548+
None,
549+
make::use_tree(make::join_paths(use_tree_paths), None, None, false),
550+
);
551+
self.use_items.insert(0, ast::Item::from(use_));
571552
}
572553
}
573554

0 commit comments

Comments
 (0)