1 use ide_db
::famous_defs
::FamousDefs
;
2 use stdx
::{format_to, to_lower_snake_case}
;
4 ast
::{self, AstNode, HasName, HasVisibility}
,
9 utils
::{convert_reference_type, find_impl_block_end, find_struct_impl, generate_impl_text}
,
10 AssistContext
, AssistId
, AssistKind
, Assists
, GroupLabel
,
13 // Assist: generate_getter
15 // Generate a getter method.
18 // # //- minicore: as_ref
19 // # pub struct String;
20 // # impl AsRef<str> for String {
21 // # fn as_ref(&self) -> &str {
32 // # pub struct String;
33 // # impl AsRef<str> for String {
34 // # fn as_ref(&self) -> &str {
44 // fn $0name(&self) -> &str {
49 pub(crate) fn generate_getter(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
50 generate_getter_impl(acc
, ctx
, false)
53 // Assist: generate_getter_mut
55 // Generate a mut getter method.
69 // fn $0name_mut(&mut self) -> &mut String {
74 pub(crate) fn generate_getter_mut(acc
: &mut Assists
, ctx
: &AssistContext
<'_
>) -> Option
<()> {
75 generate_getter_impl(acc
, ctx
, true)
78 #[derive(Clone, Debug)]
79 struct RecordFieldInfo
{
80 field_name
: syntax
::ast
::Name
,
81 field_ty
: syntax
::ast
::Type
,
87 impl_def
: Option
<ast
::Impl
>,
92 pub(crate) fn generate_getter_impl(
94 ctx
: &AssistContext
<'_
>,
97 // This if condition denotes two modes this assist can work in:
98 // - First is acting upon selection of record fields
99 // - Next is acting upon a single record field
101 // This is the only part where implementation diverges a bit,
102 // subsequent code is generic for both of these modes
104 let (strukt
, info_of_record_fields
, fn_names
) = if !ctx
.has_empty_selection() {
106 let node
= ctx
.covering_element();
108 let node
= match node
{
109 syntax
::NodeOrToken
::Node(n
) => n
,
110 syntax
::NodeOrToken
::Token(t
) => t
.parent()?
,
113 let parent_struct
= node
.ancestors().find_map(ast
::Struct
::cast
)?
;
115 let (info_of_record_fields
, field_names
) =
116 extract_and_parse_record_fields(&parent_struct
, ctx
.selection_trimmed(), mutable
)?
;
118 (parent_struct
, info_of_record_fields
, field_names
)
120 // Single Record Field mode
121 let strukt
= ctx
.find_node_at_offset
::<ast
::Struct
>()?
;
122 let field
= ctx
.find_node_at_offset
::<ast
::RecordField
>()?
;
124 let record_field_info
= parse_record_field(field
, mutable
)?
;
126 let fn_name
= record_field_info
.fn_name
.clone();
128 (strukt
, vec
![record_field_info
], vec
![fn_name
])
131 // No record fields to do work on :(
132 if info_of_record_fields
.len() == 0 {
136 let impl_def
= find_struct_impl(ctx
, &ast
::Adt
::Struct(strukt
.clone()), &fn_names
)?
;
138 let (id
, label
) = if mutable
{
139 ("generate_getter_mut", "Generate a mut getter method")
141 ("generate_getter", "Generate a getter method")
144 // Computing collective text range of all record fields in selected region
145 let target
: TextRange
= info_of_record_fields
147 .map(|record_field_info
| record_field_info
.target
)
148 .reduce(|acc
, target
| acc
.cover(target
))?
;
150 let getter_info
= GetterInfo { impl_def, strukt, mutable }
;
153 &GroupLabel("Generate getter/setter".to_owned()),
154 AssistId(id
, AssistKind
::Generate
),
158 let record_fields_count
= info_of_record_fields
.len();
160 let mut buf
= String
::with_capacity(512);
162 // Check if an impl exists
163 if let Some(impl_def
) = &getter_info
.impl_def
{
164 // Check if impl is empty
165 if let Some(assoc_item_list
) = impl_def
.assoc_item_list() {
166 if assoc_item_list
.assoc_items().next().is_some() {
167 // If not empty then only insert a new line
173 for (i
, record_field_info
) in info_of_record_fields
.iter().enumerate() {
174 // this buf inserts a newline at the end of a getter
175 // automatically, if one wants to add one more newline
176 // for separating it from other assoc items, that needs
177 // to be handled spearately
179 generate_getter_from_info(ctx
, &getter_info
, record_field_info
);
181 // Insert `$0` only for last getter we generate
182 if i
== record_fields_count
- 1 {
183 if ctx
.config
.snippet_cap
.is_some() {
184 getter_buf
= getter_buf
.replacen("fn ", "fn $0", 1);
188 // For first element we do not merge with '\n', as
189 // that can be inserted by impl_def check defined
190 // above, for other cases which are:
192 // - impl exists but it empty, here we would ideally
193 // not want to keep newline between impl <struct> {
194 // and fn <fn-name>() { line
196 // - next if impl itself does not exist, in this
197 // case we ourselves generate a new impl and that
198 // again ends up with the same reasoning as above
199 // for not keeping newline
201 buf
= buf
+ &getter_buf
;
203 buf
= buf
+ "\n" + &getter_buf
;
206 // We don't insert a new line at the end of
207 // last getter as it will end up in the end
208 // of an impl where we would not like to keep
209 // getter and end of impl ( i.e. `}` ) with an
210 // extra line for no reason
211 if i
< record_fields_count
- 1 {
216 let start_offset
= getter_info
219 .and_then(|impl_def
| find_impl_block_end(impl_def
.to_owned(), &mut buf
))
221 buf
= generate_impl_text(&ast
::Adt
::Struct(getter_info
.strukt
.clone()), &buf
);
222 getter_info
.strukt
.syntax().text_range().end()
225 match ctx
.config
.snippet_cap
{
226 Some(cap
) => builder
.insert_snippet(cap
, start_offset
, buf
),
227 None
=> builder
.insert(start_offset
, buf
),
233 fn generate_getter_from_info(
234 ctx
: &AssistContext
<'_
>,
236 record_field_info
: &RecordFieldInfo
,
238 let mut buf
= String
::with_capacity(512);
240 let vis
= info
.strukt
.visibility().map_or(String
::new(), |v
| format
!("{v} "));
241 let (ty
, body
) = if info
.mutable
{
243 format
!("&mut {}", record_field_info
.field_ty
),
244 format
!("&mut self.{}", record_field_info
.field_name
),
248 let krate
= ctx
.sema
.scope(record_field_info
.field_ty
.syntax())?
.krate();
249 let famous_defs
= &FamousDefs(&ctx
.sema
, krate
);
251 .resolve_type(&record_field_info
.field_ty
)
252 .and_then(|ty
| convert_reference_type(ty
, ctx
.db(), famous_defs
))
254 cov_mark
::hit
!(convert_reference_type
);
256 conversion
.convert_type(ctx
.db()),
257 conversion
.getter(record_field_info
.field_name
.to_string()),
263 format
!("&{}", record_field_info
.field_ty
),
264 format
!("&self.{}", record_field_info
.field_name
),
271 " {}fn {}(&{}self) -> {} {{
275 record_field_info
.fn_name
,
276 info
.mutable
.then_some("mut ").unwrap_or_default(),
284 fn extract_and_parse_record_fields(
286 selection_range
: TextRange
,
288 ) -> Option
<(Vec
<RecordFieldInfo
>, Vec
<String
>)> {
289 let mut field_names
: Vec
<String
> = vec
![];
290 let field_list
= node
.field_list()?
;
293 ast
::FieldList
::RecordFieldList(ele
) => {
294 let info_of_record_fields_in_selection
= ele
296 .filter_map(|record_field
| {
297 if selection_range
.contains_range(record_field
.syntax().text_range()) {
298 let record_field_info
= parse_record_field(record_field
, mutable
)?
;
299 field_names
.push(record_field_info
.fn_name
.clone());
300 return Some(record_field_info
);
305 .collect
::<Vec
<RecordFieldInfo
>>();
307 if info_of_record_fields_in_selection
.len() == 0 {
311 Some((info_of_record_fields_in_selection
, field_names
))
313 ast
::FieldList
::TupleFieldList(_
) => {
319 fn parse_record_field(record_field
: ast
::RecordField
, mutable
: bool
) -> Option
<RecordFieldInfo
> {
320 let field_name
= record_field
.name()?
;
321 let field_ty
= record_field
.ty()?
;
323 let mut fn_name
= to_lower_snake_case(&field_name
.to_string());
325 format_to
!(fn_name
, "_mut");
328 let target
= record_field
.syntax().text_range();
330 Some(RecordFieldInfo { field_name, field_ty, fn_name, target }
)
335 use crate::tests
::{check_assist, check_assist_no_snippet_cap, check_assist_not_applicable}
;
340 fn test_generate_getter_from_field() {
354 fn $0data(&self) -> &Data {
374 fn $0data_mut(&mut self) -> &mut Data {
383 fn test_generate_getter_from_field_no_snippet_cap() {
384 check_assist_no_snippet_cap(
397 fn data(&self) -> &Data {
404 check_assist_no_snippet_cap(
417 fn data_mut(&mut self) -> &mut Data {
426 fn test_generate_getter_already_implemented() {
427 check_assist_not_applicable(
435 fn data(&self) -> &Data {
442 check_assist_not_applicable(
450 fn data_mut(&mut self) -> &mut Data {
459 fn test_generate_getter_from_field_with_visibility_marker() {
463 pub(crate) struct Context {
468 pub(crate) struct Context {
473 pub(crate) fn $0data(&self) -> &Data {
482 fn test_generate_getter_from_field_with_visibility_marker_no_snippet_cap() {
483 check_assist_no_snippet_cap(
486 pub(crate) struct Context {
491 pub(crate) struct Context {
496 pub(crate) fn data(&self) -> &Data {
505 fn test_multiple_generate_getter() {
515 fn data(&self) -> &Data {
527 fn data(&self) -> &Data {
531 fn $0count(&self) -> &usize {
540 fn test_multiple_generate_getter_no_snippet_cap() {
541 check_assist_no_snippet_cap(
550 fn data(&self) -> &Data {
562 fn data(&self) -> &Data {
566 fn count(&self) -> &usize {
575 fn test_not_a_special_case() {
576 cov_mark
::check_count
!(convert_reference_type
, 0);
577 // Fake string which doesn't implement AsRef<str>
583 struct S { foo: $0String }
588 struct S { foo: String }
591 fn $0foo(&self) -> &String {
600 fn test_convert_reference_type() {
601 cov_mark
::check_count
!(convert_reference_type
, 6);
608 struct S { foo: $0bool }
611 struct S { foo: bool }
614 fn $0foo(&self) -> bool {
627 impl AsRef<str> for String {
628 fn as_ref(&self) -> &str {
633 struct S { foo: $0String }
637 impl AsRef<str> for String {
638 fn as_ref(&self) -> &str {
643 struct S { foo: String }
646 fn $0foo(&self) -> &str {
660 pub struct Box<T>(T);
661 impl<T> AsRef<T> for Box<T> {
662 fn as_ref(&self) -> &T {
667 struct S { foo: $0Box<Sweets> }
672 pub struct Box<T>(T);
673 impl<T> AsRef<T> for Box<T> {
674 fn as_ref(&self) -> &T {
679 struct S { foo: Box<Sweets> }
682 fn $0foo(&self) -> &Sweets {
695 impl<T> AsRef<[T]> for Vec<T> {
696 fn as_ref(&self) -> &[T] {
701 struct S { foo: $0Vec<()> }
705 impl<T> AsRef<[T]> for Vec<T> {
706 fn as_ref(&self) -> &[T] {
711 struct S { foo: Vec<()> }
714 fn $0foo(&self) -> &[()] {
728 struct S { foo: $0Option<Failure> }
733 struct S { foo: Option<Failure> }
736 fn $0foo(&self) -> Option<&Failure> {
749 dat$0a: Result<bool, i32>,
754 data: Result<bool, i32>,
758 fn $0data(&self) -> Result<&bool, &i32> {
767 fn test_generate_multiple_getters_from_selection() {
783 fn data(&self) -> &Data {
787 fn $0count(&self) -> &usize {
796 fn test_generate_multiple_getters_from_selection_one_already_exists() {
797 // As impl for one of the fields already exist, skip it
798 check_assist_not_applicable(
807 fn data(&self) -> &Data {