7664: refactor impl generation in assists r=Veykril a=jDomantas

Follow-up to #7659: all impl generation in assists (at least what I found) is now done through `utils::{generate_impl_text, generate_trait_impl_text}`.

Co-authored-by: Domantas Jadenkus <djadenkus@gmail.com>
This commit is contained in:
bors[bot] 2021-02-13 21:37:35 +00:00 committed by GitHub
commit 84c9970db5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 98 deletions

View file

@ -1,15 +1,9 @@
use ast::GenericParamsOwner;
use ide_db::helpers::FamousDefs;
use ide_db::RootDatabase;
use itertools::Itertools;
use stdx::format_to;
use syntax::{
ast::{self, AstNode, NameOwner},
SmolStr,
};
use syntax::ast::{self, AstNode, NameOwner};
use test_utils::mark;
use crate::{AssistContext, AssistId, AssistKind, Assists};
use crate::{utils::generate_trait_impl_text, AssistContext, AssistId, AssistKind, Assists};
// Assist: generate_from_impl_for_enum
//
@ -31,8 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let variant_name = variant.name()?;
let enum_name = variant.parent_enum().name()?;
let enum_type_params = variant.parent_enum().generic_param_list();
let enum_ = ast::Adt::Enum(variant.parent_enum());
let (field_name, field_type) = match variant.kind() {
ast::StructKind::Tuple(field_list) => {
if field_list.fields().count() != 1 {
@ -62,49 +55,27 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
target,
|edit| {
let start_offset = variant.parent_enum().syntax().text_range().end();
let mut buf = String::from("\n\nimpl");
if let Some(type_params) = &enum_type_params {
format_to!(buf, "{}", type_params.syntax());
}
format_to!(buf, " From<{}> for {}", field_type.syntax(), enum_name);
if let Some(type_params) = enum_type_params {
let lifetime_params = type_params
.lifetime_params()
.filter_map(|it| it.lifetime())
.map(|it| SmolStr::from(it.text()));
let type_params = type_params
.type_params()
.filter_map(|it| it.name())
.map(|it| SmolStr::from(it.text()));
let generic_params = lifetime_params.chain(type_params).format(", ");
format_to!(buf, "<{}>", generic_params)
}
if let Some(name) = field_name {
format_to!(
buf,
r#" {{
fn from({0}: {1}) -> Self {{
let from_trait = format!("From<{}>", field_type.syntax());
let impl_code = if let Some(name) = field_name {
format!(
r#" fn from({0}: {1}) -> Self {{
Self::{2} {{ {0} }}
}}
}}"#,
name.text(),
field_type.syntax(),
variant_name,
);
)
} else {
format_to!(
buf,
r#" {{
fn from(v: {}) -> Self {{
format!(
r#" fn from(v: {}) -> Self {{
Self::{}(v)
}}
}}"#,
field_type.syntax(),
variant_name,
);
}
edit.insert(start_offset, buf);
)
};
let from_impl = generate_trait_impl_text(&enum_, &from_trait, &impl_code);
edit.insert(start_offset, from_impl);
},
)
}

View file

@ -1,11 +1,6 @@
use itertools::Itertools;
use stdx::format_to;
use syntax::{
ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
SmolStr,
};
use syntax::ast::{self, AstNode, NameOwner};
use crate::{AssistContext, AssistId, AssistKind, Assists};
use crate::{utils::generate_impl_text, AssistContext, AssistId, AssistKind, Assists};
// Assist: generate_impl
//
@ -36,44 +31,15 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
format!("Generate impl for `{}`", name),
target,
|edit| {
let type_params = nominal.generic_param_list();
let start_offset = nominal.syntax().text_range().end();
let mut buf = String::new();
buf.push_str("\n\n");
nominal
.attrs()
.filter(|attr| {
attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false)
})
.for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str()));
buf.push_str("impl");
if let Some(type_params) = &type_params {
format_to!(buf, "{}", type_params.syntax());
}
buf.push_str(" ");
buf.push_str(name.text());
if let Some(type_params) = type_params {
let lifetime_params = type_params
.lifetime_params()
.filter_map(|it| it.lifetime())
.map(|it| SmolStr::from(it.text()));
let type_params = type_params
.type_params()
.filter_map(|it| it.name())
.map(|it| SmolStr::from(it.text()));
let generic_params = lifetime_params.chain(type_params).format(", ");
format_to!(buf, "<{}>", generic_params)
}
match ctx.config.snippet_cap {
Some(cap) => {
buf.push_str(" {\n $0\n}");
edit.insert_snippet(cap, start_offset, buf);
let snippet = generate_impl_text(&nominal, " $0");
edit.insert_snippet(cap, start_offset, snippet);
}
None => {
buf.push_str(" {\n}");
edit.insert(start_offset, buf);
let snippet = generate_impl_text(&nominal, "");
edit.insert(start_offset, snippet);
}
}
},

View file

@ -2,8 +2,7 @@ use ide_db::helpers::mod_path_to_ast;
use ide_db::imports_locator;
use itertools::Itertools;
use syntax::{
ast::{self, make, AstNode},
Direction,
ast::{self, make, AstNode, NameOwner},
SyntaxKind::{IDENT, WHITESPACE},
TextSize,
};
@ -11,7 +10,8 @@ use syntax::{
use crate::{
assist_context::{AssistBuilder, AssistContext, Assists},
utils::{
add_trait_assoc_items_to_impl, filter_assoc_items, render_snippet, Cursor, DefaultMethods,
add_trait_assoc_items_to_impl, filter_assoc_items, generate_trait_impl_text,
render_snippet, Cursor, DefaultMethods,
},
AssistId, AssistKind,
};
@ -57,8 +57,9 @@ pub(crate) fn replace_derive_with_manual_impl(
let trait_token = ctx.token_at_offset().find(|t| t.kind() == IDENT && t.text() != "derive")?;
let trait_path = make::path_unqualified(make::path_segment(make::name_ref(trait_token.text())));
let annotated_name = attr.syntax().siblings(Direction::Next).find_map(ast::Name::cast)?;
let insert_pos = annotated_name.syntax().parent()?.text_range().end();
let adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
let annotated_name = adt.name()?;
let insert_pos = adt.syntax().text_range().end();
let current_module = ctx.sema.scope(annotated_name.syntax()).module()?;
let current_crate = current_module.krate();
@ -82,10 +83,10 @@ pub(crate) fn replace_derive_with_manual_impl(
let mut no_traits_found = true;
for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) {
add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &annotated_name, insert_pos)?;
add_assist(acc, ctx, &attr, &trait_path, Some(trait_), &adt, &annotated_name, insert_pos)?;
}
if no_traits_found {
add_assist(acc, ctx, &attr, &trait_path, None, &annotated_name, insert_pos)?;
add_assist(acc, ctx, &attr, &trait_path, None, &adt, &annotated_name, insert_pos)?;
}
Some(())
}
@ -96,6 +97,7 @@ fn add_assist(
attr: &ast::Attr,
trait_path: &ast::Path,
trait_: Option<hir::Trait>,
adt: &ast::Adt,
annotated_name: &ast::Name,
insert_pos: TextSize,
) -> Option<()> {
@ -112,15 +114,15 @@ fn add_assist(
let impl_def_with_items =
impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
update_attribute(builder, &input, &trait_name, &attr);
let trait_path = format!("{}", trait_path);
match (ctx.config.snippet_cap, impl_def_with_items) {
(None, _) => builder.insert(
insert_pos,
format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name),
),
(None, _) => {
builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
}
(Some(cap), None) => builder.insert_snippet(
cap,
insert_pos,
format!("\n\nimpl {} for {} {{\n $0\n}}", trait_path, annotated_name),
generate_trait_impl_text(adt, &trait_path, " $0"),
),
(Some(cap), Some((impl_def, first_assoc_item))) => {
let mut cursor = Cursor::Before(first_assoc_item.syntax());

View file

@ -367,13 +367,31 @@ pub(crate) fn find_impl_block_end(impl_def: ast::Impl, buf: &mut String) -> Opti
// Generates the surrounding `impl Type { <code> }` including type and lifetime
// parameters
pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String {
generate_impl_text_inner(adt, None, code)
}
// Generates the surrounding `impl <trait> for Type { <code> }` including type
// and lifetime parameters
pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &str) -> String {
generate_impl_text_inner(adt, Some(trait_text), code)
}
fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
let type_params = adt.generic_param_list();
let mut buf = String::with_capacity(code.len());
buf.push_str("\n\nimpl");
buf.push_str("\n\n");
adt.attrs()
.filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
.for_each(|attr| buf.push_str(format!("{}\n", attr.to_string()).as_str()));
buf.push_str("impl");
if let Some(type_params) = &type_params {
format_to!(buf, "{}", type_params.syntax());
}
buf.push(' ');
if let Some(trait_text) = trait_text {
buf.push_str(trait_text);
buf.push_str(" for ");
}
buf.push_str(adt.name().unwrap().text());
if let Some(type_params) = type_params {
let lifetime_params = type_params