diff options
author | Andrea Pretto <[email protected]> | 2019-02-11 17:07:21 +0000 |
---|---|---|
committer | Andrea Pretto <[email protected]> | 2019-02-11 17:07:21 +0000 |
commit | 5c9c0d3ae2735b4b32a44742bac800ca616fdde8 (patch) | |
tree | 8ad5dc9a548915729bcc87085a1a145d4ba2a00a /crates | |
parent | af62fde57fe58f4aa06608568dc26535731800a0 (diff) |
ra_assists: assist "providers" can produce multiple assists
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/add_derive.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/add_impl.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 41 | ||||
-rw-r--r-- | crates/ra_assists/src/auto_import.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/change_visibility.rs | 20 | ||||
-rw-r--r-- | crates/ra_assists/src/fill_match_arms.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/flip_comma.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/introduce_variable.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 75 | ||||
-rw-r--r-- | crates/ra_assists/src/remove_dbg.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/replace_if_let_with_match.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/split_import.rs | 8 |
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] |
8 | join_to_string = "0.1.3" | 8 | join_to_string = "0.1.3" |
9 | itertools = "0.8.0" | ||
9 | 10 | ||
10 | ra_syntax = { path = "../ra_syntax" } | 11 | ra_syntax = { path = "../ra_syntax" } |
11 | ra_text_edit = { path = "../ra_text_edit" } | 12 | ra_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 | ||
8 | use crate::{AssistCtx, Assist}; | 8 | use crate::{AssistCtx, Assist}; |
9 | 9 | ||
10 | pub(crate) fn add_derive(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 10 | pub(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 | ||
8 | use crate::{AssistCtx, Assist}; | 8 | use crate::{AssistCtx, Assist}; |
9 | 9 | ||
10 | pub(crate) fn add_impl(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 10 | pub(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 | ||
10 | use crate::{AssistLabel, AssistAction}; | 10 | use crate::{AssistLabel, AssistAction}; |
11 | 11 | ||
12 | #[derive(Clone, Debug)] | ||
12 | pub(crate) enum Assist { | 13 | pub(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 | ||
55 | impl<'a, DB> Clone for AssistCtx<'a, DB> { | 57 | impl<'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 | ||
483 | pub(crate) fn auto_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 483 | pub(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 | ||
17 | fn add_vis(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 17 | fn 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 | ||
51 | fn vis_offset(node: &SyntaxNode) -> TextUnit { | 53 | fn 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 | ||
62 | fn change_vis(ctx: AssistCtx<impl HirDatabase>, vis: &ast::Visibility) -> Option<Assist> { | 64 | fn 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 | ||
9 | use crate::{AssistCtx, Assist}; | 9 | use crate::{AssistCtx, Assist}; |
10 | 10 | ||
11 | pub(crate) fn fill_match_arms(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 11 | pub(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 | ||
7 | use crate::{AssistCtx, Assist, non_trivia_sibling}; | 7 | use crate::{AssistCtx, Assist, non_trivia_sibling}; |
8 | 8 | ||
9 | pub(crate) fn flip_comma(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 9 | pub(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 | ||
9 | use crate::{AssistCtx, Assist}; | 9 | use crate::{AssistCtx, Assist}; |
10 | 10 | ||
11 | pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 11 | pub(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 | ||
74 | fn valid_covering_node(node: &SyntaxNode) -> bool { | 76 | fn 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 | ||
8 | mod assist_ctx; | 8 | mod assist_ctx; |
9 | 9 | ||
10 | use itertools::Itertools; | ||
11 | |||
10 | use ra_text_edit::TextEdit; | 12 | use ra_text_edit::TextEdit; |
11 | use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction}; | 13 | use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction}; |
12 | use ra_db::FileRange; | 14 | use ra_db::FileRange; |
@@ -14,12 +16,13 @@ use hir::db::HirDatabase; | |||
14 | 16 | ||
15 | pub(crate) use crate::assist_ctx::{AssistCtx, Assist}; | 17 | pub(crate) use crate::assist_ctx::{AssistCtx, Assist}; |
16 | 18 | ||
17 | #[derive(Debug)] | 19 | #[derive(Debug, Clone)] |
18 | pub struct AssistLabel { | 20 | pub 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)] | ||
23 | pub struct AssistAction { | 26 | pub 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 | }; |
9 | use crate::{AssistCtx, Assist}; | 9 | use crate::{AssistCtx, Assist}; |
10 | 10 | ||
11 | pub(crate) fn remove_dbg(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 11 | pub(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 | ||
5 | use crate::{AssistCtx, Assist}; | 5 | use crate::{AssistCtx, Assist}; |
6 | 6 | ||
7 | pub(crate) fn replace_if_let_with_match(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 7 | pub(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 | ||
26 | fn build_match_expr( | 28 | fn 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 | ||
8 | use crate::{AssistCtx, Assist}; | 8 | use crate::{AssistCtx, Assist}; |
9 | 9 | ||
10 | pub(crate) fn split_import(ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | 10 | pub(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)] |