aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src')
-rw-r--r--crates/ide_assists/src/handlers/expand_glob_import.rs2
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs148
-rw-r--r--crates/ide_assists/src/handlers/fix_visibility.rs2
-rw-r--r--crates/ide_assists/src/handlers/generate_getter.rs292
-rw-r--r--crates/ide_assists/src/handlers/generate_getter_mut.rs195
-rw-r--r--crates/ide_assists/src/lib.rs3
-rw-r--r--crates/ide_assists/src/tests.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs10
8 files changed, 341 insertions, 313 deletions
diff --git a/crates/ide_assists/src/handlers/expand_glob_import.rs b/crates/ide_assists/src/handlers/expand_glob_import.rs
index 79cb08d69..6da880b52 100644
--- a/crates/ide_assists/src/handlers/expand_glob_import.rs
+++ b/crates/ide_assists/src/handlers/expand_glob_import.rs
@@ -55,7 +55,7 @@ pub(crate) fn expand_glob_import(acc: &mut Assists, ctx: &AssistContext) -> Opti
55 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?; 55 let refs_in_target = find_refs_in_mod(ctx, target_module, Some(current_module))?;
56 let imported_defs = find_imported_defs(ctx, star)?; 56 let imported_defs = find_imported_defs(ctx, star)?;
57 57
58 let target = parent.clone().either(|n| n.syntax().clone(), |n| n.syntax().clone()); 58 let target = parent.either(|n| n.syntax().clone(), |n| n.syntax().clone());
59 acc.add( 59 acc.add(
60 AssistId("expand_glob_import", AssistKind::RefactorRewrite), 60 AssistId("expand_glob_import", AssistKind::RefactorRewrite),
61 "Expand glob import", 61 "Expand glob import",
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 97435f021..3d2cd739a 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -31,8 +31,8 @@ use crate::{
31// 31//
32// fn handle(action: Action) { 32// fn handle(action: Action) {
33// match action { 33// match action {
34// $0Action::Move { distance } => {} 34// $0Action::Move { distance } => todo!(),
35// Action::Stop => {} 35// Action::Stop => todo!(),
36// } 36// }
37// } 37// }
38// ``` 38// ```
@@ -129,7 +129,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
129 |builder| { 129 |builder| {
130 let new_match_arm_list = match_arm_list.clone_for_update(); 130 let new_match_arm_list = match_arm_list.clone_for_update();
131 let missing_arms = missing_pats 131 let missing_arms = missing_pats
132 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 132 .map(|pat| make::match_arm(iter::once(pat), make::ext::expr_todo()))
133 .map(|it| it.clone_for_update()); 133 .map(|it| it.clone_for_update());
134 134
135 let catch_all_arm = new_match_arm_list 135 let catch_all_arm = new_match_arm_list
@@ -350,8 +350,8 @@ fn foo(a: bool) {
350 r#" 350 r#"
351fn foo(a: bool) { 351fn foo(a: bool) {
352 match a { 352 match a {
353 $0true => {} 353 $0true => todo!(),
354 false => {} 354 false => todo!(),
355 } 355 }
356} 356}
357"#, 357"#,
@@ -373,7 +373,7 @@ fn foo(a: bool) {
373fn foo(a: bool) { 373fn foo(a: bool) {
374 match a { 374 match a {
375 true => {} 375 true => {}
376 $0false => {} 376 $0false => todo!(),
377 } 377 }
378} 378}
379"#, 379"#,
@@ -410,10 +410,10 @@ fn foo(a: bool) {
410 r#" 410 r#"
411fn foo(a: bool) { 411fn foo(a: bool) {
412 match (a, a) { 412 match (a, a) {
413 $0(true, true) => {} 413 $0(true, true) => todo!(),
414 (true, false) => {} 414 (true, false) => todo!(),
415 (false, true) => {} 415 (false, true) => todo!(),
416 (false, false) => {} 416 (false, false) => todo!(),
417 } 417 }
418} 418}
419"#, 419"#,
@@ -435,9 +435,9 @@ fn foo(a: bool) {
435fn foo(a: bool) { 435fn foo(a: bool) {
436 match (a, a) { 436 match (a, a) {
437 (false, true) => {} 437 (false, true) => {}
438 $0(true, true) => {} 438 $0(true, true) => todo!(),
439 (true, false) => {} 439 (true, false) => todo!(),
440 (false, false) => {} 440 (false, false) => todo!(),
441 } 441 }
442} 442}
443"#, 443"#,
@@ -471,7 +471,7 @@ fn main() {
471 match A::As { 471 match A::As {
472 A::Bs { x, y: Some(_) } => {} 472 A::Bs { x, y: Some(_) } => {}
473 A::Cs(_, Some(_)) => {} 473 A::Cs(_, Some(_)) => {}
474 $0A::As => {} 474 $0A::As => todo!(),
475 } 475 }
476} 476}
477"#, 477"#,
@@ -499,7 +499,7 @@ use Option::*;
499fn main() { 499fn main() {
500 match None { 500 match None {
501 None => {} 501 None => {}
502 Some(${0:_}) => {} 502 Some(${0:_}) => todo!(),
503 } 503 }
504} 504}
505 "#, 505 "#,
@@ -523,7 +523,7 @@ enum A { As, Bs, Cs(Option<i32>) }
523fn main() { 523fn main() {
524 match A::As { 524 match A::As {
525 A::Cs(_) | A::Bs => {} 525 A::Cs(_) | A::Bs => {}
526 $0A::As => {} 526 $0A::As => todo!(),
527 } 527 }
528} 528}
529"#, 529"#,
@@ -553,8 +553,8 @@ fn main() {
553 A::Bs if 0 < 1 => {} 553 A::Bs if 0 < 1 => {}
554 A::Ds(_value) => { let x = 1; } 554 A::Ds(_value) => { let x = 1; }
555 A::Es(B::Xs) => (), 555 A::Es(B::Xs) => (),
556 $0A::As => {} 556 $0A::As => todo!(),
557 A::Cs => {} 557 A::Cs => todo!(),
558 } 558 }
559} 559}
560"#, 560"#,
@@ -580,7 +580,7 @@ fn main() {
580 match A::As { 580 match A::As {
581 A::As(_) => {} 581 A::As(_) => {}
582 a @ A::Bs(_) => {} 582 a @ A::Bs(_) => {}
583 A::Cs(${0:_}) => {} 583 A::Cs(${0:_}) => todo!(),
584 } 584 }
585} 585}
586"#, 586"#,
@@ -605,11 +605,11 @@ enum A { As, Bs, Cs(String), Ds(String, String), Es { x: usize, y: usize } }
605fn main() { 605fn main() {
606 let a = A::As; 606 let a = A::As;
607 match a { 607 match a {
608 $0A::As => {} 608 $0A::As => todo!(),
609 A::Bs => {} 609 A::Bs => todo!(),
610 A::Cs(_) => {} 610 A::Cs(_) => todo!(),
611 A::Ds(_, _) => {} 611 A::Ds(_, _) => todo!(),
612 A::Es { x, y } => {} 612 A::Es { x, y } => todo!(),
613 } 613 }
614} 614}
615"#, 615"#,
@@ -638,10 +638,10 @@ fn main() {
638 let a = A::One; 638 let a = A::One;
639 let b = B::One; 639 let b = B::One;
640 match (a, b) { 640 match (a, b) {
641 $0(A::One, B::One) => {} 641 $0(A::One, B::One) => todo!(),
642 (A::One, B::Two) => {} 642 (A::One, B::Two) => todo!(),
643 (A::Two, B::One) => {} 643 (A::Two, B::One) => todo!(),
644 (A::Two, B::Two) => {} 644 (A::Two, B::Two) => todo!(),
645 } 645 }
646} 646}
647"#, 647"#,
@@ -670,10 +670,10 @@ fn main() {
670 let a = A::One; 670 let a = A::One;
671 let b = B::One; 671 let b = B::One;
672 match (&a, &b) { 672 match (&a, &b) {
673 $0(A::One, B::One) => {} 673 $0(A::One, B::One) => todo!(),
674 (A::One, B::Two) => {} 674 (A::One, B::Two) => todo!(),
675 (A::Two, B::One) => {} 675 (A::Two, B::One) => todo!(),
676 (A::Two, B::Two) => {} 676 (A::Two, B::Two) => todo!(),
677 } 677 }
678} 678}
679"#, 679"#,
@@ -705,9 +705,9 @@ fn main() {
705 let b = B::One; 705 let b = B::One;
706 match (a, b) { 706 match (a, b) {
707 (A::Two, B::One) => {} 707 (A::Two, B::One) => {}
708 $0(A::One, B::One) => {} 708 $0(A::One, B::One) => todo!(),
709 (A::One, B::Two) => {} 709 (A::One, B::Two) => todo!(),
710 (A::Two, B::Two) => {} 710 (A::Two, B::Two) => todo!(),
711 } 711 }
712} 712}
713"#, 713"#,
@@ -736,7 +736,7 @@ fn main() {
736 match (a, b) { 736 match (a, b) {
737 (Some(_), _) => {} 737 (Some(_), _) => {}
738 (None, Some(_)) => {} 738 (None, Some(_)) => {}
739 $0(None, None) => {} 739 $0(None, None) => todo!(),
740 } 740 }
741} 741}
742"#, 742"#,
@@ -801,8 +801,8 @@ enum A { One, Two }
801fn main() { 801fn main() {
802 let a = A::One; 802 let a = A::One;
803 match (a, ) { 803 match (a, ) {
804 $0(A::One,) => {} 804 $0(A::One,) => todo!(),
805 (A::Two,) => {} 805 (A::Two,) => todo!(),
806 } 806 }
807} 807}
808"#, 808"#,
@@ -826,7 +826,7 @@ enum A { As }
826 826
827fn foo(a: &A) { 827fn foo(a: &A) {
828 match a { 828 match a {
829 $0A::As => {} 829 $0A::As => todo!(),
830 } 830 }
831} 831}
832"#, 832"#,
@@ -851,7 +851,7 @@ enum A {
851 851
852fn foo(a: &mut A) { 852fn foo(a: &mut A) {
853 match a { 853 match a {
854 $0A::Es { x, y } => {} 854 $0A::Es { x, y } => todo!(),
855 } 855 }
856} 856}
857"#, 857"#,
@@ -891,8 +891,8 @@ enum E { X, Y }
891 891
892fn main() { 892fn main() {
893 match E::X { 893 match E::X {
894 $0E::X => {} 894 $0E::X => todo!(),
895 E::Y => {} 895 E::Y => todo!(),
896 } 896 }
897} 897}
898"#, 898"#,
@@ -919,8 +919,8 @@ use foo::E::X;
919 919
920fn main() { 920fn main() {
921 match X { 921 match X {
922 $0X => {} 922 $0X => todo!(),
923 foo::E::Y => {} 923 foo::E::Y => todo!(),
924 } 924 }
925} 925}
926"#, 926"#,
@@ -947,7 +947,7 @@ fn foo(a: A) {
947 match a { 947 match a {
948 // foo bar baz 948 // foo bar baz
949 A::One => {} 949 A::One => {}
950 $0A::Two => {} 950 $0A::Two => todo!(),
951 // This is where the rest should be 951 // This is where the rest should be
952 } 952 }
953} 953}
@@ -971,8 +971,8 @@ fn foo(a: A) {
971enum A { One, Two } 971enum A { One, Two }
972fn foo(a: A) { 972fn foo(a: A) {
973 match a { 973 match a {
974 $0A::One => {} 974 $0A::One => todo!(),
975 A::Two => {} 975 A::Two => todo!(),
976 // foo bar baz 976 // foo bar baz
977 } 977 }
978} 978}
@@ -996,8 +996,8 @@ fn foo(a: A) {
996enum A { One, Two, } 996enum A { One, Two, }
997fn foo(a: A) { 997fn foo(a: A) {
998 match a { 998 match a {
999 $0A::One => {} 999 $0A::One => todo!(),
1000 A::Two => {} 1000 A::Two => todo!(),
1001 } 1001 }
1002} 1002}
1003"#, 1003"#,
@@ -1021,8 +1021,8 @@ fn foo(opt: Option<i32>) {
1021 r#" 1021 r#"
1022fn foo(opt: Option<i32>) { 1022fn foo(opt: Option<i32>) {
1023 match opt { 1023 match opt {
1024 Some(${0:_}) => {} 1024 Some(${0:_}) => todo!(),
1025 None => {} 1025 None => todo!(),
1026 } 1026 }
1027} 1027}
1028"#, 1028"#,
@@ -1054,9 +1054,9 @@ enum Test {
1054 1054
1055fn foo(t: Test) { 1055fn foo(t: Test) {
1056 m!(match t { 1056 m!(match t {
1057 $0Test::A => {} 1057 $0Test::A => todo!(),
1058 Test::B => {} 1058 Test::B => todo!(),
1059 Test::C => {} 1059 Test::C => todo!(),
1060}); 1060});
1061}"#, 1061}"#,
1062 ); 1062 );
@@ -1076,4 +1076,44 @@ fn foo(tuple: (A, A)) {
1076"#, 1076"#,
1077 ); 1077 );
1078 } 1078 }
1079
1080 #[test]
1081 fn adds_comma_before_new_arms() {
1082 check_assist(
1083 fill_match_arms,
1084 r#"
1085fn foo(t: bool) {
1086 match $0t {
1087 true => 1 + 2
1088 }
1089}"#,
1090 r#"
1091fn foo(t: bool) {
1092 match t {
1093 true => 1 + 2,
1094 $0false => todo!(),
1095 }
1096}"#,
1097 );
1098 }
1099
1100 #[test]
1101 fn does_not_add_extra_comma() {
1102 check_assist(
1103 fill_match_arms,
1104 r#"
1105fn foo(t: bool) {
1106 match $0t {
1107 true => 1 + 2,
1108 }
1109}"#,
1110 r#"
1111fn foo(t: bool) {
1112 match t {
1113 true => 1 + 2,
1114 $0false => todo!(),
1115 }
1116}"#,
1117 );
1118 }
1079} 1119}
diff --git a/crates/ide_assists/src/handlers/fix_visibility.rs b/crates/ide_assists/src/handlers/fix_visibility.rs
index 6c7824e55..89f7b2c2c 100644
--- a/crates/ide_assists/src/handlers/fix_visibility.rs
+++ b/crates/ide_assists/src/handlers/fix_visibility.rs
@@ -85,7 +85,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
85 85
86fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 86fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
87 let record_field: ast::RecordExprField = ctx.find_node_at_offset()?; 87 let record_field: ast::RecordExprField = ctx.find_node_at_offset()?;
88 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?; 88 let (record_field_def, _, _) = ctx.sema.resolve_record_field(&record_field)?;
89 89
90 let current_module = ctx.sema.scope(record_field.syntax()).module()?; 90 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
91 let visibility = record_field_def.visibility(ctx.db()); 91 let visibility = record_field_def.visibility(ctx.db());
diff --git a/crates/ide_assists/src/handlers/generate_getter.rs b/crates/ide_assists/src/handlers/generate_getter.rs
index df7d1bb95..09971226e 100644
--- a/crates/ide_assists/src/handlers/generate_getter.rs
+++ b/crates/ide_assists/src/handlers/generate_getter.rs
@@ -23,12 +23,46 @@ use crate::{
23// 23//
24// impl Person { 24// impl Person {
25// /// Get a reference to the person's name. 25// /// Get a reference to the person's name.
26// fn name(&self) -> &String { 26// fn $0name(&self) -> &str {
27// &self.name 27// self.name.as_str()
28// } 28// }
29// } 29// }
30// ``` 30// ```
31pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 31pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
32 generate_getter_impl(acc, ctx, false)
33}
34
35// Assist: generate_getter_mut
36//
37// Generate a mut getter method.
38//
39// ```
40// struct Person {
41// nam$0e: String,
42// }
43// ```
44// ->
45// ```
46// struct Person {
47// name: String,
48// }
49//
50// impl Person {
51// /// Get a mutable reference to the person's name.
52// fn $0name_mut(&mut self) -> &mut String {
53// &mut self.name
54// }
55// }
56// ```
57pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
58 generate_getter_impl(acc, ctx, true)
59}
60
61pub(crate) fn generate_getter_impl(
62 acc: &mut Assists,
63 ctx: &AssistContext,
64 mutable: bool,
65) -> Option<()> {
32 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 66 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
33 let field = ctx.find_node_at_offset::<ast::RecordField>()?; 67 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
34 68
@@ -37,39 +71,52 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
37 let field_ty = field.ty()?; 71 let field_ty = field.ty()?;
38 72
39 // Return early if we've found an existing fn 73 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string()); 74 let mut fn_name = to_lower_snake_case(&field_name.to_string());
75 if mutable {
76 format_to!(fn_name, "_mut");
77 }
41 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?; 78 let impl_def = find_struct_impl(&ctx, &ast::Adt::Struct(strukt.clone()), fn_name.as_str())?;
42 79
80 let (id, label) = if mutable {
81 ("generate_getter_mut", "Generate a mut getter method")
82 } else {
83 ("generate_getter", "Generate a getter method")
84 };
43 let target = field.syntax().text_range(); 85 let target = field.syntax().text_range();
44 acc.add_group( 86 acc.add_group(
45 &GroupLabel("Generate getter/setter".to_owned()), 87 &GroupLabel("Generate getter/setter".to_owned()),
46 AssistId("generate_getter", AssistKind::Generate), 88 AssistId(id, AssistKind::Generate),
47 "Generate a getter method", 89 label,
48 target, 90 target,
49 |builder| { 91 |builder| {
50 let mut buf = String::with_capacity(512); 92 let mut buf = String::with_capacity(512);
51 93
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() { 94 if impl_def.is_some() {
57 buf.push('\n'); 95 buf.push('\n');
58 } 96 }
59 97
60 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); 98 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
99 let (ty, body) = if mutable {
100 (format!("&mut {}", field_ty), format!("&mut self.{}", field_name))
101 } else {
102 useless_type_special_case(&field_name.to_string(), &field_ty)
103 .unwrap_or_else(|| (format!("&{}", field_ty), format!("&self.{}", field_name)))
104 };
105
61 format_to!( 106 format_to!(
62 buf, 107 buf,
63 " /// Get a reference to the {}'s {}. 108 " /// Get a {}reference to the {}'s {}.
64 {}fn {}(&self) -> &{} {{ 109 {}fn {}(&{}self) -> {} {{
65 &self.{} 110 {}
66 }}", 111 }}",
67 strukt_name_spaced, 112 mutable.then(|| "mutable ").unwrap_or_default(),
68 fn_name_spaced, 113 to_lower_snake_case(&strukt_name.to_string()).replace('_', " "),
114 fn_name.trim_end_matches("_mut").replace('_', " "),
69 vis, 115 vis,
70 fn_name, 116 fn_name,
71 field_ty, 117 mutable.then(|| "mut ").unwrap_or_default(),
72 fn_name, 118 ty,
119 body,
73 ); 120 );
74 121
75 let start_offset = impl_def 122 let start_offset = impl_def
@@ -79,56 +126,120 @@ pub(crate) fn generate_getter(acc: &mut Assists, ctx: &AssistContext) -> Option<
79 strukt.syntax().text_range().end() 126 strukt.syntax().text_range().end()
80 }); 127 });
81 128
82 builder.insert(start_offset, buf); 129 match ctx.config.snippet_cap {
130 Some(cap) => {
131 builder.insert_snippet(cap, start_offset, buf.replacen("fn ", "fn $0", 1))
132 }
133 None => builder.insert(start_offset, buf),
134 }
83 }, 135 },
84 ) 136 )
85} 137}
86 138
139fn useless_type_special_case(field_name: &str, field_ty: &ast::Type) -> Option<(String, String)> {
140 if field_ty.to_string() == "String" {
141 cov_mark::hit!(useless_type_special_case);
142 return Some(("&str".to_string(), format!("self.{}.as_str()", field_name)));
143 }
144 if let Some(arg) = ty_ctor(field_ty, "Vec") {
145 return Some((format!("&[{}]", arg), format!("self.{}.as_slice()", field_name)));
146 }
147 if let Some(arg) = ty_ctor(field_ty, "Box") {
148 return Some((format!("&{}", arg), format!("self.{}.as_ref()", field_name)));
149 }
150 if let Some(arg) = ty_ctor(field_ty, "Option") {
151 return Some((format!("Option<&{}>", arg), format!("self.{}.as_ref()", field_name)));
152 }
153 None
154}
155
156// FIXME: This should rely on semantic info.
157fn ty_ctor(ty: &ast::Type, ctor: &str) -> Option<String> {
158 let res = ty.to_string().strip_prefix(ctor)?.strip_prefix('<')?.strip_suffix('>')?.to_string();
159 Some(res)
160}
161
87#[cfg(test)] 162#[cfg(test)]
88mod tests { 163mod tests {
89 use crate::tests::{check_assist, check_assist_not_applicable}; 164 use crate::tests::{check_assist, check_assist_not_applicable};
90 165
91 use super::*; 166 use super::*;
92 167
93 fn check_not_applicable(ra_fixture: &str) {
94 check_assist_not_applicable(generate_getter, ra_fixture)
95 }
96
97 #[test] 168 #[test]
98 fn test_generate_getter_from_field() { 169 fn test_generate_getter_from_field() {
99 check_assist( 170 check_assist(
100 generate_getter, 171 generate_getter,
101 r#" 172 r#"
102struct Context<T: Clone> { 173struct Context {
103 dat$0a: T, 174 dat$0a: Data,
104}"#, 175}
176"#,
105 r#" 177 r#"
106struct Context<T: Clone> { 178struct Context {
107 data: T, 179 data: Data,
108} 180}
109 181
110impl<T: Clone> Context<T> { 182impl Context {
111 /// Get a reference to the context's data. 183 /// Get a reference to the context's data.
112 fn data(&self) -> &T { 184 fn $0data(&self) -> &Data {
113 &self.data 185 &self.data
114 } 186 }
115}"#, 187}
188"#,
189 );
190
191 check_assist(
192 generate_getter_mut,
193 r#"
194struct Context {
195 dat$0a: Data,
196}
197"#,
198 r#"
199struct Context {
200 data: Data,
201}
202
203impl Context {
204 /// Get a mutable reference to the context's data.
205 fn $0data_mut(&mut self) -> &mut Data {
206 &mut self.data
207 }
208}
209"#,
116 ); 210 );
117 } 211 }
118 212
119 #[test] 213 #[test]
120 fn test_generate_getter_already_implemented() { 214 fn test_generate_getter_already_implemented() {
121 check_not_applicable( 215 check_assist_not_applicable(
216 generate_getter,
122 r#" 217 r#"
123struct Context<T: Clone> { 218struct Context {
124 dat$0a: T, 219 dat$0a: Data,
125} 220}
126 221
127impl<T: Clone> Context<T> { 222impl Context {
128 fn data(&self) -> &T { 223 fn data(&self) -> &Data {
129 &self.data 224 &self.data
130 } 225 }
131}"#, 226}
227"#,
228 );
229
230 check_assist_not_applicable(
231 generate_getter_mut,
232 r#"
233struct Context {
234 dat$0a: Data,
235}
236
237impl Context {
238 fn data_mut(&mut self) -> &mut Data {
239 &mut self.data
240 }
241}
242"#,
132 ); 243 );
133 } 244 }
134 245
@@ -137,20 +248,22 @@ impl<T: Clone> Context<T> {
137 check_assist( 248 check_assist(
138 generate_getter, 249 generate_getter,
139 r#" 250 r#"
140pub(crate) struct Context<T: Clone> { 251pub(crate) struct Context {
141 dat$0a: T, 252 dat$0a: Data,
142}"#, 253}
254"#,
143 r#" 255 r#"
144pub(crate) struct Context<T: Clone> { 256pub(crate) struct Context {
145 data: T, 257 data: Data,
146} 258}
147 259
148impl<T: Clone> Context<T> { 260impl Context {
149 /// Get a reference to the context's data. 261 /// Get a reference to the context's data.
150 pub(crate) fn data(&self) -> &T { 262 pub(crate) fn $0data(&self) -> &Data {
151 &self.data 263 &self.data
152 } 264 }
153}"#, 265}
266"#,
154 ); 267 );
155 } 268 }
156 269
@@ -159,34 +272,105 @@ impl<T: Clone> Context<T> {
159 check_assist( 272 check_assist(
160 generate_getter, 273 generate_getter,
161 r#" 274 r#"
162struct Context<T: Clone> { 275struct Context {
163 data: T, 276 data: Data,
164 cou$0nt: usize, 277 cou$0nt: usize,
165} 278}
166 279
167impl<T: Clone> Context<T> { 280impl Context {
168 /// Get a reference to the context's data. 281 /// Get a reference to the context's data.
169 fn data(&self) -> &T { 282 fn data(&self) -> &Data {
170 &self.data 283 &self.data
171 } 284 }
172}"#, 285}
286"#,
173 r#" 287 r#"
174struct Context<T: Clone> { 288struct Context {
175 data: T, 289 data: Data,
176 count: usize, 290 count: usize,
177} 291}
178 292
179impl<T: Clone> Context<T> { 293impl Context {
180 /// Get a reference to the context's data. 294 /// Get a reference to the context's data.
181 fn data(&self) -> &T { 295 fn data(&self) -> &Data {
182 &self.data 296 &self.data
183 } 297 }
184 298
185 /// Get a reference to the context's count. 299 /// Get a reference to the context's count.
186 fn count(&self) -> &usize { 300 fn $0count(&self) -> &usize {
187 &self.count 301 &self.count
188 } 302 }
189}"#, 303}
304"#,
305 );
306 }
307
308 #[test]
309 fn test_special_cases() {
310 cov_mark::check!(useless_type_special_case);
311 check_assist(
312 generate_getter,
313 r#"
314struct S { foo: $0String }
315"#,
316 r#"
317struct S { foo: String }
318
319impl S {
320 /// Get a reference to the s's foo.
321 fn $0foo(&self) -> &str {
322 self.foo.as_str()
323 }
324}
325"#,
326 );
327 check_assist(
328 generate_getter,
329 r#"
330struct S { foo: $0Box<Sweets> }
331"#,
332 r#"
333struct S { foo: Box<Sweets> }
334
335impl S {
336 /// Get a reference to the s's foo.
337 fn $0foo(&self) -> &Sweets {
338 self.foo.as_ref()
339 }
340}
341"#,
342 );
343 check_assist(
344 generate_getter,
345 r#"
346struct S { foo: $0Vec<()> }
347"#,
348 r#"
349struct S { foo: Vec<()> }
350
351impl S {
352 /// Get a reference to the s's foo.
353 fn $0foo(&self) -> &[()] {
354 self.foo.as_slice()
355 }
356}
357"#,
358 );
359 check_assist(
360 generate_getter,
361 r#"
362struct S { foo: $0Option<Failure> }
363"#,
364 r#"
365struct S { foo: Option<Failure> }
366
367impl S {
368 /// Get a reference to the s's foo.
369 fn $0foo(&self) -> Option<&Failure> {
370 self.foo.as_ref()
371 }
372}
373"#,
190 ); 374 );
191 } 375 }
192} 376}
diff --git a/crates/ide_assists/src/handlers/generate_getter_mut.rs b/crates/ide_assists/src/handlers/generate_getter_mut.rs
deleted file mode 100644
index 821c2eed5..000000000
--- a/crates/ide_assists/src/handlers/generate_getter_mut.rs
+++ /dev/null
@@ -1,195 +0,0 @@
1use stdx::{format_to, to_lower_snake_case};
2use syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
3
4use crate::{
5 utils::{find_impl_block_end, find_struct_impl, generate_impl_text},
6 AssistContext, AssistId, AssistKind, Assists, GroupLabel,
7};
8
9// Assist: generate_getter_mut
10//
11// Generate a mut getter method.
12//
13// ```
14// struct Person {
15// nam$0e: String,
16// }
17// ```
18// ->
19// ```
20// struct Person {
21// name: String,
22// }
23//
24// impl Person {
25// /// Get a mutable reference to the person's name.
26// fn name_mut(&mut self) -> &mut String {
27// &mut self.name
28// }
29// }
30// ```
31pub(crate) fn generate_getter_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
32 let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
33 let field = ctx.find_node_at_offset::<ast::RecordField>()?;
34
35 let strukt_name = strukt.name()?;
36 let field_name = field.name()?;
37 let field_ty = field.ty()?;
38
39 // Return early if we've found an existing fn
40 let fn_name = to_lower_snake_case(&field_name.to_string());
41 let impl_def = find_struct_impl(
42 &ctx,
43 &ast::Adt::Struct(strukt.clone()),
44 format!("{}_mut", fn_name).as_str(),
45 )?;
46
47 let target = field.syntax().text_range();
48 acc.add_group(
49 &GroupLabel("Generate getter/setter".to_owned()),
50 AssistId("generate_getter_mut", AssistKind::Generate),
51 "Generate a mut getter method",
52 target,
53 |builder| {
54 let mut buf = String::with_capacity(512);
55 let fn_name_spaced = fn_name.replace('_', " ");
56 let strukt_name_spaced =
57 to_lower_snake_case(&strukt_name.to_string()).replace('_', " ");
58
59 if impl_def.is_some() {
60 buf.push('\n');
61 }
62
63 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
64 format_to!(
65 buf,
66 " /// Get a mutable reference to the {}'s {}.
67 {}fn {}_mut(&mut self) -> &mut {} {{
68 &mut self.{}
69 }}",
70 strukt_name_spaced,
71 fn_name_spaced,
72 vis,
73 fn_name,
74 field_ty,
75 fn_name,
76 );
77
78 let start_offset = impl_def
79 .and_then(|impl_def| find_impl_block_end(impl_def, &mut buf))
80 .unwrap_or_else(|| {
81 buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
82 strukt.syntax().text_range().end()
83 });
84
85 builder.insert(start_offset, buf);
86 },
87 )
88}
89
90#[cfg(test)]
91mod tests {
92 use crate::tests::{check_assist, check_assist_not_applicable};
93
94 use super::*;
95
96 fn check_not_applicable(ra_fixture: &str) {
97 check_assist_not_applicable(generate_getter_mut, ra_fixture)
98 }
99
100 #[test]
101 fn test_generate_getter_mut_from_field() {
102 check_assist(
103 generate_getter_mut,
104 r#"
105struct Context<T: Clone> {
106 dat$0a: T,
107}"#,
108 r#"
109struct Context<T: Clone> {
110 data: T,
111}
112
113impl<T: Clone> Context<T> {
114 /// Get a mutable reference to the context's data.
115 fn data_mut(&mut self) -> &mut T {
116 &mut self.data
117 }
118}"#,
119 );
120 }
121
122 #[test]
123 fn test_generate_getter_mut_already_implemented() {
124 check_not_applicable(
125 r#"
126struct Context<T: Clone> {
127 dat$0a: T,
128}
129
130impl<T: Clone> Context<T> {
131 fn data_mut(&mut self) -> &mut T {
132 &mut self.data
133 }
134}"#,
135 );
136 }
137
138 #[test]
139 fn test_generate_getter_mut_from_field_with_visibility_marker() {
140 check_assist(
141 generate_getter_mut,
142 r#"
143pub(crate) struct Context<T: Clone> {
144 dat$0a: T,
145}"#,
146 r#"
147pub(crate) struct Context<T: Clone> {
148 data: T,
149}
150
151impl<T: Clone> Context<T> {
152 /// Get a mutable reference to the context's data.
153 pub(crate) fn data_mut(&mut self) -> &mut T {
154 &mut self.data
155 }
156}"#,
157 );
158 }
159
160 #[test]
161 fn test_multiple_generate_getter_mut() {
162 check_assist(
163 generate_getter_mut,
164 r#"
165struct Context<T: Clone> {
166 data: T,
167 cou$0nt: usize,
168}
169
170impl<T: Clone> Context<T> {
171 /// Get a mutable reference to the context's data.
172 fn data_mut(&mut self) -> &mut T {
173 &mut self.data
174 }
175}"#,
176 r#"
177struct Context<T: Clone> {
178 data: T,
179 count: usize,
180}
181
182impl<T: Clone> Context<T> {
183 /// Get a mutable reference to the context's data.
184 fn data_mut(&mut self) -> &mut T {
185 &mut self.data
186 }
187
188 /// Get a mutable reference to the context's count.
189 fn count_mut(&mut self) -> &mut usize {
190 &mut self.count
191 }
192}"#,
193 );
194 }
195}
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 4cd82f8c1..16af72927 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -206,7 +206,6 @@ mod handlers {
206 mod generate_enum_projection_method; 206 mod generate_enum_projection_method;
207 mod generate_from_impl_for_enum; 207 mod generate_from_impl_for_enum;
208 mod generate_function; 208 mod generate_function;
209 mod generate_getter_mut;
210 mod generate_getter; 209 mod generate_getter;
211 mod generate_impl; 210 mod generate_impl;
212 mod generate_new; 211 mod generate_new;
@@ -276,8 +275,8 @@ mod handlers {
276 generate_enum_projection_method::generate_enum_try_into_method, 275 generate_enum_projection_method::generate_enum_try_into_method,
277 generate_from_impl_for_enum::generate_from_impl_for_enum, 276 generate_from_impl_for_enum::generate_from_impl_for_enum,
278 generate_function::generate_function, 277 generate_function::generate_function,
279 generate_getter_mut::generate_getter_mut,
280 generate_getter::generate_getter, 278 generate_getter::generate_getter,
279 generate_getter::generate_getter_mut,
281 generate_impl::generate_impl, 280 generate_impl::generate_impl,
282 generate_new::generate_new, 281 generate_new::generate_new,
283 generate_setter::generate_setter, 282 generate_setter::generate_setter,
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs
index 6a9231e07..2b7c2d581 100644
--- a/crates/ide_assists/src/tests.rs
+++ b/crates/ide_assists/src/tests.rs
@@ -215,8 +215,8 @@ fn assist_order_field_struct() {
215 215
216 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); 216 assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)");
217 assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`"); 217 assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`");
218 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
219 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); 218 assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method");
219 assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method");
220 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); 220 assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method");
221 assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`"); 221 assert_eq!(assists.next().expect("expected assist").label, "Add `#[derive]`");
222} 222}
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 4406406a2..de5d9e55a 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -455,8 +455,8 @@ enum Action { Move { distance: u32 }, Stop }
455 455
456fn handle(action: Action) { 456fn handle(action: Action) {
457 match action { 457 match action {
458 $0Action::Move { distance } => {} 458 $0Action::Move { distance } => todo!(),
459 Action::Stop => {} 459 Action::Stop => todo!(),
460 } 460 }
461} 461}
462"#####, 462"#####,
@@ -786,8 +786,8 @@ struct Person {
786 786
787impl Person { 787impl Person {
788 /// Get a reference to the person's name. 788 /// Get a reference to the person's name.
789 fn name(&self) -> &String { 789 fn $0name(&self) -> &str {
790 &self.name 790 self.name.as_str()
791 } 791 }
792} 792}
793"#####, 793"#####,
@@ -810,7 +810,7 @@ struct Person {
810 810
811impl Person { 811impl Person {
812 /// Get a mutable reference to the person's name. 812 /// Get a mutable reference to the person's name.
813 fn name_mut(&mut self) -> &mut String { 813 fn $0name_mut(&mut self) -> &mut String {
814 &mut self.name 814 &mut self.name
815 } 815 }
816} 816}