Skip to content

Commit 0ff89de

Browse files
committed
Add basic support for delegation
1 parent 1619fa9 commit 0ff89de

File tree

4 files changed

+161
-0
lines changed

4 files changed

+161
-0
lines changed

crates/hir/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3007,3 +3007,9 @@ impl HasCrate for Function {
30073007
self.module(db).krate()
30083008
}
30093009
}
3010+
3011+
impl HasCrate for Type {
3012+
fn krate(&self, _db: &dyn HirDatabase) -> Crate {
3013+
self.krate.into()
3014+
}
3015+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use hir::{self, HasCrate, HirDisplay};
2+
use stdx::format_to;
3+
use syntax::ast::{self, AstNode, HasName, HasVisibility};
4+
5+
use crate::{
6+
utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
7+
AssistContext, AssistId, AssistKind, Assists, GroupLabel,
8+
};
9+
10+
// Assist: generate_setter
11+
//
12+
// Generate a setter method.
13+
//
14+
// ```
15+
// struct Person {
16+
// nam$0e: String,
17+
// }
18+
// ```
19+
// ->
20+
// ```
21+
// struct Person {
22+
// name: String,
23+
// }
24+
//
25+
// impl Person {
26+
// /// Set the person's name.
27+
// fn set_name(&mut self, name: String) {
28+
// self.name = name;
29+
// }
30+
// }
31+
// ```
32+
pub(crate) fn generate_delegate(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33+
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
34+
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
35+
36+
let field_name = field.name()?;
37+
let field_ty = field.ty()?;
38+
39+
let sema_field_ty = ctx.sema.resolve_type(&field_ty)?;
40+
let krate = sema_field_ty.krate(ctx.db());
41+
let mut methods = vec![];
42+
sema_field_ty.iterate_assoc_items(ctx.db(), krate, |item| {
43+
if let hir::AssocItem::Function(f) = item {
44+
if f.self_param(ctx.db()).is_some() {
45+
methods.push(f)
46+
}
47+
}
48+
Some(())
49+
});
50+
51+
let target = field_ty.syntax().text_range();
52+
for method in methods {
53+
let impl_def = find_struct_impl(
54+
ctx,
55+
&ast::Adt::Struct(strukt.clone()),
56+
&method.name(ctx.db()).to_string(),
57+
)?;
58+
acc.add_group(
59+
&GroupLabel("Generate delegate".to_owned()),
60+
AssistId("generate_delegate", AssistKind::Generate),
61+
format!("Generate a delegate method for '{}'", method.name(ctx.db())),
62+
target,
63+
|builder| {
64+
let mut buf = String::with_capacity(512);
65+
66+
let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
67+
let return_type = method.ret_type(ctx.db());
68+
let return_type = if return_type.is_unit() || return_type.is_unknown() {
69+
String::new()
70+
} else {
71+
let module = match ctx.sema.scope(strukt.syntax()).module() {
72+
Some(m) => m,
73+
None => return,
74+
};
75+
match return_type.display_source_code(ctx.db(), module.into()) {
76+
Ok(rt) => format!("-> {}", rt),
77+
Err(_) => return,
78+
}
79+
};
80+
81+
format_to!(
82+
buf,
83+
"{}fn {}(&self) {} {{
84+
self.{}.{}()
85+
}}",
86+
vis,
87+
method.name(ctx.db()),
88+
return_type,
89+
field_name,
90+
method.name(ctx.db())
91+
);
92+
93+
let start_offset = impl_def
94+
.and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
95+
.unwrap_or_else(|| {
96+
buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
97+
strukt.syntax().text_range().end()
98+
});
99+
100+
builder.insert(start_offset, buf);
101+
},
102+
)?;
103+
}
104+
Some(())
105+
}
106+
107+
#[cfg(test)]
108+
mod tests {
109+
use crate::tests::check_assist;
110+
111+
use super::*;
112+
113+
#[test]
114+
fn test_generate_setter_from_field() {
115+
check_assist(
116+
generate_delegate,
117+
r#"
118+
struct Age(u8);
119+
impl Age {
120+
fn age(&self) -> u8 {
121+
self.0
122+
123+
}
124+
}
125+
126+
struct Person {
127+
ag$0e: Age,
128+
}
129+
"#,
130+
r#"
131+
struct Age(u8);
132+
impl Age {
133+
fn age(&self) -> u8 {
134+
self.0
135+
}
136+
}
137+
138+
struct Person {
139+
age: Age,
140+
}
141+
142+
impl Person {
143+
fn age(&self) -> u8 {
144+
self.age.age()
145+
}
146+
}"#,
147+
);
148+
}
149+
}

crates/ide_assists/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ mod handlers {
144144
mod generate_is_empty_from_len;
145145
mod generate_new;
146146
mod generate_setter;
147+
mod generate_delegate;
147148
mod add_return_type;
148149
mod inline_call;
149150
mod inline_local_variable;
@@ -210,6 +211,7 @@ mod handlers {
210211
generate_constant::generate_constant,
211212
generate_default_from_enum_variant::generate_default_from_enum_variant,
212213
generate_default_from_new::generate_default_from_new,
214+
generate_delegate::generate_delegate,
213215
generate_deref::generate_deref,
214216
generate_derive::generate_derive,
215217
generate_enum_is_method::generate_enum_is_method,

crates/syntax/src/ast/make.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,10 @@ pub fn assoc_item_list() -> ast::AssocItemList {
124124
ast_from_text("impl C for D {}")
125125
}
126126

127+
pub fn impl_(ty: ast::Path) -> ast::Impl {
128+
ast_from_text(&format!("impl {} {{}}", ty))
129+
}
130+
127131
pub fn impl_trait(trait_: ast::Path, ty: ast::Path) -> ast::Impl {
128132
ast_from_text(&format!("impl {} for {} {{}}", trait_, ty))
129133
}

0 commit comments

Comments
 (0)