aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assist_context.rs8
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_explicit_type.rs3
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs3
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs26
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs68
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs92
-rw-r--r--crates/ra_assists/src/handlers/add_turbo_fish.rs14
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs18
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs5
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs3
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs16
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs157
-rw-r--r--crates/ra_assists/src/handlers/extract_struct_from_enum_variant.rs4
-rw-r--r--crates/ra_assists/src/handlers/extract_variable.rs138
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs44
-rw-r--r--crates/ra_assists/src/handlers/fix_visibility.rs6
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs22
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs16
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs16
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs23
-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.rs14
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs46
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs56
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs29
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs75
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs4
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs14
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs18
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs51
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs42
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs3
-rw-r--r--crates/ra_assists/src/handlers/replace_unwrap_with_match.rs61
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs4
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs66
-rw-r--r--crates/ra_assists/src/lib.rs16
-rw-r--r--crates/ra_ide/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs22
42 files changed, 721 insertions, 506 deletions
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index 3640bb4d2..c35d0254a 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -19,7 +19,7 @@ use ra_text_edit::TextEditBuilder;
19 19
20use crate::{ 20use crate::{
21 assist_config::{AssistConfig, SnippetCap}, 21 assist_config::{AssistConfig, SnippetCap},
22 Assist, AssistId, GroupLabel, ResolvedAssist, 22 Assist, AssistId, AssistKind, GroupLabel, ResolvedAssist,
23}; 23};
24 24
25/// `AssistContext` allows to apply an assist or check if it could be applied. 25/// `AssistContext` allows to apply an assist or check if it could be applied.
@@ -135,22 +135,24 @@ impl Assists {
135 pub(crate) fn add( 135 pub(crate) fn add(
136 &mut self, 136 &mut self,
137 id: AssistId, 137 id: AssistId,
138 kind: AssistKind,
138 label: impl Into<String>, 139 label: impl Into<String>,
139 target: TextRange, 140 target: TextRange,
140 f: impl FnOnce(&mut AssistBuilder), 141 f: impl FnOnce(&mut AssistBuilder),
141 ) -> Option<()> { 142 ) -> Option<()> {
142 let label = Assist::new(id, label.into(), None, target); 143 let label = Assist::new(id, kind, label.into(), None, target);
143 self.add_impl(label, f) 144 self.add_impl(label, f)
144 } 145 }
145 pub(crate) fn add_group( 146 pub(crate) fn add_group(
146 &mut self, 147 &mut self,
147 group: &GroupLabel, 148 group: &GroupLabel,
148 id: AssistId, 149 id: AssistId,
150 kind: AssistKind,
149 label: impl Into<String>, 151 label: impl Into<String>,
150 target: TextRange, 152 target: TextRange,
151 f: impl FnOnce(&mut AssistBuilder), 153 f: impl FnOnce(&mut AssistBuilder),
152 ) -> Option<()> { 154 ) -> Option<()> {
153 let label = Assist::new(id, label.into(), Some(group.clone()), target); 155 let label = Assist::new(id, kind, label.into(), Some(group.clone()), target);
154 self.add_impl(label, f) 156 self.add_impl(label, f)
155 } 157 }
156 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { 158 fn add_impl(&mut self, label: Assist, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> {
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index fa70c8496..e86b01dbb 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_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index b123b8498..3b7a570b0 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/add_derive.rs
@@ -4,7 +4,7 @@ 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: add_derive
10// 10//
@@ -29,7 +29,7 @@ pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
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(AssistId("add_derive"), AssistKind::Refactor, "Add `#[derive]`", target, |builder| {
33 let derive_attr = nominal 33 let derive_attr = nominal
34 .attrs() 34 .attrs()
35 .filter_map(|x| x.as_simple_call()) 35 .filter_map(|x| x.as_simple_call())
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index 11df922a2..653137d43 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//
@@ -60,6 +60,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
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"),
63 AssistKind::RefactorRewrite,
63 format!("Insert explicit type `{}`", inferred_type), 64 format!("Insert explicit type `{}`", inferred_type),
64 pat_range, 65 pat_range,
65 |builder| match ascribed_ty { 66 |builder| match ascribed_ty {
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index b0e56e1b5..505085c54 100644
--- a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -2,7 +2,7 @@ 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: add_from_impl_for_enum
8// 8//
@@ -46,6 +46,7 @@ pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) ->
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("add_from_impl_for_enum"),
49 AssistKind::Refactor,
49 "Add From impl for this enum variant", 50 "Add From impl for this enum variant",
50 target, 51 target,
51 |edit| { 52 |edit| {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index fc4e82309..a11bc2551 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -13,7 +13,7 @@ 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: add_function
@@ -62,15 +62,21 @@ 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("add_function"),
67 builder.edit_file(function_template.file); 67 AssistKind::RefactorExtract,
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 "Add function",
69 match ctx.config.snippet_cap { 69 target,
70 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn), 70 |builder| {
71 None => builder.insert(function_template.insert_offset, new_fn), 71 let function_template = function_builder.render();
72 } 72 builder.edit_file(function_template.file);
73 }) 73 let new_fn = function_template.to_string(ctx.config.snippet_cap);
74 match ctx.config.snippet_cap {
75 Some(cap) => builder.insert_snippet(cap, function_template.insert_offset, new_fn),
76 None => builder.insert(function_template.insert_offset, new_fn),
77 }
78 },
79 )
74} 80}
75 81
76struct FunctionTemplate { 82struct FunctionTemplate {
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index eceba7d0a..405e3c568 100644
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
@@ -1,7 +1,7 @@
1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner}; 1use ra_syntax::ast::{self, AstNode, NameOwner, TypeParamsOwner};
2use stdx::{format_to, SepBy}; 2use stdx::{format_to, SepBy};
3 3
4use crate::{AssistContext, AssistId, Assists}; 4use crate::{AssistContext, AssistId, AssistKind, Assists};
5 5
6// Assist: add_impl 6// Assist: add_impl
7// 7//
@@ -26,38 +26,46 @@ pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 26 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
27 let name = nominal.name()?; 27 let name = nominal.name()?;
28 let target = nominal.syntax().text_range(); 28 let target = nominal.syntax().text_range();
29 acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| { 29 acc.add(
30 let type_params = nominal.type_param_list(); 30 AssistId("add_impl"),
31 let start_offset = nominal.syntax().text_range().end(); 31 AssistKind::Refactor,
32 let mut buf = String::new(); 32 format!("Implement {}", name.text().as_str()),
33 buf.push_str("\n\nimpl"); 33 target,
34 if let Some(type_params) = &type_params { 34 |edit| {
35 format_to!(buf, "{}", type_params.syntax()); 35 let type_params = nominal.type_param_list();
36 } 36 let start_offset = nominal.syntax().text_range().end();
37 buf.push_str(" "); 37 let mut buf = String::new();
38 buf.push_str(name.text().as_str()); 38 buf.push_str("\n\nimpl");
39 if let Some(type_params) = type_params { 39 if let Some(type_params) = &type_params {
40 let lifetime_params = type_params 40 format_to!(buf, "{}", type_params.syntax());
41 .lifetime_params() 41 }
42 .filter_map(|it| it.lifetime_token()) 42 buf.push_str(" ");
43 .map(|it| it.text().clone()); 43 buf.push_str(name.text().as_str());
44 let type_params = 44 if let Some(type_params) = type_params {
45 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); 45 let lifetime_params = type_params
46 .lifetime_params()
47 .filter_map(|it| it.lifetime_token())
48 .map(|it| it.text().clone());
49 let type_params = type_params
50 .type_params()
51 .filter_map(|it| it.name())
52 .map(|it| it.text().clone());
46 53
47 let generic_params = lifetime_params.chain(type_params).sep_by(", "); 54 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
48 format_to!(buf, "<{}>", generic_params) 55 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 } 56 }
55 None => { 57 match ctx.config.snippet_cap {
56 buf.push_str(" {\n}"); 58 Some(cap) => {
57 edit.insert(start_offset, buf); 59 buf.push_str(" {\n $0\n}");
60 edit.insert_snippet(cap, start_offset, buf);
61 }
62 None => {
63 buf.push_str(" {\n}");
64 edit.insert(start_offset, buf);
65 }
58 } 66 }
59 } 67 },
60 }) 68 )
61} 69}
62 70
63#[cfg(test)] 71#[cfg(test)]
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..13b441a29 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_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index e41b2aa06..4cd3ca264 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -7,7 +7,7 @@ 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: add_new
13// 13//
@@ -42,50 +42,56 @@ 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(
46 let mut buf = String::with_capacity(512); 46 AssistId("add_new"),
47 47 AssistKind::Refactor,
48 if impl_def.is_some() { 48 "Add default constructor",
49 buf.push('\n'); 49 target,
50 } 50 |builder| {
51 51 let mut buf = String::with_capacity(512);
52 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v)); 52
53 53 if impl_def.is_some() {
54 let params = field_list
55 .fields()
56 .filter_map(|f| {
57 Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
58 })
59 .sep_by(", ");
60 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
61
62 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
63
64 let start_offset = impl_def
65 .and_then(|impl_def| {
66 buf.push('\n'); 54 buf.push('\n');
67 let start = impl_def
68 .syntax()
69 .descendants_with_tokens()
70 .find(|t| t.kind() == T!['{'])?
71 .text_range()
72 .end();
73
74 Some(start)
75 })
76 .unwrap_or_else(|| {
77 buf = generate_impl_text(&strukt, &buf);
78 strukt.syntax().text_range().end()
79 });
80
81 match ctx.config.snippet_cap {
82 None => builder.insert(start_offset, buf),
83 Some(cap) => {
84 buf = buf.replace("fn new", "fn $0new");
85 builder.insert_snippet(cap, start_offset, buf);
86 } 55 }
87 } 56
88 }) 57 let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));
58
59 let params = field_list
60 .fields()
61 .filter_map(|f| {
62 Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
63 })
64 .sep_by(", ");
65 let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");
66
67 format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);
68
69 let start_offset = impl_def
70 .and_then(|impl_def| {
71 buf.push('\n');
72 let start = impl_def
73 .syntax()
74 .descendants_with_tokens()
75 .find(|t| t.kind() == T!['{'])?
76 .text_range()
77 .end();
78
79 Some(start)
80 })
81 .unwrap_or_else(|| {
82 buf = generate_impl_text(&strukt, &buf);
83 strukt.syntax().text_range().end()
84 });
85
86 match ctx.config.snippet_cap {
87 None => builder.insert(start_offset, buf),
88 Some(cap) => {
89 buf = buf.replace("fn new", "fn $0new");
90 builder.insert_snippet(cap, start_offset, buf);
91 }
92 }
93 },
94 )
89} 95}
90 96
91// Generates the surrounding `impl Type { <code> }` including type and lifetime 97// Generates the surrounding `impl Type { <code> }` including type and lifetime
diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs
index 26acf81f2..7a807fbde 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,16 @@ 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"),
50 AssistKind::RefactorRewrite,
51 "Add `::<>`",
52 ident.text_range(),
53 |builder| match ctx.config.snippet_cap {
50 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"), 54 Some(cap) => builder.insert_snippet(cap, ident.text_range().end(), "::<${0:_}>"),
51 None => builder.insert(ident.text_range().end(), "::<_>"), 55 None => builder.insert(ident.text_range().end(), "::<_>"),
52 } 56 },
53 }) 57 )
54} 58}
55 59
56#[cfg(test)] 60#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 233e8fb8e..a1fd6e112 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,17 @@ 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"),
44 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); 44 AssistKind::RefactorRewrite,
45 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); 45 "Apply De Morgan's law",
46 }) 46 op_range,
47 |edit| {
48 edit.replace(op_range, opposite_op);
49 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
50 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
51 },
52 )
47} 53}
48 54
49// Return the opposite text for a given logical operator, if it makes sense 55// 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 4cd77adbf..4d97ed101 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//
@@ -47,6 +49,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
47 acc.add_group( 49 acc.add_group(
48 &group, 50 &group,
49 AssistId("auto_import"), 51 AssistId("auto_import"),
52 AssistKind::QuickFix,
50 format!("Import `{}`", &import), 53 format!("Import `{}`", &import),
51 range, 54 range,
52 |builder| { 55 |builder| {
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..2be6d5e49 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
@@ -36,6 +36,7 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex
36 36
37 acc.add( 37 acc.add(
38 AssistId("change_return_type_to_result"), 38 AssistId("change_return_type_to_result"),
39 AssistKind::RefactorRewrite,
39 "Change return type to Result", 40 "Change return type to Result",
40 type_ref.syntax().text_range(), 41 type_ref.syntax().text_range(),
41 |builder| { 42 |builder| {
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 157c7b665..34c7e481d 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,9 +62,15 @@ 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"),
67 }) 67 AssistKind::RefactorRewrite,
68 "Change visibility to pub(crate)",
69 target,
70 |edit| {
71 edit.insert(offset, "pub(crate) ");
72 },
73 )
68} 74}
69 75
70fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { 76fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
@@ -72,6 +78,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
72 let target = vis.syntax().text_range(); 78 let target = vis.syntax().text_range();
73 return acc.add( 79 return acc.add(
74 AssistId("change_visibility"), 80 AssistId("change_visibility"),
81 AssistKind::RefactorRewrite,
75 "Change Visibility to pub(crate)", 82 "Change Visibility to pub(crate)",
76 target, 83 target,
77 |edit| { 84 |edit| {
@@ -83,6 +90,7 @@ fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> {
83 let target = vis.syntax().text_range(); 90 let target = vis.syntax().text_range();
84 return acc.add( 91 return acc.add(
85 AssistId("change_visibility"), 92 AssistId("change_visibility"),
93 AssistKind::RefactorRewrite,
86 "Change visibility to pub", 94 "Change visibility to pub",
87 target, 95 target,
88 |edit| { 96 |edit| {
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index dfade7432..9ccd42cd7 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,93 @@ 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"),
104 let new_block = match if_let_pat { 104 AssistKind::RefactorRewrite,
105 None => { 105 "Convert to guarded return",
106 // If. 106 target,
107 let new_expr = { 107 |edit| {
108 let then_branch = 108 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
109 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 109 let new_block = match if_let_pat {
110 let cond = invert_boolean_expression(cond_expr); 110 None => {
111 make::expr_if(make::condition(cond, None), then_branch).indent(if_indent_level) 111 // If.
112 }; 112 let new_expr = {
113 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 113 let then_branch =
114 } 114 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
115 Some((path, bound_ident)) => { 115 let cond = invert_boolean_expression(cond_expr);
116 // If-let. 116 make::expr_if(make::condition(cond, None), then_branch)
117 let match_expr = { 117 .indent(if_indent_level)
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 }; 118 };
119 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
120 }
121 Some((path, bound_ident)) => {
122 // If-let.
123 let match_expr = {
124 let happy_arm = {
125 let pat = make::tuple_struct_pat(
126 path,
127 once(make::bind_pat(make::name("it")).into()),
128 );
129 let expr = {
130 let name_ref = make::name_ref("it");
131 let segment = make::path_segment(name_ref);
132 let path = make::path_unqualified(segment);
133 make::expr_path(path)
134 };
135 make::match_arm(once(pat.into()), expr)
136 };
131 137
132 let sad_arm = make::match_arm( 138 let sad_arm = make::match_arm(
133 // FIXME: would be cool to use `None` or `Err(_)` if appropriate 139 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
134 once(make::placeholder_pat().into()), 140 once(make::placeholder_pat().into()),
135 early_expression, 141 early_expression,
136 ); 142 );
137 143
138 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) 144 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
139 }; 145 };
140 146
141 let let_stmt = make::let_stmt( 147 let let_stmt = make::let_stmt(
142 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), 148 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
143 Some(match_expr), 149 Some(match_expr),
150 );
151 let let_stmt = let_stmt.indent(if_indent_level);
152 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
153 }
154 };
155 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
156
157 fn replace(
158 new_expr: &SyntaxNode,
159 then_block: &ast::BlockExpr,
160 parent_block: &ast::BlockExpr,
161 if_expr: &ast::IfExpr,
162 ) -> SyntaxNode {
163 let then_block_items = then_block.dedent(IndentLevel(1));
164 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
165 let end_of_then =
166 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
167 end_of_then.prev_sibling_or_token().unwrap()
168 } else {
169 end_of_then
170 };
171 let mut then_statements = new_expr.children_with_tokens().chain(
172 then_block_items
173 .syntax()
174 .children_with_tokens()
175 .skip(1)
176 .take_while(|i| *i != end_of_then),
144 ); 177 );
145 let let_stmt = let_stmt.indent(if_indent_level); 178 replace_children(
146 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) 179 &parent_block.syntax(),
180 RangeInclusive::new(
181 if_expr.clone().syntax().clone().into(),
182 if_expr.syntax().clone().into(),
183 ),
184 &mut then_statements,
185 )
147 } 186 }
148 }; 187 },
149 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); 188 )
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} 189}
183 190
184#[cfg(test)] 191#[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..e1ae485c9 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
@@ -49,6 +50,7 @@ pub(crate) fn extract_struct_from_enum_variant(
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"),
53 AssistKind::RefactorRewrite,
52 "Extract struct from enum variant", 54 "Extract struct from enum variant",
53 target, 55 target,
54 |builder| { 56 |builder| {
diff --git a/crates/ra_assists/src/handlers/extract_variable.rs b/crates/ra_assists/src/handlers/extract_variable.rs
index c4150d2bb..8f7ffdaff 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,86 @@ 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"),
48 Some(field) => field.name_ref(), 48 AssistKind::RefactorExtract,
49 None => None, 49 "Extract into variable",
50 }; 50 target,
51 51 move |edit| {
52 let mut buf = String::new(); 52 let field_shorthand = match expr.syntax().parent().and_then(ast::RecordField::cast) {
53 53 Some(field) => field.name_ref(),
54 let var_name = match &field_shorthand { 54 None => None,
55 Some(it) => it.to_string(), 55 };
56 None => "var_name".to_string(), 56
57 }; 57 let mut buf = String::new();
58 let expr_range = match &field_shorthand { 58
59 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()), 59 let var_name = match &field_shorthand {
60 None => expr.syntax().text_range(), 60 Some(it) => it.to_string(),
61 }; 61 None => "var_name".to_string(),
62 62 };
63 if wrap_in_block { 63 let expr_range = match &field_shorthand {
64 format_to!(buf, "{{ let {} = ", var_name); 64 Some(it) => it.syntax().text_range().cover(expr.syntax().text_range()),
65 } else { 65 None => expr.syntax().text_range(),
66 format_to!(buf, "let {} = ", var_name); 66 };
67 }; 67
68 format_to!(buf, "{}", expr.syntax()); 68 if wrap_in_block {
69 69 format_to!(buf, "{{ let {} = ", var_name);
70 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone()); 70 } else {
71 let is_full_stmt = if let Some(expr_stmt) = &full_stmt { 71 format_to!(buf, "let {} = ", var_name);
72 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone()) 72 };
73 } else { 73 format_to!(buf, "{}", expr.syntax());
74 false 74
75 }; 75 let full_stmt = ast::ExprStmt::cast(anchor_stmt.clone());
76 if is_full_stmt { 76 let is_full_stmt = if let Some(expr_stmt) = &full_stmt {
77 mark::hit!(test_extract_var_expr_stmt); 77 Some(expr.syntax().clone()) == expr_stmt.expr().map(|e| e.syntax().clone())
78 if full_stmt.unwrap().semicolon_token().is_none() { 78 } else {
79 buf.push_str(";"); 79 false
80 };
81 if is_full_stmt {
82 mark::hit!(test_extract_var_expr_stmt);
83 if full_stmt.unwrap().semicolon_token().is_none() {
84 buf.push_str(";");
85 }
86 match ctx.config.snippet_cap {
87 Some(cap) => {
88 let snip = buf
89 .replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
90 edit.replace_snippet(cap, expr_range, snip)
91 }
92 None => edit.replace(expr_range, buf),
93 }
94 return;
95 }
96
97 buf.push_str(";");
98
99 // We want to maintain the indent level,
100 // but we do not want to duplicate possible
101 // extra newlines in the indent block
102 let text = indent.text();
103 if text.starts_with('\n') {
104 buf.push_str("\n");
105 buf.push_str(text.trim_start_matches('\n'));
106 } else {
107 buf.push_str(text);
80 } 108 }
109
110 edit.replace(expr_range, var_name.clone());
111 let offset = anchor_stmt.text_range().start();
81 match ctx.config.snippet_cap { 112 match ctx.config.snippet_cap {
82 Some(cap) => { 113 Some(cap) => {
83 let snip = 114 let snip =
84 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name)); 115 buf.replace(&format!("let {}", var_name), &format!("let $0{}", var_name));
85 edit.replace_snippet(cap, expr_range, snip) 116 edit.insert_snippet(cap, offset, snip)
86 } 117 }
87 None => edit.replace(expr_range, buf), 118 None => edit.insert(offset, buf),
88 } 119 }
89 return;
90 }
91 120
92 buf.push_str(";"); 121 if wrap_in_block {
93 122 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 } 123 }
113 None => edit.insert(offset, buf), 124 },
114 } 125 )
115
116 if wrap_in_block {
117 edit.insert(anchor_stmt.text_range().end(), " }");
118 }
119 })
120} 126}
121 127
122/// Check whether the node is a valid expression which can be extracted to a variable. 128/// 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..96610dbf5 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,30 @@ 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"),
108 let n_old_arms = new_arm_list.arms().count(); 108 AssistKind::RefactorRewrite,
109 let new_arm_list = new_arm_list.append_arms(missing_arms); 109 "Fill match arms",
110 let first_new_arm = new_arm_list.arms().nth(n_old_arms); 110 target,
111 let old_range = match_arm_list.syntax().text_range(); 111 |builder| {
112 match (first_new_arm, ctx.config.snippet_cap) { 112 let new_arm_list = match_arm_list.remove_placeholder();
113 (Some(first_new_arm), Some(cap)) => { 113 let n_old_arms = new_arm_list.arms().count();
114 let snippet = render_snippet( 114 let new_arm_list = new_arm_list.append_arms(missing_arms);
115 cap, 115 let first_new_arm = new_arm_list.arms().nth(n_old_arms);
116 new_arm_list.syntax(), 116 let old_range = match_arm_list.syntax().text_range();
117 Cursor::Before(first_new_arm.syntax()), 117 match (first_new_arm, ctx.config.snippet_cap) {
118 ); 118 (Some(first_new_arm), Some(cap)) => {
119 builder.replace_snippet(cap, old_range, snippet); 119 let snippet = render_snippet(
120 } 120 cap,
121 _ => builder.replace(old_range, new_arm_list.to_string()), 121 new_arm_list.syntax(),
122 } 122 Cursor::Before(first_new_arm.syntax()),
123 }) 123 );
124 builder.replace_snippet(cap, old_range, snippet);
125 }
126 _ => builder.replace(old_range, new_arm_list.to_string()),
127 }
128 },
129 )
124} 130}
125 131
126fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 132fn 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..9c6f9efc2 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..aee55762f 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,19 @@ 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"),
38 edit.replace(op_range, new_op); 38 AssistKind::RefactorRewrite,
39 } 39 "Flip binary expression",
40 edit.replace(lhs.text_range(), rhs.text()); 40 op_range,
41 edit.replace(rhs.text_range(), lhs.text()); 41 |edit| {
42 }) 42 if let FlipAction::FlipAndReplaceOp(new_op) = action {
43 edit.replace(op_range, new_op);
44 }
45 edit.replace(lhs.text_range(), rhs.text());
46 edit.replace(rhs.text_range(), lhs.text());
47 },
48 )
43} 49}
44 50
45enum FlipAction { 51enum FlipAction {
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index a57a1c463..9971ffa71 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,16 @@ 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"),
33 edit.replace(next.text_range(), prev.to_string()); 33 AssistKind::RefactorRewrite,
34 }) 34 "Flip comma",
35 comma.text_range(),
36 |edit| {
37 edit.replace(prev.text_range(), next.to_string());
38 edit.replace(next.text_range(), prev.to_string());
39 },
40 )
35} 41}
36 42
37#[cfg(test)] 43#[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..192f70ccb 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,16 @@ 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"),
38 edit.replace(after.text_range(), before.to_string()); 38 AssistKind::RefactorRewrite,
39 }) 39 "Flip trait bounds",
40 target,
41 |edit| {
42 edit.replace(before.text_range(), after.to_string());
43 edit.replace(after.text_range(), before.to_string());
44 },
45 )
40} 46}
41 47
42#[cfg(test)] 48#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 259839535..f1bd8b46d 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,20 @@ 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"),
115 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 115 AssistKind::RefactorInline,
116 let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; 116 "Inline variable",
117 builder.replace(desc.file_range.range, replacement) 117 target,
118 } 118 move |builder| {
119 }) 119 builder.delete(delete_range);
120 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
121 let replacement =
122 if should_wrap { init_in_paren.clone() } else { init_str.clone() };
123 builder.replace(desc.file_range.range, replacement)
124 }
125 },
126 )
120} 127}
121 128
122#[cfg(test)] 129#[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..374274824 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..eebb2364d 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..b0458e17f 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,15 @@ 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"),
61 }) 61 AssistKind::RefactorRewrite,
62 "Merge imports",
63 target,
64 |builder| {
65 builder.rewrite(rewriter);
66 },
67 )
62} 68}
63 69
64fn next_prev() -> impl Iterator<Item = Direction> { 70fn 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..f0f11a046 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,31 @@ 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"),
64 "_".into() 64 AssistKind::RefactorRewrite,
65 } else { 65 "Merge match arms",
66 arms_to_merge 66 current_text_range,
67 .iter() 67 |edit| {
68 .filter_map(ast::MatchArm::pat) 68 let pats = if arms_to_merge.iter().any(contains_placeholder) {
69 .map(|x| x.syntax().to_string()) 69 "_".into()
70 .collect::<Vec<String>>() 70 } else {
71 .join(" | ") 71 arms_to_merge
72 }; 72 .iter()
73 73 .filter_map(ast::MatchArm::pat)
74 let arm = format!("{} => {}", pats, current_expr.syntax().text()); 74 .map(|x| x.syntax().to_string())
75 75 .collect::<Vec<String>>()
76 let start = arms_to_merge.first().unwrap().syntax().text_range().start(); 76 .join(" | ")
77 let end = arms_to_merge.last().unwrap().syntax().text_range().end(); 77 };
78 78
79 edit.replace(TextRange::new(start, end), arm); 79 let arm = format!("{} => {}", pats, current_expr.syntax().text());
80 }) 80
81 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
82 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
83
84 edit.replace(TextRange::new(start, end), arm);
85 },
86 )
81} 87}
82 88
83fn contains_placeholder(a: &ast::MatchArm) -> bool { 89fn 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..bcedd39a8 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,37 @@ 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"),
55 .type_params() 55 AssistKind::RefactorRewrite,
56 .filter(|it| it.type_bound_list().is_some()) 56 "Move to where clause",
57 .map(|type_param| { 57 target,
58 let without_bounds = type_param.remove_bounds(); 58 |edit| {
59 (type_param, without_bounds) 59 let new_params = type_param_list
60 }); 60 .type_params()
61 61 .filter(|it| it.type_bound_list().is_some())
62 let new_type_param_list = type_param_list.replace_descendants(new_params); 62 .map(|type_param| {
63 edit.replace_ast(type_param_list.clone(), new_type_param_list); 63 let without_bounds = type_param.remove_bounds();
64 64 (type_param, without_bounds)
65 let where_clause = { 65 });
66 let predicates = type_param_list.type_params().filter_map(build_predicate); 66
67 make::where_clause(predicates) 67 let new_type_param_list = type_param_list.replace_descendants(new_params);
68 }; 68 edit.replace_ast(type_param_list.clone(), new_type_param_list);
69 69
70 let to_insert = match anchor.prev_sibling_or_token() { 70 let where_clause = {
71 Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), 71 let predicates = type_param_list.type_params().filter_map(build_predicate);
72 _ => format!(" {}", where_clause.syntax()), 72 make::where_clause(predicates)
73 }; 73 };
74 edit.insert(anchor.text_range().start(), to_insert); 74
75 }) 75 let to_insert = match anchor.prev_sibling_or_token() {
76 Some(ref elem) if elem.kind() == WHITESPACE => {
77 format!("{} ", where_clause.syntax())
78 }
79 _ => format!(" {}", where_clause.syntax()),
80 };
81 edit.insert(anchor.text_range().start(), to_insert);
82 },
83 )
76} 84}
77 85
78fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { 86fn 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..7f3aaf4f7 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,23 @@ 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"),
45 Some(element) if element.kind() == WHITESPACE => { 45 AssistKind::RefactorExtract,
46 edit.delete(element.text_range()); 46 "Move guard to arm body",
47 } 47 target,
48 _ => (), 48 |edit| {
49 }; 49 match space_before_guard {
50 Some(element) if element.kind() == WHITESPACE => {
51 edit.delete(element.text_range());
52 }
53 _ => (),
54 };
50 55
51 edit.delete(guard.syntax().text_range()); 56 edit.delete(guard.syntax().text_range());
52 edit.replace_node_and_indent(arm_expr.syntax(), buf); 57 edit.replace_node_and_indent(arm_expr.syntax(), buf);
53 }) 58 },
59 )
54} 60}
55 61
56// Assist: move_arm_cond_to_match_guard 62// Assist: move_arm_cond_to_match_guard
@@ -101,6 +107,7 @@ pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContex
101 let target = if_expr.syntax().text_range(); 107 let target = if_expr.syntax().text_range();
102 acc.add( 108 acc.add(
103 AssistId("move_arm_cond_to_match_guard"), 109 AssistId("move_arm_cond_to_match_guard"),
110 AssistKind::RefactorRewrite,
104 "Move condition to match guard", 111 "Move condition to match guard",
105 target, 112 target,
106 |edit| { 113 |edit| {
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index d22d0aa55..8d0dac528 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,23 @@ 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"),
31 let mut hashes = String::with_capacity(max_hash_streak + 1); 31 AssistKind::RefactorRewrite,
32 for _ in 0..hashes.capacity() { 32 "Rewrite as raw string",
33 hashes.push('#'); 33 target,
34 } 34 |edit| {
35 edit.replace(token.syntax().text_range(), format!("r{}\"{}\"{}", hashes, value, hashes)); 35 let max_hash_streak = count_hashes(&value);
36 }) 36 let mut hashes = String::with_capacity(max_hash_streak + 1);
37 for _ in 0..hashes.capacity() {
38 hashes.push('#');
39 }
40 edit.replace(
41 token.syntax().text_range(),
42 format!("r{}\"{}\"{}", hashes, value, hashes),
43 );
44 },
45 )
37} 46}
38 47
39// Assist: make_usual_string 48// Assist: make_usual_string
@@ -55,11 +64,17 @@ 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)?; 64 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
56 let value = token.value()?; 65 let value = token.value()?;
57 let target = token.syntax().text_range(); 66 let target = token.syntax().text_range();
58 acc.add(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { 67 acc.add(
59 // parse inside string to escape `"` 68 AssistId("make_usual_string"),
60 let escaped = value.escape_default().to_string(); 69 AssistKind::RefactorRewrite,
61 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); 70 "Rewrite as regular string",
62 }) 71 target,
72 |edit| {
73 // parse inside string to escape `"`
74 let escaped = value.escape_default().to_string();
75 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
76 },
77 )
63} 78}
64 79
65// Assist: add_hash 80// Assist: add_hash
@@ -80,7 +95,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Optio
80pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 95pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let token = ctx.find_token_at_offset(RAW_STRING)?; 96 let token = ctx.find_token_at_offset(RAW_STRING)?;
82 let target = token.text_range(); 97 let target = token.text_range();
83 acc.add(AssistId("add_hash"), "Add # to raw string", target, |edit| { 98 acc.add(AssistId("add_hash"), AssistKind::Refactor, "Add # to raw string", target, |edit| {
84 edit.insert(token.text_range().start() + TextSize::of('r'), "#"); 99 edit.insert(token.text_range().start() + TextSize::of('r'), "#");
85 edit.insert(token.text_range().end(), "#"); 100 edit.insert(token.text_range().end(), "#");
86 }) 101 })
@@ -109,18 +124,24 @@ pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
109 return None; 124 return None;
110 } 125 }
111 let target = token.text_range(); 126 let target = token.text_range();
112 acc.add(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { 127 acc.add(
113 let result = &text[2..text.len() - 1]; 128 AssistId("remove_hash"),
114 let result = if result.starts_with('\"') { 129 AssistKind::RefactorRewrite,
115 // FIXME: this logic is wrong, not only the last has has to handled specially 130 "Remove hash from raw string",
116 // no more hash, escape 131 target,
117 let internal_str = &result[1..result.len() - 1]; 132 |edit| {
118 format!("\"{}\"", internal_str.escape_default().to_string()) 133 let result = &text[2..text.len() - 1];
119 } else { 134 let result = if result.starts_with('\"') {
120 result.to_owned() 135 // FIXME: this logic is wrong, not only the last has has to handled specially
121 }; 136 // no more hash, escape
122 edit.replace(token.text_range(), format!("r{}", result)); 137 let internal_str = &result[1..result.len() - 1];
123 }) 138 format!("\"{}\"", internal_str.escape_default().to_string())
139 } else {
140 result.to_owned()
141 };
142 edit.replace(token.text_range(), format!("r{}", result));
143 },
144 )
124} 145}
125 146
126fn count_hashes(s: &str) -> usize { 147fn 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..e5571676f 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..a8173694a 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,13 @@ 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"),
31 }) 31 AssistKind::Refactor,
32 "Remove `mut` keyword",
33 target,
34 |builder| {
35 builder.delete(TextRange::new(delete_from, delete_to));
36 },
37 )
32} 38}
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index b8cf30e7f..4a542a5d7 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,17 @@ 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"),
47 algo::diff(old, new).into_text_edit(edit.text_edit_builder()); 47 AssistKind::RefactorRewrite,
48 } 48 "Reorder record fields",
49 }) 49 target,
50 |edit| {
51 for (old, new) in fields.iter().zip(&sorted_fields) {
52 algo::diff(old, new).into_text_edit(edit.text_edit_builder());
53 }
54 },
55 )
50} 56}
51 57
52fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> { 58fn 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..9ef121a4c 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,36 @@ 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"),
53 let then_arm = { 53 AssistKind::RefactorRewrite,
54 let then_block = then_block.reset_indent().indent(IndentLevel(1)); 54 "Replace with match",
55 let then_expr = unwrap_trivial_block(then_block); 55 target,
56 make::match_arm(vec![pat.clone()], then_expr) 56 move |edit| {
57 let match_expr = {
58 let then_arm = {
59 let then_block = then_block.reset_indent().indent(IndentLevel(1));
60 let then_expr = unwrap_trivial_block(then_block);
61 make::match_arm(vec![pat.clone()], then_expr)
62 };
63 let else_arm = {
64 let pattern = ctx
65 .sema
66 .type_of_pat(&pat)
67 .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
68 .map(|it| it.sad_pattern())
69 .unwrap_or_else(|| make::placeholder_pat().into());
70 let else_expr = unwrap_trivial_block(else_block);
71 make::match_arm(vec![pattern], else_expr)
72 };
73 let match_expr =
74 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]));
75 match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
57 }; 76 };
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 77
72 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 78 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
73 }) 79 },
80 )
74} 81}
75 82
76#[cfg(test)] 83#[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..174ff1fb4 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,32 @@ 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"),
49 None => make::placeholder_pat().into(), 49 AssistKind::RefactorRewrite,
50 Some(var_name) => make::tuple_struct_pat( 50 "Replace with if-let",
51 make::path_unqualified(make::path_segment(make::name_ref(var_name))), 51 target,
52 once(make::placeholder_pat().into()), 52 |edit| {
53 ) 53 let with_placeholder: ast::Pat = match happy_variant {
54 .into(), 54 None => make::placeholder_pat().into(),
55 }; 55 Some(var_name) => make::tuple_struct_pat(
56 let block = make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); 56 make::path_unqualified(make::path_segment(make::name_ref(var_name))),
57 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); 57 once(make::placeholder_pat().into()),
58 let stmt = make::expr_stmt(if_); 58 )
59 .into(),
60 };
61 let block =
62 make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax()));
63 let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block);
64 let stmt = make::expr_stmt(if_);
59 65
60 let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); 66 let placeholder =
61 let stmt = stmt.replace_descendant(placeholder.into(), original_pat); 67 stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap();
68 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
62 69
63 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); 70 edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
64 }) 71 },
72 )
65} 73}
66 74
67#[cfg(test)] 75#[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..5e06f7f0e 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
@@ -38,6 +38,7 @@ pub(crate) fn replace_qualified_name_with_use(
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"),
41 AssistKind::RefactorRewrite,
41 "Replace qualified path with use", 42 "Replace qualified path with use",
42 target, 43 target,
43 |builder| { 44 |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..5f9705be1 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,44 @@ 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"),
51 let it = make::bind_pat(make::name("a")).into(); 51 AssistKind::RefactorRewrite,
52 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); 52 "Replace unwrap with match",
53 target,
54 |builder| {
55 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
56 let it = make::bind_pat(make::name("a")).into();
57 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
53 58
54 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); 59 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)); 60 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
56 61
57 let unreachable_call = make::expr_unreachable(); 62 let unreachable_call = make::expr_unreachable();
58 let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); 63 let err_arm =
64 make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
59 65
60 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); 66 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) 67 let match_expr = make::expr_match(caller.clone(), match_arm_list)
62 .indent(IndentLevel::from_node(method_call.syntax())); 68 .indent(IndentLevel::from_node(method_call.syntax()));
63 69
64 let range = method_call.syntax().text_range(); 70 let range = method_call.syntax().text_range();
65 match ctx.config.snippet_cap { 71 match ctx.config.snippet_cap {
66 Some(cap) => { 72 Some(cap) => {
67 let err_arm = match_expr 73 let err_arm = match_expr
68 .syntax() 74 .syntax()
69 .descendants() 75 .descendants()
70 .filter_map(ast::MatchArm::cast) 76 .filter_map(ast::MatchArm::cast)
71 .last() 77 .last()
72 .unwrap(); 78 .unwrap();
73 let snippet = 79 let snippet =
74 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax())); 80 render_snippet(cap, match_expr.syntax(), Cursor::Before(err_arm.syntax()));
75 builder.replace_snippet(cap, range, snippet) 81 builder.replace_snippet(cap, range, snippet)
82 }
83 None => builder.replace(range, match_expr.to_string()),
76 } 84 }
77 None => builder.replace(range, match_expr.to_string()), 85 },
78 } 86 )
79 })
80} 87}
81 88
82#[cfg(test)] 89#[cfg(test)]
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index 38aa199a0..c0cf8d923 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::RefactorExtract, "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..a66fba7c3 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//
@@ -50,35 +50,47 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
50 let ancestor_then_branch = ancestor.then_branch()?; 50 let ancestor_then_branch = ancestor.then_branch()?;
51 51
52 let target = then_branch.syntax().text_range(); 52 let target = then_branch.syntax().text_range();
53 return acc.add(assist_id, assist_label, target, |edit| { 53 return acc.add(
54 let range_to_del_else_if = TextRange::new( 54 assist_id,
55 ancestor_then_branch.syntax().text_range().end(), 55 AssistKind::Refactor,
56 l_curly_token.text_range().start(), 56 assist_label,
57 ); 57 target,
58 let range_to_del_rest = TextRange::new( 58 |edit| {
59 then_branch.syntax().text_range().end(), 59 let range_to_del_else_if = TextRange::new(
60 if_expr.syntax().text_range().end(), 60 ancestor_then_branch.syntax().text_range().end(),
61 ); 61 l_curly_token.text_range().start(),
62 62 );
63 edit.delete(range_to_del_rest); 63 let range_to_del_rest = TextRange::new(
64 edit.delete(range_to_del_else_if); 64 then_branch.syntax().text_range().end(),
65 edit.replace( 65 if_expr.syntax().text_range().end(),
66 target, 66 );
67 update_expr_string(then_branch.to_string(), &[' ', '{']), 67
68 ); 68 edit.delete(range_to_del_rest);
69 }); 69 edit.delete(range_to_del_else_if);
70 edit.replace(
71 target,
72 update_expr_string(then_branch.to_string(), &[' ', '{']),
73 );
74 },
75 );
70 } 76 }
71 } else { 77 } else {
72 let target = block.syntax().text_range(); 78 let target = block.syntax().text_range();
73 return acc.add(assist_id, assist_label, target, |edit| { 79 return acc.add(
74 let range_to_del = TextRange::new( 80 assist_id,
75 then_branch.syntax().text_range().end(), 81 AssistKind::RefactorRewrite,
76 l_curly_token.text_range().start(), 82 assist_label,
77 ); 83 target,
84 |edit| {
85 let range_to_del = TextRange::new(
86 then_branch.syntax().text_range().end(),
87 l_curly_token.text_range().start(),
88 );
78 89
79 edit.delete(range_to_del); 90 edit.delete(range_to_del);
80 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{'])); 91 edit.replace(target, update_expr_string(block.to_string(), &[' ', '{']));
81 }); 92 },
93 );
82 } 94 }
83 } 95 }
84 _ => return None, 96 _ => return None,
@@ -86,7 +98,7 @@ pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
86 98
87 let unwrapped = unwrap_trivial_block(block); 99 let unwrapped = unwrap_trivial_block(block);
88 let target = unwrapped.syntax().text_range(); 100 let target = unwrapped.syntax().text_range();
89 acc.add(assist_id, assist_label, target, |builder| { 101 acc.add(assist_id, AssistKind::RefactorRewrite, assist_label, target, |builder| {
90 builder.replace( 102 builder.replace(
91 parent.syntax().text_range(), 103 parent.syntax().text_range(),
92 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']), 104 update_expr_string(unwrapped.to_string(), &[' ', '{', '\n']),
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 1745f44a5..dd26e192f 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -37,6 +37,7 @@ pub struct GroupLabel(pub String);
37#[derive(Debug, Clone)] 37#[derive(Debug, Clone)]
38pub struct Assist { 38pub struct Assist {
39 pub id: AssistId, 39 pub id: AssistId,
40 pub kind: AssistKind,
40 /// Short description of the assist, as shown in the UI. 41 /// Short description of the assist, as shown in the UI.
41 pub label: String, 42 pub label: String,
42 pub group: Option<GroupLabel>, 43 pub group: Option<GroupLabel>,
@@ -51,6 +52,18 @@ pub struct ResolvedAssist {
51 pub source_change: SourceChange, 52 pub source_change: SourceChange,
52} 53}
53 54
55#[derive(Debug, Copy, Clone)]
56pub enum AssistKind {
57 None,
58 QuickFix,
59 Refactor,
60 RefactorExtract,
61 RefactorInline,
62 RefactorRewrite,
63 Source,
64 OrganizeImports,
65}
66
54impl Assist { 67impl Assist {
55 /// Return all the assists applicable at the given position. 68 /// Return all the assists applicable at the given position.
56 /// 69 ///
@@ -86,13 +99,14 @@ impl Assist {
86 99
87 pub(crate) fn new( 100 pub(crate) fn new(
88 id: AssistId, 101 id: AssistId,
102 kind: AssistKind,
89 label: String, 103 label: String,
90 group: Option<GroupLabel>, 104 group: Option<GroupLabel>,
91 target: TextRange, 105 target: TextRange,
92 ) -> Assist { 106 ) -> Assist {
93 // FIXME: make fields private, so that this invariant can't be broken 107 // FIXME: make fields private, so that this invariant can't be broken
94 assert!(label.starts_with(|c: char| c.is_uppercase())); 108 assert!(label.starts_with(|c: char| c.is_uppercase()));
95 Assist { id, label, group, target } 109 Assist { id, kind, label, group, target }
96 } 110 }
97} 111}
98 112
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 8660278f1..dcfa186dc 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -76,7 +76,7 @@ pub use crate::{
76}; 76};
77 77
78pub use hir::{Documentation, Semantics}; 78pub use hir::{Documentation, Semantics};
79pub use ra_assists::{Assist, AssistConfig, AssistId, ResolvedAssist}; 79pub use ra_assists::{Assist, AssistConfig, AssistId, AssistKind, ResolvedAssist};
80pub use ra_db::{ 80pub use ra_db::{
81 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot, 81 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRoot,
82 SourceRootId, 82 SourceRootId,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 652a44694..c3a0bff58 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -4,9 +4,9 @@ use std::path::{self, Path};
4use itertools::Itertools; 4use itertools::Itertools;
5use ra_db::{FileId, FileRange}; 5use ra_db::{FileId, FileRange};
6use ra_ide::{ 6use ra_ide::{
7 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 7 Assist, AssistKind, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold,
8 FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange, Indel, 8 FoldKind, FunctionSignature, Highlight, HighlightModifier, HighlightTag, HighlightedRange,
9 InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess, 9 Indel, InlayHint, InlayKind, InsertTextFormat, LineIndex, NavigationTarget, ReferenceAccess,
10 ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit, 10 ResolvedAssist, Runnable, Severity, SourceChange, SourceFileEdit, TextEdit,
11}; 11};
12use ra_syntax::{SyntaxKind, TextRange, TextSize}; 12use ra_syntax::{SyntaxKind, TextRange, TextSize};
@@ -627,6 +627,20 @@ pub(crate) fn call_hierarchy_item(
627 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) 627 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
628} 628}
629 629
630pub(crate) fn code_action_kind(kind: AssistKind) -> String {
631 match kind {
632 AssistKind::None => lsp_types::code_action_kind::EMPTY,
633 AssistKind::QuickFix => lsp_types::code_action_kind::QUICKFIX,
634 AssistKind::Refactor => lsp_types::code_action_kind::REFACTOR,
635 AssistKind::RefactorExtract => lsp_types::code_action_kind::REFACTOR_EXTRACT,
636 AssistKind::RefactorInline => lsp_types::code_action_kind::REFACTOR_INLINE,
637 AssistKind::RefactorRewrite => lsp_types::code_action_kind::REFACTOR_REWRITE,
638 AssistKind::Source => lsp_types::code_action_kind::SOURCE,
639 AssistKind::OrganizeImports => lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS,
640 }
641 .to_string()
642}
643
630pub(crate) fn unresolved_code_action( 644pub(crate) fn unresolved_code_action(
631 snap: &GlobalStateSnapshot, 645 snap: &GlobalStateSnapshot,
632 assist: Assist, 646 assist: Assist,
@@ -636,7 +650,7 @@ pub(crate) fn unresolved_code_action(
636 title: assist.label, 650 title: assist.label,
637 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())), 651 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())),
638 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), 652 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
639 kind: Some(String::new()), 653 kind: Some(code_action_kind(assist.kind)),
640 edit: None, 654 edit: None,
641 command: None, 655 command: None,
642 }; 656 };