aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/handlers
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/handlers')
-rw-r--r--crates/ra_assists/src/handlers/add_custom_impl.rs7
-rw-r--r--crates/ra_assists/src/handlers/add_derive.rs6
-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.rs44
-rw-r--r--crates/ra_assists/src/handlers/add_impl.rs61
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs16
-rw-r--r--crates/ra_assists/src/handlers/add_new.rs7
-rw-r--r--crates/ra_assists/src/handlers/apply_demorgan.rs5
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs5
-rw-r--r--crates/ra_assists/src/handlers/change_return_type_to_result.rs971
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs34
-rw-r--r--crates/ra_assists/src/handlers/early_return.rs161
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs7
-rw-r--r--crates/ra_assists/src/handlers/flip_binexpr.rs5
-rw-r--r--crates/ra_assists/src/handlers/flip_comma.rs5
-rw-r--r--crates/ra_assists/src/handlers/flip_trait_bound.rs6
-rw-r--r--crates/ra_assists/src/handlers/inline_local_variable.rs4
-rw-r--r--crates/ra_assists/src/handlers/introduce_variable.rs6
-rw-r--r--crates/ra_assists/src/handlers/invert_if.rs5
-rw-r--r--crates/ra_assists/src/handlers/merge_imports.rs5
-rw-r--r--crates/ra_assists/src/handlers/merge_match_arms.rs5
-rw-r--r--crates/ra_assists/src/handlers/move_bounds.rs57
-rw-r--r--crates/ra_assists/src/handlers/move_guard.rs9
-rw-r--r--crates/ra_assists/src/handlers/raw_string.rs18
-rw-r--r--crates/ra_assists/src/handlers/remove_dbg.rs6
-rw-r--r--crates/ra_assists/src/handlers/remove_mut.rs3
-rw-r--r--crates/ra_assists/src/handlers/reorder_fields.rs6
-rw-r--r--crates/ra_assists/src/handlers/replace_if_let_with_match.rs49
-rw-r--r--crates/ra_assists/src/handlers/replace_let_with_if_let.rs6
-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.rs40
-rw-r--r--crates/ra_assists/src/handlers/split_import.rs6
-rw-r--r--crates/ra_assists/src/handlers/unwrap_block.rs6
34 files changed, 1294 insertions, 289 deletions
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs
index 4ea26a550..869d4dc04 100644
--- a/crates/ra_assists/src/handlers/add_custom_impl.rs
+++ b/crates/ra_assists/src/handlers/add_custom_impl.rs
@@ -48,9 +48,8 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
48 let label = 48 let label =
49 format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); 49 format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name);
50 50
51 ctx.add_assist(AssistId("add_custom_impl"), label, |edit| { 51 let target = attr.syntax().text_range();
52 edit.target(attr.syntax().text_range()); 52 ctx.add_assist(AssistId("add_custom_impl"), label, target, |edit| {
53
54 let new_attr_input = input 53 let new_attr_input = input
55 .syntax() 54 .syntax()
56 .descendants_with_tokens() 55 .descendants_with_tokens()
@@ -95,7 +94,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> {
95 94
96#[cfg(test)] 95#[cfg(test)]
97mod tests { 96mod tests {
98 use crate::helpers::{check_assist, check_assist_not_applicable}; 97 use crate::tests::{check_assist, check_assist_not_applicable};
99 98
100 use super::*; 99 use super::*;
101 100
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs
index 6254eb7c4..2a6bb1cae 100644
--- a/crates/ra_assists/src/handlers/add_derive.rs
+++ b/crates/ra_assists/src/handlers/add_derive.rs
@@ -27,7 +27,8 @@ use crate::{Assist, AssistCtx, AssistId};
27pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> { 27pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> {
28 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 28 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
29 let node_start = derive_insertion_offset(&nominal)?; 29 let node_start = derive_insertion_offset(&nominal)?;
30 ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", |edit| { 30 let target = nominal.syntax().text_range();
31 ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", target, |edit| {
31 let derive_attr = nominal 32 let derive_attr = nominal
32 .attrs() 33 .attrs()
33 .filter_map(|x| x.as_simple_call()) 34 .filter_map(|x| x.as_simple_call())
@@ -41,7 +42,6 @@ pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> {
41 } 42 }
42 Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'), 43 Some(tt) => tt.syntax().text_range().end() - TextSize::of(')'),
43 }; 44 };
44 edit.target(nominal.syntax().text_range());
45 edit.set_cursor(offset) 45 edit.set_cursor(offset)
46 }) 46 })
47} 47}
@@ -58,7 +58,7 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextSize> {
58#[cfg(test)] 58#[cfg(test)]
59mod tests { 59mod tests {
60 use super::*; 60 use super::*;
61 use crate::helpers::{check_assist, check_assist_target}; 61 use crate::tests::{check_assist, check_assist_target};
62 62
63 #[test] 63 #[test]
64 fn add_derive_new() { 64 fn add_derive_new() {
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs
index bc313782b..a59ec16b2 100644
--- a/crates/ra_assists/src/handlers/add_explicit_type.rs
+++ b/crates/ra_assists/src/handlers/add_explicit_type.rs
@@ -62,8 +62,8 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
62 ctx.add_assist( 62 ctx.add_assist(
63 AssistId("add_explicit_type"), 63 AssistId("add_explicit_type"),
64 format!("Insert explicit type '{}'", new_type_string), 64 format!("Insert explicit type '{}'", new_type_string),
65 pat_range,
65 |edit| { 66 |edit| {
66 edit.target(pat_range);
67 if let Some(ascribed_ty) = ascribed_ty { 67 if let Some(ascribed_ty) = ascribed_ty {
68 edit.replace(ascribed_ty.syntax().text_range(), new_type_string); 68 edit.replace(ascribed_ty.syntax().text_range(), new_type_string);
69 } else { 69 } else {
@@ -77,7 +77,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> {
77mod tests { 77mod tests {
78 use super::*; 78 use super::*;
79 79
80 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 80 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
81 81
82 #[test] 82 #[test]
83 fn add_explicit_type_target() { 83 fn add_explicit_type_target() {
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 49deb6701..81deb3dfa 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
@@ -47,9 +47,11 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
47 return None; 47 return None;
48 } 48 }
49 49
50 let target = variant.syntax().text_range();
50 ctx.add_assist( 51 ctx.add_assist(
51 AssistId("add_from_impl_for_enum"), 52 AssistId("add_from_impl_for_enum"),
52 "Add From impl for this enum variant", 53 "Add From impl for this enum variant",
54 target,
53 |edit| { 55 |edit| {
54 let start_offset = variant.parent_enum().syntax().text_range().end(); 56 let start_offset = variant.parent_enum().syntax().text_range().end();
55 let mut buf = String::new(); 57 let mut buf = String::new();
@@ -97,7 +99,7 @@ fn existing_from_impl(
97mod tests { 99mod tests {
98 use super::*; 100 use super::*;
99 101
100 use crate::helpers::{check_assist, check_assist_not_applicable}; 102 use crate::tests::{check_assist, check_assist_not_applicable};
101 use test_utils::covers; 103 use test_utils::covers;
102 104
103 #[test] 105 #[test]
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs
index 6c7456579..278079665 100644
--- a/crates/ra_assists/src/handlers/add_function.rs
+++ b/crates/ra_assists/src/handlers/add_function.rs
@@ -3,9 +3,10 @@ use ra_syntax::{
3 SyntaxKind, SyntaxNode, TextSize, 3 SyntaxKind, SyntaxNode, TextSize,
4}; 4};
5 5
6use crate::{Assist, AssistCtx, AssistFile, AssistId}; 6use crate::{Assist, AssistCtx, AssistId};
7use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; 7use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner};
8use hir::HirDisplay; 8use hir::HirDisplay;
9use ra_db::FileId;
9use rustc_hash::{FxHashMap, FxHashSet}; 10use rustc_hash::{FxHashMap, FxHashSet};
10 11
11// Assist: add_function 12// Assist: add_function
@@ -57,14 +58,12 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> {
57 58
58 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; 59 let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?;
59 60
60 ctx.add_assist(AssistId("add_function"), "Add function", |edit| { 61 let target = call.syntax().text_range();
61 edit.target(call.syntax().text_range()); 62 ctx.add_assist(AssistId("add_function"), "Add function", target, |edit| {
62 63 let function_template = function_builder.render();
63 if let Some(function_template) = function_builder.render() { 64 edit.set_file(function_template.file);
64 edit.set_file(function_template.file); 65 edit.set_cursor(function_template.cursor_offset);
65 edit.set_cursor(function_template.cursor_offset); 66 edit.insert(function_template.insert_offset, function_template.fn_def.to_string());
66 edit.insert(function_template.insert_offset, function_template.fn_def.to_string());
67 }
68 }) 67 })
69} 68}
70 69
@@ -72,7 +71,7 @@ struct FunctionTemplate {
72 insert_offset: TextSize, 71 insert_offset: TextSize,
73 cursor_offset: TextSize, 72 cursor_offset: TextSize,
74 fn_def: ast::SourceFile, 73 fn_def: ast::SourceFile,
75 file: AssistFile, 74 file: FileId,
76} 75}
77 76
78struct FunctionBuilder { 77struct FunctionBuilder {
@@ -80,7 +79,7 @@ struct FunctionBuilder {
80 fn_name: ast::Name, 79 fn_name: ast::Name,
81 type_params: Option<ast::TypeParamList>, 80 type_params: Option<ast::TypeParamList>,
82 params: ast::ParamList, 81 params: ast::ParamList,
83 file: AssistFile, 82 file: FileId,
84 needs_pub: bool, 83 needs_pub: bool,
85} 84}
86 85
@@ -94,7 +93,7 @@ impl FunctionBuilder {
94 target_module: Option<hir::InFile<hir::ModuleSource>>, 93 target_module: Option<hir::InFile<hir::ModuleSource>>,
95 ) -> Option<Self> { 94 ) -> Option<Self> {
96 let needs_pub = target_module.is_some(); 95 let needs_pub = target_module.is_some();
97 let mut file = AssistFile::default(); 96 let mut file = ctx.frange.file_id;
98 let target = if let Some(target_module) = target_module { 97 let target = if let Some(target_module) = target_module {
99 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?; 98 let (in_file, target) = next_space_for_fn_in_module(ctx.sema.db, target_module)?;
100 file = in_file; 99 file = in_file;
@@ -107,7 +106,7 @@ impl FunctionBuilder {
107 Some(Self { target, fn_name, type_params, params, file, needs_pub }) 106 Some(Self { target, fn_name, type_params, params, file, needs_pub })
108 } 107 }
109 108
110 fn render(self) -> Option<FunctionTemplate> { 109 fn render(self) -> FunctionTemplate {
111 let placeholder_expr = ast::make::expr_todo(); 110 let placeholder_expr = ast::make::expr_todo();
112 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr)); 111 let fn_body = ast::make::block_expr(vec![], Some(placeholder_expr));
113 let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body); 112 let mut fn_def = ast::make::fn_def(self.fn_name, self.type_params, self.params, fn_body);
@@ -133,15 +132,11 @@ impl FunctionBuilder {
133 } 132 }
134 }; 133 };
135 134
136 let cursor_offset_from_fn_start = fn_def 135 let placeholder_expr =
137 .syntax() 136 fn_def.syntax().descendants().find_map(ast::MacroCall::cast).unwrap();
138 .descendants() 137 let cursor_offset_from_fn_start = placeholder_expr.syntax().text_range().start();
139 .find_map(ast::MacroCall::cast)?
140 .syntax()
141 .text_range()
142 .start();
143 let cursor_offset = insert_offset + cursor_offset_from_fn_start; 138 let cursor_offset = insert_offset + cursor_offset_from_fn_start;
144 Some(FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }) 139 FunctionTemplate { insert_offset, cursor_offset, fn_def, file: self.file }
145 } 140 }
146} 141}
147 142
@@ -259,9 +254,8 @@ fn next_space_for_fn_after_call_site(expr: &ast::CallExpr) -> Option<GeneratedFu
259fn next_space_for_fn_in_module( 254fn next_space_for_fn_in_module(
260 db: &dyn hir::db::AstDatabase, 255 db: &dyn hir::db::AstDatabase,
261 module: hir::InFile<hir::ModuleSource>, 256 module: hir::InFile<hir::ModuleSource>,
262) -> Option<(AssistFile, GeneratedFunctionTarget)> { 257) -> Option<(FileId, GeneratedFunctionTarget)> {
263 let file = module.file_id.original_file(db); 258 let file = module.file_id.original_file(db);
264 let assist_file = AssistFile::TargetFile(file);
265 let assist_item = match module.value { 259 let assist_item = match module.value {
266 hir::ModuleSource::SourceFile(it) => { 260 hir::ModuleSource::SourceFile(it) => {
267 if let Some(last_item) = it.items().last() { 261 if let Some(last_item) = it.items().last() {
@@ -278,12 +272,12 @@ fn next_space_for_fn_in_module(
278 } 272 }
279 } 273 }
280 }; 274 };
281 Some((assist_file, assist_item)) 275 Some((file, assist_item))
282} 276}
283 277
284#[cfg(test)] 278#[cfg(test)]
285mod tests { 279mod tests {
286 use crate::helpers::{check_assist, check_assist_not_applicable}; 280 use crate::tests::{check_assist, check_assist_not_applicable};
287 281
288 use super::*; 282 use super::*;
289 283
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs
index d26f8b93d..557344ebb 100644
--- a/crates/ra_assists/src/handlers/add_impl.rs
+++ b/crates/ra_assists/src/handlers/add_impl.rs
@@ -28,39 +28,46 @@ use crate::{Assist, AssistCtx, AssistId};
28pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> { 28pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> {
29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; 29 let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?;
30 let name = nominal.name()?; 30 let name = nominal.name()?;
31 ctx.add_assist(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), |edit| { 31 let target = nominal.syntax().text_range();
32 edit.target(nominal.syntax().text_range()); 32 ctx.add_assist(
33 let type_params = nominal.type_param_list(); 33 AssistId("add_impl"),
34 let start_offset = nominal.syntax().text_range().end(); 34 format!("Implement {}", name.text().as_str()),
35 let mut buf = String::new(); 35 target,
36 buf.push_str("\n\nimpl"); 36 |edit| {
37 if let Some(type_params) = &type_params { 37 let type_params = nominal.type_param_list();
38 format_to!(buf, "{}", type_params.syntax()); 38 let start_offset = nominal.syntax().text_range().end();
39 } 39 let mut buf = String::new();
40 buf.push_str(" "); 40 buf.push_str("\n\nimpl");
41 buf.push_str(name.text().as_str()); 41 if let Some(type_params) = &type_params {
42 if let Some(type_params) = type_params { 42 format_to!(buf, "{}", type_params.syntax());
43 let lifetime_params = type_params 43 }
44 .lifetime_params() 44 buf.push_str(" ");
45 .filter_map(|it| it.lifetime_token()) 45 buf.push_str(name.text().as_str());
46 .map(|it| it.text().clone()); 46 if let Some(type_params) = type_params {
47 let type_params = 47 let lifetime_params = type_params
48 type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); 48 .lifetime_params()
49 .filter_map(|it| it.lifetime_token())
50 .map(|it| it.text().clone());
51 let type_params = type_params
52 .type_params()
53 .filter_map(|it| it.name())
54 .map(|it| it.text().clone());
49 55
50 let generic_params = lifetime_params.chain(type_params).sep_by(", "); 56 let generic_params = lifetime_params.chain(type_params).sep_by(", ");
51 format_to!(buf, "<{}>", generic_params) 57 format_to!(buf, "<{}>", generic_params)
52 } 58 }
53 buf.push_str(" {\n"); 59 buf.push_str(" {\n");
54 edit.set_cursor(start_offset + TextSize::of(&buf)); 60 edit.set_cursor(start_offset + TextSize::of(&buf));
55 buf.push_str("\n}"); 61 buf.push_str("\n}");
56 edit.insert(start_offset, buf); 62 edit.insert(start_offset, buf);
57 }) 63 },
64 )
58} 65}
59 66
60#[cfg(test)] 67#[cfg(test)]
61mod tests { 68mod tests {
62 use super::*; 69 use super::*;
63 use crate::helpers::{check_assist, check_assist_target}; 70 use crate::tests::{check_assist, check_assist_target};
64 71
65 #[test] 72 #[test]
66 fn test_add_impl() { 73 fn test_add_impl() {
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 e47feda71..7df786590 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -107,10 +107,10 @@ fn add_missing_impl_members_inner(
107 label: &'static str, 107 label: &'static str,
108) -> Option<Assist> { 108) -> Option<Assist> {
109 let _p = ra_prof::profile("add_missing_impl_members_inner"); 109 let _p = ra_prof::profile("add_missing_impl_members_inner");
110 let impl_node = ctx.find_node_at_offset::<ast::ImplDef>()?; 110 let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?;
111 let impl_item_list = impl_node.item_list()?; 111 let impl_item_list = impl_def.item_list()?;
112 112
113 let trait_ = resolve_target_trait(&ctx.sema, &impl_node)?; 113 let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
114 114
115 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> { 115 let def_name = |item: &ast::AssocItem| -> Option<SmolStr> {
116 match item { 116 match item {
@@ -121,7 +121,7 @@ fn add_missing_impl_members_inner(
121 .map(|it| it.text().clone()) 121 .map(|it| it.text().clone())
122 }; 122 };
123 123
124 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_node) 124 let missing_items = get_missing_assoc_items(&ctx.sema, &impl_def)
125 .iter() 125 .iter()
126 .map(|i| match i { 126 .map(|i| match i {
127 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value), 127 hir::AssocItem::Function(i) => ast::AssocItem::FnDef(i.source(ctx.db).value),
@@ -143,13 +143,13 @@ fn add_missing_impl_members_inner(
143 } 143 }
144 144
145 let sema = ctx.sema; 145 let sema = ctx.sema;
146 146 let target = impl_def.syntax().text_range();
147 ctx.add_assist(AssistId(assist_id), label, |edit| { 147 ctx.add_assist(AssistId(assist_id), label, target, |edit| {
148 let n_existing_items = impl_item_list.assoc_items().count(); 148 let n_existing_items = impl_item_list.assoc_items().count();
149 let source_scope = sema.scope_for_def(trait_); 149 let source_scope = sema.scope_for_def(trait_);
150 let target_scope = sema.scope(impl_item_list.syntax()); 150 let target_scope = sema.scope(impl_item_list.syntax());
151 let ast_transform = QualifyPaths::new(&target_scope, &source_scope) 151 let ast_transform = QualifyPaths::new(&target_scope, &source_scope)
152 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_node)); 152 .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def));
153 let items = missing_items 153 let items = missing_items
154 .into_iter() 154 .into_iter()
155 .map(|it| ast_transform::apply(&*ast_transform, it)) 155 .map(|it| ast_transform::apply(&*ast_transform, it))
@@ -181,7 +181,7 @@ fn add_body(fn_def: ast::FnDef) -> ast::FnDef {
181 181
182#[cfg(test)] 182#[cfg(test)]
183mod tests { 183mod tests {
184 use crate::helpers::{check_assist, check_assist_not_applicable}; 184 use crate::tests::{check_assist, check_assist_not_applicable};
185 185
186 use super::*; 186 use super::*;
187 187
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs
index e8a36c7de..1c3f8435a 100644
--- a/crates/ra_assists/src/handlers/add_new.rs
+++ b/crates/ra_assists/src/handlers/add_new.rs
@@ -41,9 +41,8 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> {
41 // Return early if we've found an existing new fn 41 // Return early if we've found an existing new fn
42 let impl_def = find_struct_impl(&ctx, &strukt)?; 42 let impl_def = find_struct_impl(&ctx, &strukt)?;
43 43
44 ctx.add_assist(AssistId("add_new"), "Add default constructor", |edit| { 44 let target = strukt.syntax().text_range();
45 edit.target(strukt.syntax().text_range()); 45 ctx.add_assist(AssistId("add_new"), "Add default constructor", target, |edit| {
46
47 let mut buf = String::with_capacity(512); 46 let mut buf = String::with_capacity(512);
48 47
49 if impl_def.is_some() { 48 if impl_def.is_some() {
@@ -178,7 +177,7 @@ fn has_new_fn(imp: &ast::ImplDef) -> bool {
178 177
179#[cfg(test)] 178#[cfg(test)]
180mod tests { 179mod tests {
181 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 180 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
182 181
183 use super::*; 182 use super::*;
184 183
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs
index 260b9e073..a5b26e5b9 100644
--- a/crates/ra_assists/src/handlers/apply_demorgan.rs
+++ b/crates/ra_assists/src/handlers/apply_demorgan.rs
@@ -39,8 +39,7 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> {
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 ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| { 42 ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| {
43 edit.target(op_range);
44 edit.replace(op_range, opposite_op); 43 edit.replace(op_range, opposite_op);
45 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); 44 edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text()));
46 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); 45 edit.replace(rhs_range, format!("{})", not_rhs.syntax().text()));
@@ -60,7 +59,7 @@ fn opposite_logic_op(kind: ast::BinOp) -> Option<&'static str> {
60mod tests { 59mod tests {
61 use super::*; 60 use super::*;
62 61
63 use crate::helpers::{check_assist, check_assist_not_applicable}; 62 use crate::tests::{check_assist, check_assist_not_applicable};
64 63
65 #[test] 64 #[test]
66 fn demorgan_turns_and_into_or() { 65 fn demorgan_turns_and_into_or() {
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index db6c4d2fa..2224b9714 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -48,8 +48,7 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> {
48 let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; 48 let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range;
49 let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message()); 49 let mut group = ctx.add_assist_group(auto_import_assets.get_import_group_message());
50 for import in proposed_imports { 50 for import in proposed_imports {
51 group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), |edit| { 51 group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), range, |edit| {
52 edit.target(range);
53 insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit); 52 insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit);
54 }); 53 });
55 } 54 }
@@ -277,7 +276,7 @@ impl ImportCandidate {
277#[cfg(test)] 276#[cfg(test)]
278mod tests { 277mod tests {
279 use super::*; 278 use super::*;
280 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 279 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
281 280
282 #[test] 281 #[test]
283 fn applicable_when_found_an_import() { 282 fn applicable_when_found_an_import() {
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
new file mode 100644
index 000000000..1e8d986cd
--- /dev/null
+++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs
@@ -0,0 +1,971 @@
1use ra_syntax::{
2 ast, AstNode,
3 SyntaxKind::{COMMENT, WHITESPACE},
4 SyntaxNode, TextSize,
5};
6
7use crate::{Assist, AssistCtx, AssistId};
8use ast::{BlockExpr, Expr, LoopBodyOwner};
9
10// Assist: change_return_type_to_result
11//
12// Change the function's return type to Result.
13//
14// ```
15// fn foo() -> i32<|> { 42i32 }
16// ```
17// ->
18// ```
19// fn foo() -> Result<i32, > { Ok(42i32) }
20// ```
21pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> {
22 let fn_def = ctx.find_node_at_offset::<ast::FnDef>();
23 let fn_def = &mut fn_def?;
24 let ret_type = &fn_def.ret_type()?.type_ref()?;
25 if ret_type.syntax().text().to_string().starts_with("Result<") {
26 return None;
27 }
28
29 let block_expr = &fn_def.body()?;
30 let cursor_in_ret_type =
31 fn_def.ret_type()?.syntax().text_range().contains_range(ctx.frange.range);
32 if !cursor_in_ret_type {
33 return None;
34 }
35
36 ctx.add_assist(
37 AssistId("change_return_type_to_result"),
38 "Change return type to Result",
39 ret_type.syntax().text_range(),
40 |edit| {
41 let mut tail_return_expr_collector = TailReturnCollector::new();
42 tail_return_expr_collector.collect_jump_exprs(block_expr, false);
43 tail_return_expr_collector.collect_tail_exprs(block_expr);
44
45 for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap {
46 edit.replace_node_and_indent(&ret_expr_arg, format!("Ok({})", ret_expr_arg));
47 }
48 edit.replace_node_and_indent(ret_type.syntax(), format!("Result<{}, >", ret_type));
49
50 if let Some(node_start) = result_insertion_offset(&ret_type) {
51 edit.set_cursor(node_start + TextSize::of(&format!("Result<{}, ", ret_type)));
52 }
53 },
54 )
55}
56
57struct TailReturnCollector {
58 exprs_to_wrap: Vec<SyntaxNode>,
59}
60
61impl TailReturnCollector {
62 fn new() -> Self {
63 Self { exprs_to_wrap: vec![] }
64 }
65 /// Collect all`return` expression
66 fn collect_jump_exprs(&mut self, block_expr: &BlockExpr, collect_break: bool) {
67 let statements = block_expr.statements();
68 for stmt in statements {
69 let expr = match &stmt {
70 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
71 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
72 };
73 if let Some(expr) = &expr {
74 self.handle_exprs(expr, collect_break);
75 }
76 }
77
78 // Browse tail expressions for each block
79 if let Some(expr) = block_expr.expr() {
80 if let Some(last_exprs) = get_tail_expr_from_block(&expr) {
81 for last_expr in last_exprs {
82 let last_expr = match last_expr {
83 NodeType::Node(expr) | NodeType::Leaf(expr) => expr,
84 };
85
86 if let Some(last_expr) = Expr::cast(last_expr.clone()) {
87 self.handle_exprs(&last_expr, collect_break);
88 } else if let Some(expr_stmt) = ast::Stmt::cast(last_expr) {
89 let expr_stmt = match &expr_stmt {
90 ast::Stmt::ExprStmt(stmt) => stmt.expr(),
91 ast::Stmt::LetStmt(stmt) => stmt.initializer(),
92 };
93 if let Some(expr) = &expr_stmt {
94 self.handle_exprs(expr, collect_break);
95 }
96 }
97 }
98 }
99 }
100 }
101
102 fn handle_exprs(&mut self, expr: &Expr, collect_break: bool) {
103 match expr {
104 Expr::BlockExpr(block_expr) => {
105 self.collect_jump_exprs(&block_expr, collect_break);
106 }
107 Expr::ReturnExpr(ret_expr) => {
108 if let Some(ret_expr_arg) = &ret_expr.expr() {
109 self.exprs_to_wrap.push(ret_expr_arg.syntax().clone());
110 }
111 }
112 Expr::BreakExpr(break_expr) if collect_break => {
113 if let Some(break_expr_arg) = &break_expr.expr() {
114 self.exprs_to_wrap.push(break_expr_arg.syntax().clone());
115 }
116 }
117 Expr::IfExpr(if_expr) => {
118 for block in if_expr.blocks() {
119 self.collect_jump_exprs(&block, collect_break);
120 }
121 }
122 Expr::LoopExpr(loop_expr) => {
123 if let Some(block_expr) = loop_expr.loop_body() {
124 self.collect_jump_exprs(&block_expr, collect_break);
125 }
126 }
127 Expr::ForExpr(for_expr) => {
128 if let Some(block_expr) = for_expr.loop_body() {
129 self.collect_jump_exprs(&block_expr, collect_break);
130 }
131 }
132 Expr::WhileExpr(while_expr) => {
133 if let Some(block_expr) = while_expr.loop_body() {
134 self.collect_jump_exprs(&block_expr, collect_break);
135 }
136 }
137 Expr::MatchExpr(match_expr) => {
138 if let Some(arm_list) = match_expr.match_arm_list() {
139 arm_list.arms().filter_map(|match_arm| match_arm.expr()).for_each(|expr| {
140 self.handle_exprs(&expr, collect_break);
141 });
142 }
143 }
144 _ => {}
145 }
146 }
147
148 fn collect_tail_exprs(&mut self, block: &BlockExpr) {
149 if let Some(expr) = block.expr() {
150 self.handle_exprs(&expr, true);
151 self.fetch_tail_exprs(&expr);
152 }
153 }
154
155 fn fetch_tail_exprs(&mut self, expr: &Expr) {
156 if let Some(exprs) = get_tail_expr_from_block(expr) {
157 for node_type in &exprs {
158 match node_type {
159 NodeType::Leaf(expr) => {
160 self.exprs_to_wrap.push(expr.clone());
161 }
162 NodeType::Node(expr) => match &Expr::cast(expr.clone()) {
163 Some(last_expr) => {
164 self.fetch_tail_exprs(last_expr);
165 }
166 None => {
167 self.exprs_to_wrap.push(expr.clone());
168 }
169 },
170 }
171 }
172 }
173 }
174}
175
176#[derive(Debug)]
177enum NodeType {
178 Leaf(SyntaxNode),
179 Node(SyntaxNode),
180}
181
182/// Get a tail expression inside a block
183fn get_tail_expr_from_block(expr: &Expr) -> Option<Vec<NodeType>> {
184 match expr {
185 Expr::IfExpr(if_expr) => {
186 let mut nodes = vec![];
187 for block in if_expr.blocks() {
188 if let Some(block_expr) = block.expr() {
189 if let Some(tail_exprs) = get_tail_expr_from_block(&block_expr) {
190 nodes.extend(tail_exprs);
191 }
192 } else if let Some(last_expr) = block.syntax().last_child() {
193 nodes.push(NodeType::Node(last_expr));
194 } else {
195 nodes.push(NodeType::Node(block.syntax().clone()));
196 }
197 }
198 Some(nodes)
199 }
200 Expr::LoopExpr(loop_expr) => {
201 loop_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
202 }
203 Expr::ForExpr(for_expr) => {
204 for_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
205 }
206 Expr::WhileExpr(while_expr) => {
207 while_expr.syntax().last_child().map(|lc| vec![NodeType::Node(lc)])
208 }
209 Expr::BlockExpr(block_expr) => {
210 block_expr.expr().map(|lc| vec![NodeType::Node(lc.syntax().clone())])
211 }
212 Expr::MatchExpr(match_expr) => {
213 let arm_list = match_expr.match_arm_list()?;
214 let arms: Vec<NodeType> = arm_list
215 .arms()
216 .filter_map(|match_arm| match_arm.expr())
217 .map(|expr| match expr {
218 Expr::ReturnExpr(ret_expr) => NodeType::Node(ret_expr.syntax().clone()),
219 Expr::BreakExpr(break_expr) => NodeType::Node(break_expr.syntax().clone()),
220 _ => match expr.syntax().last_child() {
221 Some(last_expr) => NodeType::Node(last_expr),
222 None => NodeType::Node(expr.syntax().clone()),
223 },
224 })
225 .collect();
226
227 Some(arms)
228 }
229 Expr::BreakExpr(expr) => expr.expr().map(|e| vec![NodeType::Leaf(e.syntax().clone())]),
230 Expr::ReturnExpr(ret_expr) => Some(vec![NodeType::Node(ret_expr.syntax().clone())]),
231 Expr::CallExpr(call_expr) => Some(vec![NodeType::Leaf(call_expr.syntax().clone())]),
232 Expr::Literal(lit_expr) => Some(vec![NodeType::Leaf(lit_expr.syntax().clone())]),
233 Expr::TupleExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
234 Expr::ArrayExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
235 Expr::ParenExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
236 Expr::PathExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
237 Expr::Label(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
238 Expr::RecordLit(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
239 Expr::IndexExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
240 Expr::MethodCallExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
241 Expr::AwaitExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
242 Expr::CastExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
243 Expr::RefExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
244 Expr::PrefixExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
245 Expr::RangeExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
246 Expr::BinExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
247 Expr::MacroCall(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
248 Expr::BoxExpr(expr) => Some(vec![NodeType::Leaf(expr.syntax().clone())]),
249 _ => None,
250 }
251}
252
253fn result_insertion_offset(ret_type: &ast::TypeRef) -> Option<TextSize> {
254 let non_ws_child = ret_type
255 .syntax()
256 .children_with_tokens()
257 .find(|it| it.kind() != COMMENT && it.kind() != WHITESPACE)?;
258 Some(non_ws_child.text_range().start())
259}
260
261#[cfg(test)]
262mod tests {
263
264 use crate::tests::{check_assist, check_assist_not_applicable};
265
266 use super::*;
267
268 #[test]
269 fn change_return_type_to_result_simple() {
270 check_assist(
271 change_return_type_to_result,
272 r#"fn foo() -> i3<|>2 {
273 let test = "test";
274 return 42i32;
275 }"#,
276 r#"fn foo() -> Result<i32, <|>> {
277 let test = "test";
278 return Ok(42i32);
279 }"#,
280 );
281 }
282
283 #[test]
284 fn change_return_type_to_result_simple_return_type() {
285 check_assist(
286 change_return_type_to_result,
287 r#"fn foo() -> i32<|> {
288 let test = "test";
289 return 42i32;
290 }"#,
291 r#"fn foo() -> Result<i32, <|>> {
292 let test = "test";
293 return Ok(42i32);
294 }"#,
295 );
296 }
297
298 #[test]
299 fn change_return_type_to_result_simple_return_type_bad_cursor() {
300 check_assist_not_applicable(
301 change_return_type_to_result,
302 r#"fn foo() -> i32 {
303 let test = "test";<|>
304 return 42i32;
305 }"#,
306 );
307 }
308
309 #[test]
310 fn change_return_type_to_result_simple_with_cursor() {
311 check_assist(
312 change_return_type_to_result,
313 r#"fn foo() -> <|>i32 {
314 let test = "test";
315 return 42i32;
316 }"#,
317 r#"fn foo() -> Result<i32, <|>> {
318 let test = "test";
319 return Ok(42i32);
320 }"#,
321 );
322 }
323
324 #[test]
325 fn change_return_type_to_result_simple_with_tail() {
326 check_assist(
327 change_return_type_to_result,
328 r#"fn foo() -><|> i32 {
329 let test = "test";
330 42i32
331 }"#,
332 r#"fn foo() -> Result<i32, <|>> {
333 let test = "test";
334 Ok(42i32)
335 }"#,
336 );
337 }
338
339 #[test]
340 fn change_return_type_to_result_simple_with_tail_only() {
341 check_assist(
342 change_return_type_to_result,
343 r#"fn foo() -> i32<|> {
344 42i32
345 }"#,
346 r#"fn foo() -> Result<i32, <|>> {
347 Ok(42i32)
348 }"#,
349 );
350 }
351 #[test]
352 fn change_return_type_to_result_simple_with_tail_block_like() {
353 check_assist(
354 change_return_type_to_result,
355 r#"fn foo() -> i32<|> {
356 if true {
357 42i32
358 } else {
359 24i32
360 }
361 }"#,
362 r#"fn foo() -> Result<i32, <|>> {
363 if true {
364 Ok(42i32)
365 } else {
366 Ok(24i32)
367 }
368 }"#,
369 );
370 }
371
372 #[test]
373 fn change_return_type_to_result_simple_with_nested_if() {
374 check_assist(
375 change_return_type_to_result,
376 r#"fn foo() -> i32<|> {
377 if true {
378 if false {
379 1
380 } else {
381 2
382 }
383 } else {
384 24i32
385 }
386 }"#,
387 r#"fn foo() -> Result<i32, <|>> {
388 if true {
389 if false {
390 Ok(1)
391 } else {
392 Ok(2)
393 }
394 } else {
395 Ok(24i32)
396 }
397 }"#,
398 );
399 }
400
401 #[test]
402 fn change_return_type_to_result_simple_with_await() {
403 check_assist(
404 change_return_type_to_result,
405 r#"async fn foo() -> i<|>32 {
406 if true {
407 if false {
408 1.await
409 } else {
410 2.await
411 }
412 } else {
413 24i32.await
414 }
415 }"#,
416 r#"async fn foo() -> Result<i32, <|>> {
417 if true {
418 if false {
419 Ok(1.await)
420 } else {
421 Ok(2.await)
422 }
423 } else {
424 Ok(24i32.await)
425 }
426 }"#,
427 );
428 }
429
430 #[test]
431 fn change_return_type_to_result_simple_with_array() {
432 check_assist(
433 change_return_type_to_result,
434 r#"fn foo() -> [i32;<|> 3] {
435 [1, 2, 3]
436 }"#,
437 r#"fn foo() -> Result<[i32; 3], <|>> {
438 Ok([1, 2, 3])
439 }"#,
440 );
441 }
442
443 #[test]
444 fn change_return_type_to_result_simple_with_cast() {
445 check_assist(
446 change_return_type_to_result,
447 r#"fn foo() -<|>> i32 {
448 if true {
449 if false {
450 1 as i32
451 } else {
452 2 as i32
453 }
454 } else {
455 24 as i32
456 }
457 }"#,
458 r#"fn foo() -> Result<i32, <|>> {
459 if true {
460 if false {
461 Ok(1 as i32)
462 } else {
463 Ok(2 as i32)
464 }
465 } else {
466 Ok(24 as i32)
467 }
468 }"#,
469 );
470 }
471
472 #[test]
473 fn change_return_type_to_result_simple_with_tail_block_like_match() {
474 check_assist(
475 change_return_type_to_result,
476 r#"fn foo() -> i32<|> {
477 let my_var = 5;
478 match my_var {
479 5 => 42i32,
480 _ => 24i32,
481 }
482 }"#,
483 r#"fn foo() -> Result<i32, <|>> {
484 let my_var = 5;
485 match my_var {
486 5 => Ok(42i32),
487 _ => Ok(24i32),
488 }
489 }"#,
490 );
491 }
492
493 #[test]
494 fn change_return_type_to_result_simple_with_loop_with_tail() {
495 check_assist(
496 change_return_type_to_result,
497 r#"fn foo() -> i32<|> {
498 let my_var = 5;
499 loop {
500 println!("test");
501 5
502 }
503
504 my_var
505 }"#,
506 r#"fn foo() -> Result<i32, <|>> {
507 let my_var = 5;
508 loop {
509 println!("test");
510 5
511 }
512
513 Ok(my_var)
514 }"#,
515 );
516 }
517
518 #[test]
519 fn change_return_type_to_result_simple_with_loop_in_let_stmt() {
520 check_assist(
521 change_return_type_to_result,
522 r#"fn foo() -> i32<|> {
523 let my_var = let x = loop {
524 break 1;
525 };
526
527 my_var
528 }"#,
529 r#"fn foo() -> Result<i32, <|>> {
530 let my_var = let x = loop {
531 break 1;
532 };
533
534 Ok(my_var)
535 }"#,
536 );
537 }
538
539 #[test]
540 fn change_return_type_to_result_simple_with_tail_block_like_match_return_expr() {
541 check_assist(
542 change_return_type_to_result,
543 r#"fn foo() -> i32<|> {
544 let my_var = 5;
545 let res = match my_var {
546 5 => 42i32,
547 _ => return 24i32,
548 };
549
550 res
551 }"#,
552 r#"fn foo() -> Result<i32, <|>> {
553 let my_var = 5;
554 let res = match my_var {
555 5 => 42i32,
556 _ => return Ok(24i32),
557 };
558
559 Ok(res)
560 }"#,
561 );
562
563 check_assist(
564 change_return_type_to_result,
565 r#"fn foo() -> i32<|> {
566 let my_var = 5;
567 let res = if my_var == 5 {
568 42i32
569 } else {
570 return 24i32;
571 };
572
573 res
574 }"#,
575 r#"fn foo() -> Result<i32, <|>> {
576 let my_var = 5;
577 let res = if my_var == 5 {
578 42i32
579 } else {
580 return Ok(24i32);
581 };
582
583 Ok(res)
584 }"#,
585 );
586 }
587
588 #[test]
589 fn change_return_type_to_result_simple_with_tail_block_like_match_deeper() {
590 check_assist(
591 change_return_type_to_result,
592 r#"fn foo() -> i32<|> {
593 let my_var = 5;
594 match my_var {
595 5 => {
596 if true {
597 42i32
598 } else {
599 25i32
600 }
601 },
602 _ => {
603 let test = "test";
604 if test == "test" {
605 return bar();
606 }
607 53i32
608 },
609 }
610 }"#,
611 r#"fn foo() -> Result<i32, <|>> {
612 let my_var = 5;
613 match my_var {
614 5 => {
615 if true {
616 Ok(42i32)
617 } else {
618 Ok(25i32)
619 }
620 },
621 _ => {
622 let test = "test";
623 if test == "test" {
624 return Ok(bar());
625 }
626 Ok(53i32)
627 },
628 }
629 }"#,
630 );
631 }
632
633 #[test]
634 fn change_return_type_to_result_simple_with_tail_block_like_early_return() {
635 check_assist(
636 change_return_type_to_result,
637 r#"fn foo() -> i<|>32 {
638 let test = "test";
639 if test == "test" {
640 return 24i32;
641 }
642 53i32
643 }"#,
644 r#"fn foo() -> Result<i32, <|>> {
645 let test = "test";
646 if test == "test" {
647 return Ok(24i32);
648 }
649 Ok(53i32)
650 }"#,
651 );
652 }
653
654 #[test]
655 fn change_return_type_to_result_simple_with_closure() {
656 check_assist(
657 change_return_type_to_result,
658 r#"fn foo(the_field: u32) -><|> u32 {
659 let true_closure = || {
660 return true;
661 };
662 if the_field < 5 {
663 let mut i = 0;
664
665
666 if true_closure() {
667 return 99;
668 } else {
669 return 0;
670 }
671 }
672
673 the_field
674 }"#,
675 r#"fn foo(the_field: u32) -> Result<u32, <|>> {
676 let true_closure = || {
677 return true;
678 };
679 if the_field < 5 {
680 let mut i = 0;
681
682
683 if true_closure() {
684 return Ok(99);
685 } else {
686 return Ok(0);
687 }
688 }
689
690 Ok(the_field)
691 }"#,
692 );
693
694 check_assist(
695 change_return_type_to_result,
696 r#"fn foo(the_field: u32) -> u32<|> {
697 let true_closure = || {
698 return true;
699 };
700 if the_field < 5 {
701 let mut i = 0;
702
703
704 if true_closure() {
705 return 99;
706 } else {
707 return 0;
708 }
709 }
710 let t = None;
711
712 t.unwrap_or_else(|| the_field)
713 }"#,
714 r#"fn foo(the_field: u32) -> Result<u32, <|>> {
715 let true_closure = || {
716 return true;
717 };
718 if the_field < 5 {
719 let mut i = 0;
720
721
722 if true_closure() {
723 return Ok(99);
724 } else {
725 return Ok(0);
726 }
727 }
728 let t = None;
729
730 Ok(t.unwrap_or_else(|| the_field))
731 }"#,
732 );
733 }
734
735 #[test]
736 fn change_return_type_to_result_simple_with_weird_forms() {
737 check_assist(
738 change_return_type_to_result,
739 r#"fn foo() -> i32<|> {
740 let test = "test";
741 if test == "test" {
742 return 24i32;
743 }
744 let mut i = 0;
745 loop {
746 if i == 1 {
747 break 55;
748 }
749 i += 1;
750 }
751 }"#,
752 r#"fn foo() -> Result<i32, <|>> {
753 let test = "test";
754 if test == "test" {
755 return Ok(24i32);
756 }
757 let mut i = 0;
758 loop {
759 if i == 1 {
760 break Ok(55);
761 }
762 i += 1;
763 }
764 }"#,
765 );
766
767 check_assist(
768 change_return_type_to_result,
769 r#"fn foo() -> i32<|> {
770 let test = "test";
771 if test == "test" {
772 return 24i32;
773 }
774 let mut i = 0;
775 loop {
776 loop {
777 if i == 1 {
778 break 55;
779 }
780 i += 1;
781 }
782 }
783 }"#,
784 r#"fn foo() -> Result<i32, <|>> {
785 let test = "test";
786 if test == "test" {
787 return Ok(24i32);
788 }
789 let mut i = 0;
790 loop {
791 loop {
792 if i == 1 {
793 break Ok(55);
794 }
795 i += 1;
796 }
797 }
798 }"#,
799 );
800
801 check_assist(
802 change_return_type_to_result,
803 r#"fn foo() -> i3<|>2 {
804 let test = "test";
805 let other = 5;
806 if test == "test" {
807 let res = match other {
808 5 => 43,
809 _ => return 56,
810 };
811 }
812 let mut i = 0;
813 loop {
814 loop {
815 if i == 1 {
816 break 55;
817 }
818 i += 1;
819 }
820 }
821 }"#,
822 r#"fn foo() -> Result<i32, <|>> {
823 let test = "test";
824 let other = 5;
825 if test == "test" {
826 let res = match other {
827 5 => 43,
828 _ => return Ok(56),
829 };
830 }
831 let mut i = 0;
832 loop {
833 loop {
834 if i == 1 {
835 break Ok(55);
836 }
837 i += 1;
838 }
839 }
840 }"#,
841 );
842
843 check_assist(
844 change_return_type_to_result,
845 r#"fn foo(the_field: u32) -> u32<|> {
846 if the_field < 5 {
847 let mut i = 0;
848 loop {
849 if i > 5 {
850 return 55u32;
851 }
852 i += 3;
853 }
854
855 match i {
856 5 => return 99,
857 _ => return 0,
858 };
859 }
860
861 the_field
862 }"#,
863 r#"fn foo(the_field: u32) -> Result<u32, <|>> {
864 if the_field < 5 {
865 let mut i = 0;
866 loop {
867 if i > 5 {
868 return Ok(55u32);
869 }
870 i += 3;
871 }
872
873 match i {
874 5 => return Ok(99),
875 _ => return Ok(0),
876 };
877 }
878
879 Ok(the_field)
880 }"#,
881 );
882
883 check_assist(
884 change_return_type_to_result,
885 r#"fn foo(the_field: u32) -> u3<|>2 {
886 if the_field < 5 {
887 let mut i = 0;
888
889 match i {
890 5 => return 99,
891 _ => return 0,
892 }
893 }
894
895 the_field
896 }"#,
897 r#"fn foo(the_field: u32) -> Result<u32, <|>> {
898 if the_field < 5 {
899 let mut i = 0;
900
901 match i {
902 5 => return Ok(99),
903 _ => return Ok(0),
904 }
905 }
906
907 Ok(the_field)
908 }"#,
909 );
910
911 check_assist(
912 change_return_type_to_result,
913 r#"fn foo(the_field: u32) -> u32<|> {
914 if the_field < 5 {
915 let mut i = 0;
916
917 if i == 5 {
918 return 99
919 } else {
920 return 0
921 }
922 }
923
924 the_field
925 }"#,
926 r#"fn foo(the_field: u32) -> Result<u32, <|>> {
927 if the_field < 5 {
928 let mut i = 0;
929
930 if i == 5 {
931 return Ok(99)
932 } else {
933 return Ok(0)
934 }
935 }
936
937 Ok(the_field)
938 }"#,
939 );
940
941 check_assist(
942 change_return_type_to_result,
943 r#"fn foo(the_field: u32) -> <|>u32 {
944 if the_field < 5 {
945 let mut i = 0;
946
947 if i == 5 {
948 return 99;
949 } else {
950 return 0;
951 }
952 }
953
954 the_field
955 }"#,
956 r#"fn foo(the_field: u32) -> Result<u32, <|>> {
957 if the_field < 5 {
958 let mut i = 0;
959
960 if i == 5 {
961 return Ok(99);
962 } else {
963 return Ok(0);
964 }
965 }
966
967 Ok(the_field)
968 }"#,
969 );
970 }
971}
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index 1cd532e80..489db83e6 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -66,11 +66,15 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> {
66 return None; 66 return None;
67 }; 67 };
68 68
69 ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub(crate)", |edit| { 69 ctx.add_assist(
70 edit.target(target); 70 AssistId("change_visibility"),
71 edit.insert(offset, "pub(crate) "); 71 "Change visibility to pub(crate)",
72 edit.set_cursor(offset); 72 target,
73 }) 73 |edit| {
74 edit.insert(offset, "pub(crate) ");
75 edit.set_cursor(offset);
76 },
77 )
74} 78}
75 79
76fn vis_offset(node: &SyntaxNode) -> TextSize { 80fn vis_offset(node: &SyntaxNode) -> TextSize {
@@ -86,22 +90,28 @@ fn vis_offset(node: &SyntaxNode) -> TextSize {
86 90
87fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> { 91fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> {
88 if vis.syntax().text() == "pub" { 92 if vis.syntax().text() == "pub" {
93 let target = vis.syntax().text_range();
89 return ctx.add_assist( 94 return ctx.add_assist(
90 AssistId("change_visibility"), 95 AssistId("change_visibility"),
91 "Change Visibility to pub(crate)", 96 "Change Visibility to pub(crate)",
97 target,
92 |edit| { 98 |edit| {
93 edit.target(vis.syntax().text_range());
94 edit.replace(vis.syntax().text_range(), "pub(crate)"); 99 edit.replace(vis.syntax().text_range(), "pub(crate)");
95 edit.set_cursor(vis.syntax().text_range().start()) 100 edit.set_cursor(vis.syntax().text_range().start())
96 }, 101 },
97 ); 102 );
98 } 103 }
99 if vis.syntax().text() == "pub(crate)" { 104 if vis.syntax().text() == "pub(crate)" {
100 return ctx.add_assist(AssistId("change_visibility"), "Change visibility to pub", |edit| { 105 let target = vis.syntax().text_range();
101 edit.target(vis.syntax().text_range()); 106 return ctx.add_assist(
102 edit.replace(vis.syntax().text_range(), "pub"); 107 AssistId("change_visibility"),
103 edit.set_cursor(vis.syntax().text_range().start()); 108 "Change visibility to pub",
104 }); 109 target,
110 |edit| {
111 edit.replace(vis.syntax().text_range(), "pub");
112 edit.set_cursor(vis.syntax().text_range().start());
113 },
114 );
105 } 115 }
106 None 116 None
107} 117}
@@ -110,7 +120,7 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> {
110mod tests { 120mod tests {
111 use test_utils::covers; 121 use test_utils::covers;
112 122
113 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 123 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
114 124
115 use super::*; 125 use super::*;
116 126
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs
index eede2fe91..4bd6040b2 100644
--- a/crates/ra_assists/src/handlers/early_return.rs
+++ b/crates/ra_assists/src/handlers/early_return.rs
@@ -95,94 +95,99 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?; 95 then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;
96 let cursor_position = ctx.frange.range.start(); 96 let cursor_position = ctx.frange.range.start();
97 97
98 ctx.add_assist(AssistId("convert_to_guarded_return"), "Convert to guarded return", |edit| { 98 let target = if_expr.syntax().text_range();
99 let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); 99 ctx.add_assist(
100 let new_block = match if_let_pat { 100 AssistId("convert_to_guarded_return"),
101 None => { 101 "Convert to guarded return",
102 // If. 102 target,
103 let new_expr = { 103 |edit| {
104 let then_branch = 104 let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
105 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 105 let new_block = match if_let_pat {
106 let cond = invert_boolean_expression(cond_expr); 106 None => {
107 let e = make::expr_if(make::condition(cond, None), then_branch); 107 // If.
108 if_indent_level.increase_indent(e) 108 let new_expr = {
109 }; 109 let then_branch =
110 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 110 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
111 } 111 let cond = invert_boolean_expression(cond_expr);
112 Some((path, bound_ident)) => { 112 let e = make::expr_if(make::condition(cond, None), then_branch);
113 // If-let. 113 if_indent_level.increase_indent(e)
114 let match_expr = {
115 let happy_arm = {
116 let pat = make::tuple_struct_pat(
117 path,
118 once(make::bind_pat(make::name("it")).into()),
119 );
120 let expr = {
121 let name_ref = make::name_ref("it");
122 let segment = make::path_segment(name_ref);
123 let path = make::path_unqualified(segment);
124 make::expr_path(path)
125 };
126 make::match_arm(once(pat.into()), expr)
127 }; 114 };
115 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
116 }
117 Some((path, bound_ident)) => {
118 // If-let.
119 let match_expr = {
120 let happy_arm = {
121 let pat = make::tuple_struct_pat(
122 path,
123 once(make::bind_pat(make::name("it")).into()),
124 );
125 let expr = {
126 let name_ref = make::name_ref("it");
127 let segment = make::path_segment(name_ref);
128 let path = make::path_unqualified(segment);
129 make::expr_path(path)
130 };
131 make::match_arm(once(pat.into()), expr)
132 };
128 133
129 let sad_arm = make::match_arm( 134 let sad_arm = make::match_arm(
130 // FIXME: would be cool to use `None` or `Err(_)` if appropriate 135 // FIXME: would be cool to use `None` or `Err(_)` if appropriate
131 once(make::placeholder_pat().into()), 136 once(make::placeholder_pat().into()),
132 early_expression, 137 early_expression,
133 ); 138 );
134 139
135 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) 140 make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
136 }; 141 };
137 142
138 let let_stmt = make::let_stmt( 143 let let_stmt = make::let_stmt(
139 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), 144 make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(),
140 Some(match_expr), 145 Some(match_expr),
146 );
147 let let_stmt = if_indent_level.increase_indent(let_stmt);
148 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
149 }
150 };
151 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
152 edit.set_cursor(cursor_position);
153
154 fn replace(
155 new_expr: &SyntaxNode,
156 then_block: &ast::BlockExpr,
157 parent_block: &ast::BlockExpr,
158 if_expr: &ast::IfExpr,
159 ) -> SyntaxNode {
160 let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
161 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
162 let end_of_then =
163 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
164 end_of_then.prev_sibling_or_token().unwrap()
165 } else {
166 end_of_then
167 };
168 let mut then_statements = new_expr.children_with_tokens().chain(
169 then_block_items
170 .syntax()
171 .children_with_tokens()
172 .skip(1)
173 .take_while(|i| *i != end_of_then),
141 ); 174 );
142 let let_stmt = if_indent_level.increase_indent(let_stmt); 175 replace_children(
143 replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) 176 &parent_block.syntax(),
177 RangeInclusive::new(
178 if_expr.clone().syntax().clone().into(),
179 if_expr.syntax().clone().into(),
180 ),
181 &mut then_statements,
182 )
144 } 183 }
145 }; 184 },
146 edit.target(if_expr.syntax().text_range()); 185 )
147 edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());
148 edit.set_cursor(cursor_position);
149
150 fn replace(
151 new_expr: &SyntaxNode,
152 then_block: &ast::BlockExpr,
153 parent_block: &ast::BlockExpr,
154 if_expr: &ast::IfExpr,
155 ) -> SyntaxNode {
156 let then_block_items = IndentLevel::from(1).decrease_indent(then_block.clone());
157 let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
158 let end_of_then =
159 if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
160 end_of_then.prev_sibling_or_token().unwrap()
161 } else {
162 end_of_then
163 };
164 let mut then_statements = new_expr.children_with_tokens().chain(
165 then_block_items
166 .syntax()
167 .children_with_tokens()
168 .skip(1)
169 .take_while(|i| *i != end_of_then),
170 );
171 replace_children(
172 &parent_block.syntax(),
173 RangeInclusive::new(
174 if_expr.clone().syntax().clone().into(),
175 if_expr.syntax().clone().into(),
176 ),
177 &mut then_statements,
178 )
179 }
180 })
181} 186}
182 187
183#[cfg(test)] 188#[cfg(test)]
184mod tests { 189mod tests {
185 use crate::helpers::{check_assist, check_assist_not_applicable}; 190 use crate::tests::{check_assist, check_assist_not_applicable};
186 191
187 use super::*; 192 use super::*;
188 193
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index 8d1af9933..7c8f8bdf2 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -92,10 +92,9 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
92 return None; 92 return None;
93 } 93 }
94 94
95 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", |edit| { 95 let target = match_expr.syntax().text_range();
96 ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", target, |edit| {
96 let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms); 97 let new_arm_list = match_arm_list.remove_placeholder().append_arms(missing_arms);
97
98 edit.target(match_expr.syntax().text_range());
99 edit.set_cursor(expr.syntax().text_range().start()); 98 edit.set_cursor(expr.syntax().text_range().start());
100 edit.replace_ast(match_arm_list, new_arm_list); 99 edit.replace_ast(match_arm_list, new_arm_list);
101 }) 100 })
@@ -168,7 +167,7 @@ fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> O
168 167
169#[cfg(test)] 168#[cfg(test)]
170mod tests { 169mod tests {
171 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 170 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
172 171
173 use super::fill_match_arms; 172 use super::fill_match_arms;
174 173
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs
index 8030efb35..cb7264d7b 100644
--- a/crates/ra_assists/src/handlers/flip_binexpr.rs
+++ b/crates/ra_assists/src/handlers/flip_binexpr.rs
@@ -33,8 +33,7 @@ pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option<Assist> {
33 return None; 33 return None;
34 } 34 }
35 35
36 ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", |edit| { 36 ctx.add_assist(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| {
37 edit.target(op_range);
38 if let FlipAction::FlipAndReplaceOp(new_op) = action { 37 if let FlipAction::FlipAndReplaceOp(new_op) = action {
39 edit.replace(op_range, new_op); 38 edit.replace(op_range, new_op);
40 } 39 }
@@ -69,7 +68,7 @@ impl From<BinOp> for FlipAction {
69mod tests { 68mod tests {
70 use super::*; 69 use super::*;
71 70
72 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 71 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
73 72
74 #[test] 73 #[test]
75 fn flip_binexpr_target_is_the_op() { 74 fn flip_binexpr_target_is_the_op() {
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs
index 1dacf29f8..24982ae22 100644
--- a/crates/ra_assists/src/handlers/flip_comma.rs
+++ b/crates/ra_assists/src/handlers/flip_comma.rs
@@ -28,8 +28,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx) -> Option<Assist> {
28 return None; 28 return None;
29 } 29 }
30 30
31 ctx.add_assist(AssistId("flip_comma"), "Flip comma", |edit| { 31 ctx.add_assist(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| {
32 edit.target(comma.text_range());
33 edit.replace(prev.text_range(), next.to_string()); 32 edit.replace(prev.text_range(), next.to_string());
34 edit.replace(next.text_range(), prev.to_string()); 33 edit.replace(next.text_range(), prev.to_string());
35 }) 34 })
@@ -39,7 +38,7 @@ pub(crate) fn flip_comma(ctx: AssistCtx) -> Option<Assist> {
39mod tests { 38mod tests {
40 use super::*; 39 use super::*;
41 40
42 use crate::helpers::{check_assist, check_assist_target}; 41 use crate::tests::{check_assist, check_assist_target};
43 42
44 #[test] 43 #[test]
45 fn flip_comma_works_for_function_parameters() { 44 fn flip_comma_works_for_function_parameters() {
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs
index f56769624..6a3b2df67 100644
--- a/crates/ra_assists/src/handlers/flip_trait_bound.rs
+++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs
@@ -32,8 +32,8 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> {
32 non_trivia_sibling(plus.clone().into(), Direction::Next)?, 32 non_trivia_sibling(plus.clone().into(), Direction::Next)?,
33 ); 33 );
34 34
35 ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", |edit| { 35 let target = plus.text_range();
36 edit.target(plus.text_range()); 36 ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| {
37 edit.replace(before.text_range(), after.to_string()); 37 edit.replace(before.text_range(), after.to_string());
38 edit.replace(after.text_range(), before.to_string()); 38 edit.replace(after.text_range(), before.to_string());
39 }) 39 })
@@ -43,7 +43,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> {
43mod tests { 43mod tests {
44 use super::*; 44 use super::*;
45 45
46 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 46 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
47 47
48 #[test] 48 #[test]
49 fn flip_trait_bound_assist_available() { 49 fn flip_trait_bound_assist_available() {
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs
index 60ec536a7..e5765c845 100644
--- a/crates/ra_assists/src/handlers/inline_local_variable.rs
+++ b/crates/ra_assists/src/handlers/inline_local_variable.rs
@@ -106,9 +106,11 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
106 let init_str = initializer_expr.syntax().text().to_string(); 106 let init_str = initializer_expr.syntax().text().to_string();
107 let init_in_paren = format!("({})", &init_str); 107 let init_in_paren = format!("({})", &init_str);
108 108
109 let target = bind_pat.syntax().text_range();
109 ctx.add_assist( 110 ctx.add_assist(
110 AssistId("inline_local_variable"), 111 AssistId("inline_local_variable"),
111 "Inline variable", 112 "Inline variable",
113 target,
112 move |edit: &mut ActionBuilder| { 114 move |edit: &mut ActionBuilder| {
113 edit.delete(delete_range); 115 edit.delete(delete_range);
114 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { 116 for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) {
@@ -125,7 +127,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> {
125mod tests { 127mod tests {
126 use test_utils::covers; 128 use test_utils::covers;
127 129
128 use crate::helpers::{check_assist, check_assist_not_applicable}; 130 use crate::tests::{check_assist, check_assist_not_applicable};
129 131
130 use super::*; 132 use super::*;
131 133
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs
index 39c656305..3c340ff3b 100644
--- a/crates/ra_assists/src/handlers/introduce_variable.rs
+++ b/crates/ra_assists/src/handlers/introduce_variable.rs
@@ -42,7 +42,8 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
42 if indent.kind() != WHITESPACE { 42 if indent.kind() != WHITESPACE {
43 return None; 43 return None;
44 } 44 }
45 ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", move |edit| { 45 let target = expr.syntax().text_range();
46 ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", target, move |edit| {
46 let mut buf = String::new(); 47 let mut buf = String::new();
47 48
48 let cursor_offset = if wrap_in_block { 49 let cursor_offset = if wrap_in_block {
@@ -79,7 +80,6 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> {
79 buf.push_str(text); 80 buf.push_str(text);
80 } 81 }
81 82
82 edit.target(expr.syntax().text_range());
83 edit.replace(expr.syntax().text_range(), "var_name".to_string()); 83 edit.replace(expr.syntax().text_range(), "var_name".to_string());
84 edit.insert(anchor_stmt.text_range().start(), buf); 84 edit.insert(anchor_stmt.text_range().start(), buf);
85 if wrap_in_block { 85 if wrap_in_block {
@@ -136,7 +136,7 @@ fn anchor_stmt(expr: ast::Expr) -> Option<(SyntaxNode, bool)> {
136mod tests { 136mod tests {
137 use test_utils::covers; 137 use test_utils::covers;
138 138
139 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 139 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
140 140
141 use super::*; 141 use super::*;
142 142
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs
index 682e08512..b16271443 100644
--- a/crates/ra_assists/src/handlers/invert_if.rs
+++ b/crates/ra_assists/src/handlers/invert_if.rs
@@ -47,8 +47,7 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
47 let else_node = else_block.syntax(); 47 let else_node = else_block.syntax();
48 let else_range = else_node.text_range(); 48 let else_range = else_node.text_range();
49 let then_range = then_node.text_range(); 49 let then_range = then_node.text_range();
50 return ctx.add_assist(AssistId("invert_if"), "Invert if", |edit| { 50 return ctx.add_assist(AssistId("invert_if"), "Invert if", if_range, |edit| {
51 edit.target(if_range);
52 edit.replace(cond_range, flip_cond.syntax().text()); 51 edit.replace(cond_range, flip_cond.syntax().text());
53 edit.replace(else_range, then_node.text()); 52 edit.replace(else_range, then_node.text());
54 edit.replace(then_range, else_node.text()); 53 edit.replace(then_range, else_node.text());
@@ -62,7 +61,7 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
62mod tests { 61mod tests {
63 use super::*; 62 use super::*;
64 63
65 use crate::helpers::{check_assist, check_assist_not_applicable}; 64 use crate::tests::{check_assist, check_assist_not_applicable};
66 65
67 #[test] 66 #[test]
68 fn invert_if_remove_inequality() { 67 fn invert_if_remove_inequality() {
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs
index 4be1238f1..de74d83d8 100644
--- a/crates/ra_assists/src/handlers/merge_imports.rs
+++ b/crates/ra_assists/src/handlers/merge_imports.rs
@@ -52,7 +52,8 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> {
52 } 52 }
53 }; 53 };
54 54
55 ctx.add_assist(AssistId("merge_imports"), "Merge imports", |edit| { 55 let target = tree.syntax().text_range();
56 ctx.add_assist(AssistId("merge_imports"), "Merge imports", target, |edit| {
56 edit.rewrite(rewriter); 57 edit.rewrite(rewriter);
57 // FIXME: we only need because our diff is imprecise 58 // FIXME: we only need because our diff is imprecise
58 edit.set_cursor(offset); 59 edit.set_cursor(offset);
@@ -125,7 +126,7 @@ fn first_path(path: &ast::Path) -> ast::Path {
125 126
126#[cfg(test)] 127#[cfg(test)]
127mod tests { 128mod tests {
128 use crate::helpers::check_assist; 129 use crate::tests::check_assist;
129 130
130 use super::*; 131 use super::*;
131 132
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs
index 5a77d3dbc..7c4d9d55d 100644
--- a/crates/ra_assists/src/handlers/merge_match_arms.rs
+++ b/crates/ra_assists/src/handlers/merge_match_arms.rs
@@ -70,7 +70,7 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> {
70 return None; 70 return None;
71 } 71 }
72 72
73 ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", |edit| { 73 ctx.add_assist(AssistId("merge_match_arms"), "Merge match arms", current_text_range, |edit| {
74 let pats = if arms_to_merge.iter().any(contains_placeholder) { 74 let pats = if arms_to_merge.iter().any(contains_placeholder) {
75 "_".into() 75 "_".into()
76 } else { 76 } else {
@@ -87,7 +87,6 @@ pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> {
87 let start = arms_to_merge.first().unwrap().syntax().text_range().start(); 87 let start = arms_to_merge.first().unwrap().syntax().text_range().start();
88 let end = arms_to_merge.last().unwrap().syntax().text_range().end(); 88 let end = arms_to_merge.last().unwrap().syntax().text_range().end();
89 89
90 edit.target(current_text_range);
91 edit.set_cursor(match cursor_pos { 90 edit.set_cursor(match cursor_pos {
92 CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset, 91 CursorPos::InExpr(back_offset) => start + TextSize::of(&arm) - back_offset,
93 CursorPos::InPat(offset) => offset, 92 CursorPos::InPat(offset) => offset,
@@ -105,7 +104,7 @@ fn contains_placeholder(a: &ast::MatchArm) -> bool {
105 104
106#[cfg(test)] 105#[cfg(test)]
107mod tests { 106mod tests {
108 use crate::helpers::{check_assist, check_assist_not_applicable}; 107 use crate::tests::{check_assist, check_assist_not_applicable};
109 108
110 use super::*; 109 use super::*;
111 110
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs
index 0f26884dc..44e50cb6e 100644
--- a/crates/ra_assists/src/handlers/move_bounds.rs
+++ b/crates/ra_assists/src/handlers/move_bounds.rs
@@ -49,30 +49,37 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> {
49 } 49 }
50 }; 50 };
51 51
52 ctx.add_assist(AssistId("move_bounds_to_where_clause"), "Move to where clause", |edit| { 52 let target = type_param_list.syntax().text_range();
53 let new_params = type_param_list 53 ctx.add_assist(
54 .type_params() 54 AssistId("move_bounds_to_where_clause"),
55 .filter(|it| it.type_bound_list().is_some()) 55 "Move to where clause",
56 .map(|type_param| { 56 target,
57 let without_bounds = type_param.remove_bounds(); 57 |edit| {
58 (type_param, without_bounds) 58 let new_params = type_param_list
59 }); 59 .type_params()
60 60 .filter(|it| it.type_bound_list().is_some())
61 let new_type_param_list = type_param_list.replace_descendants(new_params); 61 .map(|type_param| {
62 edit.replace_ast(type_param_list.clone(), new_type_param_list); 62 let without_bounds = type_param.remove_bounds();
63 63 (type_param, without_bounds)
64 let where_clause = { 64 });
65 let predicates = type_param_list.type_params().filter_map(build_predicate); 65
66 make::where_clause(predicates) 66 let new_type_param_list = type_param_list.replace_descendants(new_params);
67 }; 67 edit.replace_ast(type_param_list.clone(), new_type_param_list);
68 68
69 let to_insert = match anchor.prev_sibling_or_token() { 69 let where_clause = {
70 Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), 70 let predicates = type_param_list.type_params().filter_map(build_predicate);
71 _ => format!(" {}", where_clause.syntax()), 71 make::where_clause(predicates)
72 }; 72 };
73 edit.insert(anchor.text_range().start(), to_insert); 73
74 edit.target(type_param_list.syntax().text_range()); 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> {
@@ -89,7 +96,7 @@ fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> {
89mod tests { 96mod tests {
90 use super::*; 97 use super::*;
91 98
92 use crate::helpers::check_assist; 99 use crate::tests::check_assist;
93 100
94 #[test] 101 #[test]
95 fn move_bounds_to_where_clause_fn() { 102 fn move_bounds_to_where_clause_fn() {
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs
index b084dd9ee..29bc9a9ff 100644
--- a/crates/ra_assists/src/handlers/move_guard.rs
+++ b/crates/ra_assists/src/handlers/move_guard.rs
@@ -40,8 +40,8 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> {
40 let arm_expr = match_arm.expr()?; 40 let arm_expr = match_arm.expr()?;
41 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text()); 41 let buf = format!("if {} {{ {} }}", guard_conditions.syntax().text(), arm_expr.syntax().text());
42 42
43 ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", |edit| { 43 let target = guard.syntax().text_range();
44 edit.target(guard.syntax().text_range()); 44 ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| {
45 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) { 45 let offseting_amount = match space_before_guard.and_then(|it| it.into_token()) {
46 Some(tok) => { 46 Some(tok) => {
47 if ast::Whitespace::cast(tok.clone()).is_some() { 47 if ast::Whitespace::cast(tok.clone()).is_some() {
@@ -108,11 +108,12 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
108 108
109 let buf = format!(" if {}", cond.syntax().text()); 109 let buf = format!(" if {}", cond.syntax().text());
110 110
111 let target = if_expr.syntax().text_range();
111 ctx.add_assist( 112 ctx.add_assist(
112 AssistId("move_arm_cond_to_match_guard"), 113 AssistId("move_arm_cond_to_match_guard"),
113 "Move condition to match guard", 114 "Move condition to match guard",
115 target,
114 |edit| { 116 |edit| {
115 edit.target(if_expr.syntax().text_range());
116 let then_only_expr = then_block.statements().next().is_none(); 117 let then_only_expr = then_block.statements().next().is_none();
117 118
118 match &then_block.expr() { 119 match &then_block.expr() {
@@ -132,7 +133,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> {
132mod tests { 133mod tests {
133 use super::*; 134 use super::*;
134 135
135 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 136 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
136 137
137 #[test] 138 #[test]
138 fn move_guard_to_arm_body_target() { 139 fn move_guard_to_arm_body_target() {
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs
index 567400b9c..155c679b4 100644
--- a/crates/ra_assists/src/handlers/raw_string.rs
+++ b/crates/ra_assists/src/handlers/raw_string.rs
@@ -25,8 +25,8 @@ use crate::{Assist, AssistCtx, AssistId};
25pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> { 25pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> {
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 ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", |edit| { 28 let target = token.syntax().text_range();
29 edit.target(token.syntax().text_range()); 29 ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| {
30 let max_hash_streak = count_hashes(&value); 30 let max_hash_streak = count_hashes(&value);
31 let mut hashes = String::with_capacity(max_hash_streak + 1); 31 let mut hashes = String::with_capacity(max_hash_streak + 1);
32 for _ in 0..hashes.capacity() { 32 for _ in 0..hashes.capacity() {
@@ -54,8 +54,8 @@ pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> {
54pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> { 54pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> {
55 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?; 55 let token = ctx.find_token_at_offset(RAW_STRING).and_then(ast::RawString::cast)?;
56 let value = token.value()?; 56 let value = token.value()?;
57 ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", |edit| { 57 let target = token.syntax().text_range();
58 edit.target(token.syntax().text_range()); 58 ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| {
59 // parse inside string to escape `"` 59 // parse inside string to escape `"`
60 let escaped = value.escape_default().to_string(); 60 let escaped = value.escape_default().to_string();
61 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); 61 edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped));
@@ -79,8 +79,8 @@ pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> {
79// ``` 79// ```
80pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> { 80pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> {
81 let token = ctx.find_token_at_offset(RAW_STRING)?; 81 let token = ctx.find_token_at_offset(RAW_STRING)?;
82 ctx.add_assist(AssistId("add_hash"), "Add # to raw string", |edit| { 82 let target = token.text_range();
83 edit.target(token.text_range()); 83 ctx.add_assist(AssistId("add_hash"), "Add # to raw string", target, |edit| {
84 edit.insert(token.text_range().start() + TextSize::of('r'), "#"); 84 edit.insert(token.text_range().start() + TextSize::of('r'), "#");
85 edit.insert(token.text_range().end(), "#"); 85 edit.insert(token.text_range().end(), "#");
86 }) 86 })
@@ -108,8 +108,8 @@ pub(crate) fn remove_hash(ctx: AssistCtx) -> Option<Assist> {
108 // no hash to remove 108 // no hash to remove
109 return None; 109 return None;
110 } 110 }
111 ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", |edit| { 111 let target = token.text_range();
112 edit.target(token.text_range()); 112 ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| {
113 let result = &text[2..text.len() - 1]; 113 let result = &text[2..text.len() - 1];
114 let result = if result.starts_with('\"') { 114 let result = if result.starts_with('\"') {
115 // FIXME: this logic is wrong, not only the last has has to handled specially 115 // FIXME: this logic is wrong, not only the last has has to handled specially
@@ -138,7 +138,7 @@ fn count_hashes(s: &str) -> usize {
138#[cfg(test)] 138#[cfg(test)]
139mod test { 139mod test {
140 use super::*; 140 use super::*;
141 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 141 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
142 142
143 #[test] 143 #[test]
144 fn make_raw_string_target() { 144 fn make_raw_string_target() {
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs
index 4e5eb4350..e6e02f2ae 100644
--- a/crates/ra_assists/src/handlers/remove_dbg.rs
+++ b/crates/ra_assists/src/handlers/remove_dbg.rs
@@ -57,8 +57,8 @@ pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> {
57 text.slice(without_parens).to_string() 57 text.slice(without_parens).to_string()
58 }; 58 };
59 59
60 ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", |edit| { 60 let target = macro_call.syntax().text_range();
61 edit.target(macro_call.syntax().text_range()); 61 ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| {
62 edit.replace(macro_range, macro_content); 62 edit.replace(macro_range, macro_content);
63 edit.set_cursor(cursor_pos); 63 edit.set_cursor(cursor_pos);
64 }) 64 })
@@ -90,7 +90,7 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
90#[cfg(test)] 90#[cfg(test)]
91mod tests { 91mod tests {
92 use super::*; 92 use super::*;
93 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 93 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
94 94
95 #[test] 95 #[test]
96 fn test_remove_dbg() { 96 fn test_remove_dbg() {
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs
index e598023b2..9f72f879d 100644
--- a/crates/ra_assists/src/handlers/remove_mut.rs
+++ b/crates/ra_assists/src/handlers/remove_mut.rs
@@ -25,7 +25,8 @@ pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> {
25 _ => mut_token.text_range().end(), 25 _ => mut_token.text_range().end(),
26 }; 26 };
27 27
28 ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", |edit| { 28 let target = mut_token.text_range();
29 ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| {
29 edit.set_cursor(delete_from); 30 edit.set_cursor(delete_from);
30 edit.delete(TextRange::new(delete_from, delete_to)); 31 edit.delete(TextRange::new(delete_from, delete_to));
31 }) 32 })
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs
index 5cbb98d73..0b930dea2 100644
--- a/crates/ra_assists/src/handlers/reorder_fields.rs
+++ b/crates/ra_assists/src/handlers/reorder_fields.rs
@@ -50,11 +50,11 @@ fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
50 return None; 50 return None;
51 } 51 }
52 52
53 ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", |edit| { 53 let target = record.syntax().text_range();
54 ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", target, |edit| {
54 for (old, new) in fields.iter().zip(&sorted_fields) { 55 for (old, new) in fields.iter().zip(&sorted_fields) {
55 algo::diff(old, new).into_text_edit(edit.text_edit_builder()); 56 algo::diff(old, new).into_text_edit(edit.text_edit_builder());
56 } 57 }
57 edit.target(record.syntax().text_range())
58 }) 58 })
59} 59}
60 60
@@ -109,7 +109,7 @@ fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String,
109 109
110#[cfg(test)] 110#[cfg(test)]
111mod tests { 111mod tests {
112 use crate::helpers::{check_assist, check_assist_not_applicable}; 112 use crate::tests::{check_assist, check_assist_not_applicable};
113 113
114 use super::*; 114 use super::*;
115 115
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 9841f6980..2eb8348f8 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
@@ -44,37 +44,42 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
44 }; 44 };
45 45
46 let sema = ctx.sema; 46 let sema = ctx.sema;
47 ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| { 47 let target = if_expr.syntax().text_range();
48 let match_expr = { 48 ctx.add_assist(
49 let then_arm = { 49 AssistId("replace_if_let_with_match"),
50 let then_expr = unwrap_trivial_block(then_block); 50 "Replace with match",
51 make::match_arm(vec![pat.clone()], then_expr) 51 target,
52 move |edit| {
53 let match_expr = {
54 let then_arm = {
55 let then_expr = unwrap_trivial_block(then_block);
56 make::match_arm(vec![pat.clone()], then_expr)
57 };
58 let else_arm = {
59 let pattern = sema
60 .type_of_pat(&pat)
61 .and_then(|ty| TryEnum::from_ty(sema, &ty))
62 .map(|it| it.sad_pattern())
63 .unwrap_or_else(|| make::placeholder_pat().into());
64 let else_expr = unwrap_trivial_block(else_block);
65 make::match_arm(vec![pattern], else_expr)
66 };
67 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
52 }; 68 };
53 let else_arm = {
54 let pattern = sema
55 .type_of_pat(&pat)
56 .and_then(|ty| TryEnum::from_ty(sema, &ty))
57 .map(|it| it.sad_pattern())
58 .unwrap_or_else(|| make::placeholder_pat().into());
59 let else_expr = unwrap_trivial_block(else_block);
60 make::match_arm(vec![pattern], else_expr)
61 };
62 make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
63 };
64 69
65 let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); 70 let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr);
66 71
67 edit.target(if_expr.syntax().text_range()); 72 edit.set_cursor(if_expr.syntax().text_range().start());
68 edit.set_cursor(if_expr.syntax().text_range().start()); 73 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr);
69 edit.replace_ast::<ast::Expr>(if_expr.into(), match_expr); 74 },
70 }) 75 )
71} 76}
72 77
73#[cfg(test)] 78#[cfg(test)]
74mod tests { 79mod tests {
75 use super::*; 80 use super::*;
76 81
77 use crate::helpers::{check_assist, check_assist_target}; 82 use crate::tests::{check_assist, check_assist_target};
78 83
79 #[test] 84 #[test]
80 fn test_replace_if_let_with_match_unwraps_simple_expressions() { 85 fn test_replace_if_let_with_match_unwraps_simple_expressions() {
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 0cf23b754..a5509a567 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
@@ -47,7 +47,8 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
47 let ty = ctx.sema.type_of_expr(&init)?; 47 let ty = ctx.sema.type_of_expr(&init)?;
48 let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case()); 48 let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
49 49
50 ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| { 50 let target = let_kw.text_range();
51 ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| {
51 let with_placeholder: ast::Pat = match happy_variant { 52 let with_placeholder: ast::Pat = match happy_variant {
52 None => make::placeholder_pat().into(), 53 None => make::placeholder_pat().into(),
53 Some(var_name) => make::tuple_struct_pat( 54 Some(var_name) => make::tuple_struct_pat(
@@ -67,14 +68,13 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
67 let stmt = stmt.replace_descendant(placeholder.into(), original_pat); 68 let stmt = stmt.replace_descendant(placeholder.into(), original_pat);
68 69
69 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));
70 edit.target(let_kw.text_range());
71 edit.set_cursor(target_offset); 71 edit.set_cursor(target_offset);
72 }) 72 })
73} 73}
74 74
75#[cfg(test)] 75#[cfg(test)]
76mod tests { 76mod tests {
77 use crate::helpers::check_assist; 77 use crate::tests::check_assist;
78 78
79 use super::*; 79 use super::*;
80 80
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 ff2463c77..fd41da64b 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
@@ -33,9 +33,11 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist>
33 return None; 33 return None;
34 } 34 }
35 35
36 let target = path.syntax().text_range();
36 ctx.add_assist( 37 ctx.add_assist(
37 AssistId("replace_qualified_name_with_use"), 38 AssistId("replace_qualified_name_with_use"),
38 "Replace qualified path with use", 39 "Replace qualified path with use",
40 target,
39 |edit| { 41 |edit| {
40 let path_to_import = hir_path.mod_path().clone(); 42 let path_to_import = hir_path.mod_path().clone();
41 insert_use_statement(path.syntax(), &path_to_import, edit); 43 insert_use_statement(path.syntax(), &path_to_import, edit);
@@ -74,7 +76,7 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
74 76
75#[cfg(test)] 77#[cfg(test)]
76mod tests { 78mod tests {
77 use crate::helpers::{check_assist, check_assist_not_applicable}; 79 use crate::tests::{check_assist, check_assist_not_applicable};
78 80
79 use super::*; 81 use super::*;
80 82
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 62d4ea522..c6b73da67 100644
--- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
+++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs
@@ -38,32 +38,38 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
38 let caller = method_call.expr()?; 38 let caller = method_call.expr()?;
39 let ty = ctx.sema.type_of_expr(&caller)?; 39 let ty = ctx.sema.type_of_expr(&caller)?;
40 let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case(); 40 let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
41 let target = method_call.syntax().text_range();
42 ctx.add_assist(
43 AssistId("replace_unwrap_with_match"),
44 "Replace unwrap with match",
45 target,
46 |edit| {
47 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
48 let it = make::bind_pat(make::name("a")).into();
49 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
41 50
42 ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| { 51 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a")));
43 let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); 52 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path));
44 let it = make::bind_pat(make::name("a")).into();
45 let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into();
46 53
47 let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); 54 let unreachable_call = make::unreachable_macro_call().into();
48 let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); 55 let err_arm =
56 make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call);
49 57
50 let unreachable_call = make::unreachable_macro_call().into(); 58 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]);
51 let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); 59 let match_expr = make::expr_match(caller.clone(), match_arm_list);
60 let match_expr =
61 IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr);
52 62
53 let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); 63 edit.set_cursor(caller.syntax().text_range().start());
54 let match_expr = make::expr_match(caller.clone(), match_arm_list); 64 edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
55 let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); 65 },
56 66 )
57 edit.target(method_call.syntax().text_range());
58 edit.set_cursor(caller.syntax().text_range().start());
59 edit.replace_ast::<ast::Expr>(method_call.into(), match_expr);
60 })
61} 67}
62 68
63#[cfg(test)] 69#[cfg(test)]
64mod tests { 70mod tests {
65 use super::*; 71 use super::*;
66 use crate::helpers::{check_assist, check_assist_target}; 72 use crate::tests::{check_assist, check_assist_target};
67 73
68 #[test] 74 #[test]
69 fn test_replace_result_unwrap_with_match() { 75 fn test_replace_result_unwrap_with_match() {
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs
index f25826796..d49563974 100644
--- a/crates/ra_assists/src/handlers/split_import.rs
+++ b/crates/ra_assists/src/handlers/split_import.rs
@@ -28,8 +28,8 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
28 } 28 }
29 let cursor = ctx.frange.range.start(); 29 let cursor = ctx.frange.range.start();
30 30
31 ctx.add_assist(AssistId("split_import"), "Split import", |edit| { 31 let target = colon_colon.text_range();
32 edit.target(colon_colon.text_range()); 32 ctx.add_assist(AssistId("split_import"), "Split import", target, |edit| {
33 edit.replace_ast(use_tree, new_tree); 33 edit.replace_ast(use_tree, new_tree);
34 edit.set_cursor(cursor); 34 edit.set_cursor(cursor);
35 }) 35 })
@@ -37,7 +37,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> {
37 37
38#[cfg(test)] 38#[cfg(test)]
39mod tests { 39mod tests {
40 use crate::helpers::{check_assist, check_assist_not_applicable, check_assist_target}; 40 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
41 41
42 use super::*; 42 use super::*;
43 43
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs
index 859c70ad8..6df927abb 100644
--- a/crates/ra_assists/src/handlers/unwrap_block.rs
+++ b/crates/ra_assists/src/handlers/unwrap_block.rs
@@ -57,9 +57,9 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> {
57 } 57 }
58 }; 58 };
59 59
60 ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", |edit| { 60 let target = expr_to_unwrap.syntax().text_range();
61 ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| {
61 edit.set_cursor(expr.syntax().text_range().start()); 62 edit.set_cursor(expr.syntax().text_range().start());
62 edit.target(expr_to_unwrap.syntax().text_range());
63 63
64 let pat_start: &[_] = &[' ', '{', '\n']; 64 let pat_start: &[_] = &[' ', '{', '\n'];
65 let expr_to_unwrap = expr_to_unwrap.to_string(); 65 let expr_to_unwrap = expr_to_unwrap.to_string();
@@ -89,7 +89,7 @@ fn extract_expr(cursor_range: TextRange, block: ast::BlockExpr) -> Option<ast::E
89 89
90#[cfg(test)] 90#[cfg(test)]
91mod tests { 91mod tests {
92 use crate::helpers::{check_assist, check_assist_not_applicable}; 92 use crate::tests::{check_assist, check_assist_not_applicable};
93 93
94 use super::*; 94 use super::*;
95 95