Skip to content

Commit 563cdc6

Browse files
committed
internal: migrate extract_module assist to SyntaxEditor
1 parent 7d2e672 commit 563cdc6

2 files changed

Lines changed: 104 additions & 78 deletions

File tree

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

Lines changed: 104 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,18 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ->
122122
let (usages_to_be_processed, record_fields, use_stmts_to_be_inserted) =
123123
module.get_usages_and_record_fields(ctx, module_text_range);
124124

125+
let make = SyntaxFactory::without_mappings();
126+
125127
builder.edit_file(ctx.vfs_file_id());
126128
use_stmts_to_be_inserted.into_iter().for_each(|(_, use_stmt)| {
127129
builder.insert(ctx.selection_trimmed().end(), format!("\n{use_stmt}"));
128130
});
129131

130-
let import_items = module.resolve_imports(curr_parent_module, ctx);
131-
module.change_visibility(record_fields);
132+
let import_items = module.resolve_imports(curr_parent_module, ctx, &make);
133+
module.change_visibility(record_fields, &make);
132134

133-
let module_def = generate_module_def(&impl_parent, &module).indent(old_item_indent);
135+
let module_def =
136+
generate_module_def(&impl_parent, &module, &make).indent(old_item_indent);
134137

135138
let mut usages_to_be_processed_for_cur_file = vec![];
136139
for (file_id, usages) in usages_to_be_processed {
@@ -186,6 +189,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_, '_>) ->
186189
fn generate_module_def(
187190
parent_impl: &Option<ast::Impl>,
188191
Module { name, body_items, use_items }: &Module,
192+
make: &SyntaxFactory,
189193
) -> ast::Module {
190194
let items: Vec<_> = if let Some(impl_) = parent_impl.as_ref()
191195
&& let Some(self_ty) = impl_.self_ty()
@@ -196,11 +200,15 @@ fn generate_module_def(
196200
.filter_map(ast::AssocItem::cast)
197201
.map(|it| it.indent(IndentLevel(1)))
198202
.collect_vec();
199-
let assoc_item_list = make::assoc_item_list(Some(assoc_items)).clone_for_update();
200-
let impl_ = impl_.reset_indent();
201-
ted::replace(impl_.get_or_create_assoc_item_list().syntax(), assoc_item_list.syntax());
203+
let impl_reset = impl_.reset_indent();
204+
let (editor, impl_root) = SyntaxEditor::with_ast_node(&impl_reset);
205+
let assoc_item_list = editor.make().assoc_item_list(assoc_items);
206+
if let Some(existing_list) = impl_root.assoc_item_list() {
207+
editor.replace(existing_list.syntax(), assoc_item_list.syntax());
208+
}
209+
let impl_ = ast::Impl::cast(editor.finish().new_root().clone()).unwrap();
202210
// Add the import for enum/struct corresponding to given impl block
203-
let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax());
211+
let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax(), make);
204212
once(use_impl)
205213
.chain(use_items.iter().cloned())
206214
.chain(once(ast::Item::Impl(impl_)))
@@ -210,19 +218,18 @@ fn generate_module_def(
210218
};
211219

212220
let items = items.into_iter().map(|it| it.reset_indent().indent(IndentLevel(1))).collect_vec();
213-
let module_body = make::item_list(Some(items));
214-
215-
let module_name = make::name(name);
216-
make::mod_(module_name, Some(module_body))
221+
let module_body = make.item_list(items);
222+
let module_name = make.name(name);
223+
make.mod_(module_name, Some(module_body))
217224
}
218225

219-
fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode) -> ast::Item {
220-
let super_path = make::ext::ident_path("super");
221-
let node_path = make::ext::ident_path(&node_syntax.to_string());
222-
let use_ = make::use_(
226+
fn make_use_stmt_of_node_with_super(node_syntax: &SyntaxNode, make: &SyntaxFactory) -> ast::Item {
227+
let super_path = make.ident_path("super");
228+
let node_path = make.path_from_text(&node_syntax.to_string());
229+
let use_ = make.use_(
230+
[],
223231
None,
224-
None,
225-
make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
232+
make.use_tree(make.path_concat(super_path, node_path), None, None, false),
226233
);
227234

228235
ast::Item::from(use_)
@@ -386,18 +393,30 @@ impl Module {
386393
if use_.syntax().parent().is_some_and(|parent| parent == covering_node)
387394
&& use_stmts_set.insert(use_.syntax().text_range().start())
388395
{
389-
let use_ = use_stmts_to_be_inserted
390-
.entry(use_.syntax().text_range().start())
391-
.or_insert_with(|| use_.clone_subtree().clone_for_update());
392-
for seg in use_
393-
.syntax()
394-
.descendants()
395-
.filter_map(ast::NameRef::cast)
396-
.filter(|seg| seg.syntax().to_string() == name_ref.to_string())
397-
{
398-
let new_ref = make::path_from_text(&format!("{mod_name}::{seg}"))
399-
.clone_for_update();
400-
ted::replace(seg.syntax().parent()?, new_ref.syntax());
396+
let key = use_.syntax().text_range().start();
397+
let entry =
398+
use_stmts_to_be_inserted.entry(key).or_insert_with(|| use_.clone());
399+
let (editor, edit_root) = SyntaxEditor::with_ast_node(&*entry);
400+
let replacements: Vec<_> = {
401+
let make = editor.make();
402+
edit_root
403+
.syntax()
404+
.descendants()
405+
.filter_map(ast::NameRef::cast)
406+
.filter(|seg| seg.syntax().to_string() == name_ref.to_string())
407+
.filter_map(|seg| {
408+
Some((
409+
seg.syntax().parent()?,
410+
make.path_from_text(&format!("{mod_name}::{seg}")),
411+
))
412+
})
413+
.collect()
414+
};
415+
if !replacements.is_empty() {
416+
for (parent, new_ref) in &replacements {
417+
editor.replace(parent, new_ref.syntax());
418+
}
419+
*entry = ast::Use::cast(editor.finish().new_root().clone()).unwrap();
401420
}
402421
}
403422
}
@@ -408,18 +427,23 @@ impl Module {
408427
}
409428
}
410429

411-
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
430+
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>, make: &SyntaxFactory) {
431+
for item in &mut self.body_items {
432+
let (_, root) = SyntaxEditor::with_ast_node(&item.reset_indent());
433+
*item = root;
434+
}
435+
412436
let (mut replacements, record_field_parents, impls) =
413-
get_replacements_for_visibility_change(&mut self.body_items, false);
437+
get_replacements_for_visibility_change(&self.body_items);
414438

415-
let mut impl_items = impls
439+
let impl_items = impls
416440
.into_iter()
417441
.flat_map(|impl_| impl_.syntax().descendants())
418442
.filter_map(ast::Item::cast)
419443
.collect_vec();
420444

421445
let (mut impl_item_replacements, _, _) =
422-
get_replacements_for_visibility_change(&mut impl_items, true);
446+
get_replacements_for_visibility_change(&impl_items);
423447

424448
replacements.append(&mut impl_item_replacements);
425449

@@ -433,24 +457,47 @@ impl Module {
433457
}
434458
}
435459

436-
for (vis, syntax) in replacements {
437-
let item = syntax.children_with_tokens().find(|node_or_token| {
438-
match node_or_token.kind() {
439-
// We're skipping comments, doc comments, and attribute macros that may precede the keyword
440-
// that the visibility should be placed before.
441-
SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE => false,
442-
_ => true,
443-
}
444-
});
460+
for body_item in &mut self.body_items {
461+
let insert_targets: Vec<_> = replacements
462+
.iter()
463+
.filter(|(vis, syntax)| {
464+
vis.is_none()
465+
&& (syntax == body_item.syntax()
466+
|| syntax.ancestors().any(|a| &a == body_item.syntax()))
467+
})
468+
.filter_map(|(_, syntax)| {
469+
syntax.children_with_tokens().find(|nt| {
470+
!matches!(
471+
nt.kind(),
472+
SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE
473+
)
474+
})
475+
})
476+
.collect();
445477

446-
add_change_vis(vis, item);
478+
if insert_targets.is_empty() {
479+
continue;
480+
}
481+
482+
let (editor, _) = SyntaxEditor::new(body_item.syntax().clone());
483+
for target in insert_targets {
484+
editor.insert_all(
485+
Position::before(target),
486+
vec![
487+
make.visibility_pub_crate().syntax().clone().into(),
488+
make.whitespace(" ").into(),
489+
],
490+
);
491+
}
492+
*body_item = ast::Item::cast(editor.finish().new_root().clone()).unwrap();
447493
}
448494
}
449495

450496
fn resolve_imports(
451497
&mut self,
452498
module: Option<ast::Module>,
453499
ctx: &AssistContext<'_, '_>,
500+
make: &SyntaxFactory,
454501
) -> Vec<TextRange> {
455502
let mut imports_to_remove = vec![];
456503
let mut node_set = FxHashSet::default();
@@ -477,7 +524,8 @@ impl Module {
477524
})
478525
.for_each(|(node, def)| {
479526
if node_set.insert(node.to_string())
480-
&& let Some(import) = self.process_def_in_sel(def, &node, &module, ctx)
527+
&& let Some(import) =
528+
self.process_def_in_sel(def, &node, &module, ctx, make)
481529
{
482530
check_intersection_and_push(&mut imports_to_remove, import);
483531
}
@@ -493,6 +541,7 @@ impl Module {
493541
use_node: &SyntaxNode,
494542
curr_parent_module: &Option<ast::Module>,
495543
ctx: &AssistContext<'_, '_>,
544+
make: &SyntaxFactory,
496545
) -> Option<TextRange> {
497546
//We only need to find in the current file
498547
let selection_range = ctx.selection_trimmed();
@@ -568,7 +617,7 @@ impl Module {
568617
// mod -> ust_stmt transversal
569618
// true | false -> super import insertion
570619
// true | true -> super import insertion
571-
let super_use_node = make_use_stmt_of_node_with_super(use_node);
620+
let super_use_node = make_use_stmt_of_node_with_super(use_node, make);
572621
self.use_items.insert(0, super_use_node);
573622
}
574623
None => {}
@@ -591,14 +640,14 @@ impl Module {
591640
if !first_path_in_use_tree_str.contains("super")
592641
&& !first_path_in_use_tree_str.contains("crate")
593642
{
594-
let super_path = make::ext::ident_path("super");
643+
let super_path = make.ident_path("super");
595644
use_tree_str.push(super_path);
596645
}
597646
}
598647

599648
use_tree_paths = Some(use_tree_str);
600649
} else if def_in_mod && def_out_sel {
601-
let super_use_node = make_use_stmt_of_node_with_super(use_node);
650+
let super_use_node = make_use_stmt_of_node_with_super(use_node, make);
602651
self.use_items.insert(0, super_use_node);
603652
}
604653
}
@@ -610,7 +659,7 @@ impl Module {
610659
&& let Some(first_path_in_use_tree) = use_tree_paths.first()
611660
&& first_path_in_use_tree.to_string().contains("super")
612661
{
613-
use_tree_paths.insert(0, make::ext::ident_path("super"));
662+
use_tree_paths.insert(0, make.ident_path("super"));
614663
}
615664

616665
let is_item = matches!(
@@ -625,12 +674,12 @@ impl Module {
625674
| Definition::TypeAlias(_)
626675
);
627676

628-
if (def_out_sel || !is_item) && use_stmt_not_in_sel {
629-
let use_ = make::use_(
630-
None,
631-
None,
632-
make::use_tree(make::join_paths(use_tree_paths), None, None, false),
633-
);
677+
if (def_out_sel || !is_item)
678+
&& use_stmt_not_in_sel
679+
&& let Some(joined) =
680+
use_tree_paths.into_iter().reduce(|acc, p| make.path_concat(acc, p))
681+
{
682+
let use_ = make.use_([], None, make.use_tree(joined, None, None, false));
634683
self.use_items.insert(0, ast::Item::from(use_));
635684
}
636685
}
@@ -741,8 +790,7 @@ fn check_def_in_mod_and_out_sel(
741790
}
742791

743792
fn get_replacements_for_visibility_change(
744-
items: &mut [ast::Item],
745-
is_clone_for_updated: bool,
793+
items: &[ast::Item],
746794
) -> (
747795
Vec<(Option<ast::Visibility>, SyntaxNode)>,
748796
Vec<(Option<ast::Visibility>, SyntaxNode)>,
@@ -753,9 +801,6 @@ fn get_replacements_for_visibility_change(
753801
let mut impls = Vec::new();
754802

755803
for item in items {
756-
if !is_clone_for_updated {
757-
*item = item.clone_for_update();
758-
}
759804
//Use stmts are ignored
760805
macro_rules! push_to_replacement {
761806
($it:ident) => {
@@ -812,15 +857,6 @@ fn get_use_tree_paths_from_path(
812857
Some(use_tree_str)
813858
}
814859

815-
fn add_change_vis(vis: Option<ast::Visibility>, node_or_token_opt: Option<syntax::SyntaxElement>) {
816-
if vis.is_none()
817-
&& let Some(node_or_token) = node_or_token_opt
818-
{
819-
let pub_crate_vis = make::visibility_pub_crate().clone_for_update();
820-
ted::insert(ted::Position::before(node_or_token), pub_crate_vis.syntax());
821-
}
822-
}
823-
824860
fn indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange> {
825861
node.siblings_with_tokens(syntax::Direction::Prev)
826862
.find(|x| x.kind() == WHITESPACE)

crates/syntax/src/ast/edit_in_place.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -286,16 +286,6 @@ impl ast::Use {
286286
}
287287
}
288288

289-
impl ast::Impl {
290-
pub fn get_or_create_assoc_item_list(&self) -> ast::AssocItemList {
291-
if self.assoc_item_list().is_none() {
292-
let assoc_item_list = make::assoc_item_list(None).clone_for_update();
293-
ted::append_child(self.syntax(), assoc_item_list.syntax());
294-
}
295-
self.assoc_item_list().unwrap()
296-
}
297-
}
298-
299289
impl ast::RecordExprField {
300290
/// This will either replace the initializer, or in the case that this is a shorthand convert
301291
/// the initializer into the name ref and insert the expr as the new initializer.

0 commit comments

Comments
 (0)