diff options
Diffstat (limited to 'crates/ra_assists')
-rw-r--r-- | crates/ra_assists/src/assist_ctx.rs | 25 | ||||
-rw-r--r-- | crates/ra_assists/src/doc_tests.rs | 8 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 40 |
3 files changed, 45 insertions, 28 deletions
diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index 879216a36..e13f969c7 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs | |||
@@ -1,5 +1,6 @@ | |||
1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. | 1 | //! This module defines `AssistCtx` -- the API surface that is exposed to assists. |
2 | use hir::{db::HirDatabase, InFile, SourceAnalyzer}; | 2 | use hir::{db::HirDatabase, InFile, SourceAnalyzer}; |
3 | use itertools::Either; | ||
3 | use ra_db::FileRange; | 4 | use ra_db::FileRange; |
4 | use ra_fmt::{leading_indent, reindent}; | 5 | use ra_fmt::{leading_indent, reindent}; |
5 | use ra_syntax::{ | 6 | use ra_syntax::{ |
@@ -9,12 +10,12 @@ use ra_syntax::{ | |||
9 | }; | 10 | }; |
10 | use ra_text_edit::TextEditBuilder; | 11 | use ra_text_edit::TextEditBuilder; |
11 | 12 | ||
12 | use crate::{AssistAction, AssistId, AssistLabel}; | 13 | use crate::{AssistAction, AssistId, AssistLabel, ResolvedAssist}; |
13 | 14 | ||
14 | #[derive(Clone, Debug)] | 15 | #[derive(Clone, Debug)] |
15 | pub(crate) enum Assist { | 16 | pub(crate) enum Assist { |
16 | Unresolved { label: AssistLabel }, | 17 | Unresolved { label: AssistLabel }, |
17 | Resolved { label: AssistLabel, action: AssistAction, alternative_actions: Vec<AssistAction> }, | 18 | Resolved { assist: ResolvedAssist }, |
18 | } | 19 | } |
19 | 20 | ||
20 | /// `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. |
@@ -92,7 +93,7 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
92 | f(&mut edit); | 93 | f(&mut edit); |
93 | edit.build() | 94 | edit.build() |
94 | }; | 95 | }; |
95 | Assist::Resolved { label, action, alternative_actions: Vec::default() } | 96 | Assist::Resolved { assist: ResolvedAssist { label, action_data: Either::Left(action) } } |
96 | } else { | 97 | } else { |
97 | Assist::Unresolved { label } | 98 | Assist::Unresolved { label } |
98 | }; | 99 | }; |
@@ -105,18 +106,20 @@ impl<'a, DB: HirDatabase> AssistCtx<'a, DB> { | |||
105 | self, | 106 | self, |
106 | id: AssistId, | 107 | id: AssistId, |
107 | label: impl Into<String>, | 108 | label: impl Into<String>, |
108 | f: impl FnOnce() -> (ActionBuilder, Vec<ActionBuilder>), | 109 | f: impl FnOnce() -> Vec<ActionBuilder>, |
109 | ) -> Option<Assist> { | 110 | ) -> Option<Assist> { |
110 | let label = AssistLabel { label: label.into(), id }; | 111 | let label = AssistLabel { label: label.into(), id }; |
111 | let assist = if self.should_compute_edit { | 112 | let assist = if self.should_compute_edit { |
112 | let (action, alternative_actions) = f(); | 113 | let actions = f(); |
114 | assert!(!actions.is_empty(), "Assist cannot have no"); | ||
115 | |||
113 | Assist::Resolved { | 116 | Assist::Resolved { |
114 | label, | 117 | assist: ResolvedAssist { |
115 | action: action.build(), | 118 | label, |
116 | alternative_actions: alternative_actions | 119 | action_data: Either::Right( |
117 | .into_iter() | 120 | actions.into_iter().map(ActionBuilder::build).collect(), |
118 | .map(ActionBuilder::build) | 121 | ), |
119 | .collect(), | 122 | }, |
120 | } | 123 | } |
121 | } else { | 124 | } else { |
122 | Assist::Unresolved { label } | 125 | Assist::Unresolved { label } |
diff --git a/crates/ra_assists/src/doc_tests.rs b/crates/ra_assists/src/doc_tests.rs index 21bee228d..5dc1ee233 100644 --- a/crates/ra_assists/src/doc_tests.rs +++ b/crates/ra_assists/src/doc_tests.rs | |||
@@ -15,21 +15,21 @@ fn check(assist_id: &str, before: &str, after: &str) { | |||
15 | let (db, file_id) = TestDB::with_single_file(&before); | 15 | let (db, file_id) = TestDB::with_single_file(&before); |
16 | let frange = FileRange { file_id, range: selection.into() }; | 16 | let frange = FileRange { file_id, range: selection.into() }; |
17 | 17 | ||
18 | let (_assist_id, action, _) = crate::assists(&db, frange) | 18 | let assist = crate::assists(&db, frange) |
19 | .into_iter() | 19 | .into_iter() |
20 | .find(|(id, _, _)| id.id.0 == assist_id) | 20 | .find(|assist| assist.label.id.0 == assist_id) |
21 | .unwrap_or_else(|| { | 21 | .unwrap_or_else(|| { |
22 | panic!( | 22 | panic!( |
23 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", | 23 | "\n\nAssist is not applicable: {}\nAvailable assists: {}", |
24 | assist_id, | 24 | assist_id, |
25 | crate::assists(&db, frange) | 25 | crate::assists(&db, frange) |
26 | .into_iter() | 26 | .into_iter() |
27 | .map(|(id, _, _)| id.id.0) | 27 | .map(|assist| assist.label.id.0) |
28 | .collect::<Vec<_>>() | 28 | .collect::<Vec<_>>() |
29 | .join(", ") | 29 | .join(", ") |
30 | ) | 30 | ) |
31 | }); | 31 | }); |
32 | 32 | ||
33 | let actual = action.edit.apply(&before); | 33 | let actual = assist.get_first_action().edit.apply(&before); |
34 | assert_eq_text!(after, &actual); | 34 | assert_eq_text!(after, &actual); |
35 | } | 35 | } |
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 95a530821..a2983ae87 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -14,6 +14,7 @@ mod test_db; | |||
14 | pub mod ast_transform; | 14 | pub mod ast_transform; |
15 | 15 | ||
16 | use hir::db::HirDatabase; | 16 | use hir::db::HirDatabase; |
17 | use itertools::Either; | ||
17 | use ra_db::FileRange; | 18 | use ra_db::FileRange; |
18 | use ra_syntax::{TextRange, TextUnit}; | 19 | use ra_syntax::{TextRange, TextUnit}; |
19 | use ra_text_edit::TextEdit; | 20 | use ra_text_edit::TextEdit; |
@@ -41,6 +42,21 @@ pub struct AssistAction { | |||
41 | pub target: Option<TextRange>, | 42 | pub target: Option<TextRange>, |
42 | } | 43 | } |
43 | 44 | ||
45 | #[derive(Debug, Clone)] | ||
46 | pub struct ResolvedAssist { | ||
47 | pub label: AssistLabel, | ||
48 | pub action_data: Either<AssistAction, Vec<AssistAction>>, | ||
49 | } | ||
50 | |||
51 | impl ResolvedAssist { | ||
52 | pub fn get_first_action(&self) -> AssistAction { | ||
53 | match &self.action_data { | ||
54 | Either::Left(action) => action.clone(), | ||
55 | Either::Right(actions) => actions[0].clone(), | ||
56 | } | ||
57 | } | ||
58 | } | ||
59 | |||
44 | /// Return all the assists applicable at the given position. | 60 | /// Return all the assists applicable at the given position. |
45 | /// | 61 | /// |
46 | /// Assists are returned in the "unresolved" state, that is only labels are | 62 | /// Assists are returned in the "unresolved" state, that is only labels are |
@@ -65,7 +81,7 @@ where | |||
65 | /// | 81 | /// |
66 | /// Assists are returned in the "resolved" state, that is with edit fully | 82 | /// Assists are returned in the "resolved" state, that is with edit fully |
67 | /// computed. | 83 | /// computed. |
68 | pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction, Vec<AssistAction>)> | 84 | pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist> |
69 | where | 85 | where |
70 | H: HirDatabase + 'static, | 86 | H: HirDatabase + 'static, |
71 | { | 87 | { |
@@ -76,13 +92,11 @@ where | |||
76 | .iter() | 92 | .iter() |
77 | .filter_map(|f| f(ctx.clone())) | 93 | .filter_map(|f| f(ctx.clone())) |
78 | .map(|a| match a { | 94 | .map(|a| match a { |
79 | Assist::Resolved { label, action, alternative_actions } => { | 95 | Assist::Resolved { assist } => assist, |
80 | (label, action, alternative_actions) | ||
81 | } | ||
82 | Assist::Unresolved { .. } => unreachable!(), | 96 | Assist::Unresolved { .. } => unreachable!(), |
83 | }) | 97 | }) |
84 | .collect::<Vec<_>>(); | 98 | .collect::<Vec<_>>(); |
85 | a.sort_by(|a, b| match (a.1.target, b.1.target) { | 99 | a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { |
86 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | 100 | (Some(a), Some(b)) => a.len().cmp(&b.len()), |
87 | (Some(_), None) => Ordering::Less, | 101 | (Some(_), None) => Ordering::Less, |
88 | (None, Some(_)) => Ordering::Greater, | 102 | (None, Some(_)) => Ordering::Greater, |
@@ -177,7 +191,7 @@ mod helpers { | |||
177 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 191 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
178 | let action = match assist { | 192 | let action = match assist { |
179 | Assist::Unresolved { .. } => unreachable!(), | 193 | Assist::Unresolved { .. } => unreachable!(), |
180 | Assist::Resolved { action, .. } => action, | 194 | Assist::Resolved { assist } => assist.get_first_action(), |
181 | }; | 195 | }; |
182 | 196 | ||
183 | let actual = action.edit.apply(&before); | 197 | let actual = action.edit.apply(&before); |
@@ -204,7 +218,7 @@ mod helpers { | |||
204 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 218 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
205 | let action = match assist { | 219 | let action = match assist { |
206 | Assist::Unresolved { .. } => unreachable!(), | 220 | Assist::Unresolved { .. } => unreachable!(), |
207 | Assist::Resolved { action, .. } => action, | 221 | Assist::Resolved { assist } => assist.get_first_action(), |
208 | }; | 222 | }; |
209 | 223 | ||
210 | let mut actual = action.edit.apply(&before); | 224 | let mut actual = action.edit.apply(&before); |
@@ -227,7 +241,7 @@ mod helpers { | |||
227 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 241 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
228 | let action = match assist { | 242 | let action = match assist { |
229 | Assist::Unresolved { .. } => unreachable!(), | 243 | Assist::Unresolved { .. } => unreachable!(), |
230 | Assist::Resolved { action, .. } => action, | 244 | Assist::Resolved { assist } => assist.get_first_action(), |
231 | }; | 245 | }; |
232 | 246 | ||
233 | let range = action.target.expect("expected target on action"); | 247 | let range = action.target.expect("expected target on action"); |
@@ -246,7 +260,7 @@ mod helpers { | |||
246 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 260 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); |
247 | let action = match assist { | 261 | let action = match assist { |
248 | Assist::Unresolved { .. } => unreachable!(), | 262 | Assist::Unresolved { .. } => unreachable!(), |
249 | Assist::Resolved { action, .. } => action, | 263 | Assist::Resolved { assist } => assist.get_first_action(), |
250 | }; | 264 | }; |
251 | 265 | ||
252 | let range = action.target.expect("expected target on action"); | 266 | let range = action.target.expect("expected target on action"); |
@@ -296,10 +310,10 @@ mod tests { | |||
296 | let mut assists = assists.iter(); | 310 | let mut assists = assists.iter(); |
297 | 311 | ||
298 | assert_eq!( | 312 | assert_eq!( |
299 | assists.next().expect("expected assist").0.label, | 313 | assists.next().expect("expected assist").label.label, |
300 | "Change visibility to pub(crate)" | 314 | "Change visibility to pub(crate)" |
301 | ); | 315 | ); |
302 | assert_eq!(assists.next().expect("expected assist").0.label, "Add `#[derive]`"); | 316 | assert_eq!(assists.next().expect("expected assist").label.label, "Add `#[derive]`"); |
303 | } | 317 | } |
304 | 318 | ||
305 | #[test] | 319 | #[test] |
@@ -318,7 +332,7 @@ mod tests { | |||
318 | let assists = super::assists(&db, frange); | 332 | let assists = super::assists(&db, frange); |
319 | let mut assists = assists.iter(); | 333 | let mut assists = assists.iter(); |
320 | 334 | ||
321 | assert_eq!(assists.next().expect("expected assist").0.label, "Extract into variable"); | 335 | assert_eq!(assists.next().expect("expected assist").label.label, "Extract into variable"); |
322 | assert_eq!(assists.next().expect("expected assist").0.label, "Replace with match"); | 336 | assert_eq!(assists.next().expect("expected assist").label.label, "Replace with match"); |
323 | } | 337 | } |
324 | } | 338 | } |