aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorAndrea Pretto <[email protected]>2019-02-11 17:07:21 +0000
committerAndrea Pretto <[email protected]>2019-02-11 17:07:21 +0000
commit5c9c0d3ae2735b4b32a44742bac800ca616fdde8 (patch)
tree8ad5dc9a548915729bcc87085a1a145d4ba2a00a /crates
parentaf62fde57fe58f4aa06608568dc26535731800a0 (diff)
ra_assists: assist "providers" can produce multiple assists
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/add_derive.rs8
-rw-r--r--crates/ra_assists/src/add_impl.rs8
-rw-r--r--crates/ra_assists/src/assist_ctx.rs41
-rw-r--r--crates/ra_assists/src/auto_import.rs8
-rw-r--r--crates/ra_assists/src/change_visibility.rs20
-rw-r--r--crates/ra_assists/src/fill_match_arms.rs8
-rw-r--r--crates/ra_assists/src/flip_comma.rs8
-rw-r--r--crates/ra_assists/src/introduce_variable.rs8
-rw-r--r--crates/ra_assists/src/lib.rs75
-rw-r--r--crates/ra_assists/src/remove_dbg.rs8
-rw-r--r--crates/ra_assists/src/replace_if_let_with_match.rs8
-rw-r--r--crates/ra_assists/src/split_import.rs8
13 files changed, 145 insertions, 64 deletions
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index f8fbe75b5..880bc4b18 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -6,6 +6,7 @@ authors = ["rust-analyzer developers"]
6 6
7[dependencies] 7[dependencies]
8join_to_string = "0.1.3" 8join_to_string = "0.1.3"
9itertools = "0.8.0"
9 10
10ra_syntax = { path = "../ra_syntax" } 11ra_syntax = { path = "../ra_syntax" }
11ra_text_edit = { path = "../ra_text_edit" } 12ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_assists/src/add_derive.rs b/crates/ra_assists/src/add_derive.rs
index ea9707631..0556dd69c 100644
--- a/crates/ra_assists/src/add_derive.rs
+++ b/crates/ra_assists/src/add_derive.rs
@@ -7,10 +7,10 @@ use ra_syntax::{
7 7
8use crate::{AssistCtx, Assist}; 8use crate::{AssistCtx, Assist};
9 9
10pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 10pub(crate) fn add_derive(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 let nominal = ctx.node_at_offset::<ast::NominalDef>()?; 11 let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
12 let node_start = derive_insertion_offset(nominal)?; 12 let node_start = derive_insertion_offset(nominal)?;
13 ctx.build("add `#[derive]`", |edit| { 13 ctx.add_action("add `#[derive]`", |edit| {
14 let derive_attr = nominal 14 let derive_attr = nominal
15 .attrs() 15 .attrs()
16 .filter_map(|x| x.as_call()) 16 .filter_map(|x| x.as_call())
@@ -26,7 +26,9 @@ pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
26 }; 26 };
27 edit.target(nominal.syntax().range()); 27 edit.target(nominal.syntax().range());
28 edit.set_cursor(offset) 28 edit.set_cursor(offset)
29 }) 29 });
30
31 ctx.build()
30} 32}
31 33
32// Insert `derive` after doc comments. 34// Insert `derive` after doc comments.
diff --git a/crates/ra_assists/src/add_impl.rs b/crates/ra_assists/src/add_impl.rs
index 32fc074a6..b40b9cc0c 100644
--- a/crates/ra_assists/src/add_impl.rs
+++ b/crates/ra_assists/src/add_impl.rs
@@ -7,10 +7,10 @@ use ra_syntax::{
7 7
8use crate::{AssistCtx, Assist}; 8use crate::{AssistCtx, Assist};
9 9
10pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 10pub(crate) fn add_impl(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 let nominal = ctx.node_at_offset::<ast::NominalDef>()?; 11 let nominal = ctx.node_at_offset::<ast::NominalDef>()?;
12 let name = nominal.name()?; 12 let name = nominal.name()?;
13 ctx.build("add impl", |edit| { 13 ctx.add_action("add impl", |edit| {
14 edit.target(nominal.syntax().range()); 14 edit.target(nominal.syntax().range());
15 let type_params = nominal.type_param_list(); 15 let type_params = nominal.type_param_list();
16 let start_offset = nominal.syntax().range().end(); 16 let start_offset = nominal.syntax().range().end();
@@ -32,7 +32,9 @@ pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
32 edit.set_cursor(start_offset + TextUnit::of_str(&buf)); 32 edit.set_cursor(start_offset + TextUnit::of_str(&buf));
33 buf.push_str("\n}"); 33 buf.push_str("\n}");
34 edit.insert(start_offset, buf); 34 edit.insert(start_offset, buf);
35 }) 35 });
36
37 ctx.build()
36} 38}
37 39
38#[cfg(test)] 40#[cfg(test)]
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs
index e240c35d6..e9c4f0a23 100644
--- a/crates/ra_assists/src/assist_ctx.rs
+++ b/crates/ra_assists/src/assist_ctx.rs
@@ -9,9 +9,10 @@ use ra_fmt::{leading_indent, reindent};
9 9
10use crate::{AssistLabel, AssistAction}; 10use crate::{AssistLabel, AssistAction};
11 11
12#[derive(Clone, Debug)]
12pub(crate) enum Assist { 13pub(crate) enum Assist {
13 Unresolved(AssistLabel), 14 Unresolved(Vec<AssistLabel>),
14 Resolved(AssistLabel, AssistAction), 15 Resolved(Vec<(AssistLabel, AssistAction)>),
15} 16}
16 17
17/// `AssistCtx` allows to apply an assist or check if it could be applied. 18/// `AssistCtx` allows to apply an assist or check if it could be applied.
@@ -50,6 +51,7 @@ pub(crate) struct AssistCtx<'a, DB> {
50 pub(crate) frange: FileRange, 51 pub(crate) frange: FileRange,
51 source_file: &'a SourceFile, 52 source_file: &'a SourceFile,
52 should_compute_edit: bool, 53 should_compute_edit: bool,
54 assist: Assist,
53} 55}
54 56
55impl<'a, DB> Clone for AssistCtx<'a, DB> { 57impl<'a, DB> Clone for AssistCtx<'a, DB> {
@@ -59,6 +61,7 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> {
59 frange: self.frange, 61 frange: self.frange,
60 source_file: self.source_file, 62 source_file: self.source_file,
61 should_compute_edit: self.should_compute_edit, 63 should_compute_edit: self.should_compute_edit,
64 assist: self.assist.clone(),
62 } 65 }
63 } 66 }
64} 67}
@@ -69,25 +72,35 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> {
69 F: FnOnce(AssistCtx<DB>) -> T, 72 F: FnOnce(AssistCtx<DB>) -> T,
70 { 73 {
71 let source_file = &db.parse(frange.file_id); 74 let source_file = &db.parse(frange.file_id);
72 let ctx = AssistCtx { db, frange, source_file, should_compute_edit }; 75 let assist =
76 if should_compute_edit { Assist::Resolved(vec![]) } else { Assist::Unresolved(vec![]) };
77
78 let ctx = AssistCtx { db, frange, source_file, should_compute_edit, assist };
73 f(ctx) 79 f(ctx)
74 } 80 }
75 81
76 pub(crate) fn build( 82 pub(crate) fn add_action(
77 self, 83 &mut self,
78 label: impl Into<String>, 84 label: impl Into<String>,
79 f: impl FnOnce(&mut AssistBuilder), 85 f: impl FnOnce(&mut AssistBuilder),
80 ) -> Option<Assist> { 86 ) -> &mut Self {
81 let label = AssistLabel { label: label.into() }; 87 let label = AssistLabel { label: label.into() };
82 if !self.should_compute_edit { 88 match &mut self.assist {
83 return Some(Assist::Unresolved(label)); 89 Assist::Unresolved(labels) => labels.push(label),
90 Assist::Resolved(labels_actions) => {
91 let action = {
92 let mut edit = AssistBuilder::default();
93 f(&mut edit);
94 edit.build()
95 };
96 labels_actions.push((label, action));
97 }
84 } 98 }
85 let action = { 99 self
86 let mut edit = AssistBuilder::default(); 100 }
87 f(&mut edit); 101
88 edit.build() 102 pub(crate) fn build(self) -> Option<Assist> {
89 }; 103 Some(self.assist)
90 Some(Assist::Resolved(label, action))
91 } 104 }
92 105
93 pub(crate) fn leaf_at_offset(&self) -> LeafAtOffset<&'a SyntaxNode> { 106 pub(crate) fn leaf_at_offset(&self) -> LeafAtOffset<&'a SyntaxNode> {
diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs
index 6a0c351f1..b251c9369 100644
--- a/crates/ra_assists/src/auto_import.rs
+++ b/crates/ra_assists/src/auto_import.rs
@@ -480,7 +480,7 @@ fn make_assist_add_nested_import(
480 } 480 }
481} 481}
482 482
483pub(crate) fn auto_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 483pub(crate) fn auto_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
484 let node = ctx.covering_node(); 484 let node = ctx.covering_node();
485 let current_file = node.ancestors().find_map(ast::SourceFile::cast)?; 485 let current_file = node.ancestors().find_map(ast::SourceFile::cast)?;
486 486
@@ -495,7 +495,7 @@ pub(crate) fn auto_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
495 return None; 495 return None;
496 } 496 }
497 497
498 ctx.build(format!("import {} in the current file", fmt_segments(&segments)), |edit| { 498 ctx.add_action(format!("import {} in the current file", fmt_segments(&segments)), |edit| {
499 let action = best_action_for_target(current_file.syntax(), path, &segments); 499 let action = best_action_for_target(current_file.syntax(), path, &segments);
500 make_assist(&action, segments.as_slice(), edit); 500 make_assist(&action, segments.as_slice(), edit);
501 if let Some(last_segment) = path.segment() { 501 if let Some(last_segment) = path.segment() {
@@ -506,7 +506,9 @@ pub(crate) fn auto_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
506 last_segment.syntax().range().start(), 506 last_segment.syntax().range().start(),
507 )); 507 ));
508 } 508 }
509 }) 509 });
510
511 ctx.build()
510} 512}
511 513
512#[cfg(test)] 514#[cfg(test)]
diff --git a/crates/ra_assists/src/change_visibility.rs b/crates/ra_assists/src/change_visibility.rs
index 6d9a4eec2..c2ba897a4 100644
--- a/crates/ra_assists/src/change_visibility.rs
+++ b/crates/ra_assists/src/change_visibility.rs
@@ -14,7 +14,7 @@ pub(crate) fn change_visibility(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
14 add_vis(ctx) 14 add_vis(ctx)
15} 15}
16 16
17fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 17fn add_vis(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
18 let item_keyword = ctx.leaf_at_offset().find(|leaf| match leaf.kind() { 18 let item_keyword = ctx.leaf_at_offset().find(|leaf| match leaf.kind() {
19 FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true, 19 FN_KW | MOD_KW | STRUCT_KW | ENUM_KW | TRAIT_KW => true,
20 _ => false, 20 _ => false,
@@ -41,11 +41,13 @@ fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
41 (vis_offset(field.syntax()), ident.range()) 41 (vis_offset(field.syntax()), ident.range())
42 }; 42 };
43 43
44 ctx.build("make pub(crate)", |edit| { 44 ctx.add_action("make pub(crate)", |edit| {
45 edit.target(target); 45 edit.target(target);
46 edit.insert(offset, "pub(crate) "); 46 edit.insert(offset, "pub(crate) ");
47 edit.set_cursor(offset); 47 edit.set_cursor(offset);
48 }) 48 });
49
50 ctx.build()
49} 51}
50 52
51fn vis_offset(node: &SyntaxNode) -> TextUnit { 53fn vis_offset(node: &SyntaxNode) -> TextUnit {
@@ -59,20 +61,24 @@ fn vis_offset(node: &SyntaxNode) -> TextUnit {
59 .unwrap_or(node.range().start()) 61 .unwrap_or(node.range().start())
60} 62}
61 63
62fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> { 64fn change_vis(mut ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> {
63 if vis.syntax().text() == "pub" { 65 if vis.syntax().text() == "pub" {
64 return ctx.build("change to pub(crate)", |edit| { 66 ctx.add_action("change to pub(crate)", |edit| {
65 edit.target(vis.syntax().range()); 67 edit.target(vis.syntax().range());
66 edit.replace(vis.syntax().range(), "pub(crate)"); 68 edit.replace(vis.syntax().range(), "pub(crate)");
67 edit.set_cursor(vis.syntax().range().start()); 69 edit.set_cursor(vis.syntax().range().start())
68 }); 70 });
71
72 return ctx.build();
69 } 73 }
70 if vis.syntax().text() == "pub(crate)" { 74 if vis.syntax().text() == "pub(crate)" {
71 return ctx.build("change to pub", |edit| { 75 ctx.add_action("change to pub", |edit| {
72 edit.target(vis.syntax().range()); 76 edit.target(vis.syntax().range());
73 edit.replace(vis.syntax().range(), "pub"); 77 edit.replace(vis.syntax().range(), "pub");
74 edit.set_cursor(vis.syntax().range().start()); 78 edit.set_cursor(vis.syntax().range().start());
75 }); 79 });
80
81 return ctx.build();
76 } 82 }
77 None 83 None
78} 84}
diff --git a/crates/ra_assists/src/fill_match_arms.rs b/crates/ra_assists/src/fill_match_arms.rs
index 69b535a27..d8e40b4b7 100644
--- a/crates/ra_assists/src/fill_match_arms.rs
+++ b/crates/ra_assists/src/fill_match_arms.rs
@@ -8,7 +8,7 @@ use ra_syntax::ast::{self, AstNode};
8 8
9use crate::{AssistCtx, Assist}; 9use crate::{AssistCtx, Assist};
10 10
11pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 11pub(crate) fn fill_match_arms(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?; 12 let match_expr = ctx.node_at_offset::<ast::MatchExpr>()?;
13 13
14 // We already have some match arms, so we don't provide any assists. 14 // We already have some match arms, so we don't provide any assists.
@@ -33,7 +33,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
33 let enum_name = enum_def.name(ctx.db)?; 33 let enum_name = enum_def.name(ctx.db)?;
34 let db = ctx.db; 34 let db = ctx.db;
35 35
36 ctx.build("fill match arms", |edit| { 36 ctx.add_action("fill match arms", |edit| {
37 let mut buf = format!("match {} {{\n", expr.syntax().text().to_string()); 37 let mut buf = format!("match {} {{\n", expr.syntax().text().to_string());
38 let variants = enum_def.variants(db); 38 let variants = enum_def.variants(db);
39 for variant in variants { 39 for variant in variants {
@@ -68,7 +68,9 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist
68 edit.target(match_expr.syntax().range()); 68 edit.target(match_expr.syntax().range());
69 edit.set_cursor(expr.syntax().range().start()); 69 edit.set_cursor(expr.syntax().range().start());
70 edit.replace_node_and_indent(match_expr.syntax(), buf); 70 edit.replace_node_and_indent(match_expr.syntax(), buf);
71 }) 71 });
72
73 ctx.build()
72} 74}
73 75
74#[cfg(test)] 76#[cfg(test)]
diff --git a/crates/ra_assists/src/flip_comma.rs b/crates/ra_assists/src/flip_comma.rs
index 33da58f17..08644d720 100644
--- a/crates/ra_assists/src/flip_comma.rs
+++ b/crates/ra_assists/src/flip_comma.rs
@@ -6,15 +6,17 @@ use ra_syntax::{
6 6
7use crate::{AssistCtx, Assist, non_trivia_sibling}; 7use crate::{AssistCtx, Assist, non_trivia_sibling};
8 8
9pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 9pub(crate) fn flip_comma(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
10 let comma = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COMMA)?; 10 let comma = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COMMA)?;
11 let prev = non_trivia_sibling(comma, Direction::Prev)?; 11 let prev = non_trivia_sibling(comma, Direction::Prev)?;
12 let next = non_trivia_sibling(comma, Direction::Next)?; 12 let next = non_trivia_sibling(comma, Direction::Next)?;
13 ctx.build("flip comma", |edit| { 13 ctx.add_action("flip comma", |edit| {
14 edit.target(comma.range()); 14 edit.target(comma.range());
15 edit.replace(prev.range(), next.text()); 15 edit.replace(prev.range(), next.text());
16 edit.replace(next.range(), prev.text()); 16 edit.replace(next.range(), prev.text());
17 }) 17 });
18
19 ctx.build()
18} 20}
19 21
20#[cfg(test)] 22#[cfg(test)]
diff --git a/crates/ra_assists/src/introduce_variable.rs b/crates/ra_assists/src/introduce_variable.rs
index 954b97b05..f0e012105 100644
--- a/crates/ra_assists/src/introduce_variable.rs
+++ b/crates/ra_assists/src/introduce_variable.rs
@@ -8,7 +8,7 @@ use ra_syntax::{
8 8
9use crate::{AssistCtx, Assist}; 9use crate::{AssistCtx, Assist};
10 10
11pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 11pub(crate) fn introduce_variable(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let node = ctx.covering_node(); 12 let node = ctx.covering_node();
13 if !valid_covering_node(node) { 13 if !valid_covering_node(node) {
14 return None; 14 return None;
@@ -19,7 +19,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
19 if indent.kind() != WHITESPACE { 19 if indent.kind() != WHITESPACE {
20 return None; 20 return None;
21 } 21 }
22 ctx.build("introduce variable", move |edit| { 22 ctx.add_action("introduce variable", move |edit| {
23 let mut buf = String::new(); 23 let mut buf = String::new();
24 24
25 let cursor_offset = if wrap_in_block { 25 let cursor_offset = if wrap_in_block {
@@ -68,7 +68,9 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
68 } 68 }
69 } 69 }
70 edit.set_cursor(anchor_stmt.range().start() + cursor_offset); 70 edit.set_cursor(anchor_stmt.range().start() + cursor_offset);
71 }) 71 });
72
73 ctx.build()
72} 74}
73 75
74fn valid_covering_node(node: &SyntaxNode) -> bool { 76fn valid_covering_node(node: &SyntaxNode) -> bool {
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index af578893e..c607a5142 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -7,6 +7,8 @@
7 7
8mod assist_ctx; 8mod assist_ctx;
9 9
10use itertools::Itertools;
11
10use ra_text_edit::TextEdit; 12use ra_text_edit::TextEdit;
11use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction}; 13use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction};
12use ra_db::FileRange; 14use ra_db::FileRange;
@@ -14,12 +16,13 @@ use hir::db::HirDatabase;
14 16
15pub(crate) use crate::assist_ctx::{AssistCtx, Assist}; 17pub(crate) use crate::assist_ctx::{AssistCtx, Assist};
16 18
17#[derive(Debug)] 19#[derive(Debug, Clone)]
18pub struct AssistLabel { 20pub struct AssistLabel {
19 /// Short description of the assist, as shown in the UI. 21 /// Short description of the assist, as shown in the UI.
20 pub label: String, 22 pub label: String,
21} 23}
22 24
25#[derive(Debug, Clone)]
23pub struct AssistAction { 26pub struct AssistAction {
24 pub edit: TextEdit, 27 pub edit: TextEdit,
25 pub cursor_position: Option<TextUnit>, 28 pub cursor_position: Option<TextUnit>,
@@ -39,10 +42,10 @@ where
39 .iter() 42 .iter()
40 .filter_map(|f| f(ctx.clone())) 43 .filter_map(|f| f(ctx.clone()))
41 .map(|a| match a { 44 .map(|a| match a {
42 Assist::Unresolved(label) => label, 45 Assist::Unresolved(labels) => labels,
43 Assist::Resolved(..) => unreachable!(), 46 Assist::Resolved(..) => unreachable!(),
44 }) 47 })
45 .collect() 48 .concat()
46 }) 49 })
47} 50}
48 51
@@ -61,10 +64,10 @@ where
61 .iter() 64 .iter()
62 .filter_map(|f| f(ctx.clone())) 65 .filter_map(|f| f(ctx.clone()))
63 .map(|a| match a { 66 .map(|a| match a {
64 Assist::Resolved(label, action) => (label, action), 67 Assist::Resolved(labels_actions) => labels_actions,
65 Assist::Unresolved(..) => unreachable!(), 68 Assist::Unresolved(..) => unreachable!(),
66 }) 69 })
67 .collect::<Vec<(AssistLabel, AssistAction)>>(); 70 .concat();
68 a.sort_by(|a, b| match (a.1.target, b.1.target) { 71 a.sort_by(|a, b| match (a.1.target, b.1.target) {
69 (Some(a), Some(b)) => a.len().cmp(&b.len()), 72 (Some(a), Some(b)) => a.len().cmp(&b.len()),
70 (Some(_), None) => Ordering::Less, 73 (Some(_), None) => Ordering::Less,
@@ -119,17 +122,51 @@ mod helpers {
119 before: &str, 122 before: &str,
120 after: &str, 123 after: &str,
121 ) { 124 ) {
125 check_assist_nth_action(assist, before, after, 0)
126 }
127
128 pub(crate) fn check_assist_range(
129 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
130 before: &str,
131 after: &str,
132 ) {
133 check_assist_range_nth_action(assist, before, after, 0)
134 }
135
136 pub(crate) fn check_assist_target(
137 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
138 before: &str,
139 target: &str,
140 ) {
141 check_assist_target_nth_action(assist, before, target, 0)
142 }
143
144 pub(crate) fn check_assist_range_target(
145 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
146 before: &str,
147 target: &str,
148 ) {
149 check_assist_range_target_nth_action(assist, before, target, 0)
150 }
151
152 pub(crate) fn check_assist_nth_action(
153 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
154 before: &str,
155 after: &str,
156 index: usize,
157 ) {
122 let (before_cursor_pos, before) = extract_offset(before); 158 let (before_cursor_pos, before) = extract_offset(before);
123 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 159 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
124 let frange = 160 let frange =
125 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 161 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
126 let assist = 162 let assist =
127 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 163 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
128 let action = match assist { 164 let labels_actions = match assist {
129 Assist::Unresolved(_) => unreachable!(), 165 Assist::Unresolved(_) => unreachable!(),
130 Assist::Resolved(_, it) => it, 166 Assist::Resolved(labels_actions) => labels_actions,
131 }; 167 };
132 168
169 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
133 let actual = action.edit.apply(&before); 170 let actual = action.edit.apply(&before);
134 let actual_cursor_pos = match action.cursor_position { 171 let actual_cursor_pos = match action.cursor_position {
135 None => action 172 None => action
@@ -142,21 +179,23 @@ mod helpers {
142 assert_eq_text!(after, &actual); 179 assert_eq_text!(after, &actual);
143 } 180 }
144 181
145 pub(crate) fn check_assist_range( 182 pub(crate) fn check_assist_range_nth_action(
146 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 183 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
147 before: &str, 184 before: &str,
148 after: &str, 185 after: &str,
186 index: usize,
149 ) { 187 ) {
150 let (range, before) = extract_range(before); 188 let (range, before) = extract_range(before);
151 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 189 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
152 let frange = FileRange { file_id, range }; 190 let frange = FileRange { file_id, range };
153 let assist = 191 let assist =
154 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 192 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
155 let action = match assist { 193 let labels_actions = match assist {
156 Assist::Unresolved(_) => unreachable!(), 194 Assist::Unresolved(_) => unreachable!(),
157 Assist::Resolved(_, it) => it, 195 Assist::Resolved(labels_actions) => labels_actions,
158 }; 196 };
159 197
198 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
160 let mut actual = action.edit.apply(&before); 199 let mut actual = action.edit.apply(&before);
161 if let Some(pos) = action.cursor_position { 200 if let Some(pos) = action.cursor_position {
162 actual = add_cursor(&actual, pos); 201 actual = add_cursor(&actual, pos);
@@ -164,10 +203,11 @@ mod helpers {
164 assert_eq_text!(after, &actual); 203 assert_eq_text!(after, &actual);
165 } 204 }
166 205
167 pub(crate) fn check_assist_target( 206 pub(crate) fn check_assist_target_nth_action(
168 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 207 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
169 before: &str, 208 before: &str,
170 target: &str, 209 target: &str,
210 index: usize,
171 ) { 211 ) {
172 let (before_cursor_pos, before) = extract_offset(before); 212 let (before_cursor_pos, before) = extract_offset(before);
173 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 213 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
@@ -175,30 +215,33 @@ mod helpers {
175 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 215 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
176 let assist = 216 let assist =
177 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 217 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
178 let action = match assist { 218 let labels_actions = match assist {
179 Assist::Unresolved(_) => unreachable!(), 219 Assist::Unresolved(_) => unreachable!(),
180 Assist::Resolved(_, it) => it, 220 Assist::Resolved(labels_actions) => labels_actions,
181 }; 221 };
182 222
223 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
183 let range = action.target.expect("expected target on action"); 224 let range = action.target.expect("expected target on action");
184 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 225 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
185 } 226 }
186 227
187 pub(crate) fn check_assist_range_target( 228 pub(crate) fn check_assist_range_target_nth_action(
188 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 229 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
189 before: &str, 230 before: &str,
190 target: &str, 231 target: &str,
232 index: usize,
191 ) { 233 ) {
192 let (range, before) = extract_range(before); 234 let (range, before) = extract_range(before);
193 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); 235 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
194 let frange = FileRange { file_id, range }; 236 let frange = FileRange { file_id, range };
195 let assist = 237 let assist =
196 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 238 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
197 let action = match assist { 239 let labels_actions = match assist {
198 Assist::Unresolved(_) => unreachable!(), 240 Assist::Unresolved(_) => unreachable!(),
199 Assist::Resolved(_, it) => it, 241 Assist::Resolved(labels_actions) => labels_actions,
200 }; 242 };
201 243
244 let (_, action) = labels_actions.get(index).expect("expect assist action at index");
202 let range = action.target.expect("expected target on action"); 245 let range = action.target.expect("expected target on action");
203 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); 246 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
204 } 247 }
diff --git a/crates/ra_assists/src/remove_dbg.rs b/crates/ra_assists/src/remove_dbg.rs
index e9d0a635b..db260c6ca 100644
--- a/crates/ra_assists/src/remove_dbg.rs
+++ b/crates/ra_assists/src/remove_dbg.rs
@@ -8,7 +8,7 @@ use ra_syntax::{
8}; 8};
9use crate::{AssistCtx, Assist}; 9use crate::{AssistCtx, Assist};
10 10
11pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 11pub(crate) fn remove_dbg(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
12 let macro_call = ctx.node_at_offset::<ast::MacroCall>()?; 12 let macro_call = ctx.node_at_offset::<ast::MacroCall>()?;
13 13
14 if !is_valid_macrocall(macro_call, "dbg")? { 14 if !is_valid_macrocall(macro_call, "dbg")? {
@@ -46,11 +46,13 @@ pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
46 macro_args.text().slice(start..end).to_string() 46 macro_args.text().slice(start..end).to_string()
47 }; 47 };
48 48
49 ctx.build("remove dbg!()", |edit| { 49 ctx.add_action("remove dbg!()", |edit| {
50 edit.target(macro_call.syntax().range()); 50 edit.target(macro_call.syntax().range());
51 edit.replace(macro_range, macro_content); 51 edit.replace(macro_range, macro_content);
52 edit.set_cursor(cursor_pos); 52 edit.set_cursor(cursor_pos);
53 }) 53 });
54
55 ctx.build()
54} 56}
55 57
56/// Verifies that the given macro_call actually matches the given name 58/// Verifies that the given macro_call actually matches the given name
diff --git a/crates/ra_assists/src/replace_if_let_with_match.rs b/crates/ra_assists/src/replace_if_let_with_match.rs
index f74ab3c67..87a2c1185 100644
--- a/crates/ra_assists/src/replace_if_let_with_match.rs
+++ b/crates/ra_assists/src/replace_if_let_with_match.rs
@@ -4,7 +4,7 @@ use hir::db::HirDatabase;
4 4
5use crate::{AssistCtx, Assist}; 5use crate::{AssistCtx, Assist};
6 6
7pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 7pub(crate) fn replace_if_let_with_match(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
8 let if_expr: &ast::IfExpr = ctx.node_at_offset()?; 8 let if_expr: &ast::IfExpr = ctx.node_at_offset()?;
9 let cond = if_expr.condition()?; 9 let cond = if_expr.condition()?;
10 let pat = cond.pat()?; 10 let pat = cond.pat()?;
@@ -15,12 +15,14 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Opt
15 ast::ElseBranchFlavor::IfExpr(_) => return None, 15 ast::ElseBranchFlavor::IfExpr(_) => return None,
16 }; 16 };
17 17
18 ctx.build("replace with match", |edit| { 18 ctx.add_action("replace with match", |edit| {
19 let match_expr = build_match_expr(expr, pat, then_block, else_block); 19 let match_expr = build_match_expr(expr, pat, then_block, else_block);
20 edit.target(if_expr.syntax().range()); 20 edit.target(if_expr.syntax().range());
21 edit.replace_node_and_indent(if_expr.syntax(), match_expr); 21 edit.replace_node_and_indent(if_expr.syntax(), match_expr);
22 edit.set_cursor(if_expr.syntax().range().start()) 22 edit.set_cursor(if_expr.syntax().range().start())
23 }) 23 });
24
25 ctx.build()
24} 26}
25 27
26fn build_match_expr( 28fn build_match_expr(
diff --git a/crates/ra_assists/src/split_import.rs b/crates/ra_assists/src/split_import.rs
index 051bc6fec..f043be636 100644
--- a/crates/ra_assists/src/split_import.rs
+++ b/crates/ra_assists/src/split_import.rs
@@ -7,7 +7,7 @@ use ra_syntax::{
7 7
8use crate::{AssistCtx, Assist}; 8use crate::{AssistCtx, Assist};
9 9
10pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { 10pub(crate) fn split_import(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
11 let colon_colon = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COLONCOLON)?; 11 let colon_colon = ctx.leaf_at_offset().find(|leaf| leaf.kind() == COLONCOLON)?;
12 let path = colon_colon.parent().and_then(ast::Path::cast)?; 12 let path = colon_colon.parent().and_then(ast::Path::cast)?;
13 let top_path = generate(Some(path), |it| it.parent_path()).last()?; 13 let top_path = generate(Some(path), |it| it.parent_path()).last()?;
@@ -23,12 +23,14 @@ pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
23 None => top_path.syntax().range().end(), 23 None => top_path.syntax().range().end(),
24 }; 24 };
25 25
26 ctx.build("split import", |edit| { 26 ctx.add_action("split import", |edit| {
27 edit.target(colon_colon.range()); 27 edit.target(colon_colon.range());
28 edit.insert(l_curly, "{"); 28 edit.insert(l_curly, "{");
29 edit.insert(r_curly, "}"); 29 edit.insert(r_curly, "}");
30 edit.set_cursor(l_curly + TextUnit::of_str("{")); 30 edit.set_cursor(l_curly + TextUnit::of_str("{"));
31 }) 31 });
32
33 ctx.build()
32} 34}
33 35
34#[cfg(test)] 36#[cfg(test)]