From cb3f4d43d9646deadf27da54e0d2a204685ed665 Mon Sep 17 00:00:00 2001 From: Chetan Khilosiya Date: Sun, 28 Feb 2021 01:48:51 +0530 Subject: 7708: Initial implementation of generate Default assist. The Generate Default impl from new function. --- .../src/handlers/generate_default_from_new.rs | 221 +++++++++++++++++++++ crates/ide_assists/src/lib.rs | 2 + 2 files changed, 223 insertions(+) create mode 100644 crates/ide_assists/src/handlers/generate_default_from_new.rs diff --git a/crates/ide_assists/src/handlers/generate_default_from_new.rs b/crates/ide_assists/src/handlers/generate_default_from_new.rs new file mode 100644 index 000000000..a1174d315 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_default_from_new.rs @@ -0,0 +1,221 @@ +use crate::{AssistId, assist_context::{AssistContext, Assists}}; +use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxText, ast::{self, NameOwner}}; +use test_utils::mark; + +// Assist: generate_default_from_new +// +// Generates default implementation from new method +// +// ``` +// struct Example { _inner: () } +// +// impl Example { +// pu|b fn new() -> Self { +// Self { _inner: () } +// } +// } +// ``` +// -> +// ``` +// struct Example { _inner: () } + +// impl Example { +// pub fn new() -> Self { +// Self { _inner: () } +// } +// } + +// impl Default for Example { +// fn default() -> Self { +// Self::new() +// } +// } +// ``` +pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { + let fn_node: ast::Fn = ctx.find_node_at_offset()?; + let fn_name = fn_node.name()?.to_string(); + + if !fn_name.eq("new") { + mark::hit!(other_function_than_new); + return None; + } + + if fn_node.param_list()?.params().count() != 0 { + mark::hit!(new_function_with_parameters); + return None; + } + + let insert_after = scope_for_fn_insertion_node(&fn_node.syntax())?; + let impl_obj = ast::Impl::cast(insert_after)?; + let struct_name = impl_obj.self_ty()?.syntax().text(); + + let default_fn_syntax = default_fn_node_for_new(struct_name); + + + acc.add( + AssistId("generate_default_from_new", crate::AssistKind::Generate), + "Generate a Default impl from a new fn", + impl_obj.syntax().text_range(), + move |builder| { + // TODO: indentation logic can also go here. + // let new_indent = IndentLevel::from_node(&insert_after); + let insert_location = impl_obj.syntax().text_range().end(); + builder.insert(insert_location, default_fn_syntax); + }, + ) +} + +fn scope_for_fn_insertion_node(node: &SyntaxNode) -> Option { + node.ancestors().into_iter().find(|node| node.kind() == SyntaxKind::IMPL) +} + +fn default_fn_node_for_new(struct_name: SyntaxText) -> String { + // TODO: Update the implementation to consider the code indentation. + format!( + r#" + +impl Default for {} {{ + fn default() -> Self {{ + Self::new() + }} +}}"# + ,struct_name) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn generate_default() { + check_assist( + generate_default_from_new, + r#" +struct Example { _inner: () } + +impl Example { + pub fn ne$0w() -> Self { + Self { _inner: () } + } +} + +fn main() {} +"#, + r#" +struct Example { _inner: () } + +impl Example { + pub fn new() -> Self { + Self { _inner: () } + } +} + +impl Default for Example { + fn default() -> Self { + Self::new() + } +} + +fn main() {} +"#, + ); + } + + #[test] + fn generate_default2() { + check_assist( + generate_default_from_new, + r#" +struct Test { value: u32 } + +impl Test { + pub fn ne$0w() -> Self { + Self { value: 0 } + } +} +"#, + r#" +struct Test { value: u32 } + +impl Test { + pub fn new() -> Self { + Self { value: 0 } + } +} + +impl Default for Test { + fn default() -> Self { + Self::new() + } +} +"#, + ); + } + + #[test] + fn new_function_with_parameters() { + mark::check!(new_function_with_parameters); + check_assist_not_applicable(generate_default_from_new, + r#" +struct Example { _inner: () } + +impl Example { + pub fn $0new(value: ()) -> Self { + Self { _inner: value } + } +} +"# + ); + } + + #[test] + fn other_function_than_new() { + mark::check!(other_function_than_new); + check_assist_not_applicable(generate_default_from_new, + r#" +struct Example { _inner: () } + +impl Exmaple { + pub fn a$0dd() -> Self { + Self { _inner: () } + } +} + +"# + ); + } + +// #[test] +// fn default_block_is_already_present() { +// check_assist_not_applicable(generate_default_from_new, +// r#" +// struct Example { _inner: () } + +// impl Exmaple { +// pub fn n$0ew() -> Self { +// Self { _inner: () } +// } +// } + +// impl Default for Example { +// fn default() -> Self { +// Self::new() +// } +// } +// "#, +// ); +// } + + #[test] + fn standalone_new_function() { + check_assist_not_applicable(generate_default_from_new, + r#" +fn n$0ew() -> u32 { + 0 +} +"# + ); + } +} diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 9c8148462..ea62d5f5d 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs @@ -127,6 +127,7 @@ mod handlers { mod flip_comma; mod flip_trait_bound; mod generate_default_from_enum_variant; + mod generate_default_from_new; mod generate_derive; mod generate_enum_is_method; mod generate_enum_projection_method; @@ -189,6 +190,7 @@ mod handlers { flip_comma::flip_comma, flip_trait_bound::flip_trait_bound, generate_default_from_enum_variant::generate_default_from_enum_variant, + generate_default_from_new::generate_default_from_new, generate_derive::generate_derive, generate_enum_is_method::generate_enum_is_method, generate_enum_projection_method::generate_enum_as_method, -- cgit v1.2.3