mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Merge pull request #19938 from A4-Tacks/gen-impl-trait
Add ide-assist: generate_impl_trait for generate_impl
This commit is contained in:
commit
48ccbe0cd8
@ -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) {
|
||||
fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &impl Indent) {
|
||||
let indent = nominal.indent_level();
|
||||
|
||||
impl_.indent(indent);
|
||||
editor.insert_all(
|
||||
Position::after(nominal.syntax()),
|
||||
vec![
|
||||
@ -120,6 +125,115 @@ 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:_} {
|
||||
// fn foo(&self) -> i32 {
|
||||
// $0todo!()
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
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 mut editor = edit.make_editor(trait_.syntax());
|
||||
|
||||
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();
|
||||
|
||||
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() {
|
||||
let placeholder = edit.make_placeholder_snippet(cap);
|
||||
editor.add_annotation(generic.syntax(), placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ty) = impl_.self_ty() {
|
||||
let placeholder = edit.make_placeholder_snippet(cap);
|
||||
editor.add_annotation(ty.syntax(), placeholder);
|
||||
}
|
||||
|
||||
if let Some(expr) =
|
||||
impl_.assoc_item_list().and_then(|it| it.assoc_items().find_map(extract_expr))
|
||||
{
|
||||
let tabstop = edit.make_tabstop_before(cap);
|
||||
editor.add_annotation(expr.syntax(), tabstop);
|
||||
} else if let Some(l_curly) =
|
||||
impl_.assoc_item_list().and_then(|it| it.l_curly_token())
|
||||
{
|
||||
let tabstop = edit.make_tabstop_after(cap);
|
||||
editor.add_annotation(l_curly, tabstop);
|
||||
}
|
||||
}
|
||||
|
||||
insert_impl(&mut editor, &impl_, &trait_);
|
||||
edit.add_file_edits(ctx.vfs_file_id(), editor);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn extract_expr(item: ast::AssocItem) -> Option<ast::Expr> {
|
||||
let ast::AssocItem::Fn(f) = item else {
|
||||
return None;
|
||||
};
|
||||
f.body()?.tail_expr()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{check_assist, check_assist_target};
|
||||
@ -492,4 +606,209 @@ 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:_} {
|
||||
fn foo(&self) -> i32 {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[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:_} {
|
||||
fn foo(&self) -> _ {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
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:_} {
|
||||
fn foo(&self) -> _ {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[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:_} {
|
||||
fn foo(&self) -> i32 {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[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:_} {
|
||||
type Output;
|
||||
|
||||
fn foo(&self) -> Self::Output {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_impl_trait_indent() {
|
||||
check_assist(
|
||||
generate_impl_trait,
|
||||
r#"
|
||||
mod foo {
|
||||
mod bar {
|
||||
trait $0Foo {
|
||||
type Output;
|
||||
|
||||
fn foo(&self) -> Self::Output;
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
mod foo {
|
||||
mod bar {
|
||||
trait Foo {
|
||||
type Output;
|
||||
|
||||
fn foo(&self) -> Self::Output;
|
||||
}
|
||||
|
||||
impl Foo for ${1:_} {
|
||||
type Output;
|
||||
|
||||
fn foo(&self) -> Self::Output {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_impl_trait_empty() {
|
||||
check_assist(
|
||||
generate_impl_trait,
|
||||
r#"
|
||||
trait $0Foo {}
|
||||
"#,
|
||||
r#"
|
||||
trait Foo {}
|
||||
|
||||
impl Foo for ${1:_} {$0}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -302,6 +302,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,
|
||||
|
@ -1880,6 +1880,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:_} {
|
||||
fn foo(&self) -> i32 {
|
||||
$0todo!()
|
||||
}
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_generate_is_empty_from_len() {
|
||||
check_doc_test(
|
||||
|
Loading…
x
Reference in New Issue
Block a user