aboutsummaryrefslogtreecommitdiff
path: root/crates/assists/src/handlers/generate_getter.rs
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2021-02-10 10:32:36 +0000
committerGitHub <[email protected]>2021-02-10 10:32:36 +0000
commit5e39d7a68032ba40d3ed76d41d0f01ad628f5c95 (patch)
tree2cee8cecf4214092f42dbee3432afa944e219671 /crates/assists/src/handlers/generate_getter.rs
parentff5ef2830c4cc6bf4116b99b440885bf0c94b459 (diff)
parente8d7bcc35507425f384cff25feb564ac41a5c5a7 (diff)
Merge #7617
7617: Add getter/setter assists r=Veykril a=yoshuawuyts This patch makes progress towards the design outlined in https://github.com/rust-analyzer/rust-analyzer/issues/5943, and includes a small refactor which closes https://github.com/rust-analyzer/rust-analyzer/issues/7607. All together this patch does 4 things: - Adds a `generate_getter` assist. - Adds a `generate_getter_mut` assist. - Adds a `generate_setter` assist. - Moves the `generate_impl_text` function from `generate_new` into `utils` (which closes #7607). ## Design Notes I've chosen to follow the [Rust API guidelines on getters](https://rust-lang.github.io/api-guidelines/naming.html#getter-names-follow-rust-convention-c-getter) as closely as possible. This deliberately leaves "builder pattern"-style setters out of scope. Also, similar to https://github.com/rust-analyzer/rust-analyzer/pull/7570 this assist generates doc comments. I think this should work well in most cases, and for the few where it doesn't it's probably easily edited. This makes it slightly less correct than the #7570 implementation, but I think this is still useful enough to include for many of the same reasons. The reason why this PR contains 3 assists, rather than 1, is because each of them is so similar to the others that it felt more noisy to do them separately than all at once. The amount of code added does not necessarily reflect that, but hope that still makes sense. ## Examples **Input** ```rust struct Person { name: String, // <- cursor on "name" } ``` **generate getter** ```rust struct Person { name: String, } impl Person { /// Get a reference to the person's name. fn name(&self) -> &String { &self.name } } ``` **generate mut getter** ```rust struct Person { name: String, } impl Person { /// Get a mutable reference to the person's name. fn name_mut(&mut self) -> &mut String { &mut self.name } } ``` **generate setter** ```rust struct Person { name: String, } impl Person { /// Set the person's name. fn set_name(&mut self, name: String) { self.name = name; } } ``` Co-authored-by: Yoshua Wuyts <[email protected]>
Diffstat (limited to 'crates/assists/src/handlers/generate_getter.rs')
-rw-r--r--crates/assists/src/handlers/generate_getter.rs156
1 files changed, 156 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/generate_getter.rs b/crates/assists/src/handlers/generate_getter.rs
new file mode 100644
index 000000000..b63dfce41
--- /dev/null
+++ b/crates/assists/src/handlers/generate_getter.rs
@@ -0,0 +1,156 @@
1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::VisibilityOwner;
3use syntax::ast::{self, AstNode, NameOwner};
4
5use crate::{
6 utils::{find_impl_block, find_struct_impl, generate_impl_text},
7 AssistContext, AssistId, AssistKind, Assists,
8};
9
10// Assist: generate_getter
11//
12// Generate a getter 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// /// Get a reference to the person's name.
27// fn name(&self) -> &String {
28// &self.name
29// }
30// }
31// ```
32pub(crate) fn generate_getter(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 strukt_name = strukt.name()?;
37 let field_name = field.name()?;
38 let field_ty = field.ty()?;
39
40 // Return early if we've found an existing fn
41 let fn_name = to_lower_snake_case(&field_name.to_string());
42 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
43
44 let target = field.syntax().text_range();
45 acc.add(
46 AssistId("generate_getter", AssistKind::Generate),
47 "Generate a getter method",
48 target,
49 |builder| {
50 let mut buf = String::with_capacity(512);
51
52 let fn_name_spaced = fn_name.replace('_', " ");
53 let strukt_name_spaced =
54 to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
55
56 if impl_def.is_some() {
57 buf.push('\n');
58 }
59
60 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
61 format_to!(
62 buf,
63 " /// Get a reference to the {}'s {}.
64 {}fn {}(&self) -> &{} {{
65 &self.{}
66 }}",
67 strukt_name_spaced,
68 fn_name_spaced,
69 vis,
70 fn_name,
71 field_ty,
72 fn_name,
73 );
74
75 let start_offset = impl_def
76 .and_then(|impl_def| find_impl_block(impl_def, &mut buf))
77 .unwrap_or_else(|| {
78 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
79 strukt.syntax().text_range().end()
80 });
81
82 builder.insert(start_offset, buf);
83 },
84 )
85}
86
87#[cfg(test)]
88mod tests {
89 use crate::tests::{check_assist, check_assist_not_applicable};
90
91 use super::*;
92
93 fn check_not_applicable(ra_fixture: &str) {
94 check_assist_not_applicable(generate_getter, ra_fixture)
95 }
96
97 #[test]
98 fn test_generate_getter_from_field() {
99 check_assist(
100 generate_getter,
101 r#"
102struct Context<T: Clone> {
103 dat$0a: T,
104}"#,
105 r#"
106struct Context<T: Clone> {
107 data: T,
108}
109
110impl<T: Clone> Context<T> {
111 /// Get a reference to the context's data.
112 fn data(&self) -> &T {
113 &self.data
114 }
115}"#,
116 );
117 }
118
119 #[test]
120 fn test_generate_getter_already_implemented() {
121 check_not_applicable(
122 r#"
123struct Context<T: Clone> {
124 dat$0a: T,
125}
126
127impl<T: Clone> Context<T> {
128 fn data(&self) -> &T {
129 &self.data
130 }
131}"#,
132 );
133 }
134
135 #[test]
136 fn test_generate_getter_from_field_with_visibility_marker() {
137 check_assist(
138 generate_getter,
139 r#"
140pub(crate) struct Context<T: Clone> {
141 dat$0a: T,
142}"#,
143 r#"
144pub(crate) struct Context<T: Clone> {
145 data: T,
146}
147
148impl<T: Clone> Context<T> {
149 /// Get a reference to the context's data.
150 pub(crate) fn data(&self) -> &T {
151 &self.data
152 }
153}"#,
154 );
155 }
156}