diff options
author | Aleksey Kladov <[email protected]> | 2020-05-06 17:45:35 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-05-07 15:27:54 +0100 |
commit | 4867968d22899395e6551f22641b3617e995140c (patch) | |
tree | 4f3ab3a70fbbb901ccec3cd162da00eaa9cbad09 /crates/ra_assists | |
parent | f4cd75ac06dff7f5a95065a6c3868669e5c2ab27 (diff) |
Refactor assists API to be more convenient for adding new assists
It now duplicates completion API in its shape.
Diffstat (limited to 'crates/ra_assists')
38 files changed, 521 insertions, 593 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_context.rs index 871671de2..203ad1273 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_context.rs | |||
@@ -1,4 +1,6 @@ | |||
1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. | 1 | //! See `AssistContext` |
2 | |||
3 | use algo::find_covering_element; | ||
2 | use hir::Semantics; | 4 | use hir::Semantics; |
3 | use ra_db::{FileId, FileRange}; | 5 | use ra_db::{FileId, FileRange}; |
4 | use ra_fmt::{leading_indent, reindent}; | 6 | use ra_fmt::{leading_indent, reindent}; |
@@ -7,7 +9,7 @@ use ra_ide_db::{ | |||
7 | RootDatabase, | 9 | RootDatabase, |
8 | }; | 10 | }; |
9 | use ra_syntax::{ | 11 | use ra_syntax::{ |
10 | algo::{self, find_covering_element, find_node_at_offset, SyntaxRewriter}, | 12 | algo::{self, find_node_at_offset, SyntaxRewriter}, |
11 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, | 13 | AstNode, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, |
12 | TokenAtOffset, | 14 | TokenAtOffset, |
13 | }; | 15 | }; |
@@ -15,46 +17,17 @@ use ra_text_edit::TextEditBuilder; | |||
15 | 17 | ||
16 | use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; | 18 | use crate::{AssistId, AssistLabel, GroupLabel, ResolvedAssist}; |
17 | 19 | ||
18 | #[derive(Clone, Debug)] | 20 | /// `AssistContext` allows to apply an assist or check if it could be applied. |
19 | pub(crate) struct Assist(pub(crate) Vec<AssistInfo>); | ||
20 | |||
21 | #[derive(Clone, Debug)] | ||
22 | pub(crate) struct AssistInfo { | ||
23 | pub(crate) label: AssistLabel, | ||
24 | pub(crate) group_label: Option<GroupLabel>, | ||
25 | pub(crate) source_change: Option<SourceChange>, | ||
26 | } | ||
27 | |||
28 | impl AssistInfo { | ||
29 | fn new(label: AssistLabel) -> AssistInfo { | ||
30 | AssistInfo { label, group_label: None, source_change: None } | ||
31 | } | ||
32 | |||
33 | fn resolved(self, source_change: SourceChange) -> AssistInfo { | ||
34 | AssistInfo { source_change: Some(source_change), ..self } | ||
35 | } | ||
36 | |||
37 | fn with_group(self, group_label: GroupLabel) -> AssistInfo { | ||
38 | AssistInfo { group_label: Some(group_label), ..self } | ||
39 | } | ||
40 | |||
41 | pub(crate) fn into_resolved(self) -> Option<ResolvedAssist> { | ||
42 | let label = self.label; | ||
43 | self.source_change.map(|source_change| ResolvedAssist { label, source_change }) | ||
44 | } | ||
45 | } | ||
46 | |||
47 | /// `AssistCtx` allows to apply an assist or check if it could be applied. | ||
48 | /// | 21 | /// |
49 | /// Assists use a somewhat over-engineered approach, given the current needs. The | 22 | /// Assists use a somewhat over-engineered approach, given the current needs. |
50 | /// assists workflow consists of two phases. In the first phase, a user asks for | 23 | /// The assists workflow consists of two phases. In the first phase, a user asks |
51 | /// the list of available assists. In the second phase, the user picks a | 24 | /// for the list of available assists. In the second phase, the user picks a |
52 | /// particular assist and it gets applied. | 25 | /// particular assist and it gets applied. |
53 | /// | 26 | /// |
54 | /// There are two peculiarities here: | 27 | /// There are two peculiarities here: |
55 | /// | 28 | /// |
56 | /// * first, we ideally avoid computing more things then necessary to answer | 29 | /// * first, we ideally avoid computing more things then necessary to answer "is |
57 | /// "is assist applicable" in the first phase. | 30 | /// assist applicable" in the first phase. |
58 | /// * second, when we are applying assist, we don't have a guarantee that there | 31 | /// * second, when we are applying assist, we don't have a guarantee that there |
59 | /// weren't any changes between the point when user asked for assists and when | 32 | /// weren't any changes between the point when user asked for assists and when |
60 | /// they applied a particular assist. So, when applying assist, we need to do | 33 | /// they applied a particular assist. So, when applying assist, we need to do |
@@ -63,152 +36,158 @@ impl AssistInfo { | |||
63 | /// To avoid repeating the same code twice for both "check" and "apply" | 36 | /// To avoid repeating the same code twice for both "check" and "apply" |
64 | /// functions, we use an approach reminiscent of that of Django's function based | 37 | /// functions, we use an approach reminiscent of that of Django's function based |
65 | /// views dealing with forms. Each assist receives a runtime parameter, | 38 | /// views dealing with forms. Each assist receives a runtime parameter, |
66 | /// `should_compute_edit`. It first check if an edit is applicable (potentially | 39 | /// `resolve`. It first check if an edit is applicable (potentially computing |
67 | /// computing info required to compute the actual edit). If it is applicable, | 40 | /// info required to compute the actual edit). If it is applicable, and |
68 | /// and `should_compute_edit` is `true`, it then computes the actual edit. | 41 | /// `resolve` is `true`, it then computes the actual edit. |
69 | /// | 42 | /// |
70 | /// So, to implement the original assists workflow, we can first apply each edit | 43 | /// So, to implement the original assists workflow, we can first apply each edit |
71 | /// with `should_compute_edit = false`, and then applying the selected edit | 44 | /// with `resolve = false`, and then applying the selected edit again, with |
72 | /// again, with `should_compute_edit = true` this time. | 45 | /// `resolve = true` this time. |
73 | /// | 46 | /// |
74 | /// Note, however, that we don't actually use such two-phase logic at the | 47 | /// Note, however, that we don't actually use such two-phase logic at the |
75 | /// moment, because the LSP API is pretty awkward in this place, and it's much | 48 | /// moment, because the LSP API is pretty awkward in this place, and it's much |
76 | /// easier to just compute the edit eagerly :-) | 49 | /// easier to just compute the edit eagerly :-) |
77 | #[derive(Clone)] | 50 | pub(crate) struct AssistContext<'a> { |
78 | pub(crate) struct AssistCtx<'a> { | 51 | pub(crate) sema: Semantics<'a, RootDatabase>, |
79 | pub(crate) sema: &'a Semantics<'a, RootDatabase>, | 52 | pub(super) db: &'a RootDatabase, |
80 | pub(crate) db: &'a RootDatabase, | ||
81 | pub(crate) frange: FileRange, | 53 | pub(crate) frange: FileRange, |
82 | source_file: SourceFile, | 54 | source_file: SourceFile, |
83 | should_compute_edit: bool, | ||
84 | } | 55 | } |
85 | 56 | ||
86 | impl<'a> AssistCtx<'a> { | 57 | impl<'a> AssistContext<'a> { |
87 | pub fn new( | 58 | pub fn new(sema: Semantics<'a, RootDatabase>, frange: FileRange) -> AssistContext<'a> { |
88 | sema: &'a Semantics<'a, RootDatabase>, | ||
89 | frange: FileRange, | ||
90 | should_compute_edit: bool, | ||
91 | ) -> AssistCtx<'a> { | ||
92 | let source_file = sema.parse(frange.file_id); | 59 | let source_file = sema.parse(frange.file_id); |
93 | AssistCtx { sema, db: sema.db, frange, source_file, should_compute_edit } | 60 | let db = sema.db; |
61 | AssistContext { sema, db, frange, source_file } | ||
94 | } | 62 | } |
95 | 63 | ||
96 | pub(crate) fn add_assist( | 64 | // NB, this ignores active selection. |
97 | self, | 65 | pub(crate) fn offset(&self) -> TextSize { |
98 | id: AssistId, | 66 | self.frange.range.start() |
99 | label: impl Into<String>, | ||
100 | target: TextRange, | ||
101 | f: impl FnOnce(&mut ActionBuilder), | ||
102 | ) -> Option<Assist> { | ||
103 | let label = AssistLabel::new(id, label.into(), None, target); | ||
104 | let change_label = label.label.clone(); | ||
105 | let mut info = AssistInfo::new(label); | ||
106 | if self.should_compute_edit { | ||
107 | let source_change = { | ||
108 | let mut edit = ActionBuilder::new(&self); | ||
109 | f(&mut edit); | ||
110 | edit.build(change_label) | ||
111 | }; | ||
112 | info = info.resolved(source_change) | ||
113 | }; | ||
114 | |||
115 | Some(Assist(vec![info])) | ||
116 | } | ||
117 | |||
118 | pub(crate) fn add_assist_group(self, group_name: impl Into<String>) -> AssistGroup<'a> { | ||
119 | let group = GroupLabel(group_name.into()); | ||
120 | AssistGroup { ctx: self, group, assists: Vec::new() } | ||
121 | } | 67 | } |
122 | 68 | ||
123 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { | 69 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { |
124 | self.source_file.syntax().token_at_offset(self.frange.range.start()) | 70 | self.source_file.syntax().token_at_offset(self.offset()) |
125 | } | 71 | } |
126 | |||
127 | pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> { | 72 | pub(crate) fn find_token_at_offset(&self, kind: SyntaxKind) -> Option<SyntaxToken> { |
128 | self.token_at_offset().find(|it| it.kind() == kind) | 73 | self.token_at_offset().find(|it| it.kind() == kind) |
129 | } | 74 | } |
130 | |||
131 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { | 75 | pub(crate) fn find_node_at_offset<N: AstNode>(&self) -> Option<N> { |
132 | find_node_at_offset(self.source_file.syntax(), self.frange.range.start()) | 76 | find_node_at_offset(self.source_file.syntax(), self.offset()) |
133 | } | 77 | } |
134 | |||
135 | pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { | 78 | pub(crate) fn find_node_at_offset_with_descend<N: AstNode>(&self) -> Option<N> { |
136 | self.sema | 79 | self.sema |
137 | .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) | 80 | .find_node_at_offset_with_descend(self.source_file.syntax(), self.frange.range.start()) |
138 | } | 81 | } |
139 | |||
140 | pub(crate) fn covering_element(&self) -> SyntaxElement { | 82 | pub(crate) fn covering_element(&self) -> SyntaxElement { |
141 | find_covering_element(self.source_file.syntax(), self.frange.range) | 83 | find_covering_element(self.source_file.syntax(), self.frange.range) |
142 | } | 84 | } |
85 | // FIXME: remove | ||
143 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { | 86 | pub(crate) fn covering_node_for_range(&self, range: TextRange) -> SyntaxElement { |
144 | find_covering_element(self.source_file.syntax(), range) | 87 | find_covering_element(self.source_file.syntax(), range) |
145 | } | 88 | } |
146 | } | 89 | } |
147 | 90 | ||
148 | pub(crate) struct AssistGroup<'a> { | 91 | pub(crate) struct Assists { |
149 | ctx: AssistCtx<'a>, | 92 | resolve: bool, |
150 | group: GroupLabel, | 93 | file: FileId, |
151 | assists: Vec<AssistInfo>, | 94 | buf: Vec<(AssistLabel, Option<SourceChange>)>, |
152 | } | 95 | } |
153 | 96 | ||
154 | impl<'a> AssistGroup<'a> { | 97 | impl Assists { |
155 | pub(crate) fn add_assist( | 98 | pub(crate) fn new_resolved(ctx: &AssistContext) -> Assists { |
99 | Assists { resolve: true, file: ctx.frange.file_id, buf: Vec::new() } | ||
100 | } | ||
101 | pub(crate) fn new_unresolved(ctx: &AssistContext) -> Assists { | ||
102 | Assists { resolve: false, file: ctx.frange.file_id, buf: Vec::new() } | ||
103 | } | ||
104 | |||
105 | pub(crate) fn finish_unresolved(self) -> Vec<AssistLabel> { | ||
106 | assert!(!self.resolve); | ||
107 | self.finish() | ||
108 | .into_iter() | ||
109 | .map(|(label, edit)| { | ||
110 | assert!(edit.is_none()); | ||
111 | label | ||
112 | }) | ||
113 | .collect() | ||
114 | } | ||
115 | |||
116 | pub(crate) fn finish_resolved(self) -> Vec<ResolvedAssist> { | ||
117 | assert!(self.resolve); | ||
118 | self.finish() | ||
119 | .into_iter() | ||
120 | .map(|(label, edit)| ResolvedAssist { label, source_change: edit.unwrap() }) | ||
121 | .collect() | ||
122 | } | ||
123 | |||
124 | pub(crate) fn add( | ||
156 | &mut self, | 125 | &mut self, |
157 | id: AssistId, | 126 | id: AssistId, |
158 | label: impl Into<String>, | 127 | label: impl Into<String>, |
159 | target: TextRange, | 128 | target: TextRange, |
160 | f: impl FnOnce(&mut ActionBuilder), | 129 | f: impl FnOnce(&mut AssistBuilder), |
161 | ) { | 130 | ) -> Option<()> { |
162 | let label = AssistLabel::new(id, label.into(), Some(self.group.clone()), target); | 131 | let label = AssistLabel::new(id, label.into(), None, target); |
132 | self.add_impl(label, f) | ||
133 | } | ||
134 | pub(crate) fn add_group( | ||
135 | &mut self, | ||
136 | group: &GroupLabel, | ||
137 | id: AssistId, | ||
138 | label: impl Into<String>, | ||
139 | target: TextRange, | ||
140 | f: impl FnOnce(&mut AssistBuilder), | ||
141 | ) -> Option<()> { | ||
142 | let label = AssistLabel::new(id, label.into(), Some(group.clone()), target); | ||
143 | self.add_impl(label, f) | ||
144 | } | ||
145 | fn add_impl(&mut self, label: AssistLabel, f: impl FnOnce(&mut AssistBuilder)) -> Option<()> { | ||
163 | let change_label = label.label.clone(); | 146 | let change_label = label.label.clone(); |
164 | let mut info = AssistInfo::new(label).with_group(self.group.clone()); | 147 | let source_change = if self.resolve { |
165 | if self.ctx.should_compute_edit { | 148 | let mut builder = AssistBuilder::new(self.file); |
166 | let source_change = { | 149 | f(&mut builder); |
167 | let mut edit = ActionBuilder::new(&self.ctx); | 150 | Some(builder.finish(change_label)) |
168 | f(&mut edit); | 151 | } else { |
169 | edit.build(change_label) | 152 | None |
170 | }; | ||
171 | info = info.resolved(source_change) | ||
172 | }; | 153 | }; |
173 | 154 | ||
174 | self.assists.push(info) | 155 | self.buf.push((label, source_change)); |
156 | Some(()) | ||
175 | } | 157 | } |
176 | 158 | ||
177 | pub(crate) fn finish(self) -> Option<Assist> { | 159 | fn finish(mut self) -> Vec<(AssistLabel, Option<SourceChange>)> { |
178 | if self.assists.is_empty() { | 160 | self.buf.sort_by_key(|(label, _edit)| label.target.len()); |
179 | None | 161 | self.buf |
180 | } else { | ||
181 | Some(Assist(self.assists)) | ||
182 | } | ||
183 | } | 162 | } |
184 | } | 163 | } |
185 | 164 | ||
186 | pub(crate) struct ActionBuilder<'a, 'b> { | 165 | pub(crate) struct AssistBuilder { |
187 | edit: TextEditBuilder, | 166 | edit: TextEditBuilder, |
188 | cursor_position: Option<TextSize>, | 167 | cursor_position: Option<TextSize>, |
189 | file: FileId, | 168 | file: FileId, |
190 | ctx: &'a AssistCtx<'b>, | ||
191 | } | 169 | } |
192 | 170 | ||
193 | impl<'a, 'b> ActionBuilder<'a, 'b> { | 171 | impl AssistBuilder { |
194 | fn new(ctx: &'a AssistCtx<'b>) -> Self { | 172 | pub(crate) fn new(file: FileId) -> AssistBuilder { |
195 | Self { | 173 | AssistBuilder { edit: TextEditBuilder::default(), cursor_position: None, file } |
196 | edit: TextEditBuilder::default(), | ||
197 | cursor_position: None, | ||
198 | file: ctx.frange.file_id, | ||
199 | ctx, | ||
200 | } | ||
201 | } | 174 | } |
202 | 175 | ||
203 | pub(crate) fn ctx(&self) -> &AssistCtx<'b> { | 176 | /// Remove specified `range` of text. |
204 | &self.ctx | 177 | pub(crate) fn delete(&mut self, range: TextRange) { |
178 | self.edit.delete(range) | ||
179 | } | ||
180 | /// Append specified `text` at the given `offset` | ||
181 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { | ||
182 | self.edit.insert(offset, text.into()) | ||
205 | } | 183 | } |
206 | |||
207 | /// Replaces specified `range` of text with a given string. | 184 | /// Replaces specified `range` of text with a given string. |
208 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { | 185 | pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into<String>) { |
209 | self.edit.replace(range, replace_with.into()) | 186 | self.edit.replace(range, replace_with.into()) |
210 | } | 187 | } |
211 | 188 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | |
189 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | ||
190 | } | ||
212 | /// Replaces specified `node` of text with a given string, reindenting the | 191 | /// Replaces specified `node` of text with a given string, reindenting the |
213 | /// string to maintain `node`'s existing indent. | 192 | /// string to maintain `node`'s existing indent. |
214 | // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent | 193 | // FIXME: remove in favor of ra_syntax::edit::IndentLevel::increase_indent |
@@ -223,42 +202,28 @@ impl<'a, 'b> ActionBuilder<'a, 'b> { | |||
223 | } | 202 | } |
224 | self.replace(node.text_range(), replace_with) | 203 | self.replace(node.text_range(), replace_with) |
225 | } | 204 | } |
226 | 205 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | |
227 | /// Remove specified `range` of text. | 206 | let node = rewriter.rewrite_root().unwrap(); |
228 | #[allow(unused)] | 207 | let new = rewriter.rewrite(&node); |
229 | pub(crate) fn delete(&mut self, range: TextRange) { | 208 | algo::diff(&node, &new).into_text_edit(&mut self.edit) |
230 | self.edit.delete(range) | ||
231 | } | ||
232 | |||
233 | /// Append specified `text` at the given `offset` | ||
234 | pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into<String>) { | ||
235 | self.edit.insert(offset, text.into()) | ||
236 | } | 209 | } |
237 | 210 | ||
238 | /// Specify desired position of the cursor after the assist is applied. | 211 | /// Specify desired position of the cursor after the assist is applied. |
239 | pub(crate) fn set_cursor(&mut self, offset: TextSize) { | 212 | pub(crate) fn set_cursor(&mut self, offset: TextSize) { |
240 | self.cursor_position = Some(offset) | 213 | self.cursor_position = Some(offset) |
241 | } | 214 | } |
215 | // FIXME: better API | ||
216 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | ||
217 | self.file = assist_file; | ||
218 | } | ||
242 | 219 | ||
220 | // FIXME: kill this API | ||
243 | /// Get access to the raw `TextEditBuilder`. | 221 | /// Get access to the raw `TextEditBuilder`. |
244 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { | 222 | pub(crate) fn text_edit_builder(&mut self) -> &mut TextEditBuilder { |
245 | &mut self.edit | 223 | &mut self.edit |
246 | } | 224 | } |
247 | 225 | ||
248 | pub(crate) fn replace_ast<N: AstNode>(&mut self, old: N, new: N) { | 226 | fn finish(self, change_label: String) -> SourceChange { |
249 | algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) | ||
250 | } | ||
251 | pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { | ||
252 | let node = rewriter.rewrite_root().unwrap(); | ||
253 | let new = rewriter.rewrite(&node); | ||
254 | algo::diff(&node, &new).into_text_edit(&mut self.edit) | ||
255 | } | ||
256 | |||
257 | pub(crate) fn set_file(&mut self, assist_file: FileId) { | ||
258 | self.file = assist_file; | ||
259 | } | ||
260 | |||
261 | fn build(self, change_label: String) -> SourceChange { | ||
262 | let edit = self.edit.finish(); | 227 | let edit = self.edit.finish(); |
263 | if edit.is_empty() && self.cursor_position.is_none() { | 228 | if edit.is_empty() && self.cursor_position.is_none() { |
264 | panic!("Only call `add_assist` if the assist can be applied") | 229 | panic!("Only call `add_assist` if the assist can be applied") |
diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 869d4dc04..795a225a4 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs | |||
@@ -6,7 +6,10 @@ use ra_syntax::{ | |||
6 | }; | 6 | }; |
7 | use stdx::SepBy; | 7 | use stdx::SepBy; |
8 | 8 | ||
9 | use crate::{Assist, AssistCtx, AssistId}; | 9 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | ||
11 | AssistId, | ||
12 | }; | ||
10 | 13 | ||
11 | // Assist: add_custom_impl | 14 | // Assist: add_custom_impl |
12 | // | 15 | // |
@@ -25,7 +28,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
25 | // | 28 | // |
26 | // } | 29 | // } |
27 | // ``` | 30 | // ``` |
28 | pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { | 31 | pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
29 | let input = ctx.find_node_at_offset::<ast::AttrInput>()?; | 32 | let input = ctx.find_node_at_offset::<ast::AttrInput>()?; |
30 | let attr = input.syntax().parent().and_then(ast::Attr::cast)?; | 33 | let attr = input.syntax().parent().and_then(ast::Attr::cast)?; |
31 | 34 | ||
@@ -49,7 +52,7 @@ pub(crate) fn add_custom_impl(ctx: AssistCtx) -> Option<Assist> { | |||
49 | format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); | 52 | format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); |
50 | 53 | ||
51 | let target = attr.syntax().text_range(); | 54 | let target = attr.syntax().text_range(); |
52 | ctx.add_assist(AssistId("add_custom_impl"), label, target, |edit| { | 55 | acc.add(AssistId("add_custom_impl"), label, target, |edit| { |
53 | let new_attr_input = input | 56 | let new_attr_input = input |
54 | .syntax() | 57 | .syntax() |
55 | .descendants_with_tokens() | 58 | .descendants_with_tokens() |
diff --git a/crates/ra_assists/src/handlers/add_derive.rs b/crates/ra_assists/src/handlers/add_derive.rs index 2a6bb1cae..fb08c19e9 100644 --- a/crates/ra_assists/src/handlers/add_derive.rs +++ b/crates/ra_assists/src/handlers/add_derive.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextSize, | 4 | TextSize, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: add_derive | 9 | // Assist: add_derive |
10 | // | 10 | // |
@@ -24,11 +24,11 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
24 | // y: u32, | 24 | // y: u32, |
25 | // } | 25 | // } |
26 | // ``` | 26 | // ``` |
27 | pub(crate) fn add_derive(ctx: AssistCtx) -> Option<Assist> { | 27 | pub(crate) fn add_derive(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
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 | let target = nominal.syntax().text_range(); | 30 | let target = nominal.syntax().text_range(); |
31 | ctx.add_assist(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { | 31 | acc.add(AssistId("add_derive"), "Add `#[derive]`", target, |edit| { |
32 | let derive_attr = nominal | 32 | let derive_attr = nominal |
33 | .attrs() | 33 | .attrs() |
34 | .filter_map(|x| x.as_simple_call()) | 34 | .filter_map(|x| x.as_simple_call()) |
@@ -57,9 +57,10 @@ fn derive_insertion_offset(nominal: &ast::NominalDef) -> Option<TextSize> { | |||
57 | 57 | ||
58 | #[cfg(test)] | 58 | #[cfg(test)] |
59 | mod tests { | 59 | mod tests { |
60 | use super::*; | ||
61 | use crate::tests::{check_assist, check_assist_target}; | 60 | use crate::tests::{check_assist, check_assist_target}; |
62 | 61 | ||
62 | use super::*; | ||
63 | |||
63 | #[test] | 64 | #[test] |
64 | fn add_derive_new() { | 65 | fn add_derive_new() { |
65 | check_assist( | 66 | check_assist( |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index a59ec16b2..55409e501 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextRange, | 4 | TextRange, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: add_explicit_type | 9 | // Assist: add_explicit_type |
10 | // | 10 | // |
@@ -21,7 +21,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
21 | // let x: i32 = 92; | 21 | // let x: i32 = 92; |
22 | // } | 22 | // } |
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | 24 | pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; | 25 | let stmt = ctx.find_node_at_offset::<LetStmt>()?; |
26 | let expr = stmt.initializer()?; | 26 | let expr = stmt.initializer()?; |
27 | let pat = stmt.pat()?; | 27 | let pat = stmt.pat()?; |
@@ -59,7 +59,7 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx) -> Option<Assist> { | |||
59 | 59 | ||
60 | let db = ctx.db; | 60 | let db = ctx.db; |
61 | let new_type_string = ty.display_truncated(db, None).to_string(); | 61 | let new_type_string = ty.display_truncated(db, None).to_string(); |
62 | ctx.add_assist( | 62 | acc.add( |
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 | pat_range, |
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 81deb3dfa..275184e24 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 | |||
@@ -4,10 +4,10 @@ use ra_syntax::{ | |||
4 | TextSize, | 4 | TextSize, |
5 | }; | 5 | }; |
6 | use stdx::format_to; | 6 | use stdx::format_to; |
7 | |||
8 | use crate::{utils::FamousDefs, Assist, AssistCtx, AssistId}; | ||
9 | use test_utils::tested_by; | 7 | use test_utils::tested_by; |
10 | 8 | ||
9 | use crate::{utils::FamousDefs, AssistContext, AssistId, Assists}; | ||
10 | |||
11 | // Assist add_from_impl_for_enum | 11 | // Assist add_from_impl_for_enum |
12 | // | 12 | // |
13 | // Adds a From impl for an enum variant with one tuple field | 13 | // Adds a From impl for an enum variant with one tuple field |
@@ -25,7 +25,7 @@ use test_utils::tested_by; | |||
25 | // } | 25 | // } |
26 | // } | 26 | // } |
27 | // ``` | 27 | // ``` |
28 | pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { | 28 | pub(crate) fn add_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
29 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; | 29 | let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?; |
30 | let variant_name = variant.name()?; | 30 | let variant_name = variant.name()?; |
31 | let enum_name = variant.parent_enum().name()?; | 31 | let enum_name = variant.parent_enum().name()?; |
@@ -42,13 +42,13 @@ pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> { | |||
42 | _ => return None, | 42 | _ => return None, |
43 | }; | 43 | }; |
44 | 44 | ||
45 | if existing_from_impl(ctx.sema, &variant).is_some() { | 45 | if existing_from_impl(&ctx.sema, &variant).is_some() { |
46 | tested_by!(test_add_from_impl_already_exists); | 46 | tested_by!(test_add_from_impl_already_exists); |
47 | return None; | 47 | return None; |
48 | } | 48 | } |
49 | 49 | ||
50 | let target = variant.syntax().text_range(); | 50 | let target = variant.syntax().text_range(); |
51 | ctx.add_assist( | 51 | acc.add( |
52 | AssistId("add_from_impl_for_enum"), | 52 | AssistId("add_from_impl_for_enum"), |
53 | "Add From impl for this enum variant", | 53 | "Add From impl for this enum variant", |
54 | target, | 54 | target, |
diff --git a/crates/ra_assists/src/handlers/add_function.rs b/crates/ra_assists/src/handlers/add_function.rs index 278079665..6b5616aa9 100644 --- a/crates/ra_assists/src/handlers/add_function.rs +++ b/crates/ra_assists/src/handlers/add_function.rs | |||
@@ -1,14 +1,13 @@ | |||
1 | use hir::HirDisplay; | ||
2 | use ra_db::FileId; | ||
1 | use ra_syntax::{ | 3 | use ra_syntax::{ |
2 | ast::{self, AstNode}, | 4 | ast::{self, edit::IndentLevel, ArgListOwner, AstNode, ModuleItemOwner}, |
3 | SyntaxKind, SyntaxNode, TextSize, | 5 | SyntaxKind, SyntaxNode, TextSize, |
4 | }; | 6 | }; |
5 | |||
6 | use crate::{Assist, AssistCtx, AssistId}; | ||
7 | use ast::{edit::IndentLevel, ArgListOwner, ModuleItemOwner}; | ||
8 | use hir::HirDisplay; | ||
9 | use ra_db::FileId; | ||
10 | use rustc_hash::{FxHashMap, FxHashSet}; | 7 | use rustc_hash::{FxHashMap, FxHashSet}; |
11 | 8 | ||
9 | use crate::{AssistContext, AssistId, Assists}; | ||
10 | |||
12 | // Assist: add_function | 11 | // Assist: add_function |
13 | // | 12 | // |
14 | // Adds a stub function with a signature matching the function under the cursor. | 13 | // Adds a stub function with a signature matching the function under the cursor. |
@@ -34,7 +33,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; | |||
34 | // } | 33 | // } |
35 | // | 34 | // |
36 | // ``` | 35 | // ``` |
37 | pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | 36 | pub(crate) fn add_function(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
38 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; | 37 | let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; |
39 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; | 38 | let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?; |
40 | let path = path_expr.path()?; | 39 | let path = path_expr.path()?; |
@@ -59,7 +58,7 @@ pub(crate) fn add_function(ctx: AssistCtx) -> Option<Assist> { | |||
59 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; | 58 | let function_builder = FunctionBuilder::from_call(&ctx, &call, &path, target_module)?; |
60 | 59 | ||
61 | let target = call.syntax().text_range(); | 60 | let target = call.syntax().text_range(); |
62 | ctx.add_assist(AssistId("add_function"), "Add function", target, |edit| { | 61 | acc.add(AssistId("add_function"), "Add function", target, |edit| { |
63 | let function_template = function_builder.render(); | 62 | let function_template = function_builder.render(); |
64 | edit.set_file(function_template.file); | 63 | edit.set_file(function_template.file); |
65 | edit.set_cursor(function_template.cursor_offset); | 64 | edit.set_cursor(function_template.cursor_offset); |
@@ -87,7 +86,7 @@ impl FunctionBuilder { | |||
87 | /// Prepares a generated function that matches `call` in `generate_in` | 86 | /// Prepares a generated function that matches `call` in `generate_in` |
88 | /// (or as close to `call` as possible, if `generate_in` is `None`) | 87 | /// (or as close to `call` as possible, if `generate_in` is `None`) |
89 | fn from_call( | 88 | fn from_call( |
90 | ctx: &AssistCtx, | 89 | ctx: &AssistContext, |
91 | call: &ast::CallExpr, | 90 | call: &ast::CallExpr, |
92 | path: &ast::Path, | 91 | path: &ast::Path, |
93 | target_module: Option<hir::InFile<hir::ModuleSource>>, | 92 | target_module: Option<hir::InFile<hir::ModuleSource>>, |
@@ -152,7 +151,7 @@ fn fn_name(call: &ast::Path) -> Option<ast::Name> { | |||
152 | 151 | ||
153 | /// Computes the type variables and arguments required for the generated function | 152 | /// Computes the type variables and arguments required for the generated function |
154 | fn fn_args( | 153 | fn fn_args( |
155 | ctx: &AssistCtx, | 154 | ctx: &AssistContext, |
156 | call: &ast::CallExpr, | 155 | call: &ast::CallExpr, |
157 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { | 156 | ) -> Option<(Option<ast::TypeParamList>, ast::ParamList)> { |
158 | let mut arg_names = Vec::new(); | 157 | let mut arg_names = Vec::new(); |
@@ -219,7 +218,7 @@ fn fn_arg_name(fn_arg: &ast::Expr) -> Option<String> { | |||
219 | } | 218 | } |
220 | } | 219 | } |
221 | 220 | ||
222 | fn fn_arg_type(ctx: &AssistCtx, fn_arg: &ast::Expr) -> Option<String> { | 221 | fn fn_arg_type(ctx: &AssistContext, fn_arg: &ast::Expr) -> Option<String> { |
223 | let ty = ctx.sema.type_of_expr(fn_arg)?; | 222 | let ty = ctx.sema.type_of_expr(fn_arg)?; |
224 | if ty.is_unknown() { | 223 | if ty.is_unknown() { |
225 | return None; | 224 | return None; |
diff --git a/crates/ra_assists/src/handlers/add_impl.rs b/crates/ra_assists/src/handlers/add_impl.rs index 557344ebb..df114a0d8 100644 --- a/crates/ra_assists/src/handlers/add_impl.rs +++ b/crates/ra_assists/src/handlers/add_impl.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | }; | 4 | }; |
5 | use stdx::{format_to, SepBy}; | 5 | use stdx::{format_to, SepBy}; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: add_impl | 9 | // Assist: add_impl |
10 | // | 10 | // |
@@ -25,43 +25,36 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
25 | // | 25 | // |
26 | // } | 26 | // } |
27 | // ``` | 27 | // ``` |
28 | pub(crate) fn add_impl(ctx: AssistCtx) -> Option<Assist> { | 28 | pub(crate) fn add_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; | 29 | let nominal = ctx.find_node_at_offset::<ast::NominalDef>()?; |
30 | let name = nominal.name()?; | 30 | let name = nominal.name()?; |
31 | let target = nominal.syntax().text_range(); | 31 | let target = nominal.syntax().text_range(); |
32 | ctx.add_assist( | 32 | acc.add(AssistId("add_impl"), format!("Implement {}", name.text().as_str()), target, |edit| { |
33 | AssistId("add_impl"), | 33 | let type_params = nominal.type_param_list(); |
34 | format!("Implement {}", name.text().as_str()), | 34 | let start_offset = nominal.syntax().text_range().end(); |
35 | target, | 35 | let mut buf = String::new(); |
36 | |edit| { | 36 | buf.push_str("\n\nimpl"); |
37 | let type_params = nominal.type_param_list(); | 37 | if let Some(type_params) = &type_params { |
38 | let start_offset = nominal.syntax().text_range().end(); | 38 | format_to!(buf, "{}", type_params.syntax()); |
39 | let mut buf = String::new(); | 39 | } |
40 | buf.push_str("\n\nimpl"); | 40 | buf.push_str(" "); |
41 | if let Some(type_params) = &type_params { | 41 | buf.push_str(name.text().as_str()); |
42 | format_to!(buf, "{}", type_params.syntax()); | 42 | if let Some(type_params) = type_params { |
43 | } | 43 | let lifetime_params = type_params |
44 | buf.push_str(" "); | 44 | .lifetime_params() |
45 | buf.push_str(name.text().as_str()); | 45 | .filter_map(|it| it.lifetime_token()) |
46 | if let Some(type_params) = type_params { | 46 | .map(|it| it.text().clone()); |
47 | let lifetime_params = type_params | 47 | let type_params = |
48 | .lifetime_params() | 48 | type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); |
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()); | ||
55 | 49 | ||
56 | let generic_params = lifetime_params.chain(type_params).sep_by(", "); | 50 | let generic_params = lifetime_params.chain(type_params).sep_by(", "); |
57 | format_to!(buf, "<{}>", generic_params) | 51 | format_to!(buf, "<{}>", generic_params) |
58 | } | 52 | } |
59 | buf.push_str(" {\n"); | 53 | buf.push_str(" {\n"); |
60 | edit.set_cursor(start_offset + TextSize::of(&buf)); | 54 | edit.set_cursor(start_offset + TextSize::of(&buf)); |
61 | buf.push_str("\n}"); | 55 | buf.push_str("\n}"); |
62 | edit.insert(start_offset, buf); | 56 | edit.insert(start_offset, buf); |
63 | }, | 57 | }) |
64 | ) | ||
65 | } | 58 | } |
66 | 59 | ||
67 | #[cfg(test)] | 60 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs index 7df786590..3482a75bf 100644 --- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs | |||
@@ -9,9 +9,10 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | assist_context::{AssistContext, Assists}, | ||
12 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, | 13 | ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams}, |
13 | utils::{get_missing_assoc_items, resolve_target_trait}, | 14 | utils::{get_missing_assoc_items, resolve_target_trait}, |
14 | Assist, AssistCtx, AssistId, | 15 | AssistId, |
15 | }; | 16 | }; |
16 | 17 | ||
17 | #[derive(PartialEq)] | 18 | #[derive(PartialEq)] |
@@ -50,8 +51,9 @@ enum AddMissingImplMembersMode { | |||
50 | // | 51 | // |
51 | // } | 52 | // } |
52 | // ``` | 53 | // ``` |
53 | pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> { | 54 | pub(crate) fn add_missing_impl_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
54 | add_missing_impl_members_inner( | 55 | add_missing_impl_members_inner( |
56 | acc, | ||
55 | ctx, | 57 | ctx, |
56 | AddMissingImplMembersMode::NoDefaultMethods, | 58 | AddMissingImplMembersMode::NoDefaultMethods, |
57 | "add_impl_missing_members", | 59 | "add_impl_missing_members", |
@@ -91,8 +93,9 @@ pub(crate) fn add_missing_impl_members(ctx: AssistCtx) -> Option<Assist> { | |||
91 | // | 93 | // |
92 | // } | 94 | // } |
93 | // ``` | 95 | // ``` |
94 | pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> { | 96 | pub(crate) fn add_missing_default_members(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
95 | add_missing_impl_members_inner( | 97 | add_missing_impl_members_inner( |
98 | acc, | ||
96 | ctx, | 99 | ctx, |
97 | AddMissingImplMembersMode::DefaultMethodsOnly, | 100 | AddMissingImplMembersMode::DefaultMethodsOnly, |
98 | "add_impl_default_members", | 101 | "add_impl_default_members", |
@@ -101,11 +104,12 @@ pub(crate) fn add_missing_default_members(ctx: AssistCtx) -> Option<Assist> { | |||
101 | } | 104 | } |
102 | 105 | ||
103 | fn add_missing_impl_members_inner( | 106 | fn add_missing_impl_members_inner( |
104 | ctx: AssistCtx, | 107 | acc: &mut Assists, |
108 | ctx: &AssistContext, | ||
105 | mode: AddMissingImplMembersMode, | 109 | mode: AddMissingImplMembersMode, |
106 | assist_id: &'static str, | 110 | assist_id: &'static str, |
107 | label: &'static str, | 111 | label: &'static str, |
108 | ) -> Option<Assist> { | 112 | ) -> Option<()> { |
109 | let _p = ra_prof::profile("add_missing_impl_members_inner"); | 113 | let _p = ra_prof::profile("add_missing_impl_members_inner"); |
110 | let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?; | 114 | let impl_def = ctx.find_node_at_offset::<ast::ImplDef>()?; |
111 | let impl_item_list = impl_def.item_list()?; | 115 | let impl_item_list = impl_def.item_list()?; |
@@ -142,12 +146,11 @@ fn add_missing_impl_members_inner( | |||
142 | return None; | 146 | return None; |
143 | } | 147 | } |
144 | 148 | ||
145 | let sema = ctx.sema; | ||
146 | let target = impl_def.syntax().text_range(); | 149 | let target = impl_def.syntax().text_range(); |
147 | ctx.add_assist(AssistId(assist_id), label, target, |edit| { | 150 | acc.add(AssistId(assist_id), label, target, |edit| { |
148 | let n_existing_items = impl_item_list.assoc_items().count(); | 151 | let n_existing_items = impl_item_list.assoc_items().count(); |
149 | let source_scope = sema.scope_for_def(trait_); | 152 | let source_scope = ctx.sema.scope_for_def(trait_); |
150 | let target_scope = sema.scope(impl_item_list.syntax()); | 153 | let target_scope = ctx.sema.scope(impl_item_list.syntax()); |
151 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) | 154 | let ast_transform = QualifyPaths::new(&target_scope, &source_scope) |
152 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def)); | 155 | .or(SubstituteTypeParams::for_trait_impl(&source_scope, trait_, impl_def)); |
153 | let items = missing_items | 156 | let items = missing_items |
@@ -170,13 +173,12 @@ fn add_missing_impl_members_inner( | |||
170 | } | 173 | } |
171 | 174 | ||
172 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { | 175 | fn add_body(fn_def: ast::FnDef) -> ast::FnDef { |
173 | if fn_def.body().is_none() { | 176 | if fn_def.body().is_some() { |
174 | let body = make::block_expr(None, Some(make::expr_todo())); | 177 | return fn_def; |
175 | let body = IndentLevel(1).increase_indent(body); | ||
176 | fn_def.with_body(body) | ||
177 | } else { | ||
178 | fn_def | ||
179 | } | 178 | } |
179 | let body = make::block_expr(None, Some(make::expr_todo())); | ||
180 | let body = IndentLevel(1).increase_indent(body); | ||
181 | fn_def.with_body(body) | ||
180 | } | 182 | } |
181 | 183 | ||
182 | #[cfg(test)] | 184 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/add_new.rs b/crates/ra_assists/src/handlers/add_new.rs index 1c3f8435a..fe7451dcf 100644 --- a/crates/ra_assists/src/handlers/add_new.rs +++ b/crates/ra_assists/src/handlers/add_new.rs | |||
@@ -7,7 +7,7 @@ use ra_syntax::{ | |||
7 | }; | 7 | }; |
8 | use stdx::{format_to, SepBy}; | 8 | use stdx::{format_to, SepBy}; |
9 | 9 | ||
10 | use crate::{Assist, AssistCtx, AssistId}; | 10 | use crate::{AssistContext, AssistId, Assists}; |
11 | 11 | ||
12 | // Assist: add_new | 12 | // Assist: add_new |
13 | // | 13 | // |
@@ -29,7 +29,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
29 | // } | 29 | // } |
30 | // | 30 | // |
31 | // ``` | 31 | // ``` |
32 | pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> { | 32 | pub(crate) fn add_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
33 | let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; | 33 | let strukt = ctx.find_node_at_offset::<ast::StructDef>()?; |
34 | 34 | ||
35 | // We want to only apply this to non-union structs with named fields | 35 | // We want to only apply this to non-union structs with named fields |
@@ -42,7 +42,7 @@ pub(crate) fn add_new(ctx: AssistCtx) -> Option<Assist> { | |||
42 | let impl_def = find_struct_impl(&ctx, &strukt)?; | 42 | let impl_def = find_struct_impl(&ctx, &strukt)?; |
43 | 43 | ||
44 | let target = strukt.syntax().text_range(); | 44 | let target = strukt.syntax().text_range(); |
45 | ctx.add_assist(AssistId("add_new"), "Add default constructor", target, |edit| { | 45 | acc.add(AssistId("add_new"), "Add default constructor", target, |edit| { |
46 | let mut buf = String::with_capacity(512); | 46 | let mut buf = String::with_capacity(512); |
47 | 47 | ||
48 | if impl_def.is_some() { | 48 | if impl_def.is_some() { |
@@ -123,7 +123,7 @@ fn generate_impl_text(strukt: &ast::StructDef, code: &str) -> String { | |||
123 | // | 123 | // |
124 | // FIXME: change the new fn checking to a more semantic approach when that's more | 124 | // FIXME: change the new fn checking to a more semantic approach when that's more |
125 | // viable (e.g. we process proc macros, etc) | 125 | // viable (e.g. we process proc macros, etc) |
126 | fn find_struct_impl(ctx: &AssistCtx, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { | 126 | fn find_struct_impl(ctx: &AssistContext, strukt: &ast::StructDef) -> Option<Option<ast::ImplDef>> { |
127 | let db = ctx.db; | 127 | let db = ctx.db; |
128 | let module = strukt.syntax().ancestors().find(|node| { | 128 | let module = strukt.syntax().ancestors().find(|node| { |
129 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) | 129 | ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind()) |
diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index a5b26e5b9..0feba5e11 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::ast::{self, AstNode}; | 1 | use ra_syntax::ast::{self, AstNode}; |
2 | 2 | ||
3 | use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; | 3 | use crate::{utils::invert_boolean_expression, AssistContext, AssistId, Assists}; |
4 | 4 | ||
5 | // Assist: apply_demorgan | 5 | // Assist: apply_demorgan |
6 | // | 6 | // |
@@ -21,7 +21,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; | |||
21 | // if !(x == 4 && y) {} | 21 | // if !(x == 4 && y) {} |
22 | // } | 22 | // } |
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> { | 24 | pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let expr = ctx.find_node_at_offset::<ast::BinExpr>()?; | 25 | let expr = ctx.find_node_at_offset::<ast::BinExpr>()?; |
26 | let op = expr.op_kind()?; | 26 | let op = expr.op_kind()?; |
27 | let op_range = expr.op_token()?.text_range(); | 27 | let op_range = expr.op_token()?.text_range(); |
@@ -39,7 +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", op_range, |edit| { | 42 | acc.add(AssistId("apply_demorgan"), "Apply De Morgan's law", op_range, |edit| { |
43 | edit.replace(op_range, opposite_op); | 43 | edit.replace(op_range, opposite_op); |
44 | edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); | 44 | edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); |
45 | edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); | 45 | edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); |
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs index 2224b9714..78d23150d 100644 --- a/crates/ra_assists/src/handlers/auto_import.rs +++ b/crates/ra_assists/src/handlers/auto_import.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | use std::collections::BTreeSet; | 1 | use std::collections::BTreeSet; |
2 | 2 | ||
3 | use either::Either; | ||
3 | use hir::{ | 4 | use hir::{ |
4 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, | 5 | AsAssocItem, AssocItemContainer, ModPath, Module, ModuleDef, PathResolution, Semantics, Trait, |
5 | Type, | 6 | Type, |
@@ -12,12 +13,7 @@ use ra_syntax::{ | |||
12 | }; | 13 | }; |
13 | use rustc_hash::FxHashSet; | 14 | use rustc_hash::FxHashSet; |
14 | 15 | ||
15 | use crate::{ | 16 | use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists, GroupLabel}; |
16 | assist_ctx::{Assist, AssistCtx}, | ||
17 | utils::insert_use_statement, | ||
18 | AssistId, | ||
19 | }; | ||
20 | use either::Either; | ||
21 | 17 | ||
22 | // Assist: auto_import | 18 | // Assist: auto_import |
23 | // | 19 | // |
@@ -38,7 +34,7 @@ use either::Either; | |||
38 | // } | 34 | // } |
39 | // # pub mod std { pub mod collections { pub struct HashMap { } } } | 35 | // # pub mod std { pub mod collections { pub struct HashMap { } } } |
40 | // ``` | 36 | // ``` |
41 | pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | 37 | pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
42 | let auto_import_assets = AutoImportAssets::new(&ctx)?; | 38 | let auto_import_assets = AutoImportAssets::new(&ctx)?; |
43 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); | 39 | let proposed_imports = auto_import_assets.search_for_imports(ctx.db); |
44 | if proposed_imports.is_empty() { | 40 | if proposed_imports.is_empty() { |
@@ -46,13 +42,19 @@ pub(crate) fn auto_import(ctx: AssistCtx) -> Option<Assist> { | |||
46 | } | 42 | } |
47 | 43 | ||
48 | let range = ctx.sema.original_range(&auto_import_assets.syntax_under_caret).range; | 44 | 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()); | 45 | let group = auto_import_assets.get_import_group_message(); |
50 | for import in proposed_imports { | 46 | for import in proposed_imports { |
51 | group.add_assist(AssistId("auto_import"), format!("Import `{}`", &import), range, |edit| { | 47 | acc.add_group( |
52 | insert_use_statement(&auto_import_assets.syntax_under_caret, &import, edit); | 48 | &group, |
53 | }); | 49 | AssistId("auto_import"), |
50 | format!("Import `{}`", &import), | ||
51 | range, | ||
52 | |builder| { | ||
53 | insert_use_statement(&auto_import_assets.syntax_under_caret, &import, ctx, builder); | ||
54 | }, | ||
55 | ); | ||
54 | } | 56 | } |
55 | group.finish() | 57 | Some(()) |
56 | } | 58 | } |
57 | 59 | ||
58 | #[derive(Debug)] | 60 | #[derive(Debug)] |
@@ -63,7 +65,7 @@ struct AutoImportAssets { | |||
63 | } | 65 | } |
64 | 66 | ||
65 | impl AutoImportAssets { | 67 | impl AutoImportAssets { |
66 | fn new(ctx: &AssistCtx) -> Option<Self> { | 68 | fn new(ctx: &AssistContext) -> Option<Self> { |
67 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { | 69 | if let Some(path_under_caret) = ctx.find_node_at_offset_with_descend::<ast::Path>() { |
68 | Self::for_regular_path(path_under_caret, &ctx) | 70 | Self::for_regular_path(path_under_caret, &ctx) |
69 | } else { | 71 | } else { |
@@ -71,7 +73,7 @@ impl AutoImportAssets { | |||
71 | } | 73 | } |
72 | } | 74 | } |
73 | 75 | ||
74 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistCtx) -> Option<Self> { | 76 | fn for_method_call(method_call: ast::MethodCallExpr, ctx: &AssistContext) -> Option<Self> { |
75 | let syntax_under_caret = method_call.syntax().to_owned(); | 77 | let syntax_under_caret = method_call.syntax().to_owned(); |
76 | let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; | 78 | let module_with_name_to_import = ctx.sema.scope(&syntax_under_caret).module()?; |
77 | Some(Self { | 79 | Some(Self { |
@@ -81,7 +83,7 @@ impl AutoImportAssets { | |||
81 | }) | 83 | }) |
82 | } | 84 | } |
83 | 85 | ||
84 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistCtx) -> Option<Self> { | 86 | fn for_regular_path(path_under_caret: ast::Path, ctx: &AssistContext) -> Option<Self> { |
85 | let syntax_under_caret = path_under_caret.syntax().to_owned(); | 87 | let syntax_under_caret = path_under_caret.syntax().to_owned(); |
86 | if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { | 88 | if syntax_under_caret.ancestors().find_map(ast::UseItem::cast).is_some() { |
87 | return None; | 89 | return None; |
@@ -104,8 +106,8 @@ impl AutoImportAssets { | |||
104 | } | 106 | } |
105 | } | 107 | } |
106 | 108 | ||
107 | fn get_import_group_message(&self) -> String { | 109 | fn get_import_group_message(&self) -> GroupLabel { |
108 | match &self.import_candidate { | 110 | let name = match &self.import_candidate { |
109 | ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), | 111 | ImportCandidate::UnqualifiedName(name) => format!("Import {}", name), |
110 | ImportCandidate::QualifierStart(qualifier_start) => { | 112 | ImportCandidate::QualifierStart(qualifier_start) => { |
111 | format!("Import {}", qualifier_start) | 113 | format!("Import {}", qualifier_start) |
@@ -116,7 +118,8 @@ impl AutoImportAssets { | |||
116 | ImportCandidate::TraitMethod(_, trait_method_name) => { | 118 | ImportCandidate::TraitMethod(_, trait_method_name) => { |
117 | format!("Import a trait for method {}", trait_method_name) | 119 | format!("Import a trait for method {}", trait_method_name) |
118 | } | 120 | } |
119 | } | 121 | }; |
122 | GroupLabel(name) | ||
120 | } | 123 | } |
121 | 124 | ||
122 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { | 125 | fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { |
@@ -383,7 +386,7 @@ mod tests { | |||
383 | } | 386 | } |
384 | ", | 387 | ", |
385 | r" | 388 | r" |
386 | use PubMod1::PubStruct; | 389 | use PubMod3::PubStruct; |
387 | 390 | ||
388 | PubSt<|>ruct | 391 | PubSt<|>ruct |
389 | 392 | ||
diff --git a/crates/ra_assists/src/handlers/change_return_type_to_result.rs b/crates/ra_assists/src/handlers/change_return_type_to_result.rs index 1e8d986cd..5c907097e 100644 --- a/crates/ra_assists/src/handlers/change_return_type_to_result.rs +++ b/crates/ra_assists/src/handlers/change_return_type_to_result.rs | |||
@@ -1,11 +1,11 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast, AstNode, | 2 | ast::{self, BlockExpr, Expr, LoopBodyOwner}, |
3 | AstNode, | ||
3 | SyntaxKind::{COMMENT, WHITESPACE}, | 4 | SyntaxKind::{COMMENT, WHITESPACE}, |
4 | SyntaxNode, TextSize, | 5 | SyntaxNode, TextSize, |
5 | }; | 6 | }; |
6 | 7 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{AssistContext, AssistId, Assists}; |
8 | use ast::{BlockExpr, Expr, LoopBodyOwner}; | ||
9 | 9 | ||
10 | // Assist: change_return_type_to_result | 10 | // Assist: change_return_type_to_result |
11 | // | 11 | // |
@@ -18,7 +18,7 @@ use ast::{BlockExpr, Expr, LoopBodyOwner}; | |||
18 | // ``` | 18 | // ``` |
19 | // fn foo() -> Result<i32, > { Ok(42i32) } | 19 | // fn foo() -> Result<i32, > { Ok(42i32) } |
20 | // ``` | 20 | // ``` |
21 | pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> { | 21 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
22 | let fn_def = ctx.find_node_at_offset::<ast::FnDef>(); | 22 | let fn_def = ctx.find_node_at_offset::<ast::FnDef>(); |
23 | let fn_def = &mut fn_def?; | 23 | let fn_def = &mut fn_def?; |
24 | let ret_type = &fn_def.ret_type()?.type_ref()?; | 24 | let ret_type = &fn_def.ret_type()?.type_ref()?; |
@@ -33,7 +33,7 @@ pub(crate) fn change_return_type_to_result(ctx: AssistCtx) -> Option<Assist> { | |||
33 | return None; | 33 | return None; |
34 | } | 34 | } |
35 | 35 | ||
36 | ctx.add_assist( | 36 | acc.add( |
37 | AssistId("change_return_type_to_result"), | 37 | AssistId("change_return_type_to_result"), |
38 | "Change return type to Result", | 38 | "Change return type to Result", |
39 | ret_type.syntax().text_range(), | 39 | ret_type.syntax().text_range(), |
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs index 489db83e6..e631766ef 100644 --- a/crates/ra_assists/src/handlers/change_visibility.rs +++ b/crates/ra_assists/src/handlers/change_visibility.rs | |||
@@ -7,10 +7,10 @@ use ra_syntax::{ | |||
7 | }, | 7 | }, |
8 | SyntaxNode, TextSize, T, | 8 | SyntaxNode, TextSize, T, |
9 | }; | 9 | }; |
10 | |||
11 | use crate::{Assist, AssistCtx, AssistId}; | ||
12 | use test_utils::tested_by; | 10 | use test_utils::tested_by; |
13 | 11 | ||
12 | use crate::{AssistContext, AssistId, Assists}; | ||
13 | |||
14 | // Assist: change_visibility | 14 | // Assist: change_visibility |
15 | // | 15 | // |
16 | // Adds or changes existing visibility specifier. | 16 | // Adds or changes existing visibility specifier. |
@@ -22,14 +22,14 @@ use test_utils::tested_by; | |||
22 | // ``` | 22 | // ``` |
23 | // pub(crate) fn frobnicate() {} | 23 | // pub(crate) fn frobnicate() {} |
24 | // ``` | 24 | // ``` |
25 | pub(crate) fn change_visibility(ctx: AssistCtx) -> Option<Assist> { | 25 | pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
26 | if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() { | 26 | if let Some(vis) = ctx.find_node_at_offset::<ast::Visibility>() { |
27 | return change_vis(ctx, vis); | 27 | return change_vis(acc, vis); |
28 | } | 28 | } |
29 | add_vis(ctx) | 29 | add_vis(acc, ctx) |
30 | } | 30 | } |
31 | 31 | ||
32 | fn add_vis(ctx: AssistCtx) -> Option<Assist> { | 32 | fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
33 | let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { | 33 | let item_keyword = ctx.token_at_offset().find(|leaf| match leaf.kind() { |
34 | T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, | 34 | T![const] | T![fn] | T![mod] | T![struct] | T![enum] | T![trait] => true, |
35 | _ => false, | 35 | _ => false, |
@@ -66,15 +66,10 @@ fn add_vis(ctx: AssistCtx) -> Option<Assist> { | |||
66 | return None; | 66 | return None; |
67 | }; | 67 | }; |
68 | 68 | ||
69 | ctx.add_assist( | 69 | acc.add(AssistId("change_visibility"), "Change visibility to pub(crate)", target, |edit| { |
70 | AssistId("change_visibility"), | 70 | edit.insert(offset, "pub(crate) "); |
71 | "Change visibility to pub(crate)", | 71 | edit.set_cursor(offset); |
72 | target, | 72 | }) |
73 | |edit| { | ||
74 | edit.insert(offset, "pub(crate) "); | ||
75 | edit.set_cursor(offset); | ||
76 | }, | ||
77 | ) | ||
78 | } | 73 | } |
79 | 74 | ||
80 | fn vis_offset(node: &SyntaxNode) -> TextSize { | 75 | fn vis_offset(node: &SyntaxNode) -> TextSize { |
@@ -88,10 +83,10 @@ fn vis_offset(node: &SyntaxNode) -> TextSize { | |||
88 | .unwrap_or_else(|| node.text_range().start()) | 83 | .unwrap_or_else(|| node.text_range().start()) |
89 | } | 84 | } |
90 | 85 | ||
91 | fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> { | 86 | fn change_vis(acc: &mut Assists, vis: ast::Visibility) -> Option<()> { |
92 | if vis.syntax().text() == "pub" { | 87 | if vis.syntax().text() == "pub" { |
93 | let target = vis.syntax().text_range(); | 88 | let target = vis.syntax().text_range(); |
94 | return ctx.add_assist( | 89 | return acc.add( |
95 | AssistId("change_visibility"), | 90 | AssistId("change_visibility"), |
96 | "Change Visibility to pub(crate)", | 91 | "Change Visibility to pub(crate)", |
97 | target, | 92 | target, |
@@ -103,7 +98,7 @@ fn change_vis(ctx: AssistCtx, vis: ast::Visibility) -> Option<Assist> { | |||
103 | } | 98 | } |
104 | if vis.syntax().text() == "pub(crate)" { | 99 | if vis.syntax().text() == "pub(crate)" { |
105 | let target = vis.syntax().text_range(); | 100 | let target = vis.syntax().text_range(); |
106 | return ctx.add_assist( | 101 | return acc.add( |
107 | AssistId("change_visibility"), | 102 | AssistId("change_visibility"), |
108 | "Change visibility to pub", | 103 | "Change visibility to pub", |
109 | target, | 104 | target, |
diff --git a/crates/ra_assists/src/handlers/early_return.rs b/crates/ra_assists/src/handlers/early_return.rs index 4bd6040b2..ccf91797c 100644 --- a/crates/ra_assists/src/handlers/early_return.rs +++ b/crates/ra_assists/src/handlers/early_return.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{ | 11 | use crate::{ |
12 | assist_ctx::{Assist, AssistCtx}, | 12 | assist_context::{AssistContext, Assists}, |
13 | utils::invert_boolean_expression, | 13 | utils::invert_boolean_expression, |
14 | AssistId, | 14 | AssistId, |
15 | }; | 15 | }; |
@@ -36,7 +36,7 @@ use crate::{ | |||
36 | // bar(); | 36 | // bar(); |
37 | // } | 37 | // } |
38 | // ``` | 38 | // ``` |
39 | pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> { | 39 | pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
40 | let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; | 40 | let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; |
41 | if if_expr.else_branch().is_some() { | 41 | if if_expr.else_branch().is_some() { |
42 | return None; | 42 | return None; |
@@ -96,93 +96,88 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> { | |||
96 | let cursor_position = ctx.frange.range.start(); | 96 | let cursor_position = ctx.frange.range.start(); |
97 | 97 | ||
98 | let target = if_expr.syntax().text_range(); | 98 | let target = if_expr.syntax().text_range(); |
99 | ctx.add_assist( | 99 | acc.add(AssistId("convert_to_guarded_return"), "Convert to guarded return", target, |edit| { |
100 | AssistId("convert_to_guarded_return"), | 100 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); |
101 | "Convert to guarded return", | 101 | let new_block = match if_let_pat { |
102 | target, | 102 | None => { |
103 | |edit| { | 103 | // If. |
104 | let if_indent_level = IndentLevel::from_node(&if_expr.syntax()); | 104 | let new_expr = { |
105 | let new_block = match if_let_pat { | 105 | let then_branch = |
106 | None => { | 106 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
107 | // If. | 107 | let cond = invert_boolean_expression(cond_expr); |
108 | let new_expr = { | 108 | let e = make::expr_if(make::condition(cond, None), then_branch); |
109 | let then_branch = | 109 | if_indent_level.increase_indent(e) |
110 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); | 110 | }; |
111 | let cond = invert_boolean_expression(cond_expr); | 111 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) |
112 | let e = make::expr_if(make::condition(cond, None), then_branch); | 112 | } |
113 | if_indent_level.increase_indent(e) | 113 | Some((path, bound_ident)) => { |
114 | }; | 114 | // If-let. |
115 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 115 | let match_expr = { |
116 | } | 116 | let happy_arm = { |
117 | Some((path, bound_ident)) => { | 117 | let pat = make::tuple_struct_pat( |
118 | // If-let. | 118 | path, |
119 | let match_expr = { | 119 | once(make::bind_pat(make::name("it")).into()), |
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 | }; | ||
133 | |||
134 | let sad_arm = make::match_arm( | ||
135 | // FIXME: would be cool to use `None` or `Err(_)` if appropriate | ||
136 | once(make::placeholder_pat().into()), | ||
137 | early_expression, | ||
138 | ); | 120 | ); |
139 | 121 | let expr = { | |
140 | make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) | 122 | let name_ref = make::name_ref("it"); |
123 | let segment = make::path_segment(name_ref); | ||
124 | let path = make::path_unqualified(segment); | ||
125 | make::expr_path(path) | ||
126 | }; | ||
127 | make::match_arm(once(pat.into()), expr) | ||
141 | }; | 128 | }; |
142 | 129 | ||
143 | let let_stmt = make::let_stmt( | 130 | let sad_arm = make::match_arm( |
144 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), | 131 | // FIXME: would be cool to use `None` or `Err(_)` if appropriate |
145 | Some(match_expr), | 132 | once(make::placeholder_pat().into()), |
133 | early_expression, | ||
146 | ); | 134 | ); |
147 | let let_stmt = if_indent_level.increase_indent(let_stmt); | 135 | |
148 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) | 136 | make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) |
149 | } | 137 | }; |
150 | }; | 138 | |
151 | edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap()); | 139 | let let_stmt = make::let_stmt( |
152 | edit.set_cursor(cursor_position); | 140 | make::bind_pat(make::name(&bound_ident.syntax().to_string())).into(), |
153 | 141 | Some(match_expr), | |
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), | ||
174 | ); | 142 | ); |
175 | replace_children( | 143 | let let_stmt = if_indent_level.increase_indent(let_stmt); |
176 | &parent_block.syntax(), | 144 | replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr) |
177 | RangeInclusive::new( | ||
178 | if_expr.clone().syntax().clone().into(), | ||
179 | if_expr.syntax().clone().into(), | ||
180 | ), | ||
181 | &mut then_statements, | ||
182 | ) | ||
183 | } | 145 | } |
184 | }, | 146 | }; |
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 | }) | ||
186 | } | 181 | } |
187 | 182 | ||
188 | #[cfg(test)] | 183 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index 7c8f8bdf2..13c1e7e80 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -5,7 +5,7 @@ use itertools::Itertools; | |||
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; | 6 | use ra_syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{AssistContext, AssistId, Assists}; |
9 | 9 | ||
10 | // Assist: fill_match_arms | 10 | // Assist: fill_match_arms |
11 | // | 11 | // |
@@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
31 | // } | 31 | // } |
32 | // } | 32 | // } |
33 | // ``` | 33 | // ``` |
34 | pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | 34 | pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
35 | let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?; | 35 | let match_expr = ctx.find_node_at_offset::<ast::MatchExpr>()?; |
36 | let match_arm_list = match_expr.match_arm_list()?; | 36 | let match_arm_list = match_expr.match_arm_list()?; |
37 | 37 | ||
@@ -93,7 +93,7 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
93 | } | 93 | } |
94 | 94 | ||
95 | let target = match_expr.syntax().text_range(); | 95 | let target = match_expr.syntax().text_range(); |
96 | ctx.add_assist(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { | 96 | acc.add(AssistId("fill_match_arms"), "Fill match arms", target, |edit| { |
97 | 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); |
98 | edit.set_cursor(expr.syntax().text_range().start()); | 98 | edit.set_cursor(expr.syntax().text_range().start()); |
99 | edit.replace_ast(match_arm_list, new_arm_list); | 99 | edit.replace_ast(match_arm_list, new_arm_list); |
diff --git a/crates/ra_assists/src/handlers/flip_binexpr.rs b/crates/ra_assists/src/handlers/flip_binexpr.rs index cb7264d7b..692ba4895 100644 --- a/crates/ra_assists/src/handlers/flip_binexpr.rs +++ b/crates/ra_assists/src/handlers/flip_binexpr.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; | 1 | use ra_syntax::ast::{AstNode, BinExpr, BinOp}; |
2 | 2 | ||
3 | use crate::{Assist, AssistCtx, AssistId}; | 3 | use crate::{AssistContext, AssistId, Assists}; |
4 | 4 | ||
5 | // Assist: flip_binexpr | 5 | // Assist: flip_binexpr |
6 | // | 6 | // |
@@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
17 | // let _ = 2 + 90; | 17 | // let _ = 2 + 90; |
18 | // } | 18 | // } |
19 | // ``` | 19 | // ``` |
20 | pub(crate) fn flip_binexpr(ctx: AssistCtx) -> Option<Assist> { | 20 | pub(crate) fn flip_binexpr(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
21 | let expr = ctx.find_node_at_offset::<BinExpr>()?; | 21 | let expr = ctx.find_node_at_offset::<BinExpr>()?; |
22 | let lhs = expr.lhs()?.syntax().clone(); | 22 | let lhs = expr.lhs()?.syntax().clone(); |
23 | let rhs = expr.rhs()?.syntax().clone(); | 23 | let rhs = expr.rhs()?.syntax().clone(); |
@@ -33,7 +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", op_range, |edit| { | 36 | acc.add(AssistId("flip_binexpr"), "Flip binary expression", op_range, |edit| { |
37 | if let FlipAction::FlipAndReplaceOp(new_op) = action { | 37 | if let FlipAction::FlipAndReplaceOp(new_op) = action { |
38 | edit.replace(op_range, new_op); | 38 | edit.replace(op_range, new_op); |
39 | } | 39 | } |
diff --git a/crates/ra_assists/src/handlers/flip_comma.rs b/crates/ra_assists/src/handlers/flip_comma.rs index 24982ae22..dfe2a7fed 100644 --- a/crates/ra_assists/src/handlers/flip_comma.rs +++ b/crates/ra_assists/src/handlers/flip_comma.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::{algo::non_trivia_sibling, Direction, T}; | 1 | use ra_syntax::{algo::non_trivia_sibling, Direction, T}; |
2 | 2 | ||
3 | use crate::{Assist, AssistCtx, AssistId}; | 3 | use crate::{AssistContext, AssistId, Assists}; |
4 | 4 | ||
5 | // Assist: flip_comma | 5 | // Assist: flip_comma |
6 | // | 6 | // |
@@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
17 | // ((3, 4), (1, 2)); | 17 | // ((3, 4), (1, 2)); |
18 | // } | 18 | // } |
19 | // ``` | 19 | // ``` |
20 | pub(crate) fn flip_comma(ctx: AssistCtx) -> Option<Assist> { | 20 | pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
21 | let comma = ctx.find_token_at_offset(T![,])?; | 21 | let comma = ctx.find_token_at_offset(T![,])?; |
22 | let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; | 22 | let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; |
23 | let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; | 23 | let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; |
@@ -28,7 +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", comma.text_range(), |edit| { | 31 | acc.add(AssistId("flip_comma"), "Flip comma", comma.text_range(), |edit| { |
32 | edit.replace(prev.text_range(), next.to_string()); | 32 | edit.replace(prev.text_range(), next.to_string()); |
33 | edit.replace(next.text_range(), prev.to_string()); | 33 | edit.replace(next.text_range(), prev.to_string()); |
34 | }) | 34 | }) |
diff --git a/crates/ra_assists/src/handlers/flip_trait_bound.rs b/crates/ra_assists/src/handlers/flip_trait_bound.rs index 6a3b2df67..8a08702ab 100644 --- a/crates/ra_assists/src/handlers/flip_trait_bound.rs +++ b/crates/ra_assists/src/handlers/flip_trait_bound.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | Direction, T, | 4 | Direction, T, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: flip_trait_bound | 9 | // Assist: flip_trait_bound |
10 | // | 10 | // |
@@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
17 | // ``` | 17 | // ``` |
18 | // fn foo<T: Copy + Clone>() { } | 18 | // fn foo<T: Copy + Clone>() { } |
19 | // ``` | 19 | // ``` |
20 | pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> { | 20 | pub(crate) fn flip_trait_bound(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
21 | // We want to replicate the behavior of `flip_binexpr` by only suggesting | 21 | // We want to replicate the behavior of `flip_binexpr` by only suggesting |
22 | // the assist when the cursor is on a `+` | 22 | // the assist when the cursor is on a `+` |
23 | let plus = ctx.find_token_at_offset(T![+])?; | 23 | let plus = ctx.find_token_at_offset(T![+])?; |
@@ -33,7 +33,7 @@ pub(crate) fn flip_trait_bound(ctx: AssistCtx) -> Option<Assist> { | |||
33 | ); | 33 | ); |
34 | 34 | ||
35 | let target = plus.text_range(); | 35 | let target = plus.text_range(); |
36 | ctx.add_assist(AssistId("flip_trait_bound"), "Flip trait bounds", target, |edit| { | 36 | acc.add(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 | }) |
diff --git a/crates/ra_assists/src/handlers/inline_local_variable.rs b/crates/ra_assists/src/handlers/inline_local_variable.rs index e5765c845..5b26814d3 100644 --- a/crates/ra_assists/src/handlers/inline_local_variable.rs +++ b/crates/ra_assists/src/handlers/inline_local_variable.rs | |||
@@ -5,7 +5,10 @@ use ra_syntax::{ | |||
5 | }; | 5 | }; |
6 | use test_utils::tested_by; | 6 | use test_utils::tested_by; |
7 | 7 | ||
8 | use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId}; | 8 | use crate::{ |
9 | assist_context::{AssistContext, Assists}, | ||
10 | AssistId, | ||
11 | }; | ||
9 | 12 | ||
10 | // Assist: inline_local_variable | 13 | // Assist: inline_local_variable |
11 | // | 14 | // |
@@ -23,7 +26,7 @@ use crate::{assist_ctx::ActionBuilder, Assist, AssistCtx, AssistId}; | |||
23 | // (1 + 2) * 4; | 26 | // (1 + 2) * 4; |
24 | // } | 27 | // } |
25 | // ``` | 28 | // ``` |
26 | pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> { | 29 | pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
27 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; | 30 | let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?; |
28 | let bind_pat = match let_stmt.pat()? { | 31 | let bind_pat = match let_stmt.pat()? { |
29 | ast::Pat::BindPat(pat) => pat, | 32 | ast::Pat::BindPat(pat) => pat, |
@@ -33,7 +36,7 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> { | |||
33 | tested_by!(test_not_inline_mut_variable); | 36 | tested_by!(test_not_inline_mut_variable); |
34 | return None; | 37 | return None; |
35 | } | 38 | } |
36 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.frange.range.start()) { | 39 | if !bind_pat.syntax().text_range().contains_inclusive(ctx.offset()) { |
37 | tested_by!(not_applicable_outside_of_bind_pat); | 40 | tested_by!(not_applicable_outside_of_bind_pat); |
38 | return None; | 41 | return None; |
39 | } | 42 | } |
@@ -107,20 +110,14 @@ pub(crate) fn inline_local_variable(ctx: AssistCtx) -> Option<Assist> { | |||
107 | let init_in_paren = format!("({})", &init_str); | 110 | let init_in_paren = format!("({})", &init_str); |
108 | 111 | ||
109 | let target = bind_pat.syntax().text_range(); | 112 | let target = bind_pat.syntax().text_range(); |
110 | ctx.add_assist( | 113 | acc.add(AssistId("inline_local_variable"), "Inline variable", target, move |builder| { |
111 | AssistId("inline_local_variable"), | 114 | builder.delete(delete_range); |
112 | "Inline variable", | 115 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { |
113 | target, | 116 | let replacement = if should_wrap { init_in_paren.clone() } else { init_str.clone() }; |
114 | move |edit: &mut ActionBuilder| { | 117 | builder.replace(desc.file_range.range, replacement) |
115 | edit.delete(delete_range); | 118 | } |
116 | for (desc, should_wrap) in refs.iter().zip(wrap_in_parens) { | 119 | builder.set_cursor(delete_range.start()) |
117 | let replacement = | 120 | }) |
118 | if should_wrap { init_in_paren.clone() } else { init_str.clone() }; | ||
119 | edit.replace(desc.file_range.range, replacement) | ||
120 | } | ||
121 | edit.set_cursor(delete_range.start()) | ||
122 | }, | ||
123 | ) | ||
124 | } | 121 | } |
125 | 122 | ||
126 | #[cfg(test)] | 123 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/introduce_variable.rs b/crates/ra_assists/src/handlers/introduce_variable.rs index 3c340ff3b..fdf3ada0d 100644 --- a/crates/ra_assists/src/handlers/introduce_variable.rs +++ b/crates/ra_assists/src/handlers/introduce_variable.rs | |||
@@ -9,7 +9,7 @@ use ra_syntax::{ | |||
9 | use stdx::format_to; | 9 | use stdx::format_to; |
10 | use test_utils::tested_by; | 10 | use test_utils::tested_by; |
11 | 11 | ||
12 | use crate::{Assist, AssistCtx, AssistId}; | 12 | use crate::{AssistContext, AssistId, Assists}; |
13 | 13 | ||
14 | // Assist: introduce_variable | 14 | // Assist: introduce_variable |
15 | // | 15 | // |
@@ -27,7 +27,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
27 | // var_name * 4; | 27 | // var_name * 4; |
28 | // } | 28 | // } |
29 | // ``` | 29 | // ``` |
30 | pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> { | 30 | pub(crate) fn introduce_variable(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
31 | if ctx.frange.range.is_empty() { | 31 | if ctx.frange.range.is_empty() { |
32 | return None; | 32 | return None; |
33 | } | 33 | } |
@@ -43,7 +43,7 @@ pub(crate) fn introduce_variable(ctx: AssistCtx) -> Option<Assist> { | |||
43 | return None; | 43 | return None; |
44 | } | 44 | } |
45 | let target = expr.syntax().text_range(); | 45 | let target = expr.syntax().text_range(); |
46 | ctx.add_assist(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { | 46 | acc.add(AssistId("introduce_variable"), "Extract into variable", target, move |edit| { |
47 | let mut buf = String::new(); | 47 | let mut buf = String::new(); |
48 | 48 | ||
49 | let cursor_offset = if wrap_in_block { | 49 | let cursor_offset = if wrap_in_block { |
diff --git a/crates/ra_assists/src/handlers/invert_if.rs b/crates/ra_assists/src/handlers/invert_if.rs index b16271443..527c7caef 100644 --- a/crates/ra_assists/src/handlers/invert_if.rs +++ b/crates/ra_assists/src/handlers/invert_if.rs | |||
@@ -3,7 +3,11 @@ use ra_syntax::{ | |||
3 | T, | 3 | T, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; | 6 | use crate::{ |
7 | assist_context::{AssistContext, Assists}, | ||
8 | utils::invert_boolean_expression, | ||
9 | AssistId, | ||
10 | }; | ||
7 | 11 | ||
8 | // Assist: invert_if | 12 | // Assist: invert_if |
9 | // | 13 | // |
@@ -24,7 +28,7 @@ use crate::{utils::invert_boolean_expression, Assist, AssistCtx, AssistId}; | |||
24 | // } | 28 | // } |
25 | // ``` | 29 | // ``` |
26 | 30 | ||
27 | pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> { | 31 | pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
28 | let if_keyword = ctx.find_token_at_offset(T![if])?; | 32 | let if_keyword = ctx.find_token_at_offset(T![if])?; |
29 | let expr = ast::IfExpr::cast(if_keyword.parent())?; | 33 | let expr = ast::IfExpr::cast(if_keyword.parent())?; |
30 | let if_range = if_keyword.text_range(); | 34 | let if_range = if_keyword.text_range(); |
@@ -40,21 +44,21 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> { | |||
40 | 44 | ||
41 | let cond = expr.condition()?.expr()?; | 45 | let cond = expr.condition()?.expr()?; |
42 | let then_node = expr.then_branch()?.syntax().clone(); | 46 | let then_node = expr.then_branch()?.syntax().clone(); |
47 | let else_block = match expr.else_branch()? { | ||
48 | ast::ElseBranch::Block(it) => it, | ||
49 | ast::ElseBranch::IfExpr(_) => return None, | ||
50 | }; | ||
43 | 51 | ||
44 | if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { | 52 | let cond_range = cond.syntax().text_range(); |
45 | let cond_range = cond.syntax().text_range(); | 53 | let flip_cond = invert_boolean_expression(cond); |
46 | let flip_cond = invert_boolean_expression(cond); | 54 | let else_node = else_block.syntax(); |
47 | let else_node = else_block.syntax(); | 55 | let else_range = else_node.text_range(); |
48 | let else_range = else_node.text_range(); | 56 | let then_range = then_node.text_range(); |
49 | let then_range = then_node.text_range(); | 57 | acc.add(AssistId("invert_if"), "Invert if", if_range, |edit| { |
50 | return ctx.add_assist(AssistId("invert_if"), "Invert if", if_range, |edit| { | 58 | edit.replace(cond_range, flip_cond.syntax().text()); |
51 | edit.replace(cond_range, flip_cond.syntax().text()); | 59 | edit.replace(else_range, then_node.text()); |
52 | edit.replace(else_range, then_node.text()); | 60 | edit.replace(then_range, else_node.text()); |
53 | edit.replace(then_range, else_node.text()); | 61 | }) |
54 | }); | ||
55 | } | ||
56 | |||
57 | None | ||
58 | } | 62 | } |
59 | 63 | ||
60 | #[cfg(test)] | 64 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/merge_imports.rs b/crates/ra_assists/src/handlers/merge_imports.rs index de74d83d8..8e1d93312 100644 --- a/crates/ra_assists/src/handlers/merge_imports.rs +++ b/crates/ra_assists/src/handlers/merge_imports.rs | |||
@@ -6,7 +6,10 @@ use ra_syntax::{ | |||
6 | AstNode, Direction, InsertPosition, SyntaxElement, T, | 6 | AstNode, Direction, InsertPosition, SyntaxElement, T, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{Assist, AssistCtx, AssistId}; | 9 | use crate::{ |
10 | assist_context::{AssistContext, Assists}, | ||
11 | AssistId, | ||
12 | }; | ||
10 | 13 | ||
11 | // Assist: merge_imports | 14 | // Assist: merge_imports |
12 | // | 15 | // |
@@ -20,7 +23,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
20 | // ``` | 23 | // ``` |
21 | // use std::{fmt::Formatter, io}; | 24 | // use std::{fmt::Formatter, io}; |
22 | // ``` | 25 | // ``` |
23 | pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> { | 26 | pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
24 | let tree: ast::UseTree = ctx.find_node_at_offset()?; | 27 | let tree: ast::UseTree = ctx.find_node_at_offset()?; |
25 | let mut rewriter = SyntaxRewriter::default(); | 28 | let mut rewriter = SyntaxRewriter::default(); |
26 | let mut offset = ctx.frange.range.start(); | 29 | let mut offset = ctx.frange.range.start(); |
@@ -53,10 +56,10 @@ pub(crate) fn merge_imports(ctx: AssistCtx) -> Option<Assist> { | |||
53 | }; | 56 | }; |
54 | 57 | ||
55 | let target = tree.syntax().text_range(); | 58 | let target = tree.syntax().text_range(); |
56 | ctx.add_assist(AssistId("merge_imports"), "Merge imports", target, |edit| { | 59 | acc.add(AssistId("merge_imports"), "Merge imports", target, |builder| { |
57 | edit.rewrite(rewriter); | 60 | builder.rewrite(rewriter); |
58 | // FIXME: we only need because our diff is imprecise | 61 | // FIXME: we only need because our diff is imprecise |
59 | edit.set_cursor(offset); | 62 | builder.set_cursor(offset); |
60 | }) | 63 | }) |
61 | } | 64 | } |
62 | 65 | ||
diff --git a/crates/ra_assists/src/handlers/merge_match_arms.rs b/crates/ra_assists/src/handlers/merge_match_arms.rs index 7c4d9d55d..cfe4df47b 100644 --- a/crates/ra_assists/src/handlers/merge_match_arms.rs +++ b/crates/ra_assists/src/handlers/merge_match_arms.rs | |||
@@ -6,7 +6,7 @@ use ra_syntax::{ | |||
6 | Direction, TextSize, | 6 | Direction, TextSize, |
7 | }; | 7 | }; |
8 | 8 | ||
9 | use crate::{Assist, AssistCtx, AssistId, TextRange}; | 9 | use crate::{AssistContext, AssistId, Assists, TextRange}; |
10 | 10 | ||
11 | // Assist: merge_match_arms | 11 | // Assist: merge_match_arms |
12 | // | 12 | // |
@@ -32,7 +32,7 @@ use crate::{Assist, AssistCtx, AssistId, TextRange}; | |||
32 | // } | 32 | // } |
33 | // } | 33 | // } |
34 | // ``` | 34 | // ``` |
35 | pub(crate) fn merge_match_arms(ctx: AssistCtx) -> Option<Assist> { | 35 | pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
36 | let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?; | 36 | let current_arm = ctx.find_node_at_offset::<ast::MatchArm>()?; |
37 | // Don't try to handle arms with guards for now - can add support for this later | 37 | // Don't try to handle arms with guards for now - can add support for this later |
38 | if current_arm.guard().is_some() { | 38 | if current_arm.guard().is_some() { |
@@ -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", current_text_range, |edit| { | 73 | acc.add(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 { |
diff --git a/crates/ra_assists/src/handlers/move_bounds.rs b/crates/ra_assists/src/handlers/move_bounds.rs index 44e50cb6e..a41aacfc3 100644 --- a/crates/ra_assists/src/handlers/move_bounds.rs +++ b/crates/ra_assists/src/handlers/move_bounds.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | T, | 5 | T, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{AssistContext, AssistId, Assists}; |
9 | 9 | ||
10 | // Assist: move_bounds_to_where_clause | 10 | // Assist: move_bounds_to_where_clause |
11 | // | 11 | // |
@@ -22,7 +22,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
22 | // f(x) | 22 | // f(x) |
23 | // } | 23 | // } |
24 | // ``` | 24 | // ``` |
25 | pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> { | 25 | pub(crate) fn move_bounds_to_where_clause(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
26 | let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?; | 26 | let type_param_list = ctx.find_node_at_offset::<ast::TypeParamList>()?; |
27 | 27 | ||
28 | let mut type_params = type_param_list.type_params(); | 28 | let mut type_params = type_param_list.type_params(); |
@@ -50,36 +50,29 @@ pub(crate) fn move_bounds_to_where_clause(ctx: AssistCtx) -> Option<Assist> { | |||
50 | }; | 50 | }; |
51 | 51 | ||
52 | let target = type_param_list.syntax().text_range(); | 52 | let target = type_param_list.syntax().text_range(); |
53 | ctx.add_assist( | 53 | acc.add(AssistId("move_bounds_to_where_clause"), "Move to where clause", target, |edit| { |
54 | AssistId("move_bounds_to_where_clause"), | 54 | let new_params = type_param_list |
55 | "Move to where clause", | 55 | .type_params() |
56 | target, | 56 | .filter(|it| it.type_bound_list().is_some()) |
57 | |edit| { | 57 | .map(|type_param| { |
58 | let new_params = type_param_list | 58 | let without_bounds = type_param.remove_bounds(); |
59 | .type_params() | 59 | (type_param, without_bounds) |
60 | .filter(|it| it.type_bound_list().is_some()) | 60 | }); |
61 | .map(|type_param| { | 61 | |
62 | let without_bounds = type_param.remove_bounds(); | 62 | let new_type_param_list = type_param_list.replace_descendants(new_params); |
63 | (type_param, without_bounds) | 63 | edit.replace_ast(type_param_list.clone(), new_type_param_list); |
64 | }); | 64 | |
65 | 65 | let where_clause = { | |
66 | let new_type_param_list = type_param_list.replace_descendants(new_params); | 66 | let predicates = type_param_list.type_params().filter_map(build_predicate); |
67 | edit.replace_ast(type_param_list.clone(), new_type_param_list); | 67 | make::where_clause(predicates) |
68 | 68 | }; | |
69 | let where_clause = { | 69 | |
70 | let predicates = type_param_list.type_params().filter_map(build_predicate); | 70 | let to_insert = match anchor.prev_sibling_or_token() { |
71 | make::where_clause(predicates) | 71 | Some(ref elem) if elem.kind() == WHITESPACE => format!("{} ", where_clause.syntax()), |
72 | }; | 72 | _ => format!(" {}", where_clause.syntax()), |
73 | 73 | }; | |
74 | let to_insert = match anchor.prev_sibling_or_token() { | 74 | edit.insert(anchor.text_range().start(), to_insert); |
75 | Some(ref elem) if elem.kind() == WHITESPACE => { | 75 | }) |
76 | format!("{} ", where_clause.syntax()) | ||
77 | } | ||
78 | _ => format!(" {}", where_clause.syntax()), | ||
79 | }; | ||
80 | edit.insert(anchor.text_range().start(), to_insert); | ||
81 | }, | ||
82 | ) | ||
83 | } | 76 | } |
84 | 77 | ||
85 | fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { | 78 | fn build_predicate(param: ast::TypeParam) -> Option<ast::WherePred> { |
diff --git a/crates/ra_assists/src/handlers/move_guard.rs b/crates/ra_assists/src/handlers/move_guard.rs index 29bc9a9ff..fc0335b57 100644 --- a/crates/ra_assists/src/handlers/move_guard.rs +++ b/crates/ra_assists/src/handlers/move_guard.rs | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | TextSize, | 4 | TextSize, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{Assist, AssistCtx, AssistId}; | 7 | use crate::{AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: move_guard_to_arm_body | 9 | // Assist: move_guard_to_arm_body |
10 | // | 10 | // |
@@ -31,7 +31,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
31 | // } | 31 | // } |
32 | // } | 32 | // } |
33 | // ``` | 33 | // ``` |
34 | pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> { | 34 | pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
35 | let match_arm = ctx.find_node_at_offset::<MatchArm>()?; | 35 | let match_arm = ctx.find_node_at_offset::<MatchArm>()?; |
36 | let guard = match_arm.guard()?; | 36 | let guard = match_arm.guard()?; |
37 | let space_before_guard = guard.syntax().prev_sibling_or_token(); | 37 | let space_before_guard = guard.syntax().prev_sibling_or_token(); |
@@ -41,7 +41,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> { | |||
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 | let target = guard.syntax().text_range(); | 43 | let target = guard.syntax().text_range(); |
44 | ctx.add_assist(AssistId("move_guard_to_arm_body"), "Move guard to arm body", target, |edit| { | 44 | acc.add(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() { |
@@ -88,7 +88,7 @@ pub(crate) fn move_guard_to_arm_body(ctx: AssistCtx) -> Option<Assist> { | |||
88 | // } | 88 | // } |
89 | // } | 89 | // } |
90 | // ``` | 90 | // ``` |
91 | pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { | 91 | pub(crate) fn move_arm_cond_to_match_guard(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
92 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; | 92 | let match_arm: MatchArm = ctx.find_node_at_offset::<MatchArm>()?; |
93 | let match_pat = match_arm.pat()?; | 93 | let match_pat = match_arm.pat()?; |
94 | 94 | ||
@@ -109,7 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard(ctx: AssistCtx) -> Option<Assist> { | |||
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 | let target = if_expr.syntax().text_range(); |
112 | ctx.add_assist( | 112 | acc.add( |
113 | AssistId("move_arm_cond_to_match_guard"), | 113 | AssistId("move_arm_cond_to_match_guard"), |
114 | "Move condition to match guard", | 114 | "Move condition to match guard", |
115 | target, | 115 | target, |
diff --git a/crates/ra_assists/src/handlers/raw_string.rs b/crates/ra_assists/src/handlers/raw_string.rs index 155c679b4..c20ffe0b3 100644 --- a/crates/ra_assists/src/handlers/raw_string.rs +++ b/crates/ra_assists/src/handlers/raw_string.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | TextSize, | 5 | TextSize, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use crate::{Assist, AssistCtx, AssistId}; | 8 | use crate::{AssistContext, AssistId, Assists}; |
9 | 9 | ||
10 | // Assist: make_raw_string | 10 | // Assist: make_raw_string |
11 | // | 11 | // |
@@ -22,11 +22,11 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
22 | // r#"Hello, World!"#; | 22 | // r#"Hello, World!"#; |
23 | // } | 23 | // } |
24 | // ``` | 24 | // ``` |
25 | pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> { | 25 | pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
26 | let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; | 26 | let token = ctx.find_token_at_offset(STRING).and_then(ast::String::cast)?; |
27 | let value = token.value()?; | 27 | let value = token.value()?; |
28 | let target = token.syntax().text_range(); | 28 | let target = token.syntax().text_range(); |
29 | ctx.add_assist(AssistId("make_raw_string"), "Rewrite as raw string", target, |edit| { | 29 | acc.add(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() { |
@@ -51,11 +51,11 @@ pub(crate) fn make_raw_string(ctx: AssistCtx) -> Option<Assist> { | |||
51 | // "Hello, \"World!\""; | 51 | // "Hello, \"World!\""; |
52 | // } | 52 | // } |
53 | // ``` | 53 | // ``` |
54 | pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> { | 54 | pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
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 | let target = token.syntax().text_range(); | 57 | let target = token.syntax().text_range(); |
58 | ctx.add_assist(AssistId("make_usual_string"), "Rewrite as regular string", target, |edit| { | 58 | acc.add(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)); |
@@ -77,10 +77,10 @@ pub(crate) fn make_usual_string(ctx: AssistCtx) -> Option<Assist> { | |||
77 | // r##"Hello, World!"##; | 77 | // r##"Hello, World!"##; |
78 | // } | 78 | // } |
79 | // ``` | 79 | // ``` |
80 | pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> { | 80 | pub(crate) fn add_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
81 | let token = ctx.find_token_at_offset(RAW_STRING)?; | 81 | let token = ctx.find_token_at_offset(RAW_STRING)?; |
82 | let target = token.text_range(); | 82 | let target = token.text_range(); |
83 | ctx.add_assist(AssistId("add_hash"), "Add # to raw string", target, |edit| { | 83 | acc.add(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 | }) |
@@ -101,7 +101,7 @@ pub(crate) fn add_hash(ctx: AssistCtx) -> Option<Assist> { | |||
101 | // r"Hello, World!"; | 101 | // r"Hello, World!"; |
102 | // } | 102 | // } |
103 | // ``` | 103 | // ``` |
104 | pub(crate) fn remove_hash(ctx: AssistCtx) -> Option<Assist> { | 104 | pub(crate) fn remove_hash(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
105 | let token = ctx.find_token_at_offset(RAW_STRING)?; | 105 | let token = ctx.find_token_at_offset(RAW_STRING)?; |
106 | let text = token.text().as_str(); | 106 | let text = token.text().as_str(); |
107 | if text.starts_with("r\"") { | 107 | if text.starts_with("r\"") { |
@@ -109,7 +109,7 @@ pub(crate) fn remove_hash(ctx: AssistCtx) -> Option<Assist> { | |||
109 | return None; | 109 | return None; |
110 | } | 110 | } |
111 | let target = token.text_range(); | 111 | let target = token.text_range(); |
112 | ctx.add_assist(AssistId("remove_hash"), "Remove hash from raw string", target, |edit| { | 112 | acc.add(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 |
diff --git a/crates/ra_assists/src/handlers/remove_dbg.rs b/crates/ra_assists/src/handlers/remove_dbg.rs index e6e02f2ae..8eef578cf 100644 --- a/crates/ra_assists/src/handlers/remove_dbg.rs +++ b/crates/ra_assists/src/handlers/remove_dbg.rs | |||
@@ -3,7 +3,7 @@ use ra_syntax::{ | |||
3 | TextSize, T, | 3 | TextSize, T, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | use crate::{Assist, AssistCtx, AssistId}; | 6 | use crate::{AssistContext, AssistId, Assists}; |
7 | 7 | ||
8 | // Assist: remove_dbg | 8 | // Assist: remove_dbg |
9 | // | 9 | // |
@@ -20,7 +20,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
20 | // 92; | 20 | // 92; |
21 | // } | 21 | // } |
22 | // ``` | 22 | // ``` |
23 | pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> { | 23 | pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
24 | let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; | 24 | let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?; |
25 | 25 | ||
26 | if !is_valid_macrocall(¯o_call, "dbg")? { | 26 | if !is_valid_macrocall(¯o_call, "dbg")? { |
@@ -58,7 +58,7 @@ pub(crate) fn remove_dbg(ctx: AssistCtx) -> Option<Assist> { | |||
58 | }; | 58 | }; |
59 | 59 | ||
60 | let target = macro_call.syntax().text_range(); | 60 | let target = macro_call.syntax().text_range(); |
61 | ctx.add_assist(AssistId("remove_dbg"), "Remove dbg!()", target, |edit| { | 61 | acc.add(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 | }) |
diff --git a/crates/ra_assists/src/handlers/remove_mut.rs b/crates/ra_assists/src/handlers/remove_mut.rs index 9f72f879d..dce546db7 100644 --- a/crates/ra_assists/src/handlers/remove_mut.rs +++ b/crates/ra_assists/src/handlers/remove_mut.rs | |||
@@ -1,6 +1,6 @@ | |||
1 | use ra_syntax::{SyntaxKind, TextRange, T}; | 1 | use ra_syntax::{SyntaxKind, TextRange, T}; |
2 | 2 | ||
3 | use crate::{Assist, AssistCtx, AssistId}; | 3 | use crate::{AssistContext, AssistId, Assists}; |
4 | 4 | ||
5 | // Assist: remove_mut | 5 | // Assist: remove_mut |
6 | // | 6 | // |
@@ -17,7 +17,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
17 | // fn feed(&self, amount: u32) {} | 17 | // fn feed(&self, amount: u32) {} |
18 | // } | 18 | // } |
19 | // ``` | 19 | // ``` |
20 | pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> { | 20 | pub(crate) fn remove_mut(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
21 | let mut_token = ctx.find_token_at_offset(T![mut])?; | 21 | let mut_token = ctx.find_token_at_offset(T![mut])?; |
22 | let delete_from = mut_token.text_range().start(); | 22 | let delete_from = mut_token.text_range().start(); |
23 | let delete_to = match mut_token.next_token() { | 23 | let delete_to = match mut_token.next_token() { |
@@ -26,7 +26,7 @@ pub(crate) fn remove_mut(ctx: AssistCtx) -> Option<Assist> { | |||
26 | }; | 26 | }; |
27 | 27 | ||
28 | let target = mut_token.text_range(); | 28 | let target = mut_token.text_range(); |
29 | ctx.add_assist(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { | 29 | acc.add(AssistId("remove_mut"), "Remove `mut` keyword", target, |edit| { |
30 | edit.set_cursor(delete_from); | 30 | edit.set_cursor(delete_from); |
31 | edit.delete(TextRange::new(delete_from, delete_to)); | 31 | edit.delete(TextRange::new(delete_from, delete_to)); |
32 | }) | 32 | }) |
diff --git a/crates/ra_assists/src/handlers/reorder_fields.rs b/crates/ra_assists/src/handlers/reorder_fields.rs index 0b930dea2..757f6406e 100644 --- a/crates/ra_assists/src/handlers/reorder_fields.rs +++ b/crates/ra_assists/src/handlers/reorder_fields.rs | |||
@@ -3,18 +3,9 @@ use std::collections::HashMap; | |||
3 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; | 3 | use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct}; |
4 | use itertools::Itertools; | 4 | use itertools::Itertools; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{algo, ast, match_ast, AstNode, SyntaxKind, SyntaxKind::*, SyntaxNode}; |
7 | algo, | 7 | |
8 | ast::{self, Path, RecordLit, RecordPat}, | 8 | use crate::{AssistContext, AssistId, Assists}; |
9 | match_ast, AstNode, SyntaxKind, | ||
10 | SyntaxKind::*, | ||
11 | SyntaxNode, | ||
12 | }; | ||
13 | |||
14 | use crate::{ | ||
15 | assist_ctx::{Assist, AssistCtx}, | ||
16 | AssistId, | ||
17 | }; | ||
18 | 9 | ||
19 | // Assist: reorder_fields | 10 | // Assist: reorder_fields |
20 | // | 11 | // |
@@ -31,13 +22,13 @@ use crate::{ | |||
31 | // const test: Foo = Foo {foo: 1, bar: 0} | 22 | // const test: Foo = Foo {foo: 1, bar: 0} |
32 | // ``` | 23 | // ``` |
33 | // | 24 | // |
34 | pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> { | 25 | pub(crate) fn reorder_fields(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
35 | reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx)) | 26 | reorder::<ast::RecordLit>(acc, ctx.clone()).or_else(|| reorder::<ast::RecordPat>(acc, ctx)) |
36 | } | 27 | } |
37 | 28 | ||
38 | fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> { | 29 | fn reorder<R: AstNode>(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
39 | let record = ctx.find_node_at_offset::<R>()?; | 30 | let record = ctx.find_node_at_offset::<R>()?; |
40 | let path = record.syntax().children().find_map(Path::cast)?; | 31 | let path = record.syntax().children().find_map(ast::Path::cast)?; |
41 | 32 | ||
42 | let ranks = compute_fields_ranks(&path, &ctx)?; | 33 | let ranks = compute_fields_ranks(&path, &ctx)?; |
43 | 34 | ||
@@ -51,7 +42,7 @@ fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> { | |||
51 | } | 42 | } |
52 | 43 | ||
53 | let target = record.syntax().text_range(); | 44 | let target = record.syntax().text_range(); |
54 | ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { | 45 | acc.add(AssistId("reorder_fields"), "Reorder record fields", target, |edit| { |
55 | for (old, new) in fields.iter().zip(&sorted_fields) { | 46 | for (old, new) in fields.iter().zip(&sorted_fields) { |
56 | algo::diff(old, new).into_text_edit(edit.text_edit_builder()); | 47 | algo::diff(old, new).into_text_edit(edit.text_edit_builder()); |
57 | } | 48 | } |
@@ -96,9 +87,9 @@ fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option | |||
96 | } | 87 | } |
97 | } | 88 | } |
98 | 89 | ||
99 | fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String, usize>> { | 90 | fn compute_fields_ranks(path: &ast::Path, ctx: &AssistContext) -> Option<HashMap<String, usize>> { |
100 | Some( | 91 | Some( |
101 | struct_definition(path, ctx.sema)? | 92 | struct_definition(path, &ctx.sema)? |
102 | .fields(ctx.db) | 93 | .fields(ctx.db) |
103 | .iter() | 94 | .iter() |
104 | .enumerate() | 95 | .enumerate() |
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 2eb8348f8..a59a06efa 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 | |||
@@ -4,7 +4,7 @@ use ra_syntax::{ | |||
4 | AstNode, | 4 | AstNode, |
5 | }; | 5 | }; |
6 | 6 | ||
7 | use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; | 7 | use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; |
8 | 8 | ||
9 | // Assist: replace_if_let_with_match | 9 | // Assist: replace_if_let_with_match |
10 | // | 10 | // |
@@ -32,7 +32,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; | |||
32 | // } | 32 | // } |
33 | // } | 33 | // } |
34 | // ``` | 34 | // ``` |
35 | pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> { | 35 | pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
36 | let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; | 36 | let if_expr: ast::IfExpr = ctx.find_node_at_offset()?; |
37 | let cond = if_expr.condition()?; | 37 | let cond = if_expr.condition()?; |
38 | let pat = cond.pat()?; | 38 | let pat = cond.pat()?; |
@@ -43,36 +43,31 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> { | |||
43 | ast::ElseBranch::IfExpr(_) => return None, | 43 | ast::ElseBranch::IfExpr(_) => return None, |
44 | }; | 44 | }; |
45 | 45 | ||
46 | let sema = ctx.sema; | ||
47 | let target = if_expr.syntax().text_range(); | 46 | let target = if_expr.syntax().text_range(); |
48 | ctx.add_assist( | 47 | acc.add(AssistId("replace_if_let_with_match"), "Replace with match", target, move |edit| { |
49 | AssistId("replace_if_let_with_match"), | 48 | let match_expr = { |
50 | "Replace with match", | 49 | let then_arm = { |
51 | target, | 50 | let then_expr = unwrap_trivial_block(then_block); |
52 | move |edit| { | 51 | make::match_arm(vec![pat.clone()], then_expr) |
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])) | ||
68 | }; | 52 | }; |
53 | let else_arm = { | ||
54 | let pattern = ctx | ||
55 | .sema | ||
56 | .type_of_pat(&pat) | ||
57 | .and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty)) | ||
58 | .map(|it| it.sad_pattern()) | ||
59 | .unwrap_or_else(|| make::placeholder_pat().into()); | ||
60 | let else_expr = unwrap_trivial_block(else_block); | ||
61 | make::match_arm(vec![pattern], else_expr) | ||
62 | }; | ||
63 | make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm])) | ||
64 | }; | ||
69 | 65 | ||
70 | let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); | 66 | let match_expr = IndentLevel::from_node(if_expr.syntax()).increase_indent(match_expr); |
71 | 67 | ||
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 | ) | ||
76 | } | 71 | } |
77 | 72 | ||
78 | #[cfg(test)] | 73 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs index a5509a567..d3f214591 100644 --- a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -9,11 +9,7 @@ use ra_syntax::{ | |||
9 | AstNode, T, | 9 | AstNode, T, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{ | 12 | use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; |
13 | assist_ctx::{Assist, AssistCtx}, | ||
14 | utils::TryEnum, | ||
15 | AssistId, | ||
16 | }; | ||
17 | 13 | ||
18 | // Assist: replace_let_with_if_let | 14 | // Assist: replace_let_with_if_let |
19 | // | 15 | // |
@@ -39,16 +35,16 @@ use crate::{ | |||
39 | // | 35 | // |
40 | // fn compute() -> Option<i32> { None } | 36 | // fn compute() -> Option<i32> { None } |
41 | // ``` | 37 | // ``` |
42 | pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> { | 38 | pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
43 | let let_kw = ctx.find_token_at_offset(T![let])?; | 39 | let let_kw = ctx.find_token_at_offset(T![let])?; |
44 | let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?; | 40 | let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?; |
45 | let init = let_stmt.initializer()?; | 41 | let init = let_stmt.initializer()?; |
46 | let original_pat = let_stmt.pat()?; | 42 | let original_pat = let_stmt.pat()?; |
47 | let ty = ctx.sema.type_of_expr(&init)?; | 43 | let ty = ctx.sema.type_of_expr(&init)?; |
48 | let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case()); | 44 | let happy_variant = TryEnum::from_ty(&ctx.sema, &ty).map(|it| it.happy_case()); |
49 | 45 | ||
50 | let target = let_kw.text_range(); | 46 | let target = let_kw.text_range(); |
51 | ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { | 47 | acc.add(AssistId("replace_let_with_if_let"), "Replace with if-let", target, |edit| { |
52 | let with_placeholder: ast::Pat = match happy_variant { | 48 | let with_placeholder: ast::Pat = match happy_variant { |
53 | None => make::placeholder_pat().into(), | 49 | None => make::placeholder_pat().into(), |
54 | Some(var_name) => make::tuple_struct_pat( | 50 | Some(var_name) => make::tuple_struct_pat( |
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 fd41da64b..1a81d8a0e 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 | |||
@@ -1,11 +1,7 @@ | |||
1 | use hir; | 1 | use hir; |
2 | use ra_syntax::{ast, AstNode, SmolStr, TextRange}; | 2 | use ra_syntax::{ast, AstNode, SmolStr, TextRange}; |
3 | 3 | ||
4 | use crate::{ | 4 | use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; |
5 | assist_ctx::{Assist, AssistCtx}, | ||
6 | utils::insert_use_statement, | ||
7 | AssistId, | ||
8 | }; | ||
9 | 5 | ||
10 | // Assist: replace_qualified_name_with_use | 6 | // Assist: replace_qualified_name_with_use |
11 | // | 7 | // |
@@ -20,7 +16,10 @@ use crate::{ | |||
20 | // | 16 | // |
21 | // fn process(map: HashMap<String, String>) {} | 17 | // fn process(map: HashMap<String, String>) {} |
22 | // ``` | 18 | // ``` |
23 | pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> { | 19 | pub(crate) fn replace_qualified_name_with_use( |
20 | acc: &mut Assists, | ||
21 | ctx: &AssistContext, | ||
22 | ) -> Option<()> { | ||
24 | let path: ast::Path = ctx.find_node_at_offset()?; | 23 | let path: ast::Path = ctx.find_node_at_offset()?; |
25 | // We don't want to mess with use statements | 24 | // We don't want to mess with use statements |
26 | if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { | 25 | if path.syntax().ancestors().find_map(ast::UseItem::cast).is_some() { |
@@ -34,18 +33,18 @@ pub(crate) fn replace_qualified_name_with_use(ctx: AssistCtx) -> Option<Assist> | |||
34 | } | 33 | } |
35 | 34 | ||
36 | let target = path.syntax().text_range(); | 35 | let target = path.syntax().text_range(); |
37 | ctx.add_assist( | 36 | acc.add( |
38 | AssistId("replace_qualified_name_with_use"), | 37 | AssistId("replace_qualified_name_with_use"), |
39 | "Replace qualified path with use", | 38 | "Replace qualified path with use", |
40 | target, | 39 | target, |
41 | |edit| { | 40 | |builder| { |
42 | let path_to_import = hir_path.mod_path().clone(); | 41 | let path_to_import = hir_path.mod_path().clone(); |
43 | insert_use_statement(path.syntax(), &path_to_import, edit); | 42 | insert_use_statement(path.syntax(), &path_to_import, ctx, builder); |
44 | 43 | ||
45 | if let Some(last) = path.segment() { | 44 | if let Some(last) = path.segment() { |
46 | // Here we are assuming the assist will provide a correct use statement | 45 | // Here we are assuming the assist will provide a correct use statement |
47 | // so we can delete the path qualifier | 46 | // so we can delete the path qualifier |
48 | edit.delete(TextRange::new( | 47 | builder.delete(TextRange::new( |
49 | path.syntax().text_range().start(), | 48 | path.syntax().text_range().start(), |
50 | last.syntax().text_range().start(), | 49 | last.syntax().text_range().start(), |
51 | )); | 50 | )); |
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 c6b73da67..a46998b8e 100644 --- a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -5,7 +5,7 @@ use ra_syntax::{ | |||
5 | AstNode, | 5 | AstNode, |
6 | }; | 6 | }; |
7 | 7 | ||
8 | use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; | 8 | use crate::{utils::TryEnum, AssistContext, AssistId, Assists}; |
9 | 9 | ||
10 | // Assist: replace_unwrap_with_match | 10 | // Assist: replace_unwrap_with_match |
11 | // | 11 | // |
@@ -29,7 +29,7 @@ use crate::{utils::TryEnum, Assist, AssistCtx, AssistId}; | |||
29 | // }; | 29 | // }; |
30 | // } | 30 | // } |
31 | // ``` | 31 | // ``` |
32 | pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> { | 32 | pub(crate) fn replace_unwrap_with_match(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
33 | let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; | 33 | let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; |
34 | let name = method_call.name_ref()?; | 34 | let name = method_call.name_ref()?; |
35 | if name.text() != "unwrap" { | 35 | if name.text() != "unwrap" { |
@@ -37,33 +37,26 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> { | |||
37 | } | 37 | } |
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(); | 41 | let target = method_call.syntax().text_range(); |
42 | ctx.add_assist( | 42 | acc.add(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", target, |edit| { |
43 | AssistId("replace_unwrap_with_match"), | 43 | let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant))); |
44 | "Replace unwrap with match", | 44 | let it = make::bind_pat(make::name("a")).into(); |
45 | target, | 45 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); |
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(); | ||
50 | 46 | ||
51 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | 47 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); |
52 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | 48 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); |
53 | 49 | ||
54 | let unreachable_call = make::unreachable_macro_call().into(); | 50 | let unreachable_call = make::unreachable_macro_call().into(); |
55 | let err_arm = | 51 | let err_arm = make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); |
56 | make::match_arm(iter::once(make::placeholder_pat().into()), unreachable_call); | ||
57 | 52 | ||
58 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | 53 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); |
59 | let match_expr = make::expr_match(caller.clone(), match_arm_list); | 54 | let match_expr = make::expr_match(caller.clone(), match_arm_list); |
60 | let match_expr = | 55 | let match_expr = IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); |
61 | IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); | ||
62 | 56 | ||
63 | edit.set_cursor(caller.syntax().text_range().start()); | 57 | edit.set_cursor(caller.syntax().text_range().start()); |
64 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | 58 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); |
65 | }, | 59 | }) |
66 | ) | ||
67 | } | 60 | } |
68 | 61 | ||
69 | #[cfg(test)] | 62 | #[cfg(test)] |
diff --git a/crates/ra_assists/src/handlers/split_import.rs b/crates/ra_assists/src/handlers/split_import.rs index d49563974..159033731 100644 --- a/crates/ra_assists/src/handlers/split_import.rs +++ b/crates/ra_assists/src/handlers/split_import.rs | |||
@@ -2,7 +2,7 @@ use std::iter::successors; | |||
2 | 2 | ||
3 | use ra_syntax::{ast, AstNode, T}; | 3 | use ra_syntax::{ast, AstNode, T}; |
4 | 4 | ||
5 | use crate::{Assist, AssistCtx, AssistId}; | 5 | use crate::{AssistContext, AssistId, Assists}; |
6 | 6 | ||
7 | // Assist: split_import | 7 | // Assist: split_import |
8 | // | 8 | // |
@@ -15,7 +15,7 @@ use crate::{Assist, AssistCtx, AssistId}; | |||
15 | // ``` | 15 | // ``` |
16 | // use std::{collections::HashMap}; | 16 | // use std::{collections::HashMap}; |
17 | // ``` | 17 | // ``` |
18 | pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> { | 18 | pub(crate) fn split_import(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
19 | let colon_colon = ctx.find_token_at_offset(T![::])?; | 19 | let colon_colon = ctx.find_token_at_offset(T![::])?; |
20 | let path = ast::Path::cast(colon_colon.parent())?.qualifier()?; | 20 | let path = ast::Path::cast(colon_colon.parent())?.qualifier()?; |
21 | let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?; | 21 | let top_path = successors(Some(path.clone()), |it| it.parent_path()).last()?; |
@@ -29,7 +29,7 @@ pub(crate) fn split_import(ctx: AssistCtx) -> Option<Assist> { | |||
29 | let cursor = ctx.frange.range.start(); | 29 | let cursor = ctx.frange.range.start(); |
30 | 30 | ||
31 | let target = colon_colon.text_range(); | 31 | let target = colon_colon.text_range(); |
32 | ctx.add_assist(AssistId("split_import"), "Split import", target, |edit| { | 32 | acc.add(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 | }) |
diff --git a/crates/ra_assists/src/handlers/unwrap_block.rs b/crates/ra_assists/src/handlers/unwrap_block.rs index 6df927abb..eba0631a4 100644 --- a/crates/ra_assists/src/handlers/unwrap_block.rs +++ b/crates/ra_assists/src/handlers/unwrap_block.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use crate::{Assist, AssistCtx, AssistId}; | 1 | use crate::{AssistContext, AssistId, Assists}; |
2 | 2 | ||
3 | use ast::LoopBodyOwner; | 3 | use ast::LoopBodyOwner; |
4 | use ra_fmt::unwrap_trivial_block; | 4 | use ra_fmt::unwrap_trivial_block; |
@@ -21,7 +21,7 @@ use ra_syntax::{ast, match_ast, AstNode, TextRange, T}; | |||
21 | // println!("foo"); | 21 | // println!("foo"); |
22 | // } | 22 | // } |
23 | // ``` | 23 | // ``` |
24 | pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> { | 24 | pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; | 25 | let l_curly_token = ctx.find_token_at_offset(T!['{'])?; |
26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; | 26 | let block = ast::BlockExpr::cast(l_curly_token.parent())?; |
27 | let parent = block.syntax().parent()?; | 27 | let parent = block.syntax().parent()?; |
@@ -58,7 +58,7 @@ pub(crate) fn unwrap_block(ctx: AssistCtx) -> Option<Assist> { | |||
58 | }; | 58 | }; |
59 | 59 | ||
60 | let target = expr_to_unwrap.syntax().text_range(); | 60 | let target = expr_to_unwrap.syntax().text_range(); |
61 | ctx.add_assist(AssistId("unwrap_block"), "Unwrap block", target, |edit| { | 61 | acc.add(AssistId("unwrap_block"), "Unwrap block", target, |edit| { |
62 | edit.set_cursor(expr.syntax().text_range().start()); | 62 | edit.set_cursor(expr.syntax().text_range().start()); |
63 | 63 | ||
64 | let pat_start: &[_] = &[' ', '{', '\n']; | 64 | let pat_start: &[_] = &[' ', '{', '\n']; |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 0473fd8c2..011613762 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -10,7 +10,7 @@ macro_rules! eprintln { | |||
10 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; | 10 | ($($tt:tt)*) => { stdx::eprintln!($($tt)*) }; |
11 | } | 11 | } |
12 | 12 | ||
13 | mod assist_ctx; | 13 | mod assist_context; |
14 | mod marks; | 14 | mod marks; |
15 | #[cfg(test)] | 15 | #[cfg(test)] |
16 | mod tests; | 16 | mod tests; |
@@ -22,7 +22,7 @@ use ra_db::FileRange; | |||
22 | use ra_ide_db::{source_change::SourceChange, RootDatabase}; | 22 | use ra_ide_db::{source_change::SourceChange, RootDatabase}; |
23 | use ra_syntax::TextRange; | 23 | use ra_syntax::TextRange; |
24 | 24 | ||
25 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; | 25 | pub(crate) use crate::assist_context::{AssistContext, Assists}; |
26 | 26 | ||
27 | /// Unique identifier of the assist, should not be shown to the user | 27 | /// Unique identifier of the assist, should not be shown to the user |
28 | /// directly. | 28 | /// directly. |
@@ -68,13 +68,12 @@ pub struct ResolvedAssist { | |||
68 | /// returned, without actual edits. | 68 | /// returned, without actual edits. |
69 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { | 69 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { |
70 | let sema = Semantics::new(db); | 70 | let sema = Semantics::new(db); |
71 | let ctx = AssistCtx::new(&sema, range, false); | 71 | let ctx = AssistContext::new(sema, range); |
72 | handlers::all() | 72 | let mut acc = Assists::new_unresolved(&ctx); |
73 | .iter() | 73 | handlers::all().iter().for_each(|handler| { |
74 | .filter_map(|f| f(ctx.clone())) | 74 | handler(&mut acc, &ctx); |
75 | .flat_map(|it| it.0) | 75 | }); |
76 | .map(|a| a.label) | 76 | acc.finish_unresolved() |
77 | .collect() | ||
78 | } | 77 | } |
79 | 78 | ||
80 | /// Return all the assists applicable at the given position. | 79 | /// Return all the assists applicable at the given position. |
@@ -83,31 +82,30 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe | |||
83 | /// computed. | 82 | /// computed. |
84 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 83 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { |
85 | let sema = Semantics::new(db); | 84 | let sema = Semantics::new(db); |
86 | let ctx = AssistCtx::new(&sema, range, true); | 85 | let ctx = AssistContext::new(sema, range); |
87 | let mut a = handlers::all() | 86 | let mut acc = Assists::new_resolved(&ctx); |
88 | .iter() | 87 | handlers::all().iter().for_each(|handler| { |
89 | .filter_map(|f| f(ctx.clone())) | 88 | handler(&mut acc, &ctx); |
90 | .flat_map(|it| it.0) | 89 | }); |
91 | .map(|it| it.into_resolved().unwrap()) | 90 | acc.finish_resolved() |
92 | .collect::<Vec<_>>(); | ||
93 | a.sort_by_key(|it| it.label.target.len()); | ||
94 | a | ||
95 | } | 91 | } |
96 | 92 | ||
97 | mod handlers { | 93 | mod handlers { |
98 | use crate::{Assist, AssistCtx}; | 94 | use crate::{AssistContext, Assists}; |
99 | 95 | ||
100 | pub(crate) type Handler = fn(AssistCtx) -> Option<Assist>; | 96 | pub(crate) type Handler = fn(&mut Assists, &AssistContext) -> Option<()>; |
101 | 97 | ||
102 | mod add_custom_impl; | 98 | mod add_custom_impl; |
103 | mod add_derive; | 99 | mod add_derive; |
104 | mod add_explicit_type; | 100 | mod add_explicit_type; |
101 | mod add_from_impl_for_enum; | ||
105 | mod add_function; | 102 | mod add_function; |
106 | mod add_impl; | 103 | mod add_impl; |
107 | mod add_missing_impl_members; | 104 | mod add_missing_impl_members; |
108 | mod add_new; | 105 | mod add_new; |
109 | mod apply_demorgan; | 106 | mod apply_demorgan; |
110 | mod auto_import; | 107 | mod auto_import; |
108 | mod change_return_type_to_result; | ||
111 | mod change_visibility; | 109 | mod change_visibility; |
112 | mod early_return; | 110 | mod early_return; |
113 | mod fill_match_arms; | 111 | mod fill_match_arms; |
@@ -124,14 +122,12 @@ mod handlers { | |||
124 | mod raw_string; | 122 | mod raw_string; |
125 | mod remove_dbg; | 123 | mod remove_dbg; |
126 | mod remove_mut; | 124 | mod remove_mut; |
125 | mod reorder_fields; | ||
127 | mod replace_if_let_with_match; | 126 | mod replace_if_let_with_match; |
128 | mod replace_let_with_if_let; | 127 | mod replace_let_with_if_let; |
129 | mod replace_qualified_name_with_use; | 128 | mod replace_qualified_name_with_use; |
130 | mod replace_unwrap_with_match; | 129 | mod replace_unwrap_with_match; |
131 | mod split_import; | 130 | mod split_import; |
132 | mod change_return_type_to_result; | ||
133 | mod add_from_impl_for_enum; | ||
134 | mod reorder_fields; | ||
135 | mod unwrap_block; | 131 | mod unwrap_block; |
136 | 132 | ||
137 | pub(crate) fn all() -> &'static [Handler] { | 133 | pub(crate) fn all() -> &'static [Handler] { |
diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 17e3ece9f..45b2d9733 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs | |||
@@ -11,7 +11,7 @@ use test_utils::{ | |||
11 | RangeOrOffset, | 11 | RangeOrOffset, |
12 | }; | 12 | }; |
13 | 13 | ||
14 | use crate::{handlers::Handler, resolved_assists, AssistCtx}; | 14 | use crate::{handlers::Handler, resolved_assists, AssistContext, Assists}; |
15 | 15 | ||
16 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 16 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
17 | let (mut db, file_id) = RootDatabase::with_single_file(text); | 17 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
@@ -71,7 +71,7 @@ enum ExpectedResult<'a> { | |||
71 | Target(&'a str), | 71 | Target(&'a str), |
72 | } | 72 | } |
73 | 73 | ||
74 | fn check(assist: Handler, before: &str, expected: ExpectedResult) { | 74 | fn check(handler: Handler, before: &str, expected: ExpectedResult) { |
75 | let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") { | 75 | let (text_without_caret, file_with_caret_id, range_or_offset, db) = if before.contains("//-") { |
76 | let (mut db, position) = RootDatabase::with_position(before); | 76 | let (mut db, position) = RootDatabase::with_position(before); |
77 | db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)])); | 77 | db.set_local_roots(Arc::new(vec![db.file_source_root(position.file_id)])); |
@@ -90,17 +90,20 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) { | |||
90 | let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; | 90 | let frange = FileRange { file_id: file_with_caret_id, range: range_or_offset.into() }; |
91 | 91 | ||
92 | let sema = Semantics::new(&db); | 92 | let sema = Semantics::new(&db); |
93 | let assist_ctx = AssistCtx::new(&sema, frange, true); | 93 | let ctx = AssistContext::new(sema, frange); |
94 | 94 | let mut acc = Assists::new_resolved(&ctx); | |
95 | match (assist(assist_ctx), expected) { | 95 | handler(&mut acc, &ctx); |
96 | let mut res = acc.finish_resolved(); | ||
97 | let assist = res.pop(); | ||
98 | match (assist, expected) { | ||
96 | (Some(assist), ExpectedResult::After(after)) => { | 99 | (Some(assist), ExpectedResult::After(after)) => { |
97 | let mut action = assist.0[0].source_change.clone().unwrap(); | 100 | let mut source_change = assist.source_change; |
98 | let change = action.source_file_edits.pop().unwrap(); | 101 | let change = source_change.source_file_edits.pop().unwrap(); |
99 | 102 | ||
100 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); | 103 | let mut actual = db.file_text(change.file_id).as_ref().to_owned(); |
101 | change.edit.apply(&mut actual); | 104 | change.edit.apply(&mut actual); |
102 | 105 | ||
103 | match action.cursor_position { | 106 | match source_change.cursor_position { |
104 | None => { | 107 | None => { |
105 | if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { | 108 | if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset { |
106 | let off = change | 109 | let off = change |
@@ -116,7 +119,7 @@ fn check(assist: Handler, before: &str, expected: ExpectedResult) { | |||
116 | assert_eq_text!(after, &actual); | 119 | assert_eq_text!(after, &actual); |
117 | } | 120 | } |
118 | (Some(assist), ExpectedResult::Target(target)) => { | 121 | (Some(assist), ExpectedResult::Target(target)) => { |
119 | let range = assist.0[0].label.target; | 122 | let range = assist.label.target; |
120 | assert_eq_text!(&text_without_caret[range], target); | 123 | assert_eq_text!(&text_without_caret[range], target); |
121 | } | 124 | } |
122 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), | 125 | (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"), |
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs index c1f447efe..1214e3cd4 100644 --- a/crates/ra_assists/src/utils/insert_use.rs +++ b/crates/ra_assists/src/utils/insert_use.rs | |||
@@ -2,7 +2,6 @@ | |||
2 | // FIXME: rewrite according to the plan, outlined in | 2 | // FIXME: rewrite according to the plan, outlined in |
3 | // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 | 3 | // https://github.com/rust-analyzer/rust-analyzer/issues/3301#issuecomment-592931553 |
4 | 4 | ||
5 | use crate::assist_ctx::ActionBuilder; | ||
6 | use hir::{self, ModPath}; | 5 | use hir::{self, ModPath}; |
7 | use ra_syntax::{ | 6 | use ra_syntax::{ |
8 | ast::{self, NameOwner}, | 7 | ast::{self, NameOwner}, |
@@ -12,6 +11,8 @@ use ra_syntax::{ | |||
12 | }; | 11 | }; |
13 | use ra_text_edit::TextEditBuilder; | 12 | use ra_text_edit::TextEditBuilder; |
14 | 13 | ||
14 | use crate::assist_context::{AssistBuilder, AssistContext}; | ||
15 | |||
15 | /// Creates and inserts a use statement for the given path to import. | 16 | /// Creates and inserts a use statement for the given path to import. |
16 | /// The use statement is inserted in the scope most appropriate to the | 17 | /// The use statement is inserted in the scope most appropriate to the |
17 | /// the cursor position given, additionally merged with the existing use imports. | 18 | /// the cursor position given, additionally merged with the existing use imports. |
@@ -19,10 +20,11 @@ pub(crate) fn insert_use_statement( | |||
19 | // Ideally the position of the cursor, used to | 20 | // Ideally the position of the cursor, used to |
20 | position: &SyntaxNode, | 21 | position: &SyntaxNode, |
21 | path_to_import: &ModPath, | 22 | path_to_import: &ModPath, |
22 | edit: &mut ActionBuilder, | 23 | ctx: &AssistContext, |
24 | builder: &mut AssistBuilder, | ||
23 | ) { | 25 | ) { |
24 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); | 26 | let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); |
25 | let container = edit.ctx().sema.ancestors_with_macros(position.clone()).find_map(|n| { | 27 | let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { |
26 | if let Some(module) = ast::Module::cast(n.clone()) { | 28 | if let Some(module) = ast::Module::cast(n.clone()) { |
27 | return module.item_list().map(|it| it.syntax().clone()); | 29 | return module.item_list().map(|it| it.syntax().clone()); |
28 | } | 30 | } |
@@ -31,7 +33,7 @@ pub(crate) fn insert_use_statement( | |||
31 | 33 | ||
32 | if let Some(container) = container { | 34 | if let Some(container) = container { |
33 | let action = best_action_for_target(container, position.clone(), &target); | 35 | let action = best_action_for_target(container, position.clone(), &target); |
34 | make_assist(&action, &target, edit.text_edit_builder()); | 36 | make_assist(&action, &target, builder.text_edit_builder()); |
35 | } | 37 | } |
36 | } | 38 | } |
37 | 39 | ||