Skip to content

Commit 680dd9d

Browse files
committed
Enable delegation generation for complex types
1 parent c9882c8 commit 680dd9d

File tree

3 files changed

+133
-34
lines changed

3 files changed

+133
-34
lines changed

crates/ide_assists/src/handlers/generate_delegate.rs

Lines changed: 72 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,50 @@
11
use hir::{self, HasCrate, HasSource, HirDisplay};
2-
use syntax::ast::{self, make, AstNode, HasName, HasVisibility};
2+
use syntax::ast::{self, make, AstNode, HasGenericParams, HasName, HasVisibility};
33

44
use crate::{
55
utils::{find_struct_impl, render_snippet, Cursor},
66
AssistContext, AssistId, AssistKind, Assists, GroupLabel,
77
};
88
use syntax::ast::edit::AstNodeEdit;
99

10-
// Assist: generate_setter
10+
// Assist: generate_delegate
1111
//
12-
// Generate a setter method.
12+
// Generate a delegate method.
1313
//
1414
// ```
15+
// struct Age(u8);
16+
// impl Age {
17+
// fn age(&self) -> u8 {
18+
// self.0
19+
// }
20+
// }
21+
//
1522
// struct Person {
16-
// nam$0e: String,
23+
// ag$0e: Age,
1724
// }
1825
// ```
1926
// ->
2027
// ```
28+
// struct Age(u8);
29+
// impl Age {
30+
// fn age(&self) -> u8 {
31+
// self.0
32+
// }
33+
// }
34+
//
2135
// struct Person {
22-
// name: String,
36+
// age: Age,
2337
// }
2438
//
2539
// impl Person {
26-
// /// Set the person's name.
27-
// fn set_name(&mut self, name: String) {
28-
// self.name = name;
40+
// $0fn age(&self) -> u8 {
41+
// self.age.age()
2942
// }
3043
// }
3144
// ```
3245
pub(crate) fn generate_delegate(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
46+
let cap = ctx.config.snippet_cap?;
47+
3348
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
3449
let strukt_name = strukt.name()?;
3550

@@ -62,37 +77,39 @@ pub(crate) fn generate_delegate(acc: &mut Assists, ctx: &AssistContext) -> Optio
6277
format!("Generate a delegate method for '{}'", method.name(ctx.db())),
6378
target,
6479
|builder| {
65-
// make function
80+
// Create the function
6681
let method_source = match method.source(ctx.db()) {
6782
Some(source) => source.value,
6883
None => return,
6984
};
7085
let method_name = method.name(ctx.db());
7186
let vis = method_source.visibility();
7287
let name = make::name(&method.name(ctx.db()).to_string());
73-
let type_params = None;
74-
let self_ty = method
75-
.self_param(ctx.db())
76-
.map(|s| s.source(ctx.db()).map(|s| s.value))
77-
.flatten();
78-
let params = make::param_list(self_ty, []);
88+
let params =
89+
method_source.param_list().unwrap_or_else(|| make::param_list(None, []));
7990
let tail_expr = make::expr_method_call(
80-
field_from_idents(["self", &field_name.to_string()]).unwrap(),
91+
make::ext::field_from_idents(["self", &field_name.to_string()]).unwrap(), // This unwrap is ok because we have at least 1 arg in the list
8192
make::name_ref(&method_name.to_string()),
8293
make::arg_list([]),
8394
);
95+
let type_params = method_source.generic_param_list();
8496
let body = make::block_expr([], Some(tail_expr));
85-
let ret_type = &method.ret_type(ctx.db()).display(ctx.db()).to_string();
86-
let ret_type = Some(make::ret_type(make::ty(ret_type)));
87-
let is_async = false;
97+
let ret_type = method.ret_type(ctx.db());
98+
let ret_type = if ret_type.is_unknown() {
99+
Some(make::ret_type(make::ty_placeholder()))
100+
} else {
101+
let ret_type = &ret_type.display(ctx.db()).to_string();
102+
Some(make::ret_type(make::ty(ret_type)))
103+
};
104+
let is_async = method_source.async_token().is_some();
88105
let f = make::fn_(vis, name, type_params, params, body, ret_type, is_async)
89106
.indent(ast::edit::IndentLevel(1))
90107
.clone_for_update();
91108

92109
let cursor = Cursor::Before(f.syntax());
93-
let cap = ctx.config.snippet_cap.unwrap(); // FIXME.
94110

95-
// Create or update an impl block, and attach the function to it.
111+
// Create or update an impl block, attach the function to it,
112+
// then insert into our code.
96113
match impl_def {
97114
Some(impl_def) => {
98115
// Remember where in our source our `impl` block lives.
@@ -110,7 +127,10 @@ pub(crate) fn generate_delegate(acc: &mut Assists, ctx: &AssistContext) -> Optio
110127
None => {
111128
// Attach the function to the impl block
112129
let name = &strukt_name.to_string();
113-
let impl_def = make::impl_(make::ext::ident_path(name)).clone_for_update();
130+
let params = strukt.generic_param_list();
131+
let ty_params = params.clone();
132+
let impl_def = make::impl_(make::ext::ident_path(name), params, ty_params)
133+
.clone_for_update();
114134
let assoc_items = impl_def.get_or_create_assoc_item_list();
115135
assoc_items.add_item(f.clone().into());
116136

@@ -127,15 +147,6 @@ pub(crate) fn generate_delegate(acc: &mut Assists, ctx: &AssistContext) -> Optio
127147
Some(())
128148
}
129149

130-
pub fn field_from_idents<'a>(
131-
parts: impl std::iter::IntoIterator<Item = &'a str>,
132-
) -> Option<ast::Expr> {
133-
let mut iter = parts.into_iter();
134-
let base = make::expr_path(make::ext::ident_path(iter.next()?));
135-
let expr = iter.fold(base, |base, s| make::expr_field(base, s));
136-
Some(expr)
137-
}
138-
139150
#[cfg(test)]
140151
mod tests {
141152
use crate::tests::check_assist;
@@ -213,6 +224,36 @@ impl Person {
213224
}"#,
214225
);
215226
}
227+
228+
#[test]
229+
fn test_generate_delegate_enable_all_attributes() {
230+
check_assist(
231+
generate_delegate,
232+
r#"
233+
struct Age<T>(T);
234+
impl<T> Age<T> {
235+
pub(crate) async fn age<J, 'a>(&'a mut self, ty: T, arg: J) -> T {
236+
self.0
237+
}
238+
}
239+
240+
struct Person<T> {
241+
ag$0e: Age<T>,
242+
}"#,
243+
r#"
244+
struct Age<T>(T);
245+
impl<T> Age<T> {
246+
pub(crate) async fn age<J, 'a>(&'a mut self, ty: T, arg: J) -> T {
247+
self.0
248+
}
249+
}
250+
251+
struct Person<T> {
252+
age: Age<T>,
253+
}
254+
255+
impl<T> Person<T> {
256+
$0pub(crate) async fn age<J, 'a>(&'a mut self, ty: T, arg: J) -> _ {
216257
self.age.age()
217258
}
218259
}"#,

crates/ide_assists/src/tests/generated.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,43 @@ impl Default for Example {
728728
)
729729
}
730730

731+
#[test]
732+
fn doctest_generate_delegate() {
733+
check_doc_test(
734+
"generate_delegate",
735+
r#####"
736+
struct Age(u8);
737+
impl Age {
738+
fn age(&self) -> u8 {
739+
self.0
740+
}
741+
}
742+
743+
struct Person {
744+
ag$0e: Age,
745+
}
746+
"#####,
747+
r#####"
748+
struct Age(u8);
749+
impl Age {
750+
fn age(&self) -> u8 {
751+
self.0
752+
}
753+
}
754+
755+
struct Person {
756+
age: Age,
757+
}
758+
759+
impl Person {
760+
$0fn age(&self) -> u8 {
761+
self.age.age()
762+
}
763+
}
764+
"#####,
765+
)
766+
}
767+
731768
#[test]
732769
fn doctest_generate_deref() {
733770
check_doc_test(

crates/syntax/src/ast/make.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ pub mod ext {
4444
Some(path)
4545
}
4646

47+
pub fn field_from_idents<'a>(
48+
parts: impl std::iter::IntoIterator<Item = &'a str>,
49+
) -> Option<ast::Expr> {
50+
let mut iter = parts.into_iter();
51+
let base = expr_path(ext::ident_path(iter.next()?));
52+
let expr = iter.fold(base, |base, s| expr_field(base, s));
53+
Some(expr)
54+
}
55+
4756
pub fn expr_unreachable() -> ast::Expr {
4857
expr_from_text("unreachable!()")
4958
}
@@ -124,8 +133,20 @@ pub fn assoc_item_list() -> ast::AssocItemList {
124133
ast_from_text("impl C for D {}")
125134
}
126135

127-
pub fn impl_(ty: ast::Path) -> ast::Impl {
128-
ast_from_text(&format!("impl {} {{}}", ty))
136+
pub fn impl_(
137+
ty: ast::Path,
138+
params: Option<ast::GenericParamList>,
139+
ty_params: Option<ast::GenericParamList>,
140+
) -> ast::Impl {
141+
let params = match params {
142+
Some(params) => params.to_string(),
143+
None => String::new(),
144+
};
145+
let ty_params = match ty_params {
146+
Some(params) => params.to_string(),
147+
None => String::new(),
148+
};
149+
ast_from_text(&format!("impl{} {}{} {{}}", params, ty, ty_params))
129150
}
130151

131152
pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
@@ -649,7 +670,7 @@ pub fn fn_(
649670
is_async: bool,
650671
) -> ast::Fn {
651672
let type_params = match type_params {
652-
Some(type_params) => format!("<{}>", type_params),
673+
Some(type_params) => format!("{}", type_params),
653674
None => "".into(),
654675
};
655676
let ret_type = match ret_type {

0 commit comments

Comments
 (0)