aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-07-03 13:44:09 +0100
committerGitHub <[email protected]>2020-07-03 13:44:09 +0100
commitf51b0cfdd6c23dd57a0a11154179730171c0425d (patch)
treef856e6b751b23a20d68e9df5350ec39d5e8ee85b
parentef6a6d75d5dba2825e6b90e67e0b147a5f7158e1 (diff)
parent4c9347ecc3356748c52847a29d5e53a65778dc13 (diff)
Merge #5116
5116: Categorize assists r=matklad a=kjeremy Categorize assists so that editors can use them. Follows the LSP spec pretty close (and some things may need adjustments) but this populates the Refactor menu in vscode and pushes quickfixes through again. This is a prerequisite to filtering out assists that the client doesn't care about. Fixes #4147 Co-authored-by: Jeremy Kolb <[email protected]> Co-authored-by: kjeremy <[email protected]>
-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.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_function.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs67
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs4
-rw-r--r--crates/ra_assists/src/handlers/add_new.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.rs6
-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/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.rs12
-rw-r--r--crates/ra_ide/src/lib.rs2
-rw-r--r--crates/rust-analyzer/src/caps.rs2
-rw-r--r--crates/rust-analyzer/src/to_proto.rs20
-rw-r--r--editors/code/src/client.ts6
43 files changed, 593 insertions, 442 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_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index b123b8498..e2b94e7f8 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::None), "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..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_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
index b0e56e1b5..a324670ed 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//
@@ -45,7 +45,7 @@ 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("add_from_impl_for_enum", AssistKind::Refactor),
49 "Add From impl for this enum variant", 49 "Add From impl for this enum variant",
50 target, 50 target,
51 |edit| { 51 |edit| {
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index fc4e82309..7150eb53a 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,7 +62,7 @@ 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(AssistId("add_function", AssistKind::None), "Add function", target, |builder| {
66 let function_template = function_builder.render(); 66 let function_template = function_builder.render();
67 builder.edit_file(function_template.file); 67 builder.edit_file(function_template.file);
68 let new_fn = function_template.to_string(ctx.config.snippet_cap); 68 let new_fn = function_template.to_string(ctx.config.snippet_cap);
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index eceba7d0a..2f603ef9c 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,45 @@ 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", AssistKind::Refactor),
31 let start_offset = nominal.syntax().text_range().end(); 31 format!("Implement {}", name.text().as_str()),
32 let mut buf = String::new(); 32 target,
33 buf.push_str("\n\nimpl"); 33 |edit| {
34 if let Some(type_params) = &type_params { 34 let type_params = nominal.type_param_list();
35 format_to!(buf, "{}", type_params.syntax()); 35 let start_offset = nominal.syntax().text_range().end();
36 } 36 let mut buf = String::new();
37 buf.push_str(" "); 37 buf.push_str("\n\nimpl");
38 buf.push_str(name.text().as_str()); 38 if let Some(type_params) = &type_params {
39 if let Some(type_params) = type_params { 39 format_to!(buf, "{}", type_params.syntax());
40 let lifetime_params = type_params 40 }
41 .lifetime_params() 41 buf.push_str(" ");
42 .filter_map(|it| it.lifetime_token()) 42 buf.push_str(name.text().as_str());
43 .map(|it| it.text().clone()); 43 if let Some(type_params) = type_params {
44 let type_params = 44 let lifetime_params = type_params
45 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); 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());
46 52
47 let generic_params = lifetime_params.chain(type_params).sep_by(", "); 53 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
48 format_to!(buf, "<{}>", generic_params) 54 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 }
55 None => { 56 match ctx.config.snippet_cap {
56 buf.push_str(" {\n}"); 57 Some(cap) => {
57 edit.insert(start_offset, buf); 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 }
58 } 65 }
59 } 66 },
60 }) 67 )
61} 68}
62 69
63#[cfg(test)] 70#[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..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_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index e41b2aa06..0b3d29c7c 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,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("add_new", AssistKind::None), "Add default constructor", 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() {
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 4cd77adbf..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| {
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/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..65cda95ee 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -26,10 +26,20 @@ 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 Refactor,
34 RefactorExtract,
35 RefactorInline,
36 RefactorRewrite,
37}
38
29/// Unique identifier of the assist, should not be shown to the user 39/// Unique identifier of the assist, should not be shown to the user
30/// directly. 40/// directly.
31#[derive(Debug, Clone, Copy, PartialEq, Eq)] 41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct AssistId(pub &'static str); 42pub struct AssistId(pub &'static str, pub AssistKind);
33 43
34#[derive(Clone, Debug)] 44#[derive(Clone, Debug)]
35pub struct GroupLabel(pub String); 45pub struct GroupLabel(pub String);
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/caps.rs b/crates/rust-analyzer/src/caps.rs
index 673795e78..070ad5e68 100644
--- a/crates/rust-analyzer/src/caps.rs
+++ b/crates/rust-analyzer/src/caps.rs
@@ -112,8 +112,6 @@ fn code_action_capabilities(client_caps: &ClientCapabilities) -> CodeActionProvi
112 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(), 112 lsp_types::code_action_kind::REFACTOR_EXTRACT.to_string(),
113 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(), 113 lsp_types::code_action_kind::REFACTOR_INLINE.to_string(),
114 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(), 114 lsp_types::code_action_kind::REFACTOR_REWRITE.to_string(),
115 lsp_types::code_action_kind::SOURCE.to_string(),
116 lsp_types::code_action_kind::SOURCE_ORGANIZE_IMPORTS.to_string(),
117 ]), 115 ]),
118 work_done_progress_options: Default::default(), 116 work_done_progress_options: Default::default(),
119 }) 117 })
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 652a44694..2312a6e4d 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,18 @@ 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 }
639 .to_string()
640}
641
630pub(crate) fn unresolved_code_action( 642pub(crate) fn unresolved_code_action(
631 snap: &GlobalStateSnapshot, 643 snap: &GlobalStateSnapshot,
632 assist: Assist, 644 assist: Assist,
@@ -636,7 +648,7 @@ pub(crate) fn unresolved_code_action(
636 title: assist.label, 648 title: assist.label,
637 id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())), 649 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), 650 group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0),
639 kind: Some(String::new()), 651 kind: Some(code_action_kind(assist.id.1)),
640 edit: None, 652 edit: None,
641 command: None, 653 command: None,
642 }; 654 };
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 3e5915c28..41ffac7b3 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -66,7 +66,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
66 return Promise.resolve(null); 66 return Promise.resolve(null);
67 }); 67 });
68 }, 68 },
69 // Using custom handling of CodeActions where each code action is resloved lazily 69 // Using custom handling of CodeActions where each code action is resolved lazily
70 // That's why we are not waiting for any command or edits 70 // That's why we are not waiting for any command or edits
71 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) { 71 async provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken, _next: lc.ProvideCodeActionsSignature) {
72 const params: lc.CodeActionParams = { 72 const params: lc.CodeActionParams = {
@@ -87,7 +87,8 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
87 continue; 87 continue;
88 } 88 }
89 assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here"); 89 assert(isCodeActionWithoutEditsAndCommands(item), "We don't expect edits or commands here");
90 const action = new vscode.CodeAction(item.title); 90 const kind = client.protocol2CodeConverter.asCodeActionKind((item as any).kind);
91 const action = new vscode.CodeAction(item.title, kind);
91 const group = (item as any).group; 92 const group = (item as any).group;
92 const id = (item as any).id; 93 const id = (item as any).id;
93 const resolveParams: ra.ResolveCodeActionParams = { 94 const resolveParams: ra.ResolveCodeActionParams = {
@@ -116,6 +117,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient
116 result[index] = items[0]; 117 result[index] = items[0];
117 } else { 118 } else {
118 const action = new vscode.CodeAction(group); 119 const action = new vscode.CodeAction(group);
120 action.kind = items[0].kind;
119 action.command = { 121 action.command = {
120 command: "rust-analyzer.applyActionGroup", 122 command: "rust-analyzer.applyActionGroup",
121 title: "", 123 title: "",