aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists')
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs98
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs13
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs17
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs146
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs4
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs17
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs156
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs5
-rw-r--r--crates/ra_assists/src/handlers/extract_variable.rs137
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs43
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs6
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs21
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs15
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs15
-rw-r--r--crates/ra_assists/src/handlers/generate_derive.rs (renamed from crates/ra_assists/src/handlers/add_derive.rs)63
-rw-r--r--crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs (renamed from crates/ra_assists/src/handlers/add_from_impl_for_enum.rs)22
-rw-r--r--crates/ra_assists/src/handlers/generate_function.rs (renamed from crates/ra_assists/src/handlers/add_function.rs)87
-rw-r--r--crates/ra_assists/src/handlers/generate_impl.rs109
-rw-r--r--crates/ra_assists/src/handlers/generate_new.rs (renamed from crates/ra_assists/src/handlers/add_new.rs)44
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs22
-rw-r--r--crates/ra_assists/src/handlers/introduce_named_lifetime.rs6
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs4
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs13
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs45
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs55
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs29
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs72
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs4
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs13
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs17
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs50
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs41
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs4
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs60
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs4
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs4
-rw-r--r--crates/ra_assists/src/lib.rs33
-rw-r--r--crates/ra_assists/src/tests/generated.rs218
41 files changed, 1009 insertions, 715 deletions
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index fa70c8496..acb07e36a 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -8,7 +8,7 @@ use stdx::SepBy;
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistContext, Assists}, 10 assist_context::{AssistContext, Assists},
11 AssistId, 11 AssistId, AssistKind,
12}; 12};
13 13
14// Assist: add_custom_impl 14// Assist: add_custom_impl
@@ -52,7 +52,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<
52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); 52 format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name);
53 53
54 let target = attr.syntax().text_range(); 54 let target = attr.syntax().text_range();
55 acc.add(AssistId("add_custom_impl"), label, target, |builder| { 55 acc.add(AssistId("add_custom_impl", AssistKind::Refactor), label, target, |builder| {
56 let new_attr_input = input 56 let new_attr_input = input
57 .syntax() 57 .syntax()
58 .descendants_with_tokens() 58 .descendants_with_tokens()
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 11df922a2..39a5321d1 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 TextRange, 4 TextRange,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: add_explicit_type 9// Assist: add_explicit_type
10// 10//
@@ -59,7 +59,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
59 59
60 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; 60 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
61 acc.add( 61 acc.add(
62 AssistId("add_explicit_type"), 62 AssistId("add_explicit_type", AssistKind::RefactorRewrite),
63 format!("Insert explicit type `{}`", inferred_type), 63 format!("Insert explicit type `{}`", inferred_type),
64 pat_range, 64 pat_range,
65 |builder| match ascribed_ty { 65 |builder| match ascribed_ty {
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
deleted file mode 100644
index eceba7d0a..000000000
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ /dev/null
@@ -1,98 +0,0 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy};
3
4use crate::{AssistContext, AssistId, Assists};
5
6// Assist: add_impl
7//
8// Adds a new inherent impl for a type.
9//
10// ```
11// struct Ctx<T: Clone> {
12// data: T,<|>
13// }
14// ```
15// ->
16// ```
17// struct Ctx<T: Clone> {
18// data: T,
19// }
20//
21// impl<T: Clone> Ctx<T> {
22// $0
23// }
24// ```
25pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range();
29 acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| {
30 let type_params = nominal.type_param_list();
31 let start_offset = nominal.syntax().text_range().end();
32 let mut buf = String::new();
33 buf.push_str("\n\nimpl");
34 if let Some(type_params) = &type_params {
35 format_to!(buf, "{}", type_params.syntax());
36 }
37 buf.push_str(" ");
38 buf.push_str(name.text().as_str());
39 if let Some(type_params) = type_params {
40 let lifetime_params = type_params
41 .lifetime_params()
42 .filter_map(|it| it.lifetime_token())
43 .map(|it| it.text().clone());
44 let type_params =
45 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone());
46
47 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
48 format_to!(buf, "<{}>", generic_params)
49 }
50 match ctx.config.snippet_cap {
51 Some(cap) => {
52 buf.push_str(" {\n $0\n}");
53 edit.insert_snippet(cap, start_offset, buf);
54 }
55 None => {
56 buf.push_str(" {\n}");
57 edit.insert(start_offset, buf);
58 }
59 }
60 })
61}
62
63#[cfg(test)]
64mod tests {
65 use crate::tests::{check_assist, check_assist_target};
66
67 use super::*;
68
69 #[test]
70 fn test_add_impl() {
71 check_assist(add_impl, "struct Foo {<|>}\n", "struct Foo {}\n\nimpl Foo {\n $0\n}\n");
72 check_assist(
73 add_impl,
74 "struct Foo<T: Clone> {<|>}",
75 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
76 );
77 check_assist(
78 add_impl,
79 "struct Foo<'a, T: Foo<'a>> {<|>}",
80 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
81 );
82 }
83
84 #[test]
85 fn add_impl_target() {
86 check_assist_target(
87 add_impl,
88 "
89struct SomeThingIrrelevant;
90/// Has a lifetime parameter
91struct Foo<'a, T: Foo<'a>> {<|>}
92struct EvenMoreIrrelevant;
93",
94 "/// Has a lifetime parameter
95struct Foo<'a, T: Foo<'a>> {}",
96 );
97 }
98}
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index 77e092f62..d6aaf53f1 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -12,7 +12,7 @@ use crate::{
12 assist_context::{AssistContext, Assists}, 12 assist_context::{AssistContext, Assists},
13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, 13 ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor}, 14 utils::{get_missing_assoc_items, render_snippet, resolve_target_trait, Cursor},
15 AssistId, 15 AssistId, AssistKind,
16}; 16};
17 17
18#[derive(PartialEq)] 18#[derive(PartialEq)]
@@ -147,7 +147,7 @@ fn add_missing_impl_members_inner(
147 } 147 }
148 148
149 let target = impl_def.syntax().text_range(); 149 let target = impl_def.syntax().text_range();
150 acc.add(AssistId(assist_id), label, target, |builder| { 150 acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
151 let n_existing_items = impl_item_list.assoc_items().count(); 151 let n_existing_items = impl_item_list.assoc_items().count();
152 let source_scope = ctx.sema.scope_for_def(trait_); 152 let source_scope = ctx.sema.scope_for_def(trait_);
153 let target_scope = ctx.sema.scope(impl_item_list.syntax()); 153 let target_scope = ctx.sema.scope(impl_item_list.syntax());
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
index 26acf81f2..f7e1a7b05 100644
--- a/crates/ra_assists/src/handlers/add_turbo_fish.rs
+++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs
@@ -4,7 +4,7 @@ use test_utils::mark;
4 4
5use crate::{ 5use crate::{
6 assist_context::{AssistContext, Assists}, 6 assist_context::{AssistContext, Assists},
7 AssistId, 7 AssistId, AssistKind,
8}; 8};
9 9
10// Assist: add_turbo_fish 10// Assist: add_turbo_fish
@@ -45,12 +45,15 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<(
45 mark::hit!(add_turbo_fish_non_generic); 45 mark::hit!(add_turbo_fish_non_generic);
46 return None; 46 return None;
47 } 47 }
48 acc.add(AssistId("add_turbo_fish"), "Add `::<>`", ident.text_range(), |builder| { 48 acc.add(
49 match ctx.config.snippet_cap { 49 AssistId("add_turbo_fish", AssistKind::RefactorRewrite),
50 "Add `::<>`",
51 ident.text_range(),
52 |builder| match ctx.config.snippet_cap {
50 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), 53 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
51 None => builder.insert(ident.text_range().end(), "::<_>"), 54 None => builder.insert(ident.text_range().end(), "::<_>"),
52 } 55 },
53 }) 56 )
54} 57}
55 58
56#[cfg(test)] 59#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 233e8fb8e..de701f8b8 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -1,6 +1,6 @@
1use ra_syntax::ast::{self, AstNode}; 1use ra_syntax::ast::{self, AstNode};
2 2
3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; 3use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: apply_demorgan 5// Assist: apply_demorgan
6// 6//
@@ -39,11 +39,16 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
39 let rhs_range = rhs.syntax().text_range(); 39 let rhs_range = rhs.syntax().text_range();
40 let not_rhs = invert_boolean_expression(rhs); 40 let not_rhs = invert_boolean_expression(rhs);
41 41
42 acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { 42 acc.add(
43 edit.replace(op_range, opposite_op); 43 AssistId("apply_demorgan", AssistKind::RefactorRewrite),
44 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); 44 "Apply De Morgan's law",
45 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); 45 op_range,
46 }) 46 |edit| {
47 edit.replace(op_range, opposite_op);
48 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
49 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
50 },
51 )
47} 52}
48 53
49// Return the opposite text for a given logical operator, if it makes sense 54// Return the opposite text for a given logical operator, if it makes sense
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index 7b6499a08..947be3b9b 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -13,7 +13,9 @@ use ra_syntax::{
13}; 13};
14use rustc_hash::FxHashSet; 14use rustc_hash::FxHashSet;
15 15
16use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; 16use crate::{
17 utils::insert_use_statement, AssistContext, AssistId, AssistKind, Assists, GroupLabel,
18};
17 19
18// Assist: auto_import 20// Assist: auto_import
19// 21//
@@ -46,7 +48,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
46 for import in proposed_imports { 48 for import in proposed_imports {
47 acc.add_group( 49 acc.add_group(
48 &group, 50 &group,
49 AssistId("auto_import"), 51 AssistId("auto_import", AssistKind::QuickFix),
50 format!("Import `{}`", &import), 52 format!("Import `{}`", &import),
51 range, 53 range,
52 |builder| { 54 |builder| {
@@ -811,6 +813,146 @@ fn main() {
811 } 813 }
812 814
813 #[test] 815 #[test]
816 fn trait_method_cross_crate() {
817 check_assist(
818 auto_import,
819 r"
820 //- /main.rs crate:main deps:dep
821 fn main() {
822 let test_struct = dep::test_mod::TestStruct {};
823 test_struct.test_meth<|>od()
824 }
825 //- /dep.rs crate:dep
826 pub mod test_mod {
827 pub trait TestTrait {
828 fn test_method(&self);
829 }
830 pub struct TestStruct {}
831 impl TestTrait for TestStruct {
832 fn test_method(&self) {}
833 }
834 }
835 ",
836 r"
837 use dep::test_mod::TestTrait;
838
839 fn main() {
840 let test_struct = dep::test_mod::TestStruct {};
841 test_struct.test_method()
842 }
843 ",
844 );
845 }
846
847 #[test]
848 fn assoc_fn_cross_crate() {
849 check_assist(
850 auto_import,
851 r"
852 //- /main.rs crate:main deps:dep
853 fn main() {
854 dep::test_mod::TestStruct::test_func<|>tion
855 }
856 //- /dep.rs crate:dep
857 pub mod test_mod {
858 pub trait TestTrait {
859 fn test_function();
860 }
861 pub struct TestStruct {}
862 impl TestTrait for TestStruct {
863 fn test_function() {}
864 }
865 }
866 ",
867 r"
868 use dep::test_mod::TestTrait;
869
870 fn main() {
871 dep::test_mod::TestStruct::test_function
872 }
873 ",
874 );
875 }
876
877 #[test]
878 fn assoc_const_cross_crate() {
879 check_assist(
880 auto_import,
881 r"
882 //- /main.rs crate:main deps:dep
883 fn main() {
884 dep::test_mod::TestStruct::CONST<|>
885 }
886 //- /dep.rs crate:dep
887 pub mod test_mod {
888 pub trait TestTrait {
889 const CONST: bool;
890 }
891 pub struct TestStruct {}
892 impl TestTrait for TestStruct {
893 const CONST: bool = true;
894 }
895 }
896 ",
897 r"
898 use dep::test_mod::TestTrait;
899
900 fn main() {
901 dep::test_mod::TestStruct::CONST
902 }
903 ",
904 );
905 }
906
907 #[test]
908 fn assoc_fn_as_method_cross_crate() {
909 check_assist_not_applicable(
910 auto_import,
911 r"
912 //- /main.rs crate:main deps:dep
913 fn main() {
914 let test_struct = dep::test_mod::TestStruct {};
915 test_struct.test_func<|>tion()
916 }
917 //- /dep.rs crate:dep
918 pub mod test_mod {
919 pub trait TestTrait {
920 fn test_function();
921 }
922 pub struct TestStruct {}
923 impl TestTrait for TestStruct {
924 fn test_function() {}
925 }
926 }
927 ",
928 );
929 }
930
931 #[test]
932 fn private_trait_cross_crate() {
933 check_assist_not_applicable(
934 auto_import,
935 r"
936 //- /main.rs crate:main deps:dep
937 fn main() {
938 let test_struct = dep::test_mod::TestStruct {};
939 test_struct.test_meth<|>od()
940 }
941 //- /dep.rs crate:dep
942 pub mod test_mod {
943 trait TestTrait {
944 fn test_method(&self);
945 }
946 pub struct TestStruct {}
947 impl TestTrait for TestStruct {
948 fn test_method(&self) {}
949 }
950 }
951 ",
952 );
953 }
954
955 #[test]
814 fn not_applicable_for_imported_trait_for_method() { 956 fn not_applicable_for_imported_trait_for_method() {
815 check_assist_not_applicable( 957 check_assist_not_applicable(
816 auto_import, 958 auto_import,
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
index 855baf187..24e5f6963 100644
--- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs
+++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 AstNode, SyntaxNode, 3 AstNode, SyntaxNode,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7use test_utils::mark; 7use test_utils::mark;
8 8
9// Assist: change_return_type_to_result 9// Assist: change_return_type_to_result
@@ -35,7 +35,7 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
35 let block_expr = &fn_def.body()?; 35 let block_expr = &fn_def.body()?;
36 36
37 acc.add( 37 acc.add(
38 AssistId("change_return_type_to_result"), 38 AssistId("change_return_type_to_result", AssistKind::RefactorRewrite),
39 "Change return type to Result", 39 "Change return type to Result",
40 type_ref.syntax().text_range(), 40 type_ref.syntax().text_range(),
41 |builder| { 41 |builder| {
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 157c7b665..703ee2143 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -6,7 +6,7 @@ use ra_syntax::{
6}; 6};
7use test_utils::mark; 7use test_utils::mark;
8 8
9use crate::{utils::vis_offset, AssistContext, AssistId, Assists}; 9use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
10 10
11// Assist: change_visibility 11// Assist: change_visibility
12// 12//
@@ -62,16 +62,21 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
62 return None; 62 return None;
63 }; 63 };
64 64
65 acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { 65 acc.add(
66 edit.insert(offset, "pub(crate) "); 66 AssistId("change_visibility", AssistKind::RefactorRewrite),
67 }) 67 "Change visibility to pub(crate)",
68 target,
69 |edit| {
70 edit.insert(offset, "pub(crate) ");
71 },
72 )
68} 73}
69 74
70fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { 75fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
71 if vis.syntax().text() == "pub" { 76 if vis.syntax().text() == "pub" {
72 let target = vis.syntax().text_range(); 77 let target = vis.syntax().text_range();
73 return acc.add( 78 return acc.add(
74 AssistId("change_visibility"), 79 AssistId("change_visibility", AssistKind::RefactorRewrite),
75 "Change Visibility to pub(crate)", 80 "Change Visibility to pub(crate)",
76 target, 81 target,
77 |edit| { 82 |edit| {
@@ -82,7 +87,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
82 if vis.syntax().text() == "pub(crate)" { 87 if vis.syntax().text() == "pub(crate)" {
83 let target = vis.syntax().text_range(); 88 let target = vis.syntax().text_range();
84 return acc.add( 89 return acc.add(
85 AssistId("change_visibility"), 90 AssistId("change_visibility", AssistKind::RefactorRewrite),
86 "Change visibility to pub", 91 "Change visibility to pub",
87 target, 92 target,
88 |edit| { 93 |edit| {
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index dfade7432..330459f3c 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -15,7 +15,7 @@ use ra_syntax::{
15use crate::{ 15use crate::{
16 assist_context::{AssistContext, Assists}, 16 assist_context::{AssistContext, Assists},
17 utils::invert_boolean_expression, 17 utils::invert_boolean_expression,
18 AssistId, 18 AssistId, AssistKind,
19}; 19};
20 20
21// Assist: convert_to_guarded_return 21// Assist: convert_to_guarded_return
@@ -99,86 +99,92 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 99 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
100 100
101 let target = if_expr.syntax().text_range(); 101 let target = if_expr.syntax().text_range();
102 acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { 102 acc.add(
103 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 103 AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
104 let new_block = match if_let_pat { 104 "Convert to guarded return",
105 None => { 105 target,
106 // If. 106 |edit| {
107 let new_expr = { 107 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
108 let then_branch = 108 let new_block = match if_let_pat {
109 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 109 None => {
110 let cond = invert_boolean_expression(cond_expr); 110 // If.
111 make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) 111 let new_expr = {
112 }; 112 let then_branch =
113 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 113 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
114 } 114 let cond = invert_boolean_expression(cond_expr);
115 Some((path, bound_ident)) => { 115 make::expr_if(make::condition(cond, None), then_branch)
116 // If-let. 116 .indent(if_indent_level)
117 let match_expr = {
118 let happy_arm = {
119 let pat = make::tuple_struct_pat(
120 path,
121 once(make::bind_pat(make::name("it")).into()),
122 );
123 let expr = {
124 let name_ref = make::name_ref("it");
125 let segment = make::path_segment(name_ref);
126 let path = make::path_unqualified(segment);
127 make::expr_path(path)
128 };
129 make::match_arm(once(pat.into()), expr)
130 }; 117 };
118 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
119 }
120 Some((path, bound_ident)) => {
121 // If-let.
122 let match_expr = {
123 let happy_arm = {
124 let pat = make::tuple_struct_pat(
125 path,
126 once(make::bind_pat(make::name("it")).into()),
127 );
128 let expr = {
129 let name_ref = make::name_ref("it");
130 let segment = make::path_segment(name_ref);
131 let path = make::path_unqualified(segment);
132 make::expr_path(path)
133 };
134 make::match_arm(once(pat.into()), expr)
135 };
131 136
132 let sad_arm = make::match_arm( 137 let sad_arm = make::match_arm(
133 // FIXME: would be cool to use `None` or `Err(_)` if appropriate 138 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
134 once(make::placeholder_pat().into()), 139 once(make::placeholder_pat().into()),
135 early_expression, 140 early_expression,
136 ); 141 );
137 142
138 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) 143 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
139 }; 144 };
140 145
141 let let_stmt = make::let_stmt( 146 let let_stmt = make::let_stmt(
142 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), 147 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
143 Some(match_expr), 148 Some(match_expr),
149 );
150 let let_stmt = let_stmt.indent(if_indent_level);
151 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
152 }
153 };
154 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
155
156 fn replace(
157 new_expr: &SyntaxNode,
158 then_block: &ast::BlockExpr,
159 parent_block: &ast::BlockExpr,
160 if_expr: &ast::IfExpr,
161 ) -> SyntaxNode {
162 let then_block_items = then_block.dedent(IndentLevel(1));
163 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
164 let end_of_then =
165 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
166 end_of_then.prev_sibling_or_token().unwrap()
167 } else {
168 end_of_then
169 };
170 let mut then_statements = new_expr.children_with_tokens().chain(
171 then_block_items
172 .syntax()
173 .children_with_tokens()
174 .skip(1)
175 .take_while(|i| *i != end_of_then),
144 ); 176 );
145 let let_stmt = let_stmt.indent(if_indent_level); 177 replace_children(
146 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) 178 &parent_block.syntax(),
179 RangeInclusive::new(
180 if_expr.clone().syntax().clone().into(),
181 if_expr.syntax().clone().into(),
182 ),
183 &mut then_statements,
184 )
147 } 185 }
148 }; 186 },
149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 187 )
150
151 fn replace(
152 new_expr: &SyntaxNode,
153 then_block: &ast::BlockExpr,
154 parent_block: &ast::BlockExpr,
155 if_expr: &ast::IfExpr,
156 ) -> SyntaxNode {
157 let then_block_items = then_block.dedent(IndentLevel(1));
158 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
159 let end_of_then =
160 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
161 end_of_then.prev_sibling_or_token().unwrap()
162 } else {
163 end_of_then
164 };
165 let mut then_statements = new_expr.children_with_tokens().chain(
166 then_block_items
167 .syntax()
168 .children_with_tokens()
169 .skip(1)
170 .take_while(|i| *i != end_of_then),
171 );
172 replace_children(
173 &parent_block.syntax(),
174 RangeInclusive::new(
175 if_expr.clone().syntax().clone().into(),
176 if_expr.syntax().clone().into(),
177 ),
178 &mut then_statements,
179 )
180 }
181 })
182} 188}
183 189
184#[cfg(test)] 190#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
index ca19cf198..2b8e273b3 100644
--- a/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -10,7 +10,8 @@ use ra_syntax::{
10use rustc_hash::FxHashSet; 10use rustc_hash::FxHashSet;
11 11
12use crate::{ 12use crate::{
13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId, Assists, 13 assist_context::AssistBuilder, utils::insert_use_statement, AssistContext, AssistId,
14 AssistKind, Assists,
14}; 15};
15 16
16// Assist: extract_struct_from_enum_variant 17// Assist: extract_struct_from_enum_variant
@@ -48,7 +49,7 @@ pub(crate) fn extract_struct_from_enum_variant(
48 let current_module = enum_hir.module(ctx.db()); 49 let current_module = enum_hir.module(ctx.db());
49 let target = variant.syntax().text_range(); 50 let target = variant.syntax().text_range();
50 acc.add( 51 acc.add(
51 AssistId("extract_struct_from_enum_variant"), 52 AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
52 "Extract struct from enum variant", 53 "Extract struct from enum variant",
53 target, 54 target,
54 |builder| { 55 |builder| {
diff --git a/crates/ra_assists/src/handlers/extract_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs
index c4150d2bb..481baf1a4 100644
--- a/crates/ra_assists/src/handlers/extract_variable.rs
+++ b/crates/ra_assists/src/handlers/extract_variable.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9use stdx::format_to; 9use stdx::format_to;
10use test_utils::mark; 10use test_utils::mark;
11 11
12use crate::{AssistContext, AssistId, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
13 13
14// Assist: extract_variable 14// Assist: extract_variable
15// 15//
@@ -43,80 +43,85 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
43 return None; 43 return None;
44 } 44 }
45 let target = expr.syntax().text_range(); 45 let target = expr.syntax().text_range();
46 acc.add(AssistId("extract_variable"), "Extract into variable", target, move |edit| { 46 acc.add(
47 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) { 47 AssistId("extract_variable", AssistKind::RefactorExtract),
48 Some(field) => field.name_ref(), 48 "Extract into variable",
49 None => None, 49 target,
50 }; 50 move |edit| {
51 51 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
52 let mut buf = String::new(); 52 Some(field) => field.name_ref(),
53 53 None => None,
54 let var_name = match &field_shorthand { 54 };
55 Some(it) => it.to_string(), 55
56 None => "var_name".to_string(), 56 let mut buf = String::new();
57 }; 57
58 let expr_range = match &field_shorthand { 58 let var_name = match &field_shorthand {
59 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()), 59 Some(it) => it.to_string(),
60 None => expr.syntax().text_range(), 60 None => "var_name".to_string(),
61 }; 61 };
62 62 let expr_range = match &field_shorthand {
63 if wrap_in_block { 63 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
64 format_to!(buf, "{{ let {} = ", var_name); 64 None => expr.syntax().text_range(),
65 } else { 65 };
66 format_to!(buf, "let {} = ", var_name); 66
67 }; 67 if wrap_in_block {
68 format_to!(buf, "{}", expr.syntax()); 68 format_to!(buf, "{{ let {} = ", var_name);
69 69 } else {
70 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 70 format_to!(buf, "let {} = ", var_name);
71 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 71 };
72 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 72 format_to!(buf, "{}", expr.syntax());
73 } else { 73
74 false 74 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
75 }; 75 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
76 if is_full_stmt { 76 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
77 mark::hit!(test_extract_var_expr_stmt); 77 } else {
78 if full_stmt.unwrap().semicolon_token().is_none() { 78 false
79 buf.push_str(";"); 79 };
80 if is_full_stmt {
81 mark::hit!(test_extract_var_expr_stmt);
82 if full_stmt.unwrap().semicolon_token().is_none() {
83 buf.push_str(";");
84 }
85 match ctx.config.snippet_cap {
86 Some(cap) => {
87 let snip = buf
88 .replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
89 edit.replace_snippet(cap, expr_range, snip)
90 }
91 None => edit.replace(expr_range, buf),
92 }
93 return;
94 }
95
96 buf.push_str(";");
97
98 // We want to maintain the indent level,
99 // but we do not want to duplicate possible
100 // extra newlines in the indent block
101 let text = indent.text();
102 if text.starts_with('\n') {
103 buf.push_str("\n");
104 buf.push_str(text.trim_start_matches('\n'));
105 } else {
106 buf.push_str(text);
80 } 107 }
108
109 edit.replace(expr_range, var_name.clone());
110 let offset = anchor_stmt.text_range().start();
81 match ctx.config.snippet_cap { 111 match ctx.config.snippet_cap {
82 Some(cap) => { 112 Some(cap) => {
83 let snip = 113 let snip =
84 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); 114 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
85 edit.replace_snippet(cap, expr_range, snip) 115 edit.insert_snippet(cap, offset, snip)
86 } 116 }
87 None => edit.replace(expr_range, buf), 117 None => edit.insert(offset, buf),
88 } 118 }
89 return;
90 }
91 119
92 buf.push_str(";"); 120 if wrap_in_block {
93 121 edit.insert(anchor_stmt.text_range().end(), " }");
94 // We want to maintain the indent level,
95 // but we do not want to duplicate possible
96 // extra newlines in the indent block
97 let text = indent.text();
98 if text.starts_with('\n') {
99 buf.push_str("\n");
100 buf.push_str(text.trim_start_matches('\n'));
101 } else {
102 buf.push_str(text);
103 }
104
105 edit.replace(expr_range, var_name.clone());
106 let offset = anchor_stmt.text_range().start();
107 match ctx.config.snippet_cap {
108 Some(cap) => {
109 let snip =
110 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
111 edit.insert_snippet(cap, offset, snip)
112 } 122 }
113 None => edit.insert(offset, buf), 123 },
114 } 124 )
115
116 if wrap_in_block {
117 edit.insert(anchor_stmt.text_range().end(), " }");
118 }
119 })
120} 125}
121 126
122/// Check whether the node is a valid expression which can be extracted to a variable. 127/// Check whether the node is a valid expression which can be extracted to a variable.
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 5b1235682..511355e07 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -8,7 +8,7 @@ use test_utils::mark;
8 8
9use crate::{ 9use crate::{
10 utils::{render_snippet, Cursor, FamousDefs}, 10 utils::{render_snippet, Cursor, FamousDefs},
11 AssistContext, AssistId, Assists, 11 AssistContext, AssistId, AssistKind, Assists,
12}; 12};
13 13
14// Assist: fill_match_arms 14// Assist: fill_match_arms
@@ -103,24 +103,29 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
103 } 103 }
104 104
105 let target = match_expr.syntax().text_range(); 105 let target = match_expr.syntax().text_range();
106 acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |builder| { 106 acc.add(
107 let new_arm_list = match_arm_list.remove_placeholder(); 107 AssistId("fill_match_arms", AssistKind::QuickFix),
108 let n_old_arms = new_arm_list.arms().count(); 108 "Fill match arms",
109 let new_arm_list = new_arm_list.append_arms(missing_arms); 109 target,
110 let first_new_arm = new_arm_list.arms().nth(n_old_arms); 110 |builder| {
111 let old_range = match_arm_list.syntax().text_range(); 111 let new_arm_list = match_arm_list.remove_placeholder();
112 match (first_new_arm, ctx.config.snippet_cap) { 112 let n_old_arms = new_arm_list.arms().count();
113 (Some(first_new_arm), Some(cap)) => { 113 let new_arm_list = new_arm_list.append_arms(missing_arms);
114 let snippet = render_snippet( 114 let first_new_arm = new_arm_list.arms().nth(n_old_arms);
115 cap, 115 let old_range = match_arm_list.syntax().text_range();
116 new_arm_list.syntax(), 116 match (first_new_arm, ctx.config.snippet_cap) {
117 Cursor::Before(first_new_arm.syntax()), 117 (Some(first_new_arm), Some(cap)) => {
118 ); 118 let snippet = render_snippet(
119 builder.replace_snippet(cap, old_range, snippet); 119 cap,
120 } 120 new_arm_list.syntax(),
121 _ => builder.replace(old_range, new_arm_list.to_string()), 121 Cursor::Before(first_new_arm.syntax()),
122 } 122 );
123 }) 123 builder.replace_snippet(cap, old_range, snippet);
124 }
125 _ => builder.replace(old_range, new_arm_list.to_string()),
126 }
127 },
128 )
124} 129}
125 130
126fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 131fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs
index c0f57c329..e212557c8 100644
--- a/crates/ra_assists/src/handlers/fix_visibility.rs
+++ b/crates/ra_assists/src/handlers/fix_visibility.rs
@@ -2,7 +2,7 @@ use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
2use ra_db::FileId; 2use ra_db::FileId;
3use ra_syntax::{ast, AstNode, TextRange, TextSize}; 3use ra_syntax::{ast, AstNode, TextRange, TextSize};
4 4
5use crate::{utils::vis_offset, AssistContext, AssistId, Assists}; 5use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists};
6 6
7// FIXME: this really should be a fix for diagnostic, rather than an assist. 7// FIXME: this really should be a fix for diagnostic, rather than an assist.
8 8
@@ -58,7 +58,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> O
58 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), 58 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
59 }; 59 };
60 60
61 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 61 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
62 builder.edit_file(target_file); 62 builder.edit_file(target_file);
63 match ctx.config.snippet_cap { 63 match ctx.config.snippet_cap {
64 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 64 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
@@ -101,7 +101,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) ->
101 let assist_label = 101 let assist_label =
102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); 102 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
103 103
104 acc.add(AssistId("fix_visibility"), assist_label, target, |builder| { 104 acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| {
105 builder.edit_file(target_file); 105 builder.edit_file(target_file);
106 match ctx.config.snippet_cap { 106 match ctx.config.snippet_cap {
107 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), 107 Some(cap) => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)),
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index 573196576..3cd532650 100644
--- a/crates/ra_assists/src/handlers/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
@@ -1,6 +1,6 @@
1use ra_syntax::ast::{AstNode, BinExpr, BinOp}; 1use ra_syntax::ast::{AstNode, BinExpr, BinOp};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: flip_binexpr 5// Assist: flip_binexpr
6// 6//
@@ -33,13 +33,18 @@ pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
33 return None; 33 return None;
34 } 34 }
35 35
36 acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { 36 acc.add(
37 if let FlipAction::FlipAndReplaceOp(new_op) = action { 37 AssistId("flip_binexpr", AssistKind::RefactorRewrite),
38 edit.replace(op_range, new_op); 38 "Flip binary expression",
39 } 39 op_range,
40 edit.replace(lhs.text_range(), rhs.text()); 40 |edit| {
41 edit.replace(rhs.text_range(), lhs.text()); 41 if let FlipAction::FlipAndReplaceOp(new_op) = action {
42 }) 42 edit.replace(op_range, new_op);
43 }
44 edit.replace(lhs.text_range(), rhs.text());
45 edit.replace(rhs.text_range(), lhs.text());
46 },
47 )
43} 48}
44 49
45enum FlipAction { 50enum FlipAction {
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index a57a1c463..55a971dc7 100644
--- a/crates/ra_assists/src/handlers/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{algo::non_trivia_sibling, Direction, T}; 1use ra_syntax::{algo::non_trivia_sibling, Direction, T};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: flip_comma 5// Assist: flip_comma
6// 6//
@@ -28,10 +28,15 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 return None; 28 return None;
29 } 29 }
30 30
31 acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { 31 acc.add(
32 edit.replace(prev.text_range(), next.to_string()); 32 AssistId("flip_comma", AssistKind::RefactorRewrite),
33 edit.replace(next.text_range(), prev.to_string()); 33 "Flip comma",
34 }) 34 comma.text_range(),
35 |edit| {
36 edit.replace(prev.text_range(), next.to_string());
37 edit.replace(next.text_range(), prev.to_string());
38 },
39 )
35} 40}
36 41
37#[cfg(test)] 42#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index 0115adc8b..1234f4d29 100644
--- a/crates/ra_assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4 Direction, T, 4 Direction, T,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: flip_trait_bound 9// Assist: flip_trait_bound
10// 10//
@@ -33,10 +33,15 @@ pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option
33 ); 33 );
34 34
35 let target = plus.text_range(); 35 let target = plus.text_range();
36 acc.add(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { 36 acc.add(
37 edit.replace(before.text_range(), after.to_string()); 37 AssistId("flip_trait_bound", AssistKind::RefactorRewrite),
38 edit.replace(after.text_range(), before.to_string()); 38 "Flip trait bounds",
39 }) 39 target,
40 |edit| {
41 edit.replace(before.text_range(), after.to_string());
42 edit.replace(after.text_range(), before.to_string());
43 },
44 )
40} 45}
41 46
42#[cfg(test)] 47#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/generate_derive.rs
index b123b8498..6ccf39900 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/generate_derive.rs
@@ -4,9 +4,9 @@ use ra_syntax::{
4 TextSize, 4 TextSize,
5}; 5};
6 6
7use crate::{AssistContext, AssistId, Assists}; 7use crate::{AssistContext, AssistId, AssistKind, Assists};
8 8
9// Assist: add_derive 9// Assist: generate_derive
10// 10//
11// Adds a new `#[derive()]` clause to a struct or enum. 11// Adds a new `#[derive()]` clause to a struct or enum.
12// 12//
@@ -24,32 +24,37 @@ use crate::{AssistContext, AssistId, Assists};
24// y: u32, 24// y: u32,
25// } 25// }
26// ``` 26// ```
27pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 27pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let cap = ctx.config.snippet_cap?; 28 let cap = ctx.config.snippet_cap?;
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let node_start = derive_insertion_offset(&nominal)?; 30 let node_start = derive_insertion_offset(&nominal)?;
31 let target = nominal.syntax().text_range(); 31 let target = nominal.syntax().text_range();
32 acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |builder| { 32 acc.add(
33 let derive_attr = nominal 33 AssistId("generate_derive", AssistKind::Generate),
34 .attrs() 34 "Add `#[derive]`",
35 .filter_map(|x| x.as_simple_call()) 35 target,
36 .filter(|(name, _arg)| name == "derive") 36 |builder| {
37 .map(|(_name, arg)| arg) 37 let derive_attr = nominal
38 .next(); 38 .attrs()
39 match derive_attr { 39 .filter_map(|x| x.as_simple_call())
40 None => { 40 .filter(|(name, _arg)| name == "derive")
41 builder.insert_snippet(cap, node_start, "#[derive($0)]\n"); 41 .map(|(_name, arg)| arg)
42 } 42 .next();
43 Some(tt) => { 43 match derive_attr {
44 // Just move the cursor. 44 None => {
45 builder.insert_snippet( 45 builder.insert_snippet(cap, node_start, "#[derive($0)]\n");
46 cap, 46 }
47 tt.syntax().text_range().end() - TextSize::of(')'), 47 Some(tt) => {
48 "$0", 48 // Just move the cursor.
49 ) 49 builder.insert_snippet(
50 } 50 cap,
51 }; 51 tt.syntax().text_range().end() - TextSize::of(')'),
52 }) 52 "$0",
53 )
54 }
55 };
56 },
57 )
53} 58}
54 59
55// Insert `derive` after doc comments. 60// Insert `derive` after doc comments.
@@ -70,12 +75,12 @@ mod tests {
70 #[test] 75 #[test]
71 fn add_derive_new() { 76 fn add_derive_new() {
72 check_assist( 77 check_assist(
73 add_derive, 78 generate_derive,
74 "struct Foo { a: i32, <|>}", 79 "struct Foo { a: i32, <|>}",
75 "#[derive($0)]\nstruct Foo { a: i32, }", 80 "#[derive($0)]\nstruct Foo { a: i32, }",
76 ); 81 );
77 check_assist( 82 check_assist(
78 add_derive, 83 generate_derive,
79 "struct Foo { <|> a: i32, }", 84 "struct Foo { <|> a: i32, }",
80 "#[derive($0)]\nstruct Foo { a: i32, }", 85 "#[derive($0)]\nstruct Foo { a: i32, }",
81 ); 86 );
@@ -84,7 +89,7 @@ mod tests {
84 #[test] 89 #[test]
85 fn add_derive_existing() { 90 fn add_derive_existing() {
86 check_assist( 91 check_assist(
87 add_derive, 92 generate_derive,
88 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }", 93 "#[derive(Clone)]\nstruct Foo { a: i32<|>, }",
89 "#[derive(Clone$0)]\nstruct Foo { a: i32, }", 94 "#[derive(Clone$0)]\nstruct Foo { a: i32, }",
90 ); 95 );
@@ -93,7 +98,7 @@ mod tests {
93 #[test] 98 #[test]
94 fn add_derive_new_with_doc_comment() { 99 fn add_derive_new_with_doc_comment() {
95 check_assist( 100 check_assist(
96 add_derive, 101 generate_derive,
97 " 102 "
98/// `Foo` is a pretty important struct. 103/// `Foo` is a pretty important struct.
99/// It does stuff. 104/// It does stuff.
@@ -111,7 +116,7 @@ struct Foo { a: i32, }
111 #[test] 116 #[test]
112 fn add_derive_target() { 117 fn add_derive_target() {
113 check_assist_target( 118 check_assist_target(
114 add_derive, 119 generate_derive,
115 " 120 "
116struct SomeThingIrrelevant; 121struct SomeThingIrrelevant;
117/// `Foo` is a pretty important struct. 122/// `Foo` is a pretty important struct.
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
index b0e56e1b5..a347e3c2e 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/generate_from_impl_for_enum.rs
@@ -2,9 +2,9 @@ use ra_ide_db::RootDatabase;
2use ra_syntax::ast::{self, AstNode, NameOwner}; 2use ra_syntax::ast::{self, AstNode, NameOwner};
3use test_utils::mark; 3use test_utils::mark;
4 4
5use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; 5use crate::{utils::FamousDefs, AssistContext, AssistId, AssistKind, Assists};
6 6
7// Assist: add_from_impl_for_enum 7// Assist: generate_from_impl_for_enum
8// 8//
9// Adds a From impl for an enum variant with one tuple field. 9// Adds a From impl for an enum variant with one tuple field.
10// 10//
@@ -21,7 +21,7 @@ use crate::{utils::FamousDefs, AssistContext, AssistId, Assists};
21// } 21// }
22// } 22// }
23// ``` 23// ```
24pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 24pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; 25 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
26 let variant_name = variant.name()?; 26 let variant_name = variant.name()?;
27 let enum_name = variant.parent_enum().name()?; 27 let enum_name = variant.parent_enum().name()?;
@@ -45,8 +45,8 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
45 45
46 let target = variant.syntax().text_range(); 46 let target = variant.syntax().text_range();
47 acc.add( 47 acc.add(
48 AssistId("add_from_impl_for_enum"), 48 AssistId("generate_from_impl_for_enum", AssistKind::Generate),
49 "Add From impl for this enum variant", 49 "Generate `From` impl for this enum variant",
50 target, 50 target,
51 |edit| { 51 |edit| {
52 let start_offset = variant.parent_enum().syntax().text_range().end(); 52 let start_offset = variant.parent_enum().syntax().text_range().end();
@@ -97,9 +97,9 @@ mod tests {
97 use super::*; 97 use super::*;
98 98
99 #[test] 99 #[test]
100 fn test_add_from_impl_for_enum() { 100 fn test_generate_from_impl_for_enum() {
101 check_assist( 101 check_assist(
102 add_from_impl_for_enum, 102 generate_from_impl_for_enum,
103 "enum A { <|>One(u32) }", 103 "enum A { <|>One(u32) }",
104 r#"enum A { One(u32) } 104 r#"enum A { One(u32) }
105 105
@@ -112,9 +112,9 @@ impl From<u32> for A {
112 } 112 }
113 113
114 #[test] 114 #[test]
115 fn test_add_from_impl_for_enum_complicated_path() { 115 fn test_generate_from_impl_for_enum_complicated_path() {
116 check_assist( 116 check_assist(
117 add_from_impl_for_enum, 117 generate_from_impl_for_enum,
118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#, 118 r#"enum A { <|>One(foo::bar::baz::Boo) }"#,
119 r#"enum A { One(foo::bar::baz::Boo) } 119 r#"enum A { One(foo::bar::baz::Boo) }
120 120
@@ -129,7 +129,7 @@ impl From<foo::bar::baz::Boo> for A {
129 fn check_not_applicable(ra_fixture: &str) { 129 fn check_not_applicable(ra_fixture: &str) {
130 let fixture = 130 let fixture =
131 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE); 131 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
132 check_assist_not_applicable(add_from_impl_for_enum, &fixture) 132 check_assist_not_applicable(generate_from_impl_for_enum, &fixture)
133 } 133 }
134 134
135 #[test] 135 #[test]
@@ -166,7 +166,7 @@ impl From<u32> for A {
166 #[test] 166 #[test]
167 fn test_add_from_impl_different_variant_impl_exists() { 167 fn test_add_from_impl_different_variant_impl_exists() {
168 check_assist( 168 check_assist(
169 add_from_impl_for_enum, 169 generate_from_impl_for_enum,
170 r#"enum A { <|>One(u32), Two(String), } 170 r#"enum A { <|>One(u32), Two(String), }
171 171
172impl From<String> for A { 172impl From<String> for A {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/generate_function.rs
index fc4e82309..b721b96bb 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/generate_function.rs
@@ -13,10 +13,10 @@ use rustc_hash::{FxHashMap, FxHashSet};
13use crate::{ 13use crate::{
14 assist_config::SnippetCap, 14 assist_config::SnippetCap,
15 utils::{render_snippet, Cursor}, 15 utils::{render_snippet, Cursor},
16 AssistContext, AssistId, Assists, 16 AssistContext, AssistId, AssistKind, Assists,
17}; 17};
18 18
19// Assist: add_function 19// Assist: generate_function
20// 20//
21// Adds a stub function with a signature matching the function under the cursor. 21// Adds a stub function with a signature matching the function under the cursor.
22// 22//
@@ -41,7 +41,7 @@ use crate::{
41// } 41// }
42// 42//
43// ``` 43// ```
44pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 44pub(crate) fn generate_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; 45 let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; 46 let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
47 let path = path_expr.path()?; 47 let path = path_expr.path()?;
@@ -62,15 +62,20 @@ pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 62 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
63 63
64 let target = call.syntax().text_range(); 64 let target = call.syntax().text_range();
65 acc.add(AssistId("add_function"), "Add function", target, |builder| { 65 acc.add(
66 let function_template = function_builder.render(); 66 AssistId("generate_function", AssistKind::Generate),
67 builder.edit_file(function_template.file); 67 format!("Generate `{}` function", function_builder.fn_name),
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 target,
69 match ctx.config.snippet_cap { 69 |builder| {
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), 70 let function_template = function_builder.render();
71 None => builder.insert(function_template.insert_offset, new_fn), 71 builder.edit_file(function_template.file);
72 } 72 let new_fn = function_template.to_string(ctx.config.snippet_cap);
73 }) 73 match ctx.config.snippet_cap {
74 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
75 None => builder.insert(function_template.insert_offset, new_fn),
76 }
77 },
78 )
74} 79}
75 80
76struct FunctionTemplate { 81struct FunctionTemplate {
@@ -333,7 +338,7 @@ mod tests {
333 #[test] 338 #[test]
334 fn add_function_with_no_args() { 339 fn add_function_with_no_args() {
335 check_assist( 340 check_assist(
336 add_function, 341 generate_function,
337 r" 342 r"
338fn foo() { 343fn foo() {
339 bar<|>(); 344 bar<|>();
@@ -356,7 +361,7 @@ fn bar() {
356 // This ensures that the function is correctly generated 361 // This ensures that the function is correctly generated
357 // in the next outer mod or file 362 // in the next outer mod or file
358 check_assist( 363 check_assist(
359 add_function, 364 generate_function,
360 r" 365 r"
361impl Foo { 366impl Foo {
362 fn foo() { 367 fn foo() {
@@ -382,7 +387,7 @@ fn bar() {
382 fn add_function_directly_after_current_block() { 387 fn add_function_directly_after_current_block() {
383 // The new fn should not be created at the end of the file or module 388 // The new fn should not be created at the end of the file or module
384 check_assist( 389 check_assist(
385 add_function, 390 generate_function,
386 r" 391 r"
387fn foo1() { 392fn foo1() {
388 bar<|>(); 393 bar<|>();
@@ -407,7 +412,7 @@ fn foo2() {}
407 #[test] 412 #[test]
408 fn add_function_with_no_args_in_same_module() { 413 fn add_function_with_no_args_in_same_module() {
409 check_assist( 414 check_assist(
410 add_function, 415 generate_function,
411 r" 416 r"
412mod baz { 417mod baz {
413 fn foo() { 418 fn foo() {
@@ -432,7 +437,7 @@ mod baz {
432 #[test] 437 #[test]
433 fn add_function_with_function_call_arg() { 438 fn add_function_with_function_call_arg() {
434 check_assist( 439 check_assist(
435 add_function, 440 generate_function,
436 r" 441 r"
437struct Baz; 442struct Baz;
438fn baz() -> Baz { todo!() } 443fn baz() -> Baz { todo!() }
@@ -457,7 +462,7 @@ fn bar(baz: Baz) {
457 #[test] 462 #[test]
458 fn add_function_with_method_call_arg() { 463 fn add_function_with_method_call_arg() {
459 check_assist( 464 check_assist(
460 add_function, 465 generate_function,
461 r" 466 r"
462struct Baz; 467struct Baz;
463impl Baz { 468impl Baz {
@@ -490,7 +495,7 @@ fn bar(baz: Baz) {
490 #[test] 495 #[test]
491 fn add_function_with_string_literal_arg() { 496 fn add_function_with_string_literal_arg() {
492 check_assist( 497 check_assist(
493 add_function, 498 generate_function,
494 r#" 499 r#"
495fn foo() { 500fn foo() {
496 <|>bar("bar") 501 <|>bar("bar")
@@ -511,7 +516,7 @@ fn bar(arg: &str) {
511 #[test] 516 #[test]
512 fn add_function_with_char_literal_arg() { 517 fn add_function_with_char_literal_arg() {
513 check_assist( 518 check_assist(
514 add_function, 519 generate_function,
515 r#" 520 r#"
516fn foo() { 521fn foo() {
517 <|>bar('x') 522 <|>bar('x')
@@ -532,7 +537,7 @@ fn bar(arg: char) {
532 #[test] 537 #[test]
533 fn add_function_with_int_literal_arg() { 538 fn add_function_with_int_literal_arg() {
534 check_assist( 539 check_assist(
535 add_function, 540 generate_function,
536 r" 541 r"
537fn foo() { 542fn foo() {
538 <|>bar(42) 543 <|>bar(42)
@@ -553,7 +558,7 @@ fn bar(arg: i32) {
553 #[test] 558 #[test]
554 fn add_function_with_cast_int_literal_arg() { 559 fn add_function_with_cast_int_literal_arg() {
555 check_assist( 560 check_assist(
556 add_function, 561 generate_function,
557 r" 562 r"
558fn foo() { 563fn foo() {
559 <|>bar(42 as u8) 564 <|>bar(42 as u8)
@@ -576,7 +581,7 @@ fn bar(arg: u8) {
576 // Ensures that the name of the cast type isn't used 581 // Ensures that the name of the cast type isn't used
577 // in the generated function signature. 582 // in the generated function signature.
578 check_assist( 583 check_assist(
579 add_function, 584 generate_function,
580 r" 585 r"
581fn foo() { 586fn foo() {
582 let x = 42; 587 let x = 42;
@@ -599,7 +604,7 @@ fn bar(x: u8) {
599 #[test] 604 #[test]
600 fn add_function_with_variable_arg() { 605 fn add_function_with_variable_arg() {
601 check_assist( 606 check_assist(
602 add_function, 607 generate_function,
603 r" 608 r"
604fn foo() { 609fn foo() {
605 let worble = (); 610 let worble = ();
@@ -622,7 +627,7 @@ fn bar(worble: ()) {
622 #[test] 627 #[test]
623 fn add_function_with_impl_trait_arg() { 628 fn add_function_with_impl_trait_arg() {
624 check_assist( 629 check_assist(
625 add_function, 630 generate_function,
626 r" 631 r"
627trait Foo {} 632trait Foo {}
628fn foo() -> impl Foo { 633fn foo() -> impl Foo {
@@ -651,7 +656,7 @@ fn bar(foo: impl Foo) {
651 #[test] 656 #[test]
652 fn borrowed_arg() { 657 fn borrowed_arg() {
653 check_assist( 658 check_assist(
654 add_function, 659 generate_function,
655 r" 660 r"
656struct Baz; 661struct Baz;
657fn baz() -> Baz { todo!() } 662fn baz() -> Baz { todo!() }
@@ -678,7 +683,7 @@ fn bar(baz: &Baz) {
678 #[test] 683 #[test]
679 fn add_function_with_qualified_path_arg() { 684 fn add_function_with_qualified_path_arg() {
680 check_assist( 685 check_assist(
681 add_function, 686 generate_function,
682 r" 687 r"
683mod Baz { 688mod Baz {
684 pub struct Bof; 689 pub struct Bof;
@@ -709,7 +714,7 @@ fn bar(baz: Baz::Bof) {
709 // FIXME fix printing the generics of a `Ty` to make this test pass 714 // FIXME fix printing the generics of a `Ty` to make this test pass
710 fn add_function_with_generic_arg() { 715 fn add_function_with_generic_arg() {
711 check_assist( 716 check_assist(
712 add_function, 717 generate_function,
713 r" 718 r"
714fn foo<T>(t: T) { 719fn foo<T>(t: T) {
715 <|>bar(t) 720 <|>bar(t)
@@ -732,7 +737,7 @@ fn bar<T>(t: T) {
732 // FIXME Fix function type printing to make this test pass 737 // FIXME Fix function type printing to make this test pass
733 fn add_function_with_fn_arg() { 738 fn add_function_with_fn_arg() {
734 check_assist( 739 check_assist(
735 add_function, 740 generate_function,
736 r" 741 r"
737struct Baz; 742struct Baz;
738impl Baz { 743impl Baz {
@@ -763,7 +768,7 @@ fn bar(arg: fn() -> Baz) {
763 // FIXME Fix closure type printing to make this test pass 768 // FIXME Fix closure type printing to make this test pass
764 fn add_function_with_closure_arg() { 769 fn add_function_with_closure_arg() {
765 check_assist( 770 check_assist(
766 add_function, 771 generate_function,
767 r" 772 r"
768fn foo() { 773fn foo() {
769 let closure = |x: i64| x - 1; 774 let closure = |x: i64| x - 1;
@@ -786,7 +791,7 @@ fn bar(closure: impl Fn(i64) -> i64) {
786 #[test] 791 #[test]
787 fn unresolveable_types_default_to_unit() { 792 fn unresolveable_types_default_to_unit() {
788 check_assist( 793 check_assist(
789 add_function, 794 generate_function,
790 r" 795 r"
791fn foo() { 796fn foo() {
792 <|>bar(baz) 797 <|>bar(baz)
@@ -807,7 +812,7 @@ fn bar(baz: ()) {
807 #[test] 812 #[test]
808 fn arg_names_dont_overlap() { 813 fn arg_names_dont_overlap() {
809 check_assist( 814 check_assist(
810 add_function, 815 generate_function,
811 r" 816 r"
812struct Baz; 817struct Baz;
813fn baz() -> Baz { Baz } 818fn baz() -> Baz { Baz }
@@ -832,7 +837,7 @@ fn bar(baz_1: Baz, baz_2: Baz) {
832 #[test] 837 #[test]
833 fn arg_name_counters_start_at_1_per_name() { 838 fn arg_name_counters_start_at_1_per_name() {
834 check_assist( 839 check_assist(
835 add_function, 840 generate_function,
836 r#" 841 r#"
837struct Baz; 842struct Baz;
838fn baz() -> Baz { Baz } 843fn baz() -> Baz { Baz }
@@ -857,7 +862,7 @@ fn bar(baz_1: Baz, baz_2: Baz, arg_1: &str, arg_2: &str) {
857 #[test] 862 #[test]
858 fn add_function_in_module() { 863 fn add_function_in_module() {
859 check_assist( 864 check_assist(
860 add_function, 865 generate_function,
861 r" 866 r"
862mod bar {} 867mod bar {}
863 868
@@ -885,7 +890,7 @@ fn foo() {
885 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165 890 // See https://github.com/rust-analyzer/rust-analyzer/issues/1165
886 fn qualified_path_uses_correct_scope() { 891 fn qualified_path_uses_correct_scope() {
887 check_assist( 892 check_assist(
888 add_function, 893 generate_function,
889 " 894 "
890mod foo { 895mod foo {
891 pub struct Foo; 896 pub struct Foo;
@@ -916,7 +921,7 @@ fn baz(foo: foo::Foo) {
916 #[test] 921 #[test]
917 fn add_function_in_module_containing_other_items() { 922 fn add_function_in_module_containing_other_items() {
918 check_assist( 923 check_assist(
919 add_function, 924 generate_function,
920 r" 925 r"
921mod bar { 926mod bar {
922 fn something_else() {} 927 fn something_else() {}
@@ -945,7 +950,7 @@ fn foo() {
945 #[test] 950 #[test]
946 fn add_function_in_nested_module() { 951 fn add_function_in_nested_module() {
947 check_assist( 952 check_assist(
948 add_function, 953 generate_function,
949 r" 954 r"
950mod bar { 955mod bar {
951 mod baz {} 956 mod baz {}
@@ -974,7 +979,7 @@ fn foo() {
974 #[test] 979 #[test]
975 fn add_function_in_another_file() { 980 fn add_function_in_another_file() {
976 check_assist( 981 check_assist(
977 add_function, 982 generate_function,
978 r" 983 r"
979//- /main.rs 984//- /main.rs
980mod foo; 985mod foo;
@@ -996,7 +1001,7 @@ pub(crate) fn bar() {
996 #[test] 1001 #[test]
997 fn add_function_not_applicable_if_function_already_exists() { 1002 fn add_function_not_applicable_if_function_already_exists() {
998 check_assist_not_applicable( 1003 check_assist_not_applicable(
999 add_function, 1004 generate_function,
1000 r" 1005 r"
1001fn foo() { 1006fn foo() {
1002 bar<|>(); 1007 bar<|>();
@@ -1013,7 +1018,7 @@ fn bar() {}
1013 // bar is resolved, but baz isn't. 1018 // bar is resolved, but baz isn't.
1014 // The assist is only active if the cursor is on an unresolved path, 1019 // The assist is only active if the cursor is on an unresolved path,
1015 // but the assist should only be offered if the path is a function call. 1020 // but the assist should only be offered if the path is a function call.
1016 add_function, 1021 generate_function,
1017 r" 1022 r"
1018fn foo() { 1023fn foo() {
1019 bar(b<|>az); 1024 bar(b<|>az);
@@ -1028,7 +1033,7 @@ fn bar(baz: ()) {}
1028 #[ignore] 1033 #[ignore]
1029 fn create_method_with_no_args() { 1034 fn create_method_with_no_args() {
1030 check_assist( 1035 check_assist(
1031 add_function, 1036 generate_function,
1032 r" 1037 r"
1033struct Foo; 1038struct Foo;
1034impl Foo { 1039impl Foo {
diff --git a/crates/ra_assists/src/handlers/generate_impl.rs b/crates/ra_assists/src/handlers/generate_impl.rs
new file mode 100644
index 000000000..cbbac1d7f
--- /dev/null
+++ b/crates/ra_assists/src/handlers/generate_impl.rs
@@ -0,0 +1,109 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy};
3
4use crate::{AssistContext, AssistId, AssistKind, Assists};
5
6// Assist: generate_impl
7//
8// Adds a new inherent impl for a type.
9//
10// ```
11// struct Ctx<T: Clone> {
12// data: T,<|>
13// }
14// ```
15// ->
16// ```
17// struct Ctx<T: Clone> {
18// data: T,
19// }
20//
21// impl<T: Clone> Ctx<T> {
22// $0
23// }
24// ```
25pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range();
29 acc.add(
30 AssistId("generate_impl", AssistKind::Generate),
31 format!("Generate impl for `{}`", name),
32 target,
33 |edit| {
34 let type_params = nominal.type_param_list();
35 let start_offset = nominal.syntax().text_range().end();
36 let mut buf = String::new();
37 buf.push_str("\n\nimpl");
38 if let Some(type_params) = &type_params {
39 format_to!(buf, "{}", type_params.syntax());
40 }
41 buf.push_str(" ");
42 buf.push_str(name.text().as_str());
43 if let Some(type_params) = type_params {
44 let lifetime_params = type_params
45 .lifetime_params()
46 .filter_map(|it| it.lifetime_token())
47 .map(|it| it.text().clone());
48 let type_params = type_params
49 .type_params()
50 .filter_map(|it| it.name())
51 .map(|it| it.text().clone());
52
53 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
54 format_to!(buf, "<{}>", generic_params)
55 }
56 match ctx.config.snippet_cap {
57 Some(cap) => {
58 buf.push_str(" {\n $0\n}");
59 edit.insert_snippet(cap, start_offset, buf);
60 }
61 None => {
62 buf.push_str(" {\n}");
63 edit.insert(start_offset, buf);
64 }
65 }
66 },
67 )
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::tests::{check_assist, check_assist_target};
73
74 use super::*;
75
76 #[test]
77 fn test_add_impl() {
78 check_assist(
79 generate_impl,
80 "struct Foo {<|>}\n",
81 "struct Foo {}\n\nimpl Foo {\n $0\n}\n",
82 );
83 check_assist(
84 generate_impl,
85 "struct Foo<T: Clone> {<|>}",
86 "struct Foo<T: Clone> {}\n\nimpl<T: Clone> Foo<T> {\n $0\n}",
87 );
88 check_assist(
89 generate_impl,
90 "struct Foo<'a, T: Foo<'a>> {<|>}",
91 "struct Foo<'a, T: Foo<'a>> {}\n\nimpl<'a, T: Foo<'a>> Foo<'a, T> {\n $0\n}",
92 );
93 }
94
95 #[test]
96 fn add_impl_target() {
97 check_assist_target(
98 generate_impl,
99 "
100struct SomeThingIrrelevant;
101/// Has a lifetime parameter
102struct Foo<'a, T: Foo<'a>> {<|>}
103struct EvenMoreIrrelevant;
104",
105 "/// Has a lifetime parameter
106struct Foo<'a, T: Foo<'a>> {}",
107 );
108 }
109}
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/generate_new.rs
index e41b2aa06..e27def1d8 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/generate_new.rs
@@ -7,9 +7,9 @@ use ra_syntax::{
7}; 7};
8use stdx::{format_to, SepBy}; 8use stdx::{format_to, SepBy};
9 9
10use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, AssistKind, Assists};
11 11
12// Assist: add_new 12// Assist: generate_new
13// 13//
14// Adds a new inherent impl for a type. 14// Adds a new inherent impl for a type.
15// 15//
@@ -29,7 +29,7 @@ use crate::{AssistContext, AssistId, Assists};
29// } 29// }
30// 30//
31// ``` 31// ```
32pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 32pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; 33 let strukt = ctx.find_node_at_offset::<ast::StructDef>()?;
34 34
35 // We want to only apply this to non-union structs with named fields 35 // We want to only apply this to non-union structs with named fields
@@ -42,7 +42,7 @@ pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 let impl_def = find_struct_impl(&ctx, &strukt)?; 42 let impl_def = find_struct_impl(&ctx, &strukt)?;
43 43
44 let target = strukt.syntax().text_range(); 44 let target = strukt.syntax().text_range();
45 acc.add(AssistId("add_new"), "Add default constructor", target, |builder| { 45 acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
46 let mut buf = String::with_capacity(512); 46 let mut buf = String::with_capacity(512);
47 47
48 if impl_def.is_some() { 48 if impl_def.is_some() {
@@ -181,10 +181,10 @@ mod tests {
181 181
182 #[test] 182 #[test]
183 #[rustfmt::skip] 183 #[rustfmt::skip]
184 fn test_add_new() { 184 fn test_generate_new() {
185 // Check output of generation 185 // Check output of generation
186 check_assist( 186 check_assist(
187 add_new, 187 generate_new,
188"struct Foo {<|>}", 188"struct Foo {<|>}",
189"struct Foo {} 189"struct Foo {}
190 190
@@ -194,7 +194,7 @@ impl Foo {
194", 194",
195 ); 195 );
196 check_assist( 196 check_assist(
197 add_new, 197 generate_new,
198"struct Foo<T: Clone> {<|>}", 198"struct Foo<T: Clone> {<|>}",
199"struct Foo<T: Clone> {} 199"struct Foo<T: Clone> {}
200 200
@@ -204,7 +204,7 @@ impl<T: Clone> Foo<T> {
204", 204",
205 ); 205 );
206 check_assist( 206 check_assist(
207 add_new, 207 generate_new,
208"struct Foo<'a, T: Foo<'a>> {<|>}", 208"struct Foo<'a, T: Foo<'a>> {<|>}",
209"struct Foo<'a, T: Foo<'a>> {} 209"struct Foo<'a, T: Foo<'a>> {}
210 210
@@ -214,7 +214,7 @@ impl<'a, T: Foo<'a>> Foo<'a, T> {
214", 214",
215 ); 215 );
216 check_assist( 216 check_assist(
217 add_new, 217 generate_new,
218"struct Foo { baz: String <|>}", 218"struct Foo { baz: String <|>}",
219"struct Foo { baz: String } 219"struct Foo { baz: String }
220 220
@@ -224,7 +224,7 @@ impl Foo {
224", 224",
225 ); 225 );
226 check_assist( 226 check_assist(
227 add_new, 227 generate_new,
228"struct Foo { baz: String, qux: Vec<i32> <|>}", 228"struct Foo { baz: String, qux: Vec<i32> <|>}",
229"struct Foo { baz: String, qux: Vec<i32> } 229"struct Foo { baz: String, qux: Vec<i32> }
230 230
@@ -236,7 +236,7 @@ impl Foo {
236 236
237 // Check that visibility modifiers don't get brought in for fields 237 // Check that visibility modifiers don't get brought in for fields
238 check_assist( 238 check_assist(
239 add_new, 239 generate_new,
240"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}", 240"struct Foo { pub baz: String, pub qux: Vec<i32> <|>}",
241"struct Foo { pub baz: String, pub qux: Vec<i32> } 241"struct Foo { pub baz: String, pub qux: Vec<i32> }
242 242
@@ -248,7 +248,7 @@ impl Foo {
248 248
249 // Check that it reuses existing impls 249 // Check that it reuses existing impls
250 check_assist( 250 check_assist(
251 add_new, 251 generate_new,
252"struct Foo {<|>} 252"struct Foo {<|>}
253 253
254impl Foo {} 254impl Foo {}
@@ -261,7 +261,7 @@ impl Foo {
261", 261",
262 ); 262 );
263 check_assist( 263 check_assist(
264 add_new, 264 generate_new,
265"struct Foo {<|>} 265"struct Foo {<|>}
266 266
267impl Foo { 267impl Foo {
@@ -279,7 +279,7 @@ impl Foo {
279 ); 279 );
280 280
281 check_assist( 281 check_assist(
282 add_new, 282 generate_new,
283"struct Foo {<|>} 283"struct Foo {<|>}
284 284
285impl Foo { 285impl Foo {
@@ -304,7 +304,7 @@ impl Foo {
304 304
305 // Check visibility of new fn based on struct 305 // Check visibility of new fn based on struct
306 check_assist( 306 check_assist(
307 add_new, 307 generate_new,
308"pub struct Foo {<|>}", 308"pub struct Foo {<|>}",
309"pub struct Foo {} 309"pub struct Foo {}
310 310
@@ -314,7 +314,7 @@ impl Foo {
314", 314",
315 ); 315 );
316 check_assist( 316 check_assist(
317 add_new, 317 generate_new,
318"pub(crate) struct Foo {<|>}", 318"pub(crate) struct Foo {<|>}",
319"pub(crate) struct Foo {} 319"pub(crate) struct Foo {}
320 320
@@ -326,9 +326,9 @@ impl Foo {
326 } 326 }
327 327
328 #[test] 328 #[test]
329 fn add_new_not_applicable_if_fn_exists() { 329 fn generate_new_not_applicable_if_fn_exists() {
330 check_assist_not_applicable( 330 check_assist_not_applicable(
331 add_new, 331 generate_new,
332 " 332 "
333struct Foo {<|>} 333struct Foo {<|>}
334 334
@@ -340,7 +340,7 @@ impl Foo {
340 ); 340 );
341 341
342 check_assist_not_applicable( 342 check_assist_not_applicable(
343 add_new, 343 generate_new,
344 " 344 "
345struct Foo {<|>} 345struct Foo {<|>}
346 346
@@ -353,9 +353,9 @@ impl Foo {
353 } 353 }
354 354
355 #[test] 355 #[test]
356 fn add_new_target() { 356 fn generate_new_target() {
357 check_assist_target( 357 check_assist_target(
358 add_new, 358 generate_new,
359 " 359 "
360struct SomeThingIrrelevant; 360struct SomeThingIrrelevant;
361/// Has a lifetime parameter 361/// Has a lifetime parameter
@@ -370,7 +370,7 @@ struct Foo<'a, T: Foo<'a>> {}",
370 #[test] 370 #[test]
371 fn test_unrelated_new() { 371 fn test_unrelated_new() {
372 check_assist( 372 check_assist(
373 add_new, 373 generate_new,
374 r##" 374 r##"
375pub struct AstId<N: AstNode> { 375pub struct AstId<N: AstNode> {
376 file_id: HirFileId, 376 file_id: HirFileId,
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 259839535..2fdfabaf5 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -7,7 +7,7 @@ use test_utils::mark;
7 7
8use crate::{ 8use crate::{
9 assist_context::{AssistContext, Assists}, 9 assist_context::{AssistContext, Assists},
10 AssistId, 10 AssistId, AssistKind,
11}; 11};
12 12
13// Assist: inline_local_variable 13// Assist: inline_local_variable
@@ -110,13 +110,19 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> O
110 let init_in_paren = format!("({})", &init_str); 110 let init_in_paren = format!("({})", &init_str);
111 111
112 let target = bind_pat.syntax().text_range(); 112 let target = bind_pat.syntax().text_range();
113 acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| { 113 acc.add(
114 builder.delete(delete_range); 114 AssistId("inline_local_variable", AssistKind::RefactorInline),
115 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 115 "Inline variable",
116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 116 target,
117 builder.replace(desc.file_range.range, replacement) 117 move |builder| {
118 } 118 builder.delete(delete_range);
119 }) 119 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
120 let replacement =
121 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
122 builder.replace(desc.file_range.range, replacement)
123 }
124 },
125 )
120} 126}
121 127
122#[cfg(test)] 128#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
index 28fcbc9ba..967593031 100644
--- a/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
+++ b/crates/ra_assists/src/handlers/introduce_named_lifetime.rs
@@ -4,7 +4,7 @@ use ra_syntax::{
4}; 4};
5use rustc_hash::FxHashSet; 5use rustc_hash::FxHashSet;
6 6
7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, Assists}; 7use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists};
8 8
9static ASSIST_NAME: &str = "introduce_named_lifetime"; 9static ASSIST_NAME: &str = "introduce_named_lifetime";
10static ASSIST_LABEL: &str = "Introduce named lifetime"; 10static ASSIST_LABEL: &str = "Introduce named lifetime";
@@ -83,7 +83,7 @@ fn generate_fn_def_assist(
83 _ => return None, 83 _ => return None,
84 } 84 }
85 }; 85 };
86 acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { 86 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
87 add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param); 87 add_lifetime_param(fn_def, builder, end_of_fn_ident, new_lifetime_param);
88 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 88 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
89 loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param))); 89 loc_needing_lifetime.map(|loc| builder.insert(loc, format!("'{} ", new_lifetime_param)));
@@ -98,7 +98,7 @@ fn generate_impl_def_assist(
98) -> Option<()> { 98) -> Option<()> {
99 let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?; 99 let new_lifetime_param = generate_unique_lifetime_param_name(&impl_def.type_param_list())?;
100 let end_of_impl_kw = impl_def.impl_token()?.text_range().end(); 100 let end_of_impl_kw = impl_def.impl_token()?.text_range().end();
101 acc.add(AssistId(ASSIST_NAME), ASSIST_LABEL, lifetime_loc, |builder| { 101 acc.add(AssistId(ASSIST_NAME, AssistKind::Refactor), ASSIST_LABEL, lifetime_loc, |builder| {
102 add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param); 102 add_lifetime_param(impl_def, builder, end_of_impl_kw, new_lifetime_param);
103 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param)); 103 builder.replace(lifetime_loc, format!("'{}", new_lifetime_param));
104 }) 104 })
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs
index 59d278eb9..bbe3f3643 100644
--- a/crates/ra_assists/src/handlers/invert_if.rs
+++ b/crates/ra_assists/src/handlers/invert_if.rs
@@ -6,7 +6,7 @@ use ra_syntax::{
6use crate::{ 6use crate::{
7 assist_context::{AssistContext, Assists}, 7 assist_context::{AssistContext, Assists},
8 utils::invert_boolean_expression, 8 utils::invert_boolean_expression,
9 AssistId, 9 AssistId, AssistKind,
10}; 10};
11 11
12// Assist: invert_if 12// Assist: invert_if
@@ -54,7 +54,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
54 let else_node = else_block.syntax(); 54 let else_node = else_block.syntax();
55 let else_range = else_node.text_range(); 55 let else_range = else_node.text_range();
56 let then_range = then_node.text_range(); 56 let then_range = then_node.text_range();
57 acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| { 57 acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
58 edit.replace(cond_range, flip_cond.syntax().text()); 58 edit.replace(cond_range, flip_cond.syntax().text());
59 edit.replace(else_range, then_node.text()); 59 edit.replace(else_range, then_node.text());
60 edit.replace(then_range, else_node.text()); 60 edit.replace(then_range, else_node.text());
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index ac0b3035c..1beccb61c 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -8,7 +8,7 @@ use ra_syntax::{
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistContext, Assists}, 10 assist_context::{AssistContext, Assists},
11 AssistId, 11 AssistId, AssistKind,
12}; 12};
13 13
14// Assist: merge_imports 14// Assist: merge_imports
@@ -56,9 +56,14 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
56 }; 56 };
57 57
58 let target = tree.syntax().text_range(); 58 let target = tree.syntax().text_range();
59 acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { 59 acc.add(
60 builder.rewrite(rewriter); 60 AssistId("merge_imports", AssistKind::RefactorRewrite),
61 }) 61 "Merge imports",
62 target,
63 |builder| {
64 builder.rewrite(rewriter);
65 },
66 )
62} 67}
63 68
64fn next_prev() -> impl Iterator<Item = Direction> { 69fn next_prev() -> impl Iterator<Item = Direction> {
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index 90ce66378..186a1f618 100644
--- a/crates/ra_assists/src/handlers/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
@@ -6,7 +6,7 @@ use ra_syntax::{
6 Direction, 6 Direction,
7}; 7};
8 8
9use crate::{AssistContext, AssistId, Assists, TextRange}; 9use crate::{AssistContext, AssistId, AssistKind, Assists, TextRange};
10 10
11// Assist: merge_match_arms 11// Assist: merge_match_arms
12// 12//
@@ -59,25 +59,30 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option
59 return None; 59 return None;
60 } 60 }
61 61
62 acc.add(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| { 62 acc.add(
63 let pats = if arms_to_merge.iter().any(contains_placeholder) { 63 AssistId("merge_match_arms", AssistKind::RefactorRewrite),
64 "_".into() 64 "Merge match arms",
65 } else { 65 current_text_range,
66 arms_to_merge 66 |edit| {
67 .iter() 67 let pats = if arms_to_merge.iter().any(contains_placeholder) {
68 .filter_map(ast::MatchArm::pat) 68 "_".into()
69 .map(|x| x.syntax().to_string()) 69 } else {
70 .collect::<Vec<String>>() 70 arms_to_merge
71 .join(" | ") 71 .iter()
72 }; 72 .filter_map(ast::MatchArm::pat)
73 73 .map(|x| x.syntax().to_string())
74 let arm = format!("{} => {}", pats, current_expr.syntax().text()); 74 .collect::<Vec<String>>()
75 75 .join(" | ")
76 let start = arms_to_merge.first().unwrap().syntax().text_range().start(); 76 };
77 let end = arms_to_merge.last().unwrap().syntax().text_range().end(); 77
78 78 let arm = format!("{} => {}", pats, current_expr.syntax().text());
79 edit.replace(TextRange::new(start, end), arm); 79
80 }) 80 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
81 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
82
83 edit.replace(TextRange::new(start, end), arm);
84 },
85 )
81} 86}
82 87
83fn contains_placeholder(a: &ast::MatchArm) -> bool { 88fn contains_placeholder(a: &ast::MatchArm) -> bool {
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
index be2a7eddc..ba3dafb99 100644
--- a/crates/ra_assists/src/handlers/move_bounds.rs
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 T, 5 T,
6}; 6};
7 7
8use crate::{AssistContext, AssistId, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
9 9
10// Assist: move_bounds_to_where_clause 10// Assist: move_bounds_to_where_clause
11// 11//
@@ -50,29 +50,36 @@ pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext
50 }; 50 };
51 51
52 let target = type_param_list.syntax().text_range(); 52 let target = type_param_list.syntax().text_range();
53 acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| { 53 acc.add(
54 let new_params = type_param_list 54 AssistId("move_bounds_to_where_clause", AssistKind::RefactorRewrite),
55 .type_params() 55 "Move to where clause",
56 .filter(|it| it.type_bound_list().is_some()) 56 target,
57 .map(|type_param| { 57 |edit| {
58 let without_bounds = type_param.remove_bounds(); 58 let new_params = type_param_list
59 (type_param, without_bounds) 59 .type_params()
60 }); 60 .filter(|it| it.type_bound_list().is_some())
61 61 .map(|type_param| {
62 let new_type_param_list = type_param_list.replace_descendants(new_params); 62 let without_bounds = type_param.remove_bounds();
63 edit.replace_ast(type_param_list.clone(), new_type_param_list); 63 (type_param, without_bounds)
64 64 });
65 let where_clause = { 65
66 let predicates = type_param_list.type_params().filter_map(build_predicate); 66 let new_type_param_list = type_param_list.replace_descendants(new_params);
67 make::where_clause(predicates) 67 edit.replace_ast(type_param_list.clone(), new_type_param_list);
68 }; 68
69 69 let where_clause = {
70 let to_insert = match anchor.prev_sibling_or_token() { 70 let predicates = type_param_list.type_params().filter_map(build_predicate);
71 Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), 71 make::where_clause(predicates)
72 _ => format!(" {}", where_clause.syntax()), 72 };
73 }; 73
74 edit.insert(anchor.text_range().start(), to_insert); 74 let to_insert = match anchor.prev_sibling_or_token() {
75 }) 75 Some(ref elem) if elem.kind() == WHITESPACE => {
76 format!("{} ", where_clause.syntax())
77 }
78 _ => format!(" {}", where_clause.syntax()),
79 };
80 edit.insert(anchor.text_range().start(), to_insert);
81 },
82 )
76} 83}
77 84
78fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 85fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs
index 7edcf0748..4060d34c6 100644
--- a/crates/ra_assists/src/handlers/move_guard.rs
+++ b/crates/ra_assists/src/handlers/move_guard.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 SyntaxKind::WHITESPACE, 3 SyntaxKind::WHITESPACE,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7 7
8// Assist: move_guard_to_arm_body 8// Assist: move_guard_to_arm_body
9// 9//
@@ -40,17 +40,22 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) ->
40 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); 40 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
41 41
42 let target = guard.syntax().text_range(); 42 let target = guard.syntax().text_range();
43 acc.add(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { 43 acc.add(
44 match space_before_guard { 44 AssistId("move_guard_to_arm_body", AssistKind::RefactorRewrite),
45 Some(element) if element.kind() == WHITESPACE => { 45 "Move guard to arm body",
46 edit.delete(element.text_range()); 46 target,
47 } 47 |edit| {
48 _ => (), 48 match space_before_guard {
49 }; 49 Some(element) if element.kind() == WHITESPACE => {
50 edit.delete(element.text_range());
51 }
52 _ => (),
53 };
50 54
51 edit.delete(guard.syntax().text_range()); 55 edit.delete(guard.syntax().text_range());
52 edit.replace_node_and_indent(arm_expr.syntax(), buf); 56 edit.replace_node_and_indent(arm_expr.syntax(), buf);
53 }) 57 },
58 )
54} 59}
55 60
56// Assist: move_arm_cond_to_match_guard 61// Assist: move_arm_cond_to_match_guard
@@ -100,7 +105,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
100 105
101 let target = if_expr.syntax().text_range(); 106 let target = if_expr.syntax().text_range();
102 acc.add( 107 acc.add(
103 AssistId("move_arm_cond_to_match_guard"), 108 AssistId("move_arm_cond_to_match_guard", AssistKind::RefactorRewrite),
104 "Move condition to match guard", 109 "Move condition to match guard",
105 target, 110 target,
106 |edit| { 111 |edit| {
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index d22d0aa55..96679e160 100644
--- a/crates/ra_assists/src/handlers/raw_string.rs
+++ b/crates/ra_assists/src/handlers/raw_string.rs
@@ -5,7 +5,7 @@ use ra_syntax::{
5 TextSize, 5 TextSize,
6}; 6};
7 7
8use crate::{AssistContext, AssistId, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
9 9
10// Assist: make_raw_string 10// Assist: make_raw_string
11// 11//
@@ -26,14 +26,22 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<
26 let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; 26 let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?;
27 let value = token.value()?; 27 let value = token.value()?;
28 let target = token.syntax().text_range(); 28 let target = token.syntax().text_range();
29 acc.add(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { 29 acc.add(
30 let max_hash_streak = count_hashes(&value); 30 AssistId("make_raw_string", AssistKind::RefactorRewrite),
31 let mut hashes = String::with_capacity(max_hash_streak + 1); 31 "Rewrite as raw string",
32 for _ in 0..hashes.capacity() { 32 target,
33 hashes.push('#'); 33 |edit| {
34 } 34 let max_hash_streak = count_hashes(&value);
35 edit.replace(token.syntax().text_range(), format!("r{}\"{}\"{}", hashes, value, hashes)); 35 let mut hashes = String::with_capacity(max_hash_streak + 1);
36 }) 36 for _ in 0..hashes.capacity() {
37 hashes.push('#');
38 }
39 edit.replace(
40 token.syntax().text_range(),
41 format!("r{}\"{}\"{}", hashes, value, hashes),
42 );
43 },
44 )
37} 45}
38 46
39// Assist: make_usual_string 47// Assist: make_usual_string
@@ -55,11 +63,16 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
55 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; 63 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
56 let value = token.value()?; 64 let value = token.value()?;
57 let target = token.syntax().text_range(); 65 let target = token.syntax().text_range();
58 acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { 66 acc.add(
59 // parse inside string to escape `"` 67 AssistId("make_usual_string", AssistKind::RefactorRewrite),
60 let escaped = value.escape_default().to_string(); 68 "Rewrite as regular string",
61 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); 69 target,
62 }) 70 |edit| {
71 // parse inside string to escape `"`
72 let escaped = value.escape_default().to_string();
73 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
74 },
75 )
63} 76}
64 77
65// Assist: add_hash 78// Assist: add_hash
@@ -80,7 +93,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
80pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 93pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let token = ctx.find_token_at_offset(RAW_STRING)?; 94 let token = ctx.find_token_at_offset(RAW_STRING)?;
82 let target = token.text_range(); 95 let target = token.text_range();
83 acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| { 96 acc.add(AssistId("add_hash", AssistKind::Refactor), "Add # to raw string", target, |edit| {
84 edit.insert(token.text_range().start() + TextSize::of('r'), "#"); 97 edit.insert(token.text_range().start() + TextSize::of('r'), "#");
85 edit.insert(token.text_range().end(), "#"); 98 edit.insert(token.text_range().end(), "#");
86 }) 99 })
@@ -109,18 +122,23 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
109 return None; 122 return None;
110 } 123 }
111 let target = token.text_range(); 124 let target = token.text_range();
112 acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { 125 acc.add(
113 let result = &text[2..text.len() - 1]; 126 AssistId("remove_hash", AssistKind::RefactorRewrite),
114 let result = if result.starts_with('\"') { 127 "Remove hash from raw string",
115 // FIXME: this logic is wrong, not only the last has has to handled specially 128 target,
116 // no more hash, escape 129 |edit| {
117 let internal_str = &result[1..result.len() - 1]; 130 let result = &text[2..text.len() - 1];
118 format!("\"{}\"", internal_str.escape_default().to_string()) 131 let result = if result.starts_with('\"') {
119 } else { 132 // FIXME: this logic is wrong, not only the last has has to handled specially
120 result.to_owned() 133 // no more hash, escape
121 }; 134 let internal_str = &result[1..result.len() - 1];
122 edit.replace(token.text_range(), format!("r{}", result)); 135 format!("\"{}\"", internal_str.escape_default().to_string())
123 }) 136 } else {
137 result.to_owned()
138 };
139 edit.replace(token.text_range(), format!("r{}", result));
140 },
141 )
124} 142}
125 143
126fn count_hashes(s: &str) -> usize { 144fn count_hashes(s: &str) -> usize {
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs
index 961ee1731..a616cca57 100644
--- a/crates/ra_assists/src/handlers/remove_dbg.rs
+++ b/crates/ra_assists/src/handlers/remove_dbg.rs
@@ -3,7 +3,7 @@ use ra_syntax::{
3 TextSize, T, 3 TextSize, T,
4}; 4};
5 5
6use crate::{AssistContext, AssistId, Assists}; 6use crate::{AssistContext, AssistId, AssistKind, Assists};
7 7
8// Assist: remove_dbg 8// Assist: remove_dbg
9// 9//
@@ -38,7 +38,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
38 }; 38 };
39 39
40 let target = macro_call.syntax().text_range(); 40 let target = macro_call.syntax().text_range();
41 acc.add(AssistId("remove_dbg"), "Remove dbg!()", target, |builder| { 41 acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", target, |builder| {
42 builder.replace(macro_range, macro_content); 42 builder.replace(macro_range, macro_content);
43 }) 43 })
44} 44}
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs
index fe4eada03..ef55c354e 100644
--- a/crates/ra_assists/src/handlers/remove_mut.rs
+++ b/crates/ra_assists/src/handlers/remove_mut.rs
@@ -1,6 +1,6 @@
1use ra_syntax::{SyntaxKind, TextRange, T}; 1use ra_syntax::{SyntaxKind, TextRange, T};
2 2
3use crate::{AssistContext, AssistId, Assists}; 3use crate::{AssistContext, AssistId, AssistKind, Assists};
4 4
5// Assist: remove_mut 5// Assist: remove_mut
6// 6//
@@ -26,7 +26,12 @@ pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 }; 26 };
27 27
28 let target = mut_token.text_range(); 28 let target = mut_token.text_range();
29 acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |builder| { 29 acc.add(
30 builder.delete(TextRange::new(delete_from, delete_to)); 30 AssistId("remove_mut", AssistKind::Refactor),
31 }) 31 "Remove `mut` keyword",
32 target,
33 |builder| {
34 builder.delete(TextRange::new(delete_from, delete_to));
35 },
36 )
32} 37}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index b8cf30e7f..2ac1c56cf 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -5,7 +5,7 @@ use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; 6use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode};
7 7
8use crate::{AssistContext, AssistId, Assists}; 8use crate::{AssistContext, AssistId, AssistKind, Assists};
9 9
10// Assist: reorder_fields 10// Assist: reorder_fields
11// 11//
@@ -42,11 +42,16 @@ fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
42 } 42 }
43 43
44 let target = record.syntax().text_range(); 44 let target = record.syntax().text_range();
45 acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { 45 acc.add(
46 for (old, new) in fields.iter().zip(&sorted_fields) { 46 AssistId("reorder_fields", AssistKind::RefactorRewrite),
47 algo::diff(old, new).into_text_edit(edit.text_edit_builder()); 47 "Reorder record fields",
48 } 48 target,
49 }) 49 |edit| {
50 for (old, new) in fields.iter().zip(&sorted_fields) {
51 algo::diff(old, new).into_text_edit(edit.text_edit_builder());
52 }
53 },
54 )
50} 55}
51 56
52fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> { 57fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
diff --git a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
index dfcd787de..b7e30a7f2 100644
--- a/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_if_let_with_match.rs
@@ -8,7 +8,7 @@ use ra_syntax::{
8 AstNode, 8 AstNode,
9}; 9};
10 10
11use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; 11use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
12 12
13// Assist: replace_if_let_with_match 13// Assist: replace_if_let_with_match
14// 14//
@@ -48,29 +48,35 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
48 }; 48 };
49 49
50 let target = if_expr.syntax().text_range(); 50 let target = if_expr.syntax().text_range();
51 acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { 51 acc.add(
52 let match_expr = { 52 AssistId("replace_if_let_with_match", AssistKind::RefactorRewrite),
53 let then_arm = { 53 "Replace with match",
54 let then_block = then_block.reset_indent().indent(IndentLevel(1)); 54 target,
55 let then_expr = unwrap_trivial_block(then_block); 55 move |edit| {
56 make::match_arm(vec![pat.clone()], then_expr) 56 let match_expr = {
57 let then_arm = {
58 let then_block = then_block.reset_indent().indent(IndentLevel(1));
59 let then_expr = unwrap_trivial_block(then_block);
60 make::match_arm(vec![pat.clone()], then_expr)
61 };
62 let else_arm = {
63 let pattern = ctx
64 .sema
65 .type_of_pat(&pat)
66 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
67 .map(|it| it.sad_pattern())
68 .unwrap_or_else(|| make::placeholder_pat().into());
69 let else_expr = unwrap_trivial_block(else_block);
70 make::match_arm(vec![pattern], else_expr)
71 };
72 let match_expr =
73 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
74 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
57 }; 75 };
58 let else_arm = {
59 let pattern = ctx
60 .sema
61 .type_of_pat(&pat)
62 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
63 .map(|it| it.sad_pattern())
64 .unwrap_or_else(|| make::placeholder_pat().into());
65 let else_expr = unwrap_trivial_block(else_block);
66 make::match_arm(vec![pattern], else_expr)
67 };
68 let match_expr = make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
69 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
70 };
71 76
72 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 77 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
73 }) 78 },
79 )
74} 80}
75 81
76#[cfg(test)] 82#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
index 761557ac0..a49292c97 100644
--- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
+++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs
@@ -9,7 +9,7 @@ use ra_syntax::{
9 AstNode, T, 9 AstNode, T,
10}; 10};
11 11
12use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; 12use crate::{utils::TryEnum, AssistContext, AssistId, AssistKind, Assists};
13 13
14// Assist: replace_let_with_if_let 14// Assist: replace_let_with_if_let
15// 15//
@@ -44,24 +44,31 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) ->
44 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case()); 44 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case());
45 45
46 let target = let_kw.text_range(); 46 let target = let_kw.text_range();
47 acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { 47 acc.add(
48 let with_placeholder: ast::Pat = match happy_variant { 48 AssistId("replace_let_with_if_let", AssistKind::RefactorRewrite),
49 None => make::placeholder_pat().into(), 49 "Replace with if-let",
50 Some(var_name) => make::tuple_struct_pat( 50 target,
51 make::path_unqualified(make::path_segment(make::name_ref(var_name))), 51 |edit| {
52 once(make::placeholder_pat().into()), 52 let with_placeholder: ast::Pat = match happy_variant {
53 ) 53 None => make::placeholder_pat().into(),
54 .into(), 54 Some(var_name) => make::tuple_struct_pat(
55 }; 55 make::path_unqualified(make::path_segment(make::name_ref(var_name))),
56 let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); 56 once(make::placeholder_pat().into()),
57 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); 57 )
58 let stmt = make::expr_stmt(if_); 58 .into(),
59 };
60 let block =
61 make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax()));
62 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
63 let stmt = make::expr_stmt(if_);
59 64
60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); 65 let placeholder =
61 let stmt = stmt.replace_descendant(placeholder.into(), original_pat); 66 stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
67 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
62 68
63 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); 69 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
64 }) 70 },
71 )
65} 72}
66 73
67#[cfg(test)] 74#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index b4784c333..dfd314abf 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -3,7 +3,7 @@ use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNo
3 3
4use crate::{ 4use crate::{
5 utils::{find_insert_use_container, insert_use_statement}, 5 utils::{find_insert_use_container, insert_use_statement},
6 AssistContext, AssistId, Assists, 6 AssistContext, AssistId, AssistKind, Assists,
7}; 7};
8 8
9// Assist: replace_qualified_name_with_use 9// Assist: replace_qualified_name_with_use
@@ -37,7 +37,7 @@ pub(crate) fn replace_qualified_name_with_use(
37 37
38 let target = path.syntax().text_range(); 38 let target = path.syntax().text_range();
39 acc.add( 39 acc.add(
40 AssistId("replace_qualified_name_with_use"), 40 AssistId("replace_qualified_name_with_use", AssistKind::RefactorRewrite),
41 "Replace qualified path with use", 41 "Replace qualified path with use",
42 target, 42 target,
43 |builder| { 43 |builder| {
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
index cff7dfb81..e5a4bb23c 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -11,7 +11,7 @@ use ra_syntax::{
11 11
12use crate::{ 12use crate::{
13 utils::{render_snippet, Cursor, TryEnum}, 13 utils::{render_snippet, Cursor, TryEnum},
14 AssistContext, AssistId, Assists, 14 AssistContext, AssistId, AssistKind, Assists,
15}; 15};
16 16
17// Assist: replace_unwrap_with_match 17// Assist: replace_unwrap_with_match
@@ -46,37 +46,43 @@ pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext)
46 let ty = ctx.sema.type_of_expr(&caller)?; 46 let ty = ctx.sema.type_of_expr(&caller)?;
47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case(); 47 let happy_variant = TryEnum::from_ty(&ctx.sema, &ty)?.happy_case();
48 let target = method_call.syntax().text_range(); 48 let target = method_call.syntax().text_range();
49 acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |builder| { 49 acc.add(
50 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 50 AssistId("replace_unwrap_with_match", AssistKind::RefactorRewrite),
51 let it = make::bind_pat(make::name("a")).into(); 51 "Replace unwrap with match",
52 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); 52 target,
53 |builder| {
54 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
55 let it = make::bind_pat(make::name("a")).into();
56 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
53 57
54 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); 58 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
55 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); 59 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
56 60
57 let unreachable_call = make::expr_unreachable(); 61 let unreachable_call = make::expr_unreachable();
58 let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); 62 let err_arm =
63 make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
59 64
60 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); 65 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
61 let match_expr = make::expr_match(caller.clone(), match_arm_list) 66 let match_expr = make::expr_match(caller.clone(), match_arm_list)
62 .indent(IndentLevel::from_node(method_call.syntax())); 67 .indent(IndentLevel::from_node(method_call.syntax()));
63 68
64 let range = method_call.syntax().text_range(); 69 let range = method_call.syntax().text_range();
65 match ctx.config.snippet_cap { 70 match ctx.config.snippet_cap {
66 Some(cap) => { 71 Some(cap) => {
67 let err_arm = match_expr 72 let err_arm = match_expr
68 .syntax() 73 .syntax()
69 .descendants() 74 .descendants()
70 .filter_map(ast::MatchArm::cast) 75 .filter_map(ast::MatchArm::cast)
71 .last() 76 .last()
72 .unwrap(); 77 .unwrap();
73 let snippet = 78 let snippet =
74 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax())); 79 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
75 builder.replace_snippet(cap, range, snippet) 80 builder.replace_snippet(cap, range, snippet)
81 }
82 None => builder.replace(range, match_expr.to_string()),
76 } 83 }
77 None => builder.replace(range, match_expr.to_string()), 84 },
78 } 85 )
79 })
80} 86}
81 87
82#[cfg(test)] 88#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index 38aa199a0..4ca5c3ca1 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -2,7 +2,7 @@ use std::iter::successors;
2 2
3use ra_syntax::{ast, AstNode, T}; 3use ra_syntax::{ast, AstNode, T};
4 4
5use crate::{AssistContext, AssistId, Assists}; 5use crate::{AssistContext, AssistId, AssistKind, Assists};
6 6
7// Assist: split_import 7// Assist: split_import
8// 8//
@@ -28,7 +28,7 @@ pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
28 } 28 }
29 29
30 let target = colon_colon.text_range(); 30 let target = colon_colon.text_range();
31 acc.add(AssistId("split_import"), "Split import", target, |edit| { 31 acc.add(AssistId("split_import", AssistKind::RefactorRewrite), "Split import", target, |edit| {
32 edit.replace_ast(use_tree, new_tree); 32 edit.replace_ast(use_tree, new_tree);
33 }) 33 })
34} 34}
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index 1fb13f481..8b38695a9 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -7,7 +7,7 @@ use ra_syntax::{
7 AstNode, TextRange, T, 7 AstNode, TextRange, T,
8}; 8};
9 9
10use crate::{AssistContext, AssistId, Assists}; 10use crate::{AssistContext, AssistId, AssistKind, Assists};
11 11
12// Assist: unwrap_block 12// Assist: unwrap_block
13// 13//
@@ -27,7 +27,7 @@ use crate::{AssistContext, AssistId, Assists};
27// } 27// }
28// ``` 28// ```
29pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 29pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
30 let assist_id = AssistId("unwrap_block"); 30 let assist_id = AssistId("unwrap_block", AssistKind::RefactorRewrite);
31 let assist_label = "Unwrap block"; 31 let assist_label = "Unwrap block";
32 32
33 let l_curly_token = ctx.find_token_at_offset(T!['{'])?; 33 let l_curly_token = ctx.find_token_at_offset(T!['{'])?;
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 1745f44a5..3d61fbded 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -26,10 +26,21 @@ pub(crate) use crate::assist_context::{AssistContext, Assists};
26 26
27pub use assist_config::AssistConfig; 27pub use assist_config::AssistConfig;
28 28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AssistKind {
31 None,
32 QuickFix,
33 Generate,
34 Refactor,
35 RefactorExtract,
36 RefactorInline,
37 RefactorRewrite,
38}
39
29/// Unique identifier of the assist, should not be shown to the user 40/// Unique identifier of the assist, should not be shown to the user
30/// directly. 41/// directly.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)] 42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct AssistId(pub &'static str); 43pub struct AssistId(pub &'static str, pub AssistKind);
33 44
34#[derive(Clone, Debug)] 45#[derive(Clone, Debug)]
35pub struct GroupLabel(pub String); 46pub struct GroupLabel(pub String);
@@ -102,13 +113,8 @@ mod handlers {
102 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; 113 pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>;
103 114
104 mod add_custom_impl; 115 mod add_custom_impl;
105 mod add_derive;
106 mod add_explicit_type; 116 mod add_explicit_type;
107 mod add_from_impl_for_enum;
108 mod add_function;
109 mod add_impl;
110 mod add_missing_impl_members; 117 mod add_missing_impl_members;
111 mod add_new;
112 mod add_turbo_fish; 118 mod add_turbo_fish;
113 mod apply_demorgan; 119 mod apply_demorgan;
114 mod auto_import; 120 mod auto_import;
@@ -122,6 +128,11 @@ mod handlers {
122 mod flip_binexpr; 128 mod flip_binexpr;
123 mod flip_comma; 129 mod flip_comma;
124 mod flip_trait_bound; 130 mod flip_trait_bound;
131 mod generate_derive;
132 mod generate_from_impl_for_enum;
133 mod generate_function;
134 mod generate_impl;
135 mod generate_new;
125 mod inline_local_variable; 136 mod inline_local_variable;
126 mod introduce_named_lifetime; 137 mod introduce_named_lifetime;
127 mod invert_if; 138 mod invert_if;
@@ -144,12 +155,7 @@ mod handlers {
144 &[ 155 &[
145 // These are alphabetic for the foolish consistency 156 // These are alphabetic for the foolish consistency
146 add_custom_impl::add_custom_impl, 157 add_custom_impl::add_custom_impl,
147 add_derive::add_derive,
148 add_explicit_type::add_explicit_type, 158 add_explicit_type::add_explicit_type,
149 add_from_impl_for_enum::add_from_impl_for_enum,
150 add_function::add_function,
151 add_impl::add_impl,
152 add_new::add_new,
153 add_turbo_fish::add_turbo_fish, 159 add_turbo_fish::add_turbo_fish,
154 apply_demorgan::apply_demorgan, 160 apply_demorgan::apply_demorgan,
155 auto_import::auto_import, 161 auto_import::auto_import,
@@ -163,6 +169,11 @@ mod handlers {
163 flip_binexpr::flip_binexpr, 169 flip_binexpr::flip_binexpr,
164 flip_comma::flip_comma, 170 flip_comma::flip_comma,
165 flip_trait_bound::flip_trait_bound, 171 flip_trait_bound::flip_trait_bound,
172 generate_derive::generate_derive,
173 generate_from_impl_for_enum::generate_from_impl_for_enum,
174 generate_function::generate_function,
175 generate_impl::generate_impl,
176 generate_new::generate_new,
166 inline_local_variable::inline_local_variable, 177 inline_local_variable::inline_local_variable,
167 introduce_named_lifetime::introduce_named_lifetime, 178 introduce_named_lifetime::introduce_named_lifetime,
168 invert_if::invert_if, 179 invert_if::invert_if,
diff --git a/crates/ra_assists/src/tests/generated.rs b/crates/ra_assists/src/tests/generated.rs
index 31ea888c5..eff7feded 100644
--- a/crates/ra_assists/src/tests/generated.rs
+++ b/crates/ra_assists/src/tests/generated.rs
@@ -22,26 +22,6 @@ impl Debug for S {
22} 22}
23 23
24#[test] 24#[test]
25fn doctest_add_derive() {
26 check_doc_test(
27 "add_derive",
28 r#####"
29struct Point {
30 x: u32,
31 y: u32,<|>
32}
33"#####,
34 r#####"
35#[derive($0)]
36struct Point {
37 x: u32,
38 y: u32,
39}
40"#####,
41 )
42}
43
44#[test]
45fn doctest_add_explicit_type() { 25fn doctest_add_explicit_type() {
46 check_doc_test( 26 check_doc_test(
47 "add_explicit_type", 27 "add_explicit_type",
@@ -59,52 +39,6 @@ fn main() {
59} 39}
60 40
61#[test] 41#[test]
62fn doctest_add_from_impl_for_enum() {
63 check_doc_test(
64 "add_from_impl_for_enum",
65 r#####"
66enum A { <|>One(u32) }
67"#####,
68 r#####"
69enum A { One(u32) }
70
71impl From<u32> for A {
72 fn from(v: u32) -> Self {
73 A::One(v)
74 }
75}
76"#####,
77 )
78}
79
80#[test]
81fn doctest_add_function() {
82 check_doc_test(
83 "add_function",
84 r#####"
85struct Baz;
86fn baz() -> Baz { Baz }
87fn foo() {
88 bar<|>("", baz());
89}
90
91"#####,
92 r#####"
93struct Baz;
94fn baz() -> Baz { Baz }
95fn foo() {
96 bar("", baz());
97}
98
99fn bar(arg: &str, baz: Baz) {
100 ${0:todo!()}
101}
102
103"#####,
104 )
105}
106
107#[test]
108fn doctest_add_hash() { 42fn doctest_add_hash() {
109 check_doc_test( 43 check_doc_test(
110 "add_hash", 44 "add_hash",
@@ -122,27 +56,6 @@ fn main() {
122} 56}
123 57
124#[test] 58#[test]
125fn doctest_add_impl() {
126 check_doc_test(
127 "add_impl",
128 r#####"
129struct Ctx<T: Clone> {
130 data: T,<|>
131}
132"#####,
133 r#####"
134struct Ctx<T: Clone> {
135 data: T,
136}
137
138impl<T: Clone> Ctx<T> {
139 $0
140}
141"#####,
142 )
143}
144
145#[test]
146fn doctest_add_impl_default_members() { 59fn doctest_add_impl_default_members() {
147 check_doc_test( 60 check_doc_test(
148 "add_impl_default_members", 61 "add_impl_default_members",
@@ -209,28 +122,6 @@ impl Trait<u32> for () {
209} 122}
210 123
211#[test] 124#[test]
212fn doctest_add_new() {
213 check_doc_test(
214 "add_new",
215 r#####"
216struct Ctx<T: Clone> {
217 data: T,<|>
218}
219"#####,
220 r#####"
221struct Ctx<T: Clone> {
222 data: T,
223}
224
225impl<T: Clone> Ctx<T> {
226 fn $0new(data: T) -> Self { Self { data } }
227}
228
229"#####,
230 )
231}
232
233#[test]
234fn doctest_add_turbo_fish() { 125fn doctest_add_turbo_fish() {
235 check_doc_test( 126 check_doc_test(
236 "add_turbo_fish", 127 "add_turbo_fish",
@@ -467,6 +358,115 @@ fn foo<T: Copy + Clone>() { }
467} 358}
468 359
469#[test] 360#[test]
361fn doctest_generate_derive() {
362 check_doc_test(
363 "generate_derive",
364 r#####"
365struct Point {
366 x: u32,
367 y: u32,<|>
368}
369"#####,
370 r#####"
371#[derive($0)]
372struct Point {
373 x: u32,
374 y: u32,
375}
376"#####,
377 )
378}
379
380#[test]
381fn doctest_generate_from_impl_for_enum() {
382 check_doc_test(
383 "generate_from_impl_for_enum",
384 r#####"
385enum A { <|>One(u32) }
386"#####,
387 r#####"
388enum A { One(u32) }
389
390impl From<u32> for A {
391 fn from(v: u32) -> Self {
392 A::One(v)
393 }
394}
395"#####,
396 )
397}
398
399#[test]
400fn doctest_generate_function() {
401 check_doc_test(
402 "generate_function",
403 r#####"
404struct Baz;
405fn baz() -> Baz { Baz }
406fn foo() {
407 bar<|>("", baz());
408}
409
410"#####,
411 r#####"
412struct Baz;
413fn baz() -> Baz { Baz }
414fn foo() {
415 bar("", baz());
416}
417
418fn bar(arg: &str, baz: Baz) {
419 ${0:todo!()}
420}
421
422"#####,
423 )
424}
425
426#[test]
427fn doctest_generate_impl() {
428 check_doc_test(
429 "generate_impl",
430 r#####"
431struct Ctx<T: Clone> {
432 data: T,<|>
433}
434"#####,
435 r#####"
436struct Ctx<T: Clone> {
437 data: T,
438}
439
440impl<T: Clone> Ctx<T> {
441 $0
442}
443"#####,
444 )
445}
446
447#[test]
448fn doctest_generate_new() {
449 check_doc_test(
450 "generate_new",
451 r#####"
452struct Ctx<T: Clone> {
453 data: T,<|>
454}
455"#####,
456 r#####"
457struct Ctx<T: Clone> {
458 data: T,
459}
460
461impl<T: Clone> Ctx<T> {
462 fn $0new(data: T) -> Self { Self { data } }
463}
464
465"#####,
466 )
467}
468
469#[test]
470fn doctest_inline_local_variable() { 470fn doctest_inline_local_variable() {
471 check_doc_test( 471 check_doc_test(
472 "inline_local_variable", 472 "inline_local_variable",