diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 36 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 83 |
2 files changed, 37 insertions, 82 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index c5e9056af..1908bdec9 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -14,8 +14,8 @@ use crate::{AssistAction, AssistId, AssistLabel}; | |||
14 | 14 | ||
15 | #[derive(Clone, Debug)] | 15 | #[derive(Clone, Debug)] |
16 | pub(crate) enum Assist { | 16 | pub(crate) enum Assist { |
17 | Unresolved(Vec<AssistLabel>), | 17 | Unresolved { label: AssistLabel }, |
18 | Resolved(Vec<(AssistLabel, AssistAction)>), | 18 | Resolved { label: AssistLabel, action: AssistAction }, |
19 | } | 19 | } |
20 | 20 | ||
21 | /// `AssistCtx` allows to apply an assist or check if it could be applied. | 21 | /// `AssistCtx` allows to apply an assist or check if it could be applied. |
@@ -54,7 +54,6 @@ pub(crate) struct AssistCtx<'a, DB> { | |||
54 | pub(crate) frange: FileRange, | 54 | pub(crate) frange: FileRange, |
55 | source_file: SourceFile, | 55 | source_file: SourceFile, |
56 | should_compute_edit: bool, | 56 | should_compute_edit: bool, |
57 | assist: Assist, | ||
58 | } | 57 | } |
59 | 58 | ||
60 | impl<'a, DB> Clone for AssistCtx<'a, DB> { | 59 | impl<'a, DB> Clone for AssistCtx<'a, DB> { |
@@ -64,7 +63,6 @@ impl<'a, DB> Clone for AssistCtx<'a, DB> { | |||
64 | frange: self.frange, | 63 | frange: self.frange, |
65 | source_file: self.source_file.clone(), | 64 | source_file: self.source_file.clone(), |
66 | should_compute_edit: self.should_compute_edit, | 65 | should_compute_edit: self.should_compute_edit, |
67 | assist: self.assist.clone(), | ||
68 | } | 66 | } |
69 | } | 67 | } |
70 | } | 68 | } |
@@ -75,32 +73,30 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
75 | F: FnOnce(AssistCtx<DB>) -> T, | 73 | F: FnOnce(AssistCtx<DB>) -> T, |
76 | { | 74 | { |
77 | let parse = db.parse(frange.file_id); | 75 | let parse = db.parse(frange.file_id); |
78 | let assist = | ||
79 | if should_compute_edit { Assist::Resolved(vec![]) } else { Assist::Unresolved(vec![]) }; | ||
80 | 76 | ||
81 | let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit, assist }; | 77 | let ctx = AssistCtx { db, frange, source_file: parse.tree(), should_compute_edit }; |
82 | f(ctx) | 78 | f(ctx) |
83 | } | 79 | } |
84 | 80 | ||
85 | pub(crate) fn add_assist( | 81 | pub(crate) fn add_assist( |
86 | mut self, | 82 | self, |
87 | id: AssistId, | 83 | id: AssistId, |
88 | label: impl Into<String>, | 84 | label: impl Into<String>, |
89 | f: impl FnOnce(&mut AssistBuilder), | 85 | f: impl FnOnce(&mut AssistBuilder), |
90 | ) -> Option<Assist> { | 86 | ) -> Option<Assist> { |
91 | let label = AssistLabel { label: label.into(), id }; | 87 | let label = AssistLabel { label: label.into(), id }; |
92 | match &mut self.assist { | 88 | let assist = if self.should_compute_edit { |
93 | Assist::Unresolved(labels) => labels.push(label), | 89 | let action = { |
94 | Assist::Resolved(labels_actions) => { | 90 | let mut edit = AssistBuilder::default(); |
95 | let action = { | 91 | f(&mut edit); |
96 | let mut edit = AssistBuilder::default(); | 92 | edit.build() |
97 | f(&mut edit); | 93 | }; |
98 | edit.build() | 94 | Assist::Resolved { label, action } |
99 | }; | 95 | } else { |
100 | labels_actions.push((label, action)); | 96 | Assist::Unresolved { label } |
101 | } | 97 | }; |
102 | } | 98 | |
103 | Some(self.assist) | 99 | Some(assist) |
104 | } | 100 | } |
105 | 101 | ||
106 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { | 102 | pub(crate) fn token_at_offset(&self) -> TokenAtOffset<SyntaxToken> { |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 7a1657d87..38599d4f1 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -11,7 +11,6 @@ mod marks; | |||
11 | mod doc_tests; | 11 | mod doc_tests; |
12 | 12 | ||
13 | use hir::db::HirDatabase; | 13 | use hir::db::HirDatabase; |
14 | use itertools::Itertools; | ||
15 | use ra_db::FileRange; | 14 | use ra_db::FileRange; |
16 | use ra_syntax::{TextRange, TextUnit}; | 15 | use ra_syntax::{TextRange, TextUnit}; |
17 | use ra_text_edit::TextEdit; | 16 | use ra_text_edit::TextEdit; |
@@ -51,10 +50,10 @@ where | |||
51 | .iter() | 50 | .iter() |
52 | .filter_map(|f| f(ctx.clone())) | 51 | .filter_map(|f| f(ctx.clone())) |
53 | .map(|a| match a { | 52 | .map(|a| match a { |
54 | Assist::Unresolved(labels) => labels, | 53 | Assist::Unresolved { label } => label, |
55 | Assist::Resolved(..) => unreachable!(), | 54 | Assist::Resolved { .. } => unreachable!(), |
56 | }) | 55 | }) |
57 | .concat() | 56 | .collect() |
58 | }) | 57 | }) |
59 | } | 58 | } |
60 | 59 | ||
@@ -73,10 +72,10 @@ where | |||
73 | .iter() | 72 | .iter() |
74 | .filter_map(|f| f(ctx.clone())) | 73 | .filter_map(|f| f(ctx.clone())) |
75 | .map(|a| match a { | 74 | .map(|a| match a { |
76 | Assist::Resolved(labels_actions) => labels_actions, | 75 | Assist::Resolved { label, action } => (label, action), |
77 | Assist::Unresolved(..) => unreachable!(), | 76 | Assist::Unresolved { .. } => unreachable!(), |
78 | }) | 77 | }) |
79 | .concat(); | 78 | .collect::<Vec<_>>(); |
80 | a.sort_by(|a, b| match (a.1.target, b.1.target) { | 79 | a.sort_by(|a, b| match (a.1.target, b.1.target) { |
81 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | 80 | (Some(a), Some(b)) => a.len().cmp(&b.len()), |
82 | (Some(_), None) => Ordering::Less, | 81 | (Some(_), None) => Ordering::Less, |
@@ -159,51 +158,17 @@ mod helpers { | |||
159 | before: &str, | 158 | before: &str, |
160 | after: &str, | 159 | after: &str, |
161 | ) { | 160 | ) { |
162 | check_assist_nth_action(assist, before, after, 0) | ||
163 | } | ||
164 | |||
165 | pub(crate) fn check_assist_range( | ||
166 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
167 | before: &str, | ||
168 | after: &str, | ||
169 | ) { | ||
170 | check_assist_range_nth_action(assist, before, after, 0) | ||
171 | } | ||
172 | |||
173 | pub(crate) fn check_assist_target( | ||
174 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
175 | before: &str, | ||
176 | target: &str, | ||
177 | ) { | ||
178 | check_assist_target_nth_action(assist, before, target, 0) | ||
179 | } | ||
180 | |||
181 | pub(crate) fn check_assist_range_target( | ||
182 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
183 | before: &str, | ||
184 | target: &str, | ||
185 | ) { | ||
186 | check_assist_range_target_nth_action(assist, before, target, 0) | ||
187 | } | ||
188 | |||
189 | pub(crate) fn check_assist_nth_action( | ||
190 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | ||
191 | before: &str, | ||
192 | after: &str, | ||
193 | index: usize, | ||
194 | ) { | ||
195 | let (before_cursor_pos, before) = extract_offset(before); | 161 | let (before_cursor_pos, before) = extract_offset(before); |
196 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 162 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
197 | let frange = | 163 | let frange = |
198 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 164 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
199 | let assist = | 165 | let assist = |
200 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 166 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
201 | let labels_actions = match assist { | 167 | let action = match assist { |
202 | Assist::Unresolved(_) => unreachable!(), | 168 | Assist::Unresolved { .. } => unreachable!(), |
203 | Assist::Resolved(labels_actions) => labels_actions, | 169 | Assist::Resolved { action, .. } => action, |
204 | }; | 170 | }; |
205 | 171 | ||
206 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
207 | let actual = action.edit.apply(&before); | 172 | let actual = action.edit.apply(&before); |
208 | let actual_cursor_pos = match action.cursor_position { | 173 | let actual_cursor_pos = match action.cursor_position { |
209 | None => action | 174 | None => action |
@@ -216,23 +181,21 @@ mod helpers { | |||
216 | assert_eq_text!(after, &actual); | 181 | assert_eq_text!(after, &actual); |
217 | } | 182 | } |
218 | 183 | ||
219 | pub(crate) fn check_assist_range_nth_action( | 184 | pub(crate) fn check_assist_range( |
220 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | 185 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, |
221 | before: &str, | 186 | before: &str, |
222 | after: &str, | 187 | after: &str, |
223 | index: usize, | ||
224 | ) { | 188 | ) { |
225 | let (range, before) = extract_range(before); | 189 | let (range, before) = extract_range(before); |
226 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 190 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
227 | let frange = FileRange { file_id, range }; | 191 | let frange = FileRange { file_id, range }; |
228 | let assist = | 192 | let assist = |
229 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 193 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
230 | let labels_actions = match assist { | 194 | let action = match assist { |
231 | Assist::Unresolved(_) => unreachable!(), | 195 | Assist::Unresolved { .. } => unreachable!(), |
232 | Assist::Resolved(labels_actions) => labels_actions, | 196 | Assist::Resolved { action, .. } => action, |
233 | }; | 197 | }; |
234 | 198 | ||
235 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
236 | let mut actual = action.edit.apply(&before); | 199 | let mut actual = action.edit.apply(&before); |
237 | if let Some(pos) = action.cursor_position { | 200 | if let Some(pos) = action.cursor_position { |
238 | actual = add_cursor(&actual, pos); | 201 | actual = add_cursor(&actual, pos); |
@@ -240,11 +203,10 @@ mod helpers { | |||
240 | assert_eq_text!(after, &actual); | 203 | assert_eq_text!(after, &actual); |
241 | } | 204 | } |
242 | 205 | ||
243 | pub(crate) fn check_assist_target_nth_action( | 206 | pub(crate) fn check_assist_target( |
244 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | 207 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, |
245 | before: &str, | 208 | before: &str, |
246 | target: &str, | 209 | target: &str, |
247 | index: usize, | ||
248 | ) { | 210 | ) { |
249 | let (before_cursor_pos, before) = extract_offset(before); | 211 | let (before_cursor_pos, before) = extract_offset(before); |
250 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 212 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
@@ -252,33 +214,30 @@ mod helpers { | |||
252 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 214 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
253 | let assist = | 215 | let assist = |
254 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 216 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
255 | let labels_actions = match assist { | 217 | let action = match assist { |
256 | Assist::Unresolved(_) => unreachable!(), | 218 | Assist::Unresolved { .. } => unreachable!(), |
257 | Assist::Resolved(labels_actions) => labels_actions, | 219 | Assist::Resolved { action, .. } => action, |
258 | }; | 220 | }; |
259 | 221 | ||
260 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
261 | let range = action.target.expect("expected target on action"); | 222 | let range = action.target.expect("expected target on action"); |
262 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | 223 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); |
263 | } | 224 | } |
264 | 225 | ||
265 | pub(crate) fn check_assist_range_target_nth_action( | 226 | pub(crate) fn check_assist_range_target( |
266 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, | 227 | assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, |
267 | before: &str, | 228 | before: &str, |
268 | target: &str, | 229 | target: &str, |
269 | index: usize, | ||
270 | ) { | 230 | ) { |
271 | let (range, before) = extract_range(before); | 231 | let (range, before) = extract_range(before); |
272 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); | 232 | let (db, _source_root, file_id) = MockDatabase::with_single_file(&before); |
273 | let frange = FileRange { file_id, range }; | 233 | let frange = FileRange { file_id, range }; |
274 | let assist = | 234 | let assist = |
275 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 235 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
276 | let labels_actions = match assist { | 236 | let action = match assist { |
277 | Assist::Unresolved(_) => unreachable!(), | 237 | Assist::Unresolved { .. } => unreachable!(), |
278 | Assist::Resolved(labels_actions) => labels_actions, | 238 | Assist::Resolved { action, .. } => action, |
279 | }; | 239 | }; |
280 | 240 | ||
281 | let (_, action) = labels_actions.get(index).expect("expect assist action at index"); | ||
282 | let range = action.target.expect("expected target on action"); | 241 | let range = action.target.expect("expected target on action"); |
283 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | 242 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); |
284 | } | 243 | } |