diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 7251b0e1ffcd..20dec4ce8a6d 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -21,8 +21,7 @@ use syntax::{ SyntaxNode, SyntaxToken, T, TextRange, TextSize, WalkEvent, ast::{ self, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, - edit::{AstNodeEdit, IndentLevel}, - edit_in_place::AttrsOwnerEdit, + edit::{AstNodeEdit, AttrsOwnerEdit, IndentLevel}, make, prec::ExprPrecedence, syntax_factory::SyntaxFactory, @@ -242,8 +241,9 @@ pub fn add_trait_assoc_items_to_impl( PathTransform::trait_impl(target_scope, &source_scope, trait_, impl_.clone()); cloned_item = ast::AssocItem::cast(transform.apply(cloned_item.syntax())).unwrap(); } - cloned_item.remove_attrs_and_docs(); - cloned_item + let (editor, cloned_item) = SyntaxEditor::with_ast_node(&cloned_item); + cloned_item.remove_attrs_and_docs(&editor); + ast::AssocItem::cast(editor.finish().new_root().clone()).unwrap() }) .filter_map(|item| match item { ast::AssocItem::Fn(fn_) if fn_.body().is_none() => { diff --git a/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index 3020fb39565f..02e14d8c8ee4 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -38,12 +38,14 @@ use ide_db::{ syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, }; use syntax::ast::HasGenericParams; +use syntax::syntax_editor::{Position, SyntaxEditor}; use syntax::{ AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr, ast::{ - self, HasGenericArgs, HasTypeBounds, edit::AstNodeEdit, edit_in_place::AttrsOwnerEdit, make, + self, HasGenericArgs, HasTypeBounds, + edit::{AstNodeEdit, AttrsOwnerEdit}, }, - format_smolstr, ted, + format_smolstr, }; use crate::{ @@ -266,20 +268,17 @@ fn get_transformed_assoc_item( ) -> Option { let trait_ = impl_def.trait_(ctx.db)?; let source_scope = &ctx.sema.scope(assoc_item.syntax())?; - let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; - let transform = PathTransform::trait_impl( - target_scope, - source_scope, - trait_, - ctx.sema.source(impl_def)?.value, - ); + let impl_source = ctx.sema.source(impl_def)?; + let target_scope = &ctx.sema.scope(impl_source.syntax().value)?; + let transform = + PathTransform::trait_impl(target_scope, source_scope, trait_, impl_source.value); - let assoc_item = assoc_item.clone_for_update(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. let assoc_item = ast::AssocItem::cast(transform.apply(assoc_item.syntax()))?; - assoc_item.remove_attrs_and_docs(); - Some(assoc_item) + let (editor, assoc_item) = SyntaxEditor::with_ast_node(&assoc_item); + assoc_item.remove_attrs_and_docs(&editor); + ast::AssocItem::cast(editor.finish().new_root().clone()) } /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. @@ -291,39 +290,37 @@ fn get_transformed_fn( ) -> Option { let trait_ = impl_def.trait_(ctx.db)?; let source_scope = &ctx.sema.scope(fn_.syntax())?; - let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; - let transform = PathTransform::trait_impl( - target_scope, - source_scope, - trait_, - ctx.sema.source(impl_def)?.value, - ); + let impl_source = ctx.sema.source(impl_def)?; + let target_scope = &ctx.sema.scope(impl_source.syntax().value)?; + let transform = + PathTransform::trait_impl(target_scope, source_scope, trait_, impl_source.value); let fn_ = fn_.reset_indent(); // FIXME: Paths in nested macros are not handled well. See // `macro_generated_assoc_item2` test. let fn_ = ast::Fn::cast(transform.apply(fn_.syntax()))?; - fn_.remove_attrs_and_docs(); + let (editor, fn_) = SyntaxEditor::with_ast_node(&fn_); + let factory = editor.make(); + fn_.remove_attrs_and_docs(&editor); match async_ { AsyncSugaring::Desugar => { match fn_.ret_type() { Some(ret_ty) => { let ty = ret_ty.ty()?; - ted::replace( + editor.replace( ty.syntax(), - make::ty(&format!("impl Future")) - .syntax() - .clone_for_update(), + factory.ty(&format!("impl Future")).syntax(), + ); + } + None => { + let ret_type = factory.ret_type(factory.ty("impl Future")); + editor.insert_with_whitespace( + Position::after(fn_.param_list()?.syntax()), + ret_type.syntax(), ); } - None => ted::append_child( - fn_.param_list()?.syntax(), - make::ret_type(make::ty("impl Future")) - .syntax() - .clone_for_update(), - ), } - fn_.async_token().unwrap().detach(); + editor.delete(fn_.async_token()?); } AsyncSugaring::Resugar => { let ty = fn_.ret_type()?.ty()?; @@ -350,18 +347,21 @@ fn get_transformed_fn( if let ast::Type::TupleType(ty) = &output && ty.fields().next().is_none() { - ted::remove(fn_.ret_type()?.syntax()); + editor.delete(fn_.ret_type()?.syntax()); } else { - ted::replace(ty.syntax(), output.syntax()); + editor.replace(ty.syntax(), output.syntax()); } } _ => (), } - ted::prepend_child(fn_.syntax(), make::token(T![async])); + editor.insert_with_whitespace( + Position::first_child_of(fn_.syntax()), + factory.token(T![async]), + ); } AsyncSugaring::Async | AsyncSugaring::Plain => (), } - Some(fn_) + ast::Fn::cast(editor.finish().new_root().clone()) } fn add_type_alias_impl( diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 0ffcbd212b3a..a99fe5d51b42 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs @@ -6,7 +6,7 @@ use std::{fmt, iter, ops}; use crate::{ AstToken, NodeOrToken, SyntaxElement, - SyntaxKind::WHITESPACE, + SyntaxKind::{ATTR, COMMENT, WHITESPACE}, SyntaxNode, SyntaxToken, ast::{self, AstNode, HasName, make}, syntax_editor::{Position, SyntaxEditor, SyntaxMappingBuilder}, @@ -197,6 +197,28 @@ pub trait AstNodeEdit: AstNode + Clone + Sized { impl AstNodeEdit for N {} +pub trait AttrsOwnerEdit: ast::HasAttrs { + fn remove_attrs_and_docs(&self, editor: &SyntaxEditor) { + let mut remove_next_ws = false; + for child in self.syntax().children_with_tokens() { + match child.kind() { + ATTR | COMMENT => { + remove_next_ws = true; + editor.delete(child); + continue; + } + WHITESPACE if remove_next_ws => { + editor.delete(child); + } + _ => (), + } + remove_next_ws = false; + } + } +} + +impl AttrsOwnerEdit for T {} + impl ast::IdentPat { pub fn set_pat(&self, pat: Option, editor: &SyntaxEditor) -> ast::IdentPat { let make = editor.make(); diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index 2b947f2d0fa3..68e4f956c21c 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -6,8 +6,6 @@ use parser::T; use crate::{ AstNode, AstToken, Direction, - SyntaxKind::{ATTR, COMMENT, WHITESPACE}, - SyntaxNode, algo::{self, neighbor}, ast::{self, edit::IndentLevel, make, syntax_factory::SyntaxFactory}, syntax_editor::SyntaxEditor, @@ -16,32 +14,6 @@ use crate::{ use super::HasName; -pub trait AttrsOwnerEdit: ast::HasAttrs { - fn remove_attrs_and_docs(&self) { - remove_attrs_and_docs(self.syntax()); - - fn remove_attrs_and_docs(node: &SyntaxNode) { - let mut remove_next_ws = false; - for child in node.children_with_tokens() { - match child.kind() { - ATTR | COMMENT => { - remove_next_ws = true; - child.detach(); - continue; - } - WHITESPACE if remove_next_ws => { - child.detach(); - } - _ => (), - } - remove_next_ws = false; - } - } - } -} - -impl AttrsOwnerEdit for T {} - impl ast::GenericParamList { /// Constructs a matching [`ast::GenericArgList`] pub fn to_generic_args(&self, make: &SyntaxFactory) -> ast::GenericArgList {