Skip to content

Commit f828748

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

2 files changed

Lines changed: 88 additions & 63 deletions

File tree

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

Lines changed: 88 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ fn generate_module_def(
187187
parent_impl: &Option<ast::Impl>,
188188
Module { name, body_items, use_items }: &Module,
189189
) -> ast::Module {
190+
let make = SyntaxFactory::without_mappings();
190191
let items: Vec<_> = if let Some(impl_) = parent_impl.as_ref()
191192
&& let Some(self_ty) = impl_.self_ty()
192193
{
@@ -196,9 +197,13 @@ fn generate_module_def(
196197
.filter_map(ast::AssocItem::cast)
197198
.map(|it| it.indent(IndentLevel(1)))
198199
.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());
200+
let impl_reset = impl_.reset_indent();
201+
let (editor, impl_root) = SyntaxEditor::with_ast_node(&impl_reset);
202+
let assoc_item_list = editor.make().assoc_item_list(assoc_items);
203+
if let Some(existing_list) = impl_root.assoc_item_list() {
204+
editor.replace(existing_list.syntax(), assoc_item_list.syntax());
205+
}
206+
let impl_ = ast::Impl::cast(editor.finish().new_root().clone()).unwrap();
202207
// Add the import for enum/struct corresponding to given impl block
203208
let use_impl = make_use_stmt_of_node_with_super(self_ty.syntax());
204209
once(use_impl)
@@ -210,19 +215,19 @@ fn generate_module_def(
210215
};
211216

212217
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))
218+
let module_body = make.item_list(items);
219+
let module_name = make.name(name);
220+
make.mod_(module_name, Some(module_body))
217221
}
218222

219223
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_(
223-
None,
224+
let make = SyntaxFactory::without_mappings();
225+
let super_path = make.ident_path("super");
226+
let node_path = make.path_from_text(&node_syntax.to_string());
227+
let use_ = make.use_(
228+
[],
224229
None,
225-
make::use_tree(make::join_paths(vec![super_path, node_path]), None, None, false),
230+
make.use_tree(make.path_concat(super_path, node_path), None, None, false),
226231
);
227232

228233
ast::Item::from(use_)
@@ -386,18 +391,32 @@ impl Module {
386391
if use_.syntax().parent().is_some_and(|parent| parent == covering_node)
387392
&& use_stmts_set.insert(use_.syntax().text_range().start())
388393
{
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_
394+
let key = use_.syntax().text_range().start();
395+
let entry = use_stmts_to_be_inserted
396+
.entry(key)
397+
.or_insert_with(|| {
398+
let (_, root) = SyntaxEditor::with_ast_node(&use_);
399+
root
400+
});
401+
let make = SyntaxFactory::without_mappings();
402+
let (editor, edit_root) = SyntaxEditor::with_ast_node(&*entry);
403+
let replacements: Vec<_> = edit_root
393404
.syntax()
394405
.descendants()
395406
.filter_map(ast::NameRef::cast)
396407
.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());
408+
.filter_map(|seg| {
409+
Some((
410+
seg.syntax().parent()?,
411+
make.path_from_text(&format!("{mod_name}::{seg}")),
412+
))
413+
})
414+
.collect();
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
}
@@ -409,8 +428,13 @@ impl Module {
409428
}
410429

411430
fn change_visibility(&mut self, record_fields: Vec<SyntaxNode>) {
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(&mut self.body_items);
414438

415439
let mut impl_items = impls
416440
.into_iter()
@@ -419,7 +443,7 @@ impl Module {
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(&mut impl_items);
423447

424448
replacements.append(&mut impl_item_replacements);
425449

@@ -433,17 +457,40 @@ 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+
let make = SyntaxFactory::without_mappings();
461+
for body_item in &mut self.body_items {
462+
let insert_targets: Vec<_> = replacements
463+
.iter()
464+
.filter(|(vis, syntax)| {
465+
vis.is_none()
466+
&& (syntax == body_item.syntax()
467+
|| syntax.ancestors().any(|a| &a == body_item.syntax()))
468+
})
469+
.filter_map(|(_, syntax)| {
470+
syntax.children_with_tokens().find(|nt| {
471+
!matches!(
472+
nt.kind(),
473+
SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE
474+
)
475+
})
476+
})
477+
.collect();
445478

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

@@ -494,6 +541,7 @@ impl Module {
494541
curr_parent_module: &Option<ast::Module>,
495542
ctx: &AssistContext<'_, '_>,
496543
) -> Option<TextRange> {
544+
let make = SyntaxFactory::without_mappings();
497545
//We only need to find in the current file
498546
let selection_range = ctx.selection_trimmed();
499547
let file_id = ctx.file_id();
@@ -591,7 +639,7 @@ impl Module {
591639
if !first_path_in_use_tree_str.contains("super")
592640
&& !first_path_in_use_tree_str.contains("crate")
593641
{
594-
let super_path = make::ext::ident_path("super");
642+
let super_path = make.ident_path("super");
595643
use_tree_str.push(super_path);
596644
}
597645
}
@@ -610,7 +658,7 @@ impl Module {
610658
&& let Some(first_path_in_use_tree) = use_tree_paths.first()
611659
&& first_path_in_use_tree.to_string().contains("super")
612660
{
613-
use_tree_paths.insert(0, make::ext::ident_path("super"));
661+
use_tree_paths.insert(0, make.ident_path("super"));
614662
}
615663

616664
let is_item = matches!(
@@ -625,12 +673,12 @@ impl Module {
625673
| Definition::TypeAlias(_)
626674
);
627675

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-
);
676+
if (def_out_sel || !is_item)
677+
&& use_stmt_not_in_sel
678+
&& let Some(joined) =
679+
use_tree_paths.into_iter().reduce(|acc, p| make.path_concat(acc, p))
680+
{
681+
let use_ = make.use_([], None, make.use_tree(joined, None, None, false));
634682
self.use_items.insert(0, ast::Item::from(use_));
635683
}
636684
}
@@ -742,7 +790,6 @@ fn check_def_in_mod_and_out_sel(
742790

743791
fn get_replacements_for_visibility_change(
744792
items: &mut [ast::Item],
745-
is_clone_for_updated: bool,
746793
) -> (
747794
Vec<(Option<ast::Visibility>, SyntaxNode)>,
748795
Vec<(Option<ast::Visibility>, SyntaxNode)>,
@@ -753,9 +800,6 @@ fn get_replacements_for_visibility_change(
753800
let mut impls = Vec::new();
754801

755802
for item in items {
756-
if !is_clone_for_updated {
757-
*item = item.clone_for_update();
758-
}
759803
//Use stmts are ignored
760804
macro_rules! push_to_replacement {
761805
($it:ident) => {
@@ -812,15 +856,6 @@ fn get_use_tree_paths_from_path(
812856
Some(use_tree_str)
813857
}
814858

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-
824859
fn indent_range_before_given_node(node: &SyntaxNode) -> Option<TextRange> {
825860
node.siblings_with_tokens(syntax::Direction::Prev)
826861
.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)