fix: simplification on extract_module

This commit is contained in:
roife 2024-03-15 02:47:41 +08:00
parent 6248b45340
commit 5b2809f329

View File

@ -1,5 +1,6 @@
use std::iter; use std::iter;
use either::Either;
use hir::{HasSource, HirFileIdExt, ModuleSource}; use hir::{HasSource, HirFileIdExt, ModuleSource};
use ide_db::{ use ide_db::{
assists::{AssistId, AssistKind}, assists::{AssistId, AssistKind},
@ -10,7 +11,6 @@
}; };
use itertools::Itertools; use itertools::Itertools;
use smallvec::SmallVec; use smallvec::SmallVec;
use stdx::format_to;
use syntax::{ use syntax::{
algo::find_node_at_range, algo::find_node_at_range,
ast::{ ast::{
@ -18,7 +18,7 @@
edit::{AstNodeEdit, IndentLevel}, edit::{AstNodeEdit, IndentLevel},
make, HasVisibility, make, HasVisibility,
}, },
match_ast, ted, AstNode, SourceFile, match_ast, ted, AstNode,
SyntaxKind::{self, WHITESPACE}, SyntaxKind::{self, WHITESPACE},
SyntaxNode, TextRange, SyntaxNode, TextRange,
}; };
@ -114,63 +114,23 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx); let import_paths_to_be_removed = module.resolve_imports(curr_parent_module, ctx);
module.change_visibility(record_fields); module.change_visibility(record_fields);
let mut body_items: Vec<String> = Vec::new(); let module_def = generate_module_def(&impl_parent, &mut module, old_item_indent);
let mut items_to_be_processed: Vec<ast::Item> = module.body_items.clone();
let new_item_indent = if impl_parent.is_some() { let mut usages_to_be_processed_for_cur_file = vec![];
old_item_indent + 2 for (file_id, usages) in usages_to_be_processed {
} else { if file_id == ctx.file_id() {
items_to_be_processed = [module.use_items.clone(), items_to_be_processed].concat(); usages_to_be_processed_for_cur_file = usages;
old_item_indent + 1
};
for item in items_to_be_processed {
let item = item.indent(IndentLevel(1));
let mut indented_item = String::new();
format_to!(indented_item, "{new_item_indent}{item}");
body_items.push(indented_item);
}
let mut body = body_items.join("\n\n");
if let Some(impl_) = &impl_parent {
if let Some(self_ty) = impl_.self_ty() {
let impl_indent = old_item_indent + 1;
let mut impl_body_def = String::new();
format_to!(
impl_body_def,
"{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}",
);
body = impl_body_def;
// Add the import for enum/struct corresponding to given impl block
module.make_use_stmt_of_node_with_super(self_ty.syntax());
for item in module.use_items {
let item_indent = old_item_indent + 1;
body = format!("{item_indent}{item}\n\n{body}");
}
}
}
let mut module_def = String::new();
let module_name = module.name;
format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}");
let mut usages_to_be_updated_for_curr_file = vec![];
for usages_to_be_updated_for_file in usages_to_be_processed {
if usages_to_be_updated_for_file.0 == ctx.file_id() {
usages_to_be_updated_for_curr_file = usages_to_be_updated_for_file.1;
continue; continue;
} }
builder.edit_file(usages_to_be_updated_for_file.0); builder.edit_file(file_id);
for usage_to_be_processed in usages_to_be_updated_for_file.1 { for (text_range, usage) in usages {
builder.replace(usage_to_be_processed.0, usage_to_be_processed.1) builder.replace(text_range, usage)
} }
} }
builder.edit_file(ctx.file_id()); builder.edit_file(ctx.file_id());
for usage_to_be_processed in usages_to_be_updated_for_curr_file { for (text_range, usage) in usages_to_be_processed_for_cur_file {
builder.replace(usage_to_be_processed.0, usage_to_be_processed.1) builder.replace(text_range, usage);
} }
if let Some(impl_) = impl_parent { if let Some(impl_) = impl_parent {
@ -205,6 +165,37 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
) )
} }
fn generate_module_def(
parent_impl: &Option<ast::Impl>,
module: &mut Module,
old_indent: IndentLevel,
) -> String {
let (items_to_be_processed, new_item_indent) = if parent_impl.is_some() {
(Either::Left(module.body_items.iter()), old_indent + 2)
} else {
(Either::Right(module.use_items.iter().chain(module.body_items.iter())), old_indent + 1)
};
let mut body = items_to_be_processed
.map(|item| item.indent(IndentLevel(1)))
.map(|item| format!("{new_item_indent}{item}"))
.join("\n\n");
if let Some(self_ty) = parent_impl.as_ref().and_then(|imp| imp.self_ty()) {
let impl_indent = old_indent + 1;
body = format!("{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}");
// Add the import for enum/struct corresponding to given impl block
module.make_use_stmt_of_node_with_super(self_ty.syntax());
for item in module.use_items.iter() {
body = format!("{impl_indent}{item}\n\n{body}");
}
}
let module_name = module.name;
format!("mod {module_name} {{\n{body}\n{old_indent}}}")
}
#[derive(Debug)] #[derive(Debug)]
struct Module { struct Module {
text_range: TextRange, text_range: TextRange,
@ -240,6 +231,7 @@ fn get_usages_and_record_fields(
//Here impl is not included as each item inside impl will be tied to the parent of //Here impl is not included as each item inside impl will be tied to the parent of
//implementing block(a struct, enum, etc), if the parent is in selected module, it will //implementing block(a struct, enum, etc), if the parent is in selected module, it will
//get updated by ADT section given below or if it is not, then we dont need to do any operation //get updated by ADT section given below or if it is not, then we dont need to do any operation
for item in &self.body_items { for item in &self.body_items {
match_ast! { match_ast! {
match (item.syntax()) { match (item.syntax()) {
@ -320,48 +312,38 @@ fn expand_and_group_usages_file_wise(
node_def: Definition, node_def: Definition,
refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>, refs_in_files: &mut FxHashMap<FileId, Vec<(TextRange, String)>>,
) { ) {
for (file_id, references) in node_def.usages(&ctx.sema).all() { let mod_name = self.name;
let out_of_sel = |node: &SyntaxNode| !self.text_range.contains_range(node.text_range());
for (file_id, refs) in node_def.usages(&ctx.sema).all() {
let source_file = ctx.sema.parse(file_id); let source_file = ctx.sema.parse(file_id);
let usages_in_file = references let usages = refs.into_iter().filter_map(|FileReference { range, name, .. }| {
.into_iter()
.filter_map(|usage| self.get_usage_to_be_processed(&source_file, usage));
refs_in_files.entry(file_id).or_default().extend(usages_in_file);
}
}
fn get_usage_to_be_processed(
&self,
source_file: &SourceFile,
FileReference { range, name, .. }: FileReference,
) -> Option<(TextRange, String)> {
let path: ast::Path = find_node_at_range(source_file.syntax(), range)?; let path: ast::Path = find_node_at_range(source_file.syntax(), range)?;
let name = name.syntax().to_string();
for desc in path.syntax().descendants() { for desc in path.syntax().descendants() {
if desc.to_string() == name.syntax().to_string() if desc.to_string() == name && out_of_sel(&desc) {
&& !self.text_range.contains_range(desc.text_range())
{
if let Some(name_ref) = ast::NameRef::cast(desc) { if let Some(name_ref) = ast::NameRef::cast(desc) {
let mod_name = self.name; let new_ref = format!("{mod_name}::{name_ref}");
return Some(( return Some((name_ref.syntax().text_range(), new_ref));
name_ref.syntax().text_range(),
format!("{mod_name}::{name_ref}"),
));
} }
} }
} }
None None
});
refs_in_files.entry(file_id).or_default().extend(usages);
}
} }
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) { fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
let (mut replacements, record_field_parents, impls) = let (mut replacements, record_field_parents, impls) =
get_replacements_for_visibility_change(&mut self.body_items, false); get_replacements_for_visibility_change(&mut self.body_items, false);
let mut impl_items: Vec<ast::Item> = impls let mut impl_items = impls
.into_iter() .into_iter()
.flat_map(|impl_| impl_.syntax().descendants()) .flat_map(|impl_| impl_.syntax().descendants())
.filter_map(ast::Item::cast) .filter_map(ast::Item::cast)
.collect(); .collect_vec();
let (mut impl_item_replacements, _, _) = let (mut impl_item_replacements, _, _) =
get_replacements_for_visibility_change(&mut impl_items, true); get_replacements_for_visibility_change(&mut impl_items, true);
@ -444,8 +426,8 @@ fn process_def_in_sel(
let file = ctx.sema.parse(file_id); let file = ctx.sema.parse(file_id);
// track uses which does not exists in `Use` // track uses which does not exists in `Use`
let mut exists_inside_sel = false; let mut uses_exist_in_sel = false;
let mut exists_outside_sel = false; let mut uses_exist_out_sel = false;
'outside: for (_, refs) in usage_res.iter() { 'outside: for (_, refs) in usage_res.iter() {
for x in refs for x in refs
.iter() .iter()
@ -453,10 +435,10 @@ fn process_def_in_sel(
.filter_map(|x| find_node_at_range::<ast::Path>(file.syntax(), x.range)) .filter_map(|x| find_node_at_range::<ast::Path>(file.syntax(), x.range))
{ {
let in_selectin = selection_range.contains_range(x.syntax().text_range()); let in_selectin = selection_range.contains_range(x.syntax().text_range());
exists_inside_sel |= in_selectin; uses_exist_in_sel |= in_selectin;
exists_outside_sel |= !in_selectin; uses_exist_out_sel |= !in_selectin;
if exists_inside_sel && exists_outside_sel { if uses_exist_in_sel && uses_exist_out_sel {
break 'outside; break 'outside;
} }
} }
@ -475,7 +457,7 @@ fn process_def_in_sel(
!selection_range.contains_range(use_stmt.syntax().text_range()) !selection_range.contains_range(use_stmt.syntax().text_range())
}); });
let mut use_tree_str_opt: Option<Vec<ast::Path>> = None; let mut use_tree_paths: Option<Vec<ast::Path>> = None;
//Exists inside and outside selection //Exists inside and outside selection
// - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new // - Use stmt for item is present -> get the use_tree_str and reconstruct the path in new
// module // module
@ -489,13 +471,13 @@ fn process_def_in_sel(
//get the use_tree_str, reconstruct the use stmt in new module //get the use_tree_str, reconstruct the use stmt in new module
let mut import_path_to_be_removed: Option<TextRange> = None; let mut import_path_to_be_removed: Option<TextRange> = None;
if exists_inside_sel && exists_outside_sel { if uses_exist_in_sel && uses_exist_out_sel {
//Changes to be made only inside new module //Changes to be made only inside new module
//If use_stmt exists, find the use_tree_str, reconstruct it inside new module //If use_stmt exists, find the use_tree_str, reconstruct it inside new module
//If not, insert a use stmt with super and the given nameref //If not, insert a use stmt with super and the given nameref
match self.process_use_stmt_for_import_resolve(use_stmt, use_node) { match self.process_use_stmt_for_import_resolve(use_stmt, use_node) {
Some((use_tree_str, _)) => use_tree_str_opt = Some(use_tree_str), Some((use_tree_str, _)) => use_tree_paths = Some(use_tree_str),
None if def_in_mod && def_out_sel => { None if def_in_mod && def_out_sel => {
//Considered only after use_stmt is not present //Considered only after use_stmt is not present
//def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel = //def_in_mod && def_out_sel | exists_outside_sel(exists_inside_sel =
@ -509,7 +491,7 @@ fn process_def_in_sel(
} }
None => {} None => {}
} }
} else if exists_inside_sel && !exists_outside_sel { } else if uses_exist_in_sel && !uses_exist_out_sel {
//Changes to be made inside new module, and remove import from outside //Changes to be made inside new module, and remove import from outside
if let Some((mut use_tree_str, text_range_opt)) = if let Some((mut use_tree_str, text_range_opt)) =
@ -531,43 +513,42 @@ fn process_def_in_sel(
} }
} }
use_tree_str_opt = Some(use_tree_str); use_tree_paths = Some(use_tree_str);
} else if def_in_mod && def_out_sel { } else if def_in_mod && def_out_sel {
self.make_use_stmt_of_node_with_super(use_node); self.make_use_stmt_of_node_with_super(use_node);
} }
} }
if let Some(use_tree_str) = use_tree_str_opt { if let Some(mut use_tree_paths) = use_tree_paths {
let mut use_tree_str = use_tree_str; use_tree_paths.reverse();
use_tree_str.reverse();
if exists_outside_sel || !exists_inside_sel || !def_in_mod || !def_out_sel { if uses_exist_out_sel || !uses_exist_in_sel || !def_in_mod || !def_out_sel {
if let Some(first_path_in_use_tree) = use_tree_str.first() { if let Some(first_path_in_use_tree) = use_tree_paths.first() {
if first_path_in_use_tree.to_string().contains("super") { if first_path_in_use_tree.to_string().contains("super") {
use_tree_str.insert(0, make::ext::ident_path("super")); use_tree_paths.insert(0, make::ext::ident_path("super"));
} }
} }
} }
let use_ = let is_item = matches!(
make::use_(None, make::use_tree(make::join_paths(use_tree_str), None, None, false)); def,
let item = ast::Item::from(use_); Definition::Macro(_)
| Definition::Module(_)
let is_item = match def { | Definition::Function(_)
Definition::Macro(_) => true, | Definition::Adt(_)
Definition::Module(_) => true, | Definition::Const(_)
Definition::Function(_) => true, | Definition::Static(_)
Definition::Adt(_) => true, | Definition::Trait(_)
Definition::Const(_) => true, | Definition::TraitAlias(_)
Definition::Static(_) => true, | Definition::TypeAlias(_)
Definition::Trait(_) => true, );
Definition::TraitAlias(_) => true,
Definition::TypeAlias(_) => true,
_ => false,
};
if (def_out_sel || !is_item) && use_stmt_not_in_sel { if (def_out_sel || !is_item) && use_stmt_not_in_sel {
self.use_items.insert(0, item.clone()); let use_ = make::use_(
None,
make::use_tree(make::join_paths(use_tree_paths), None, None, false),
);
self.use_items.insert(0, ast::Item::from(use_));
} }
} }