Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs')
-rw-r--r--crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs160
1 files changed, 67 insertions, 93 deletions
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 806c8fba9e..175f261317 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -5,12 +5,12 @@ use syntax::{
SyntaxKind::WHITESPACE,
T,
ast::{self, AstNode, HasName, make},
- ted::{self, Position},
+ syntax_editor::{Position, SyntaxEditor},
};
use crate::{
AssistConfig, AssistId,
- assist_context::{AssistContext, Assists, SourceChangeBuilder},
+ assist_context::{AssistContext, Assists},
utils::{
DefaultMethods, IgnoreAssocItems, add_trait_assoc_items_to_impl, filter_assoc_items,
gen_trait_fn_body, generate_trait_impl,
@@ -126,98 +126,56 @@ fn add_assist(
let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`");
acc.add(AssistId::refactor("replace_derive_with_manual_impl"), label, target, |builder| {
- let insert_after = ted::Position::after(builder.make_mut(adt.clone()).syntax());
+ let insert_after = Position::after(adt.syntax());
let impl_is_unsafe = trait_.map(|s| s.is_unsafe(ctx.db())).unwrap_or(false);
- let impl_def_with_items = impl_def_from_trait(
+ let impl_def = impl_def_from_trait(
&ctx.sema,
ctx.config,
adt,
&annotated_name,
trait_,
replace_trait_path,
+ impl_is_unsafe,
);
- update_attribute(builder, old_derives, old_tree, old_trait_path, attr);
- let trait_path = make::ty_path(replace_trait_path.clone());
+ let mut editor = builder.make_editor(attr.syntax());
+ update_attribute(&mut editor, old_derives, old_tree, old_trait_path, attr);
- match (ctx.config.snippet_cap, impl_def_with_items) {
- (None, None) => {
- let impl_def = generate_trait_impl(adt, trait_path);
- if impl_is_unsafe {
- ted::insert(
- Position::first_child_of(impl_def.syntax()),
- make::token(T![unsafe]),
- );
- }
+ let trait_path = make::ty_path(replace_trait_path.clone());
- ted::insert_all(
- insert_after,
- vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
- );
- }
- (None, Some((impl_def, _))) => {
- if impl_is_unsafe {
- ted::insert(
- Position::first_child_of(impl_def.syntax()),
- make::token(T![unsafe]),
- );
- }
- ted::insert_all(
- insert_after,
- vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
- );
- }
- (Some(cap), None) => {
- let impl_def = generate_trait_impl(adt, trait_path);
-
- if impl_is_unsafe {
- ted::insert(
- Position::first_child_of(impl_def.syntax()),
- make::token(T![unsafe]),
- );
- }
+ let (impl_def, first_assoc_item) = if let Some(impl_def) = impl_def {
+ (
+ impl_def.clone(),
+ impl_def.assoc_item_list().and_then(|list| list.assoc_items().next()),
+ )
+ } else {
+ (generate_trait_impl(impl_is_unsafe, adt, trait_path), None)
+ };
- if let Some(l_curly) = impl_def.assoc_item_list().and_then(|it| it.l_curly_token())
+ if let Some(cap) = ctx.config.snippet_cap {
+ if let Some(first_assoc_item) = first_assoc_item {
+ if let ast::AssocItem::Fn(ref func) = first_assoc_item
+ && let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast)
+ && m.syntax().text() == "todo!()"
{
- builder.add_tabstop_after_token(cap, l_curly);
- }
-
- ted::insert_all(
- insert_after,
- vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
- );
- }
- (Some(cap), Some((impl_def, first_assoc_item))) => {
- let mut added_snippet = false;
-
- if impl_is_unsafe {
- ted::insert(
- Position::first_child_of(impl_def.syntax()),
- make::token(T![unsafe]),
- );
- }
-
- if let ast::AssocItem::Fn(ref func) = first_assoc_item {
- if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) {
- if m.syntax().text() == "todo!()" {
- // Make the `todo!()` a placeholder
- builder.add_placeholder_snippet(cap, m);
- added_snippet = true;
- }
- }
- }
-
- if !added_snippet {
+ // Make the `todo!()` a placeholder
+ builder.add_placeholder_snippet(cap, m);
+ } else {
// If we haven't already added a snippet, add a tabstop before the generated function
builder.add_tabstop_before(cap, first_assoc_item);
}
-
- ted::insert_all(
- insert_after,
- vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
- );
+ } else if let Some(l_curly) =
+ impl_def.assoc_item_list().and_then(|it| it.l_curly_token())
+ {
+ builder.add_tabstop_after_token(cap, l_curly);
}
- };
+ }
+
+ editor.insert_all(
+ insert_after,
+ vec![make::tokens::blank_line().into(), impl_def.syntax().clone().into()],
+ );
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
})
}
@@ -228,7 +186,8 @@ fn impl_def_from_trait(
annotated_name: &ast::Name,
trait_: Option<hir::Trait>,
trait_path: &ast::Path,
-) -> Option<(ast::Impl, ast::AssocItem)> {
+ impl_is_unsafe: bool,
+) -> Option<ast::Impl> {
let trait_ = trait_?;
let target_scope = sema.scope(annotated_name.syntax())?;
@@ -245,21 +204,39 @@ fn impl_def_from_trait(
if trait_items.is_empty() {
return None;
}
- let impl_def = generate_trait_impl(adt, make::ty_path(trait_path.clone()));
+ let impl_def = generate_trait_impl(impl_is_unsafe, adt, make::ty_path(trait_path.clone()));
- let first_assoc_item =
+ let assoc_items =
add_trait_assoc_items_to_impl(sema, config, &trait_items, trait_, &impl_def, &target_scope);
+ let assoc_item_list = if let Some((first, other)) =
+ assoc_items.split_first().map(|(first, other)| (first.clone_subtree(), other))
+ {
+ let first_item = if let ast::AssocItem::Fn(ref func) = first
+ && let Some(body) = gen_trait_fn_body(func, trait_path, adt, None)
+ && let Some(func_body) = func.body()
+ {
+ let mut editor = SyntaxEditor::new(first.syntax().clone());
+ editor.replace(func_body.syntax(), body.syntax());
+ ast::AssocItem::cast(editor.finish().new_root().clone())
+ } else {
+ Some(first.clone())
+ };
+ let items = first_item.into_iter().chain(other.iter().cloned()).collect();
+ make::assoc_item_list(Some(items))
+ } else {
+ make::assoc_item_list(None)
+ }
+ .clone_for_update();
- // Generate a default `impl` function body for the derived trait.
- if let ast::AssocItem::Fn(ref func) = first_assoc_item {
- let _ = gen_trait_fn_body(func, trait_path, adt, None);
- };
-
- Some((impl_def, first_assoc_item))
+ let impl_def = impl_def.clone_subtree();
+ let mut editor = SyntaxEditor::new(impl_def.syntax().clone());
+ editor.replace(impl_def.assoc_item_list()?.syntax(), assoc_item_list.syntax());
+ let impl_def = ast::Impl::cast(editor.finish().new_root().clone())?;
+ Some(impl_def)
}
fn update_attribute(
- builder: &mut SourceChangeBuilder,
+ editor: &mut SyntaxEditor,
old_derives: &[ast::Path],
old_tree: &ast::TokenTree,
old_trait_path: &ast::Path,
@@ -272,8 +249,6 @@ fn update_attribute(
let has_more_derives = !new_derives.is_empty();
if has_more_derives {
- let old_tree = builder.make_mut(old_tree.clone());
-
// Make the paths into flat lists of tokens in a vec
let tt = new_derives.iter().map(|path| path.syntax().clone()).map(|node| {
node.descendants_with_tokens()
@@ -288,18 +263,17 @@ fn update_attribute(
let tt = tt.collect::<Vec<_>>();
let new_tree = make::token_tree(T!['('], tt).clone_for_update();
- ted::replace(old_tree.syntax(), new_tree.syntax());
+ editor.replace(old_tree.syntax(), new_tree.syntax());
} else {
// Remove the attr and any trailing whitespace
- let attr = builder.make_mut(attr.clone());
if let Some(line_break) =
attr.syntax().next_sibling_or_token().filter(|t| t.kind() == WHITESPACE)
{
- ted::remove(line_break)
+ editor.delete(line_break)
}
- ted::remove(attr.syntax())
+ editor.delete(attr.syntax())
}
}