Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/extract_module.rs')
-rw-r--r--crates/ide-assists/src/handlers/extract_module.rs177
1 files changed, 107 insertions, 70 deletions
diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs
index 40eaed0080..40c54e9882 100644
--- a/crates/ide-assists/src/handlers/extract_module.rs
+++ b/crates/ide-assists/src/handlers/extract_module.rs
@@ -17,9 +17,10 @@ use syntax::{
ast::{
self, HasVisibility,
edit::{AstNodeEdit, IndentLevel},
- make,
+ syntax_factory::SyntaxFactory,
},
- match_ast, ted,
+ match_ast,
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{AssistContext, Assists};
@@ -121,15 +122,18 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ->
let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) =
module.get_usages_and_record_fields(ctx, module_text_range);
+ let make = SyntaxFactory::without_mappings();
+
builder.edit_file(ctx.vfs_file_id());
use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| {
builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}"));
});
- let import_items = module.resolve_imports(curr_parent_module, ctx);
- module.change_visibility(record_fields);
+ let import_items = module.resolve_imports(curr_parent_module, ctx, &make);
+ module.change_visibility(record_fields, &make);
- let module_def = generate_module_def(&impl_parent, &module).indent(old_item_indent);
+ let module_def =
+ generate_module_def(&impl_parent, &module, &make).indent(old_item_indent);
let mut usages_to_be_processed_for_cur_file = vec![];
for (file_id, usages) in usages_to_be_processed {
@@ -185,6 +189,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ->
fn generate_module_def(
parent_impl: &Option<ast::Impl>,
Module { name, body_items, use_items }: &Module,
+ make: &SyntaxFactory,
) -> ast::Module {
let items: Vec<_> = if let Some(impl_) = parent_impl.as_ref()
&& let Some(self_ty) = impl_.self_ty()
@@ -195,11 +200,15 @@ fn generate_module_def(
.filter_map(ast::AssocItem::cast)
.map(|it| it.indent(IndentLevel(1)))
.collect_vec();
- let assoc_item_list = make::assoc_item_list(Some(assoc_items)).clone_for_update();
- let impl_ = impl_.reset_indent();
- ted::replace(impl_.get_or_create_assoc_item_list().syntax(), assoc_item_list.syntax());
+ let impl_reset = impl_.reset_indent();
+ let (editor, impl_root) = SyntaxEditor::with_ast_node(&impl_reset);
+ let assoc_item_list = editor.make().assoc_item_list(assoc_items);
+ if let Some(existing_list) = impl_root.assoc_item_list() {
+ editor.replace(existing_list.syntax(), assoc_item_list.syntax());
+ }
+ let impl_ = ast::Impl::cast(editor.finish().new_root().clone()).unwrap();
// Add the import for enum/struct corresponding to given impl block
- let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax());
+ let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax(), make);
once(use_impl)
.chain(use_items.iter().cloned())
.chain(once(ast::Item::Impl(impl_)))
@@ -209,19 +218,18 @@ fn generate_module_def(
};
let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec();
- let module_body = make::item_list(Some(items));
-
- let module_name = make::name(name);
- make::mod_(module_name, Some(module_body))
+ let module_body = make.item_list(items);
+ let module_name = make.name(name);
+ make.mod_(module_name, Some(module_body))
}
-fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item {
- let super_path = make::ext::ident_path("super");
- let node_path = make::ext::ident_path(&node_syntax.to_string());
- let use_ = make::use_(
+fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode, make: &SyntaxFactory) -> ast::Item {
+ let super_path = make.ident_path("super");
+ let node_path = make.path_from_text(&node_syntax.to_string());
+ let use_ = make.use_(
+ [],
None,
- None,
- make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
+ make.use_tree(make.path_concat(super_path, node_path), None, None, false),
);
ast::Item::from(use_)
@@ -385,18 +393,30 @@ impl Module {
if use_.syntax().parent().is_some_and(|parent| parent == covering_node)
&& use_stmts_set.insert(use_.syntax().text_range().start())
{
- let use_ = use_stmts_to_be_inserted
- .entry(use_.syntax().text_range().start())
- .or_insert_with(|| use_.clone_subtree().clone_for_update());
- for seg in use_
- .syntax()
- .descendants()
- .filter_map(ast::NameRef::cast)
- .filter(|seg| seg.syntax().to_string() == name_ref.to_string())
- {
- let new_ref = make::path_from_text(&format!("{mod_name}::{seg}"))
- .clone_for_update();
- ted::replace(seg.syntax().parent()?, new_ref.syntax());
+ let key = use_.syntax().text_range().start();
+ let entry =
+ use_stmts_to_be_inserted.entry(key).or_insert_with(|| use_.clone());
+ let (editor, edit_root) = SyntaxEditor::with_ast_node(&*entry);
+ let replacements: Vec<_> = {
+ let make = editor.make();
+ edit_root
+ .syntax()
+ .descendants()
+ .filter_map(ast::NameRef::cast)
+ .filter(|seg| seg.syntax().to_string() == name_ref.to_string())
+ .filter_map(|seg| {
+ Some((
+ seg.syntax().parent()?,
+ make.path_from_text(&format!("{mod_name}::{seg}")),
+ ))
+ })
+ .collect()
+ };
+ if !replacements.is_empty() {
+ for (parent, new_ref) in &replacements {
+ editor.replace(parent, new_ref.syntax());
+ }
+ *entry = ast::Use::cast(editor.finish().new_root().clone()).unwrap();
}
}
}
@@ -407,18 +427,23 @@ impl Module {
}
}
- fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
+ fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>, make: &SyntaxFactory) {
+ for item in &mut self.body_items {
+ let (_, root) = SyntaxEditor::with_ast_node(&item.reset_indent());
+ *item = root;
+ }
+
let (mut replacements, record_field_parents, impls) =
- get_replacements_for_visibility_change(&mut self.body_items, false);
+ get_replacements_for_visibility_change(&self.body_items);
- let mut impl_items = impls
+ let impl_items = impls
.into_iter()
.flat_map(|impl_| impl_.syntax().descendants())
.filter_map(ast::Item::cast)
.collect_vec();
let (mut impl_item_replacements, _, _) =
- get_replacements_for_visibility_change(&mut impl_items, true);
+ get_replacements_for_visibility_change(&impl_items);
replacements.append(&mut impl_item_replacements);
@@ -432,17 +457,39 @@ impl Module {
}
}
- for (vis, syntax) in replacements {
- let item = syntax.children_with_tokens().find(|node_or_token| {
- match node_or_token.kind() {
- // We're skipping comments, doc comments, and attribute macros that may precede the keyword
- // that the visibility should be placed before.
- SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE => false,
- _ => true,
- }
- });
+ for body_item in &mut self.body_items {
+ let insert_targets: Vec<_> = replacements
+ .iter()
+ .filter(|(vis, syntax)| {
+ vis.is_none()
+ && (syntax == body_item.syntax()
+ || syntax.ancestors().any(|a| &a == body_item.syntax()))
+ })
+ .filter_map(|(_, syntax)| {
+ syntax.children_with_tokens().find(|nt| {
+ !matches!(
+ nt.kind(),
+ SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE
+ )
+ })
+ })
+ .collect();
- add_change_vis(vis, item);
+ if insert_targets.is_empty() {
+ continue;
+ }
+
+ let (editor, _) = SyntaxEditor::new(body_item.syntax().clone());
+ for target in insert_targets {
+ editor.insert_all(
+ Position::before(target),
+ vec![
+ make.visibility_pub_crate().syntax().clone().into(),
+ make.whitespace(" ").into(),
+ ],
+ );
+ }
+ *body_item = ast::Item::cast(editor.finish().new_root().clone()).unwrap();
}
}
@@ -450,6 +497,7 @@ impl Module {
&mut self,
module: Option<ast::Module>,
ctx: &AssistContext<'_, '_>,
+ make: &SyntaxFactory,
) -> Vec<TextRange> {
let mut imports_to_remove = vec![];
let mut node_set = FxHashSet::default();
@@ -476,7 +524,8 @@ impl Module {
})
.for_each(|(node, def)| {
if node_set.insert(node.to_string())
- && let Some(import) = self.process_def_in_sel(def, &node, &module, ctx)
+ && let Some(import) =
+ self.process_def_in_sel(def, &node, &module, ctx, make)
{
check_intersection_and_push(&mut imports_to_remove, import);
}
@@ -492,6 +541,7 @@ impl Module {
use_node: &SyntaxNode,
curr_parent_module: &Option<ast::Module>,
ctx: &AssistContext<'_, '_>,
+ make: &SyntaxFactory,
) -> Option<TextRange> {
//We only need to find in the current file
let selection_range = ctx.selection_trimmed();
@@ -567,7 +617,7 @@ impl Module {
// mod -> ust_stmt transversal
// true | false -> super import insertion
// true | true -> super import insertion
- let super_use_node = make_use_stmt_of_node_with_super(use_node);
+ let super_use_node = make_use_stmt_of_node_with_super(use_node, make);
self.use_items.insert(0, super_use_node);
}
None => {}
@@ -590,14 +640,14 @@ impl Module {
if !first_path_in_use_tree_str.contains("super")
&& !first_path_in_use_tree_str.contains("crate")
{
- let super_path = make::ext::ident_path("super");
+ let super_path = make.ident_path("super");
use_tree_str.push(super_path);
}
}
use_tree_paths = Some(use_tree_str);
} else if def_in_mod && def_out_sel {
- let super_use_node = make_use_stmt_of_node_with_super(use_node);
+ let super_use_node = make_use_stmt_of_node_with_super(use_node, make);
self.use_items.insert(0, super_use_node);
}
}
@@ -609,7 +659,7 @@ impl Module {
&& let Some(first_path_in_use_tree) = use_tree_paths.first()
&& first_path_in_use_tree.to_string().contains("super")
{
- use_tree_paths.insert(0, make::ext::ident_path("super"));
+ use_tree_paths.insert(0, make.ident_path("super"));
}
let is_item = matches!(
@@ -624,12 +674,12 @@ impl Module {
| Definition::TypeAlias(_)
);
- if (def_out_sel || !is_item) && use_stmt_not_in_sel {
- let use_ = make::use_(
- None,
- None,
- make::use_tree(make::join_paths(use_tree_paths), None, None, false),
- );
+ if (def_out_sel || !is_item)
+ && use_stmt_not_in_sel
+ && let Some(joined) =
+ use_tree_paths.into_iter().reduce(|acc, p| make.path_concat(acc, p))
+ {
+ let use_ = make.use_([], None, make.use_tree(joined, None, None, false));
self.use_items.insert(0, ast::Item::from(use_));
}
}
@@ -741,8 +791,7 @@ fn check_def_in_mod_and_out_sel(
}
fn get_replacements_for_visibility_change(
- items: &mut [ast::Item],
- is_clone_for_updated: bool,
+ items: &[ast::Item],
) -> (
Vec<(Option<ast::Visibility>, SyntaxNode)>,
Vec<(Option<ast::Visibility>, SyntaxNode)>,
@@ -753,9 +802,6 @@ fn get_replacements_for_visibility_change(
let mut impls = Vec::new();
for item in items {
- if !is_clone_for_updated {
- *item = item.clone_for_update();
- }
//Use stmts are ignored
macro_rules! push_to_replacement {
($it:ident) => {
@@ -812,15 +858,6 @@ fn get_use_tree_paths_from_path(
Some(use_tree_str)
}
-fn add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>) {
- if vis.is_none()
- && let Some(node_or_token) = node_or_token_opt
- {
- let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
- ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax());
- }
-}
-
fn indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange> {
node.siblings_with_tokens(syntax::Direction::Prev)
.find(|x| x.kind() == WHITESPACE)