Skip to content

Commit cb3f4d4

Browse files
7708: Initial implementation of generate Default assist.
The Generate Default impl from new function.
1 parent 71b8fb7 commit cb3f4d4

File tree

2 files changed

+223
-0
lines changed

2 files changed

+223
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
use crate::{AssistId, assist_context::{AssistContext, Assists}};
2+
use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxText, ast::{self, NameOwner}};
3+
use test_utils::mark;
4+
5+
// Assist: generate_default_from_new
6+
//
7+
// Generates default implementation from new method
8+
//
9+
// ```
10+
// struct Example { _inner: () }
11+
//
12+
// impl Example {
13+
// pu|b fn new() -> Self {
14+
// Self { _inner: () }
15+
// }
16+
// }
17+
// ```
18+
// ->
19+
// ```
20+
// struct Example { _inner: () }
21+
22+
// impl Example {
23+
// pub fn new() -> Self {
24+
// Self { _inner: () }
25+
// }
26+
// }
27+
28+
// impl Default for Example {
29+
// fn default() -> Self {
30+
// Self::new()
31+
// }
32+
// }
33+
// ```
34+
pub(crate) fn generate_default_from_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
35+
let fn_node: ast::Fn = ctx.find_node_at_offset()?;
36+
let fn_name = fn_node.name()?.to_string();
37+
38+
if !fn_name.eq("new") {
39+
mark::hit!(other_function_than_new);
40+
return None;
41+
}
42+
43+
if fn_node.param_list()?.params().count() != 0 {
44+
mark::hit!(new_function_with_parameters);
45+
return None;
46+
}
47+
48+
let insert_after = scope_for_fn_insertion_node(&fn_node.syntax())?;
49+
let impl_obj = ast::Impl::cast(insert_after)?;
50+
let struct_name = impl_obj.self_ty()?.syntax().text();
51+
52+
let default_fn_syntax = default_fn_node_for_new(struct_name);
53+
54+
55+
acc.add(
56+
AssistId("generate_default_from_new", crate::AssistKind::Generate),
57+
"Generate a Default impl from a new fn",
58+
impl_obj.syntax().text_range(),
59+
move |builder| {
60+
// TODO: indentation logic can also go here.
61+
// let new_indent = IndentLevel::from_node(&insert_after);
62+
let insert_location = impl_obj.syntax().text_range().end();
63+
builder.insert(insert_location, default_fn_syntax);
64+
},
65+
)
66+
}
67+
68+
fn scope_for_fn_insertion_node(node: &SyntaxNode) -> Option<SyntaxNode> {
69+
node.ancestors().into_iter().find(|node| node.kind() == SyntaxKind::IMPL)
70+
}
71+
72+
fn default_fn_node_for_new(struct_name: SyntaxText) -> String {
73+
// TODO: Update the implementation to consider the code indentation.
74+
format!(
75+
r#"
76+
77+
impl Default for {} {{
78+
fn default() -> Self {{
79+
Self::new()
80+
}}
81+
}}"#
82+
,struct_name)
83+
}
84+
85+
#[cfg(test)]
86+
mod tests {
87+
use crate::tests::{check_assist, check_assist_not_applicable};
88+
89+
use super::*;
90+
91+
#[test]
92+
fn generate_default() {
93+
check_assist(
94+
generate_default_from_new,
95+
r#"
96+
struct Example { _inner: () }
97+
98+
impl Example {
99+
pub fn ne$0w() -> Self {
100+
Self { _inner: () }
101+
}
102+
}
103+
104+
fn main() {}
105+
"#,
106+
r#"
107+
struct Example { _inner: () }
108+
109+
impl Example {
110+
pub fn new() -> Self {
111+
Self { _inner: () }
112+
}
113+
}
114+
115+
impl Default for Example {
116+
fn default() -> Self {
117+
Self::new()
118+
}
119+
}
120+
121+
fn main() {}
122+
"#,
123+
);
124+
}
125+
126+
#[test]
127+
fn generate_default2() {
128+
check_assist(
129+
generate_default_from_new,
130+
r#"
131+
struct Test { value: u32 }
132+
133+
impl Test {
134+
pub fn ne$0w() -> Self {
135+
Self { value: 0 }
136+
}
137+
}
138+
"#,
139+
r#"
140+
struct Test { value: u32 }
141+
142+
impl Test {
143+
pub fn new() -> Self {
144+
Self { value: 0 }
145+
}
146+
}
147+
148+
impl Default for Test {
149+
fn default() -> Self {
150+
Self::new()
151+
}
152+
}
153+
"#,
154+
);
155+
}
156+
157+
#[test]
158+
fn new_function_with_parameters() {
159+
mark::check!(new_function_with_parameters);
160+
check_assist_not_applicable(generate_default_from_new,
161+
r#"
162+
struct Example { _inner: () }
163+
164+
impl Example {
165+
pub fn $0new(value: ()) -> Self {
166+
Self { _inner: value }
167+
}
168+
}
169+
"#
170+
);
171+
}
172+
173+
#[test]
174+
fn other_function_than_new() {
175+
mark::check!(other_function_than_new);
176+
check_assist_not_applicable(generate_default_from_new,
177+
r#"
178+
struct Example { _inner: () }
179+
180+
impl Exmaple {
181+
pub fn a$0dd() -> Self {
182+
Self { _inner: () }
183+
}
184+
}
185+
186+
"#
187+
);
188+
}
189+
190+
// #[test]
191+
// fn default_block_is_already_present() {
192+
// check_assist_not_applicable(generate_default_from_new,
193+
// r#"
194+
// struct Example { _inner: () }
195+
196+
// impl Exmaple {
197+
// pub fn n$0ew() -> Self {
198+
// Self { _inner: () }
199+
// }
200+
// }
201+
202+
// impl Default for Example {
203+
// fn default() -> Self {
204+
// Self::new()
205+
// }
206+
// }
207+
// "#,
208+
// );
209+
// }
210+
211+
#[test]
212+
fn standalone_new_function() {
213+
check_assist_not_applicable(generate_default_from_new,
214+
r#"
215+
fn n$0ew() -> u32 {
216+
0
217+
}
218+
"#
219+
);
220+
}
221+
}

crates/ide_assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ mod handlers {
127127
mod flip_comma;
128128
mod flip_trait_bound;
129129
mod generate_default_from_enum_variant;
130+
mod generate_default_from_new;
130131
mod generate_derive;
131132
mod generate_enum_is_method;
132133
mod generate_enum_projection_method;
@@ -189,6 +190,7 @@ mod handlers {
189190
flip_comma::flip_comma,
190191
flip_trait_bound::flip_trait_bound,
191192
generate_default_from_enum_variant::generate_default_from_enum_variant,
193+
generate_default_from_new::generate_default_from_new,
192194
generate_derive::generate_derive,
193195
generate_enum_is_method::generate_enum_is_method,
194196
generate_enum_projection_method::generate_enum_as_method,

0 commit comments

Comments
 (0)