2 use itertools::Itertools;
4 use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
7 utils::{find_impl_block, find_struct_impl, generate_impl_text},
8 AssistContext, AssistId, AssistKind, Assists,
11 // Assist: generate_new
13 // Adds a new inherent impl for a type.
16 // struct Ctx<T: Clone> {
22 // struct Ctx<T: Clone> {
26 // impl<T: Clone> Ctx<T> {
27 // fn $0new(data: T) -> Self { Self { data } }
30 pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
31 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
33 // We want to only apply this to non-union structs with named fields
34 let field_list = match strukt.kind() {
35 StructKind::Record(named) => named,
39 // Return early if we've found an existing new fn
40 let impl_def = find_struct_impl(&ctx, &Adt::Struct(strukt.clone()), "new")?;
42 let target = strukt.syntax().text_range();
43 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
44 let mut buf = String::with_capacity(512);
46 if impl_def.is_some() {
50 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
52 let params = field_list
54 .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax())))
56 let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
58 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
60 let start_offset = impl_def
61 .and_then(|impl_def| find_impl_block(impl_def, &mut buf))
63 buf = generate_impl_text(&Adt::Struct(strukt.clone()), &buf);
64 strukt.syntax().text_range().end()
67 match ctx.config.snippet_cap {
68 None => builder.insert(start_offset, buf),
70 buf = buf.replace("fn new", "fn $0new");
71 builder.insert_snippet(cap, start_offset, buf);
79 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
85 fn test_generate_new() {
86 // Check output of generation
93 fn $0new() -> Self { Self { } }
98 "struct Foo<T: Clone> {$0}",
99 "struct Foo<T: Clone> {}
101 impl<T: Clone> Foo<T> {
102 fn $0new() -> Self { Self { } }
107 "struct Foo<'a, T: Foo<'a>> {$0}",
108 "struct Foo<'a, T: Foo<'a>> {}
110 impl<'a, T: Foo<'a>> Foo<'a, T> {
111 fn $0new() -> Self { Self { } }
116 "struct Foo { baz: String $0}",
117 "struct Foo { baz: String }
120 fn $0new(baz: String) -> Self { Self { baz } }
125 "struct Foo { baz: String, qux: Vec<i32> $0}",
126 "struct Foo { baz: String, qux: Vec<i32> }
129 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
133 // Check that visibility modifiers don't get brought in for fields
136 "struct Foo { pub baz: String, pub qux: Vec<i32> $0}",
137 "struct Foo { pub baz: String, pub qux: Vec<i32> }
140 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
144 // Check that it reuses existing impls
154 fn $0new() -> Self { Self { } }
169 fn $0new() -> Self { Self { } }
190 fn $0new() -> Self { Self { } }
200 // Check visibility of new fn based on struct
203 "pub struct Foo {$0}",
207 pub fn $0new() -> Self { Self { } }
212 "pub(crate) struct Foo {$0}",
213 "pub(crate) struct Foo {}
216 pub(crate) fn $0new() -> Self { Self { } }
222 fn generate_new_not_applicable_if_fn_exists() {
223 check_assist_not_applicable(
235 check_assist_not_applicable(
249 fn generate_new_target() {
253 struct SomeThingIrrelevant;
254 /// Has a lifetime parameter
255 struct Foo<'a, T: Foo<'a>> {$0}
256 struct EvenMoreIrrelevant;
258 "/// Has a lifetime parameter
259 struct Foo<'a, T: Foo<'a>> {}",
264 fn test_unrelated_new() {
268 pub struct AstId<N: AstNode> {
270 file_ast_id: FileAstId<N>,
273 impl<N: AstNode> AstId<N> {
274 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
275 AstId { file_id, file_ast_id }
279 pub struct Source<T> {
280 pub file_id: HirFileId,$0
285 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
286 Source { file_id: self.file_id, ast: f(self.ast) }
290 pub struct AstId<N: AstNode> {
292 file_ast_id: FileAstId<N>,
295 impl<N: AstNode> AstId<N> {
296 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
297 AstId { file_id, file_ast_id }
301 pub struct Source<T> {
302 pub file_id: HirFileId,
307 pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
309 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
310 Source { file_id: self.file_id, ast: f(self.ast) }