about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorA4-Tacks <wdsjxhno1001@163.com>2025-06-06 16:09:45 +0800
committerA4-Tacks <wdsjxhno1001@163.com>2025-07-18 06:18:23 +0800
commitf345b76895b5c2a232d8412e3844e10871432f38 (patch)
tree7a9f68945fe3e40049b58ae9732c121c53035a24 /src/tools
parent0901d513d89b38175e64c3ebee13d8d44ae6e943 (diff)
Add generate_impl_trait for generate_impl
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs272
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs23
3 files changed, 294 insertions, 2 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
index 14601ca0207..ba95fe5d92a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
@@ -1,12 +1,17 @@
 use syntax::{
-    ast::{self, AstNode, HasName, edit_in_place::Indent, make},
+    ast::{self, AstNode, HasGenericParams, HasName, edit_in_place::Indent, make},
     syntax_editor::{Position, SyntaxEditor},
 };
 
-use crate::{AssistContext, AssistId, Assists, utils};
+use crate::{
+    AssistContext, AssistId, Assists,
+    utils::{self, DefaultMethods, IgnoreAssocItems},
+};
 
 fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
     let indent = nominal.indent_level();
+
+    impl_.indent(indent);
     editor.insert_all(
         Position::after(nominal.syntax()),
         vec![
@@ -120,6 +125,101 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
     )
 }
 
+// Assist: generate_impl_trait
+//
+// Adds this trait impl for a type.
+//
+// ```
+// trait $0Foo {
+//     fn foo(&self) -> i32;
+// }
+// ```
+// ->
+// ```
+// trait Foo {
+//     fn foo(&self) -> i32;
+// }
+//
+// impl Foo for ${1:_} {
+//     $0fn foo(&self) -> i32 {
+//         todo!()
+//     }
+// }
+// ```
+pub(crate) fn generate_impl_trait(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
+    let name = ctx.find_node_at_offset::<ast::Name>()?;
+    let trait_ = ast::Trait::cast(name.syntax().parent()?)?;
+    let target_scope = ctx.sema.scope(trait_.syntax())?;
+    let hir_trait = ctx.sema.to_def(&trait_)?;
+
+    let target = trait_.syntax().text_range();
+    acc.add(
+        AssistId::generate("generate_impl_trait"),
+        format!("Generate `{name}` impl for type"),
+        target,
+        |edit| {
+            let holder_arg = ast::GenericArg::TypeArg(make::type_arg(make::ty_placeholder()));
+            let missing_items = utils::filter_assoc_items(
+                &ctx.sema,
+                &hir_trait.items(ctx.db()),
+                DefaultMethods::No,
+                IgnoreAssocItems::DocHiddenAttrPresent,
+            );
+            let impl_ = make::impl_trait(
+                trait_.unsafe_token().is_some(),
+                None,
+                trait_.generic_param_list().map(|list| {
+                    make::generic_arg_list(list.generic_params().map(|_| holder_arg.clone()))
+                }),
+                None,
+                None,
+                false,
+                make::ty(&name.text()),
+                make::ty_placeholder(),
+                None,
+                None,
+                None,
+            )
+            .clone_for_update();
+
+            let trait_ = edit.make_mut(trait_);
+
+            if !missing_items.is_empty() {
+                utils::add_trait_assoc_items_to_impl(
+                    &ctx.sema,
+                    ctx.config,
+                    &missing_items,
+                    hir_trait,
+                    &impl_,
+                    &target_scope,
+                );
+            }
+
+            if let Some(cap) = ctx.config.snippet_cap {
+                if let Some(generics) = impl_.trait_().and_then(|it| it.generic_arg_list()) {
+                    for generic in generics.generic_args() {
+                        edit.add_placeholder_snippet(cap, generic);
+                    }
+                }
+
+                if let Some(ty) = impl_.self_ty() {
+                    edit.add_placeholder_snippet(cap, ty);
+                }
+
+                if let Some(item) = impl_.assoc_item_list().and_then(|it| it.assoc_items().next()) {
+                    edit.add_tabstop_before(cap, item);
+                } else if let Some(l_curly) =
+                    impl_.assoc_item_list().and_then(|it| it.l_curly_token())
+                {
+                    edit.add_tabstop_after_token(cap, l_curly);
+                }
+            }
+
+            insert_impl(impl_, &trait_);
+        },
+    )
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_target};
@@ -492,4 +592,172 @@ mod tests {
             "#,
         );
     }
+
+    #[test]
+    fn test_add_impl_trait() {
+        check_assist(
+            generate_impl_trait,
+            r#"
+                trait $0Foo {
+                    fn foo(&self) -> i32;
+
+                    fn bar(&self) -> i32 {
+                        self.foo()
+                    }
+                }
+            "#,
+            r#"
+                trait Foo {
+                    fn foo(&self) -> i32;
+
+                    fn bar(&self) -> i32 {
+                        self.foo()
+                    }
+                }
+
+                impl Foo for ${1:_} {
+                    $0fn foo(&self) -> i32 {
+                        todo!()
+                    }
+                }
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_impl_trait_use_generic() {
+        check_assist(
+            generate_impl_trait,
+            r#"
+                trait $0Foo<T> {
+                    fn foo(&self) -> T;
+
+                    fn bar(&self) -> T {
+                        self.foo()
+                    }
+                }
+            "#,
+            r#"
+                trait Foo<T> {
+                    fn foo(&self) -> T;
+
+                    fn bar(&self) -> T {
+                        self.foo()
+                    }
+                }
+
+                impl Foo<${1:_}> for ${2:_} {
+                    $0fn foo(&self) -> _ {
+                        todo!()
+                    }
+                }
+            "#,
+        );
+        check_assist(
+            generate_impl_trait,
+            r#"
+                trait $0Foo<T, U> {
+                    fn foo(&self) -> T;
+
+                    fn bar(&self) -> T {
+                        self.foo()
+                    }
+                }
+            "#,
+            r#"
+                trait Foo<T, U> {
+                    fn foo(&self) -> T;
+
+                    fn bar(&self) -> T {
+                        self.foo()
+                    }
+                }
+
+                impl Foo<${1:_}, ${2:_}> for ${3:_} {
+                    $0fn foo(&self) -> _ {
+                        todo!()
+                    }
+                }
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_impl_trait_docs() {
+        check_assist(
+            generate_impl_trait,
+            r#"
+                /// foo
+                trait $0Foo {
+                    /// foo method
+                    fn foo(&self) -> i32;
+
+                    fn bar(&self) -> i32 {
+                        self.foo()
+                    }
+                }
+            "#,
+            r#"
+                /// foo
+                trait Foo {
+                    /// foo method
+                    fn foo(&self) -> i32;
+
+                    fn bar(&self) -> i32 {
+                        self.foo()
+                    }
+                }
+
+                impl Foo for ${1:_} {
+                    $0fn foo(&self) -> i32 {
+                        todo!()
+                    }
+                }
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_impl_trait_assoc_types() {
+        check_assist(
+            generate_impl_trait,
+            r#"
+                trait $0Foo {
+                    type Output;
+
+                    fn foo(&self) -> Self::Output;
+                }
+            "#,
+            r#"
+                trait Foo {
+                    type Output;
+
+                    fn foo(&self) -> Self::Output;
+                }
+
+                impl Foo for ${1:_} {
+                    $0type Output;
+
+                    fn foo(&self) -> Self::Output {
+                        todo!()
+                    }
+                }
+            "#,
+        );
+    }
+
+    #[test]
+    fn test_add_impl_trait_empty() {
+        check_assist(
+            generate_impl_trait,
+            r#"
+                trait $0Foo {}
+            "#,
+            r#"
+                trait Foo {}
+
+                impl Foo for ${1:_} {$0}
+            "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index c2604432032..46a4c85f41a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -301,6 +301,7 @@ mod handlers {
             generate_function::generate_function,
             generate_impl::generate_impl,
             generate_impl::generate_trait_impl,
+            generate_impl::generate_impl_trait,
             generate_is_empty_from_len::generate_is_empty_from_len,
             generate_mut_trait_impl::generate_mut_trait_impl,
             generate_new::generate_new,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index fe74694d8a4..7c88677be1a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -1881,6 +1881,29 @@ impl<T: Clone> Ctx<T> {$0}
 }
 
 #[test]
+fn doctest_generate_impl_trait() {
+    check_doc_test(
+        "generate_impl_trait",
+        r#####"
+trait $0Foo {
+    fn foo(&self) -> i32;
+}
+"#####,
+        r#####"
+trait Foo {
+    fn foo(&self) -> i32;
+}
+
+impl Foo for ${1:_} {
+    $0fn foo(&self) -> i32 {
+        todo!()
+    }
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_generate_is_empty_from_len() {
     check_doc_test(
         "generate_is_empty_from_len",