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::helpers::FamousDefs;
use ide_db::RootDatabase; use ide_db::RootDatabase;
use itertools::Itertools; use syntax::ast::{self, AstNode, NameOwner};
use stdx::format_to;
use syntax::{
ast::{self, AstNode, NameOwner},
SmolStr,
};
use test_utils::mark; 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 // 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<()> { 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 = ctx.find_node_at_offset::<ast::Variant>()?;
let variant_name = variant.name()?; let variant_name = variant.name()?;
let enum_name = variant.parent_enum().name()?; let enum_ = ast::Adt::Enum(variant.parent_enum());
let enum_type_params = variant.parent_enum().generic_param_list();
let (field_name, field_type) = match variant.kind() { let (field_name, field_type) = match variant.kind() {
ast::StructKind::Tuple(field_list) => { ast::StructKind::Tuple(field_list) => {
if field_list.fields().count() != 1 { if field_list.fields().count() != 1 {
@ -62,49 +55,27 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
target, target,
|edit| { |edit| {
let start_offset = variant.parent_enum().syntax().text_range().end(); let start_offset = variant.parent_enum().syntax().text_range().end();
let mut buf = String::from("\n\nimpl"); let from_trait = format!("From<{}>", field_type.syntax());
if let Some(type_params) = &enum_type_params { let impl_code = if let Some(name) = field_name {
format_to!(buf, "{}", type_params.syntax()); format!(
} r#" fn from({0}: {1}) -> Self {{
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 {{
Self::{2} {{ {0} }} Self::{2} {{ {0} }}
}}
}}"#, }}"#,
name.text(), name.text(),
field_type.syntax(), field_type.syntax(),
variant_name, variant_name,
); )
} else { } else {
format_to!( format!(
buf, r#" fn from(v: {}) -> Self {{
r#" {{
fn from(v: {}) -> Self {{
Self::{}(v) Self::{}(v)
}}
}}"#, }}"#,
field_type.syntax(), field_type.syntax(),
variant_name, 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 syntax::ast::{self, AstNode, NameOwner};
use stdx::format_to;
use syntax::{
ast::{self, AstNode, AttrsOwner, GenericParamsOwner, NameOwner},
SmolStr,
};
use crate::{AssistContext, AssistId, AssistKind, Assists}; use crate::{utils::generate_impl_text, AssistContext, AssistId, AssistKind, Assists};
// Assist: generate_impl // Assist: generate_impl
// //
@ -36,44 +31,15 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()
format!("Generate impl for `{}`", name), format!("Generate impl for `{}`", name),
target, target,
|edit| { |edit| {
let type_params = nominal.generic_param_list();
let start_offset = nominal.syntax().text_range().end(); 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 { match ctx.config.snippet_cap {
Some(cap) => { Some(cap) => {
buf.push_str(" {\n $0\n}"); let snippet = generate_impl_text(&nominal, " $0");
edit.insert_snippet(cap, start_offset, buf); edit.insert_snippet(cap, start_offset, snippet);
} }
None => { None => {
buf.push_str(" {\n}"); let snippet = generate_impl_text(&nominal, "");
edit.insert(start_offset, buf); 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 ide_db::imports_locator;
use itertools::Itertools; use itertools::Itertools;
use syntax::{ use syntax::{
ast::{self, make, AstNode}, ast::{self, make, AstNode, NameOwner},
Direction,
SyntaxKind::{IDENT, WHITESPACE}, SyntaxKind::{IDENT, WHITESPACE},
TextSize, TextSize,
}; };
@ -11,7 +10,8 @@ use syntax::{
use crate::{ use crate::{
assist_context::{AssistBuilder, AssistContext, Assists}, assist_context::{AssistBuilder, AssistContext, Assists},
utils::{ 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, 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_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 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 adt = attr.syntax().parent().and_then(ast::Adt::cast)?;
let insert_pos = annotated_name.syntax().parent()?.text_range().end(); 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_module = ctx.sema.scope(annotated_name.syntax()).module()?;
let current_crate = current_module.krate(); let current_crate = current_module.krate();
@ -82,10 +83,10 @@ pub(crate) fn replace_derive_with_manual_impl(
let mut no_traits_found = true; let mut no_traits_found = true;
for (trait_path, trait_) in found_traits.inspect(|_| no_traits_found = false) { 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 { 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(()) Some(())
} }
@ -96,6 +97,7 @@ fn add_assist(
attr: &ast::Attr, attr: &ast::Attr,
trait_path: &ast::Path, trait_path: &ast::Path,
trait_: Option<hir::Trait>, trait_: Option<hir::Trait>,
adt: &ast::Adt,
annotated_name: &ast::Name, annotated_name: &ast::Name,
insert_pos: TextSize, insert_pos: TextSize,
) -> Option<()> { ) -> Option<()> {
@ -112,15 +114,15 @@ fn add_assist(
let impl_def_with_items = let impl_def_with_items =
impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path); impl_def_from_trait(&ctx.sema, annotated_name, trait_, trait_path);
update_attribute(builder, &input, &trait_name, &attr); update_attribute(builder, &input, &trait_name, &attr);
let trait_path = format!("{}", trait_path);
match (ctx.config.snippet_cap, impl_def_with_items) { match (ctx.config.snippet_cap, impl_def_with_items) {
(None, _) => builder.insert( (None, _) => {
insert_pos, builder.insert(insert_pos, generate_trait_impl_text(adt, &trait_path, ""))
format!("\n\nimpl {} for {} {{\n\n}}", trait_path, annotated_name), }
),
(Some(cap), None) => builder.insert_snippet( (Some(cap), None) => builder.insert_snippet(
cap, cap,
insert_pos, 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))) => { (Some(cap), Some((impl_def, first_assoc_item))) => {
let mut cursor = Cursor::Before(first_assoc_item.syntax()); 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 // Generates the surrounding `impl Type { <code> }` including type and lifetime
// parameters // parameters
pub(crate) fn generate_impl_text(adt: &ast::Adt, code: &str) -> String { 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 type_params = adt.generic_param_list();
let mut buf = String::with_capacity(code.len()); 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 { if let Some(type_params) = &type_params {
format_to!(buf, "{}", type_params.syntax()); format_to!(buf, "{}", type_params.syntax());
} }
buf.push(' '); 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()); buf.push_str(adt.name().unwrap().text());
if let Some(type_params) = type_params { if let Some(type_params) = type_params {
let lifetime_params = type_params let lifetime_params = type_params