Skip to content

Commit 3d1ca78

Browse files
committed
implement field stuff too
1 parent a624e2e commit 3d1ca78

File tree

1 file changed

+106
-22
lines changed

1 file changed

+106
-22
lines changed

crates/ide_assists/src/handlers/generate_deref.rs

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
use std::fmt::Display;
2+
13
use ide_db::{helpers::FamousDefs, RootDatabase};
24
use syntax::{
35
ast::{self, NameOwner},
4-
AstNode,
6+
AstNode, SyntaxNode,
57
};
68

79
use crate::{
8-
assist_context::{AssistContext, Assists},
10+
assist_context::{AssistBuilder, AssistContext, Assists},
911
utils::generate_trait_impl_text,
1012
AssistId, AssistKind,
1113
};
@@ -36,11 +38,15 @@ use crate::{
3638
// }
3739
// ```
3840
pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
41+
generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx))
42+
}
43+
44+
fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
3945
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
4046
let field = ctx.find_node_at_offset::<ast::RecordField>()?;
4147

4248
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
43-
cov_mark::hit!(test_add_deref_impl_already_exists);
49+
cov_mark::hit!(test_add_record_deref_impl_already_exists);
4450
return None;
4551
}
4652

@@ -51,26 +57,50 @@ pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<(
5157
AssistId("generate_deref", AssistKind::Generate),
5258
format!("Generate `Deref` impl using `{}`", field_name),
5359
target,
54-
|edit| {
55-
let start_offset = strukt.syntax().text_range().end();
56-
let impl_code = format!(
57-
r#" type Target = {0};
60+
|edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()),
61+
)
62+
}
63+
64+
fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
65+
let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
66+
let field = ctx.find_node_at_offset::<ast::TupleField>()?;
67+
let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?;
68+
let field_list_index =
69+
field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?;
70+
71+
if existing_deref_impl(&ctx.sema, &strukt).is_some() {
72+
cov_mark::hit!(test_add_field_deref_impl_already_exists);
73+
return None;
74+
}
75+
76+
let field_type = field.ty()?;
77+
let target = field.syntax().text_range();
78+
acc.add(
79+
AssistId("generate_deref", AssistKind::Generate),
80+
format!("Generate `Deref` impl using `{}`", field.syntax()),
81+
target,
82+
|edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index),
83+
)
84+
}
85+
86+
fn generate_edit(
87+
edit: &mut AssistBuilder,
88+
strukt: ast::Struct,
89+
field_type_syntax: &SyntaxNode,
90+
field_name: impl Display,
91+
) {
92+
let start_offset = strukt.syntax().text_range().end();
93+
let impl_code = format!(
94+
r#" type Target = {0};
5895
5996
fn deref(&self) -> &Self::Target {{
6097
&self.{1}
6198
}}"#,
62-
field_type.syntax(),
63-
field_name.syntax()
64-
);
65-
let strukt_adt = ast::Adt::Struct(strukt);
66-
// Q for reviewer: Is there a better way to specify the trait_text, e.g.
67-
// - can I have it auto `use std::ops::Deref`, and then just use `Deref` as the trait text?
68-
// Or is there a helper that might detect if `std::ops::Deref` has been used, and pick `Deref`,
69-
// otherwise, pick `std::ops::Deref` for the trait_text.
70-
let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
71-
edit.insert(start_offset, deref_impl);
72-
},
73-
)
99+
field_type_syntax, field_name
100+
);
101+
let strukt_adt = ast::Adt::Struct(strukt);
102+
let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code);
103+
edit.insert(start_offset, deref_impl);
74104
}
75105

76106
fn existing_deref_impl(
@@ -97,7 +127,7 @@ mod tests {
97127
use super::*;
98128

99129
#[test]
100-
fn test_generate_deref() {
130+
fn test_generate_record_deref() {
101131
check_assist(
102132
generate_deref,
103133
r#"struct A { }
@@ -115,6 +145,43 @@ impl std::ops::Deref for B {
115145
);
116146
}
117147

148+
#[test]
149+
fn test_generate_field_deref_idx_0() {
150+
check_assist(
151+
generate_deref,
152+
r#"struct A { }
153+
struct B($0A);"#,
154+
r#"struct A { }
155+
struct B(A);
156+
157+
impl std::ops::Deref for B {
158+
type Target = A;
159+
160+
fn deref(&self) -> &Self::Target {
161+
&self.0
162+
}
163+
}"#,
164+
);
165+
}
166+
#[test]
167+
fn test_generate_field_deref_idx_1() {
168+
check_assist(
169+
generate_deref,
170+
r#"struct A { }
171+
struct B(u8, $0A);"#,
172+
r#"struct A { }
173+
struct B(u8, A);
174+
175+
impl std::ops::Deref for B {
176+
type Target = A;
177+
178+
fn deref(&self) -> &Self::Target {
179+
&self.1
180+
}
181+
}"#,
182+
);
183+
}
184+
118185
fn check_not_applicable(ra_fixture: &str) {
119186
let fixture = format!(
120187
"//- /main.rs crate:main deps:core,std\n{}\n{}",
@@ -125,8 +192,8 @@ impl std::ops::Deref for B {
125192
}
126193

127194
#[test]
128-
fn test_generate_deref_not_applicable_if_already_impl() {
129-
cov_mark::check!(test_add_deref_impl_already_exists);
195+
fn test_generate_record_deref_not_applicable_if_already_impl() {
196+
cov_mark::check!(test_add_record_deref_impl_already_exists);
130197
check_not_applicable(
131198
r#"struct A { }
132199
struct B { $0a: A }
@@ -137,6 +204,23 @@ impl std::ops::Deref for B {
137204
fn deref(&self) -> &Self::Target {
138205
&self.a
139206
}
207+
}"#,
208+
)
209+
}
210+
211+
#[test]
212+
fn test_generate_field_deref_not_applicable_if_already_impl() {
213+
cov_mark::check!(test_add_field_deref_impl_already_exists);
214+
check_not_applicable(
215+
r#"struct A { }
216+
struct B($0A)
217+
218+
impl std::ops::Deref for B {
219+
type Target = A;
220+
221+
fn deref(&self) -> &Self::Target {
222+
&self.0
223+
}
140224
}"#,
141225
)
142226
}

0 commit comments

Comments
 (0)