diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-11 17:30:53 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-11 17:30:53 +0000 |
commit | b356ab46f2b7482bf1ae0c0f6cd5a87ece8742bf (patch) | |
tree | 094055dc8b24154fb8525c5910ae6dde4b4f4fe9 /crates/ra_assists/src/lib.rs | |
parent | 77ccac74f94fbe387fc587d46f9d93f04fce3644 (diff) | |
parent | 5c9c0d3ae2735b4b32a44742bac800ca616fdde8 (diff) |
Merge #781
781: Refactor to allow for multiple assists r=matklad a=eulerdisk
This is necessary to allow assist "providers" (which currently are simple free function) to produce multiple assists. I'm not sure this is the best possible refactoring tough.
Co-authored-by: Andrea Pretto <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/lib.rs')
-rw-r--r-- | crates/ra_assists/src/lib.rs | 75 |
1 files changed, 59 insertions, 16 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index af578893e..c607a5142 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -7,6 +7,8 @@ | |||
7 | 7 | ||
8 | mod assist_ctx; | 8 | mod assist_ctx; |
9 | 9 | ||
10 | use itertools::Itertools; | ||
11 | |||
10 | use ra_text_edit::TextEdit; | 12 | use ra_text_edit::TextEdit; |
11 | use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction}; | 13 | use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction}; |
12 | use ra_db::FileRange; | 14 | use ra_db::FileRange; |
@@ -14,12 +16,13 @@ use hir::db::HirDatabase; | |||
14 | 16 | ||
15 | pub(crate) use crate::assist_ctx::{AssistCtx, Assist}; | 17 | pub(crate) use crate::assist_ctx::{AssistCtx, Assist}; |
16 | 18 | ||
17 | #[derive(Debug)] | 19 | #[derive(Debug, Clone)] |
18 | pub struct AssistLabel { | 20 | pub struct AssistLabel { |
19 | /// Short description of the assist, as shown in the UI. | 21 | /// Short description of the assist, as shown in the UI. |
20 | pub label: String, | 22 | pub label: String, |
21 | } | 23 | } |
22 | 24 | ||
25 | #[derive(Debug, Clone)] | ||
23 | pub struct AssistAction { | 26 | pub struct AssistAction { |
24 | pub edit: TextEdit, | 27 | pub edit: TextEdit, |
25 | pub cursor_position: Option<TextUnit>, | 28 | pub cursor_position: Option<TextUnit>, |
@@ -39,10 +42,10 @@ where | |||
39 | .iter() | 42 | .iter() |
40 | .filter_map(|f| f(ctx.clone())) | 43 | .filter_map(|f| f(ctx.clone())) |
41 | .map(|a| match a { | 44 | .map(|a| match a { |
42 | Assist::Unresolved(label) => label, | 45 | Assist::Unresolved(labels) => labels, |
43 | Assist::Resolved(..) => unreachable!(), | 46 | Assist::Resolved(..) => unreachable!(), |
44 | }) | 47 | }) |
45 | .collect() | 48 | .concat() |
46 | }) | 49 | }) |
47 | } | 50 | } |
48 | 51 | ||
@@ -61,10 +64,10 @@ where | |||
61 | .iter() | 64 | .iter() |
62 | .filter_map(|f| f(ctx.clone())) | 65 | .filter_map(|f| f(ctx.clone())) |
63 | .map(|a| match a { | 66 | .map(|a| match a { |
64 | Assist::Resolved(label, action) => (label, action), | 67 | Assist::Resolved(labels_actions) => labels_actions, |
65 | Assist::Unresolved(..) => unreachable!(), | 68 | Assist::Unresolved(..) => unreachable!(), |
66 | }) | 69 | }) |
67 | .collect::<Vec<(AssistLabel, AssistAction)>>(); | 70 | .concat(); |
68 | a.sort_by(|a, b| match (a.1.target, b.1.target) { | 71 | a.sort_by(|a, b| match (a.1.target, b.1.target) { |
69 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | 72 | (Some(a), Some(b)) => a.len().cmp(&b.len()), |
70 | (Some(_), None) => Ordering::Less, | 73 | (Some(_), None) => Ordering::Less, |
@@ -119,17 +122,51 @@ mod helpers { | |||
119 | before: &str, | 122 | before: &str, |
120 | after: &str, | 123 | after: &str, |
121 | ) { | 124 | ) { |
125 | check_assist_nth_action(assist, before, after, 0) | ||
126 | } | ||
127 | |||
128 | pub(crate) fn check_assist_range( | ||
129 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
130 | before: &str, | ||
131 | after: &str, | ||
132 | ) { | ||
133 | check_assist_range_nth_action(assist, before, after, 0) | ||
134 | } | ||
135 | |||
136 | pub(crate) fn check_assist_target( | ||
137 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
138 | before: &str, | ||
139 | target: &str, | ||
140 | ) { | ||
141 | check_assist_target_nth_action(assist, before, target, 0) | ||
142 | } | ||
143 | |||
144 | pub(crate) fn check_assist_range_target( | ||
145 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
146 | before: &str, | ||
147 | target: &str, | ||
148 | ) { | ||
149 | check_assist_range_target_nth_action(assist, before, target, 0) | ||
150 | } | ||
151 | |||
152 | pub(crate) fn check_assist_nth_action( | ||
153 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
154 | before: &str, | ||
155 | after: &str, | ||
156 | index: usize, | ||
157 | ) { | ||
122 | let (before_cursor_pos, before) = extract_offset(before); | 158 | let (before_cursor_pos, before) = extract_offset(before); |
123 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 159 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
124 | let frange = | 160 | let frange = |
125 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 161 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
126 | let assist = | 162 | let assist = |
127 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 163 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
128 | let action = match assist { | 164 | let labels_actions = match assist { |
129 | Assist::Unresolved(_) => unreachable!(), | 165 | Assist::Unresolved(_) => unreachable!(), |
130 | Assist::Resolved(_, it) => it, | 166 | Assist::Resolved(labels_actions) => labels_actions, |
131 | }; | 167 | }; |
132 | 168 | ||
169 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
133 | let actual = action.edit.apply(&before); | 170 | let actual = action.edit.apply(&before); |
134 | let actual_cursor_pos = match action.cursor_position { | 171 | let actual_cursor_pos = match action.cursor_position { |
135 | None => action | 172 | None => action |
@@ -142,21 +179,23 @@ mod helpers { | |||
142 | assert_eq_text!(after, &actual); | 179 | assert_eq_text!(after, &actual); |
143 | } | 180 | } |
144 | 181 | ||
145 | pub(crate) fn check_assist_range( | 182 | pub(crate) fn check_assist_range_nth_action( |
146 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | 183 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, |
147 | before: &str, | 184 | before: &str, |
148 | after: &str, | 185 | after: &str, |
186 | index: usize, | ||
149 | ) { | 187 | ) { |
150 | let (range, before) = extract_range(before); | 188 | let (range, before) = extract_range(before); |
151 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 189 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
152 | let frange = FileRange { file_id, range }; | 190 | let frange = FileRange { file_id, range }; |
153 | let assist = | 191 | let assist = |
154 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 192 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
155 | let action = match assist { | 193 | let labels_actions = match assist { |
156 | Assist::Unresolved(_) => unreachable!(), | 194 | Assist::Unresolved(_) => unreachable!(), |
157 | Assist::Resolved(_, it) => it, | 195 | Assist::Resolved(labels_actions) => labels_actions, |
158 | }; | 196 | }; |
159 | 197 | ||
198 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
160 | let mut actual = action.edit.apply(&before); | 199 | let mut actual = action.edit.apply(&before); |
161 | if let Some(pos) = action.cursor_position { | 200 | if let Some(pos) = action.cursor_position { |
162 | actual = add_cursor(&actual, pos); | 201 | actual = add_cursor(&actual, pos); |
@@ -164,10 +203,11 @@ mod helpers { | |||
164 | assert_eq_text!(after, &actual); | 203 | assert_eq_text!(after, &actual); |
165 | } | 204 | } |
166 | 205 | ||
167 | pub(crate) fn check_assist_target( | 206 | pub(crate) fn check_assist_target_nth_action( |
168 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | 207 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, |
169 | before: &str, | 208 | before: &str, |
170 | target: &str, | 209 | target: &str, |
210 | index: usize, | ||
171 | ) { | 211 | ) { |
172 | let (before_cursor_pos, before) = extract_offset(before); | 212 | let (before_cursor_pos, before) = extract_offset(before); |
173 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 213 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
@@ -175,30 +215,33 @@ mod helpers { | |||
175 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 215 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
176 | let assist = | 216 | let assist = |
177 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 217 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
178 | let action = match assist { | 218 | let labels_actions = match assist { |
179 | Assist::Unresolved(_) => unreachable!(), | 219 | Assist::Unresolved(_) => unreachable!(), |
180 | Assist::Resolved(_, it) => it, | 220 | Assist::Resolved(labels_actions) => labels_actions, |
181 | }; | 221 | }; |
182 | 222 | ||
223 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
183 | let range = action.target.expect("expected target on action"); | 224 | let range = action.target.expect("expected target on action"); |
184 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | 225 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); |
185 | } | 226 | } |
186 | 227 | ||
187 | pub(crate) fn check_assist_range_target( | 228 | pub(crate) fn check_assist_range_target_nth_action( |
188 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | 229 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, |
189 | before: &str, | 230 | before: &str, |
190 | target: &str, | 231 | target: &str, |
232 | index: usize, | ||
191 | ) { | 233 | ) { |
192 | let (range, before) = extract_range(before); | 234 | let (range, before) = extract_range(before); |
193 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 235 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
194 | let frange = FileRange { file_id, range }; | 236 | let frange = FileRange { file_id, range }; |
195 | let assist = | 237 | let assist = |
196 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 238 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
197 | let action = match assist { | 239 | let labels_actions = match assist { |
198 | Assist::Unresolved(_) => unreachable!(), | 240 | Assist::Unresolved(_) => unreachable!(), |
199 | Assist::Resolved(_, it) => it, | 241 | Assist::Resolved(labels_actions) => labels_actions, |
200 | }; | 242 | }; |
201 | 243 | ||
244 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
202 | let range = action.target.expect("expected target on action"); | 245 | let range = action.target.expect("expected target on action"); |
203 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | 246 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); |
204 | } | 247 | } |