1 use itertools::Itertools;
3 use syntax::ast::{self, AstNode, NameOwner, StructKind, VisibilityOwner};
6 utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists,
10 // Assist: generate_new
12 // Adds a new inherent impl for a type.
15 // struct Ctx<T: Clone> {
21 // struct Ctx<T: Clone> {
25 // impl<T: Clone> Ctx<T> {
26 // fn $0new(data: T) -> Self { Self { data } }
29 pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
30 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
32 // We want to only apply this to non-union structs with named fields
33 let field_list = match strukt.kind() {
34 StructKind::Record(named) => named,
38 // Return early if we've found an existing new fn
39 let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;
41 let target = strukt.syntax().text_range();
42 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
43 let mut buf = String::with_capacity(512);
45 if impl_def.is_some() {
49 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
51 let params = field_list
53 .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax())))
55 let fields = field_list.fields().filter_map(|f| f.name()).format(", ");
57 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
59 let start_offset = impl_def
60 .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
62 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
63 strukt.syntax().text_range().end()
66 match ctx.config.snippet_cap {
67 None => builder.insert(start_offset, buf),
69 buf = buf.replace("fn new", "fn $0new");
70 builder.insert_snippet(cap, start_offset, buf);
78 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
83 fn test_generate_new() {
93 fn $0new() -> Self { Self { } }
100 struct Foo<T: Clone> {$0}
103 struct Foo<T: Clone> {}
105 impl<T: Clone> Foo<T> {
106 fn $0new() -> Self { Self { } }
113 struct Foo<'a, T: Foo<'a>> {$0}
116 struct Foo<'a, T: Foo<'a>> {}
118 impl<'a, T: Foo<'a>> Foo<'a, T> {
119 fn $0new() -> Self { Self { } }
126 struct Foo { baz: String $0}
129 struct Foo { baz: String }
132 fn $0new(baz: String) -> Self { Self { baz } }
139 struct Foo { baz: String, qux: Vec<i32> $0}
142 struct Foo { baz: String, qux: Vec<i32> }
145 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
152 fn check_that_visibility_modifiers_dont_get_brought_in() {
156 struct Foo { pub baz: String, pub qux: Vec<i32> $0}
159 struct Foo { pub baz: String, pub qux: Vec<i32> }
162 fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
169 fn check_it_reuses_existing_impls() {
181 fn $0new() -> Self { Self { } }
198 fn $0new() -> Self { Self { } }
221 fn $0new() -> Self { Self { } }
233 fn check_visibility_of_new_fn_based_on_struct() {
243 pub fn $0new() -> Self { Self { } }
250 pub(crate) struct Foo {$0}
253 pub(crate) struct Foo {}
256 pub(crate) fn $0new() -> Self { Self { } }
263 fn generate_new_not_applicable_if_fn_exists() {
264 check_assist_not_applicable(
277 check_assist_not_applicable(
292 fn generate_new_target() {
296 struct SomeThingIrrelevant;
297 /// Has a lifetime parameter
298 struct Foo<'a, T: Foo<'a>> {$0}
299 struct EvenMoreIrrelevant;
301 "/// Has a lifetime parameter
302 struct Foo<'a, T: Foo<'a>> {}",
307 fn test_unrelated_new() {
311 pub struct AstId<N: AstNode> {
313 file_ast_id: FileAstId<N>,
316 impl<N: AstNode> AstId<N> {
317 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
318 AstId { file_id, file_ast_id }
322 pub struct Source<T> {
323 pub file_id: HirFileId,$0
328 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
329 Source { file_id: self.file_id, ast: f(self.ast) }
334 pub struct AstId<N: AstNode> {
336 file_ast_id: FileAstId<N>,
339 impl<N: AstNode> AstId<N> {
340 pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
341 AstId { file_id, file_ast_id }
345 pub struct Source<T> {
346 pub file_id: HirFileId,
351 pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }
353 pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
354 Source { file_id: self.file_id, ast: f(self.ast) }