diff options
Diffstat (limited to 'crates/ra_assists/src/lib.rs')
-rw-r--r-- | crates/ra_assists/src/lib.rs | 111 |
1 files changed, 51 insertions, 60 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 3f3df3f96..eca6dec4b 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -9,16 +9,19 @@ mod assist_ctx; | |||
9 | mod marks; | 9 | mod marks; |
10 | #[cfg(test)] | 10 | #[cfg(test)] |
11 | mod doc_tests; | 11 | mod doc_tests; |
12 | mod utils; | ||
12 | pub mod ast_transform; | 13 | pub mod ast_transform; |
13 | 14 | ||
15 | use std::cmp::Ordering; | ||
16 | |||
14 | use either::Either; | 17 | use either::Either; |
15 | use ra_db::FileRange; | 18 | use ra_db::FileRange; |
16 | use ra_ide_db::RootDatabase; | 19 | use ra_ide_db::RootDatabase; |
17 | use ra_syntax::{TextRange, TextUnit}; | 20 | use ra_syntax::{TextRange, TextUnit}; |
18 | use ra_text_edit::TextEdit; | 21 | use ra_text_edit::TextEdit; |
19 | 22 | ||
20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; | 23 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; |
21 | pub use crate::assists::add_import::auto_import_text_edit; | 24 | pub use crate::handlers::add_import::auto_import_text_edit; |
22 | 25 | ||
23 | /// Unique identifier of the assist, should not be shown to the user | 26 | /// Unique identifier of the assist, should not be shown to the user |
24 | /// directly. | 27 | /// directly. |
@@ -32,11 +35,20 @@ pub struct AssistLabel { | |||
32 | pub id: AssistId, | 35 | pub id: AssistId, |
33 | } | 36 | } |
34 | 37 | ||
38 | impl AssistLabel { | ||
39 | pub(crate) fn new(label: String, id: AssistId) -> AssistLabel { | ||
40 | // FIXME: make fields private, so that this invariant can't be broken | ||
41 | assert!(label.chars().nth(0).unwrap().is_uppercase()); | ||
42 | AssistLabel { label: label.into(), id } | ||
43 | } | ||
44 | } | ||
45 | |||
35 | #[derive(Debug, Clone)] | 46 | #[derive(Debug, Clone)] |
36 | pub struct AssistAction { | 47 | pub struct AssistAction { |
37 | pub label: Option<String>, | 48 | pub label: Option<String>, |
38 | pub edit: TextEdit, | 49 | pub edit: TextEdit, |
39 | pub cursor_position: Option<TextUnit>, | 50 | pub cursor_position: Option<TextUnit>, |
51 | // FIXME: This belongs to `AssistLabel` | ||
40 | pub target: Option<TextRange>, | 52 | pub target: Option<TextRange>, |
41 | } | 53 | } |
42 | 54 | ||
@@ -60,16 +72,15 @@ impl ResolvedAssist { | |||
60 | /// Assists are returned in the "unresolved" state, that is only labels are | 72 | /// Assists are returned in the "unresolved" state, that is only labels are |
61 | /// returned, without actual edits. | 73 | /// returned, without actual edits. |
62 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { | 74 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { |
63 | AssistCtx::with_ctx(db, range, false, |ctx| { | 75 | let ctx = AssistCtx::new(db, range, false); |
64 | assists::all() | 76 | handlers::all() |
65 | .iter() | 77 | .iter() |
66 | .filter_map(|f| f(ctx.clone())) | 78 | .filter_map(|f| f(ctx.clone())) |
67 | .map(|a| match a { | 79 | .map(|a| match a { |
68 | Assist::Unresolved { label } => label, | 80 | Assist::Unresolved { label } => label, |
69 | Assist::Resolved { .. } => unreachable!(), | 81 | Assist::Resolved { .. } => unreachable!(), |
70 | }) | 82 | }) |
71 | .collect() | 83 | .collect() |
72 | }) | ||
73 | } | 84 | } |
74 | 85 | ||
75 | /// Return all the assists applicable at the given position. | 86 | /// Return all the assists applicable at the given position. |
@@ -77,22 +88,20 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe | |||
77 | /// Assists are returned in the "resolved" state, that is with edit fully | 88 | /// Assists are returned in the "resolved" state, that is with edit fully |
78 | /// computed. | 89 | /// computed. |
79 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 90 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { |
80 | AssistCtx::with_ctx(db, range, true, |ctx| { | 91 | let ctx = AssistCtx::new(db, range, true); |
81 | let mut a = assists::all() | 92 | let mut a = handlers::all() |
82 | .iter() | 93 | .iter() |
83 | .filter_map(|f| f(ctx.clone())) | 94 | .filter_map(|f| f(ctx.clone())) |
84 | .map(|a| match a { | 95 | .map(|a| match a { |
85 | Assist::Resolved { assist } => assist, | 96 | Assist::Resolved { assist } => assist, |
86 | Assist::Unresolved { .. } => unreachable!(), | 97 | Assist::Unresolved { .. } => unreachable!(), |
87 | }) | 98 | }) |
88 | .collect(); | 99 | .collect::<Vec<_>>(); |
89 | sort_assists(&mut a); | 100 | sort_assists(&mut a); |
90 | a | 101 | a |
91 | }) | ||
92 | } | 102 | } |
93 | 103 | ||
94 | fn sort_assists(assists: &mut Vec<ResolvedAssist>) { | 104 | fn sort_assists(assists: &mut [ResolvedAssist]) { |
95 | use std::cmp::Ordering; | ||
96 | assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { | 105 | assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { |
97 | (Some(a), Some(b)) => a.len().cmp(&b.len()), | 106 | (Some(a), Some(b)) => a.len().cmp(&b.len()), |
98 | (Some(_), None) => Ordering::Less, | 107 | (Some(_), None) => Ordering::Less, |
@@ -101,8 +110,8 @@ fn sort_assists(assists: &mut Vec<ResolvedAssist>) { | |||
101 | }); | 110 | }); |
102 | } | 111 | } |
103 | 112 | ||
104 | mod assists { | 113 | mod handlers { |
105 | use crate::{Assist, AssistCtx}; | 114 | use crate::AssistHandler; |
106 | 115 | ||
107 | mod add_derive; | 116 | mod add_derive; |
108 | mod add_explicit_type; | 117 | mod add_explicit_type; |
@@ -130,7 +139,7 @@ mod assists { | |||
130 | mod move_bounds; | 139 | mod move_bounds; |
131 | mod early_return; | 140 | mod early_return; |
132 | 141 | ||
133 | pub(crate) fn all() -> &'static [fn(AssistCtx) -> Option<Assist>] { | 142 | pub(crate) fn all() -> &'static [AssistHandler] { |
134 | &[ | 143 | &[ |
135 | add_derive::add_derive, | 144 | add_derive::add_derive, |
136 | add_explicit_type::add_explicit_type, | 145 | add_explicit_type::add_explicit_type, |
@@ -175,7 +184,7 @@ mod helpers { | |||
175 | use ra_syntax::TextRange; | 184 | use ra_syntax::TextRange; |
176 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; | 185 | use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; |
177 | 186 | ||
178 | use crate::{Assist, AssistCtx}; | 187 | use crate::{Assist, AssistCtx, AssistHandler}; |
179 | 188 | ||
180 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { | 189 | pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { |
181 | let (mut db, file_id) = RootDatabase::with_single_file(text); | 190 | let (mut db, file_id) = RootDatabase::with_single_file(text); |
@@ -186,13 +195,13 @@ mod helpers { | |||
186 | (db, file_id) | 195 | (db, file_id) |
187 | } | 196 | } |
188 | 197 | ||
189 | pub(crate) fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { | 198 | pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) { |
190 | let (before_cursor_pos, before) = extract_offset(before); | 199 | let (before_cursor_pos, before) = extract_offset(before); |
191 | let (db, file_id) = with_single_file(&before); | 200 | let (db, file_id) = with_single_file(&before); |
192 | let frange = | 201 | let frange = |
193 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 202 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
194 | let assist = | 203 | let assist = |
195 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 204 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); |
196 | let action = match assist { | 205 | let action = match assist { |
197 | Assist::Unresolved { .. } => unreachable!(), | 206 | Assist::Unresolved { .. } => unreachable!(), |
198 | Assist::Resolved { assist } => assist.get_first_action(), | 207 | Assist::Resolved { assist } => assist.get_first_action(), |
@@ -210,16 +219,12 @@ mod helpers { | |||
210 | assert_eq_text!(after, &actual); | 219 | assert_eq_text!(after, &actual); |
211 | } | 220 | } |
212 | 221 | ||
213 | pub(crate) fn check_assist_range( | 222 | pub(crate) fn check_assist_range(assist: AssistHandler, before: &str, after: &str) { |
214 | assist: fn(AssistCtx) -> Option<Assist>, | ||
215 | before: &str, | ||
216 | after: &str, | ||
217 | ) { | ||
218 | let (range, before) = extract_range(before); | 223 | let (range, before) = extract_range(before); |
219 | let (db, file_id) = with_single_file(&before); | 224 | let (db, file_id) = with_single_file(&before); |
220 | let frange = FileRange { file_id, range }; | 225 | let frange = FileRange { file_id, range }; |
221 | let assist = | 226 | let assist = |
222 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 227 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); |
223 | let action = match assist { | 228 | let action = match assist { |
224 | Assist::Unresolved { .. } => unreachable!(), | 229 | Assist::Unresolved { .. } => unreachable!(), |
225 | Assist::Resolved { assist } => assist.get_first_action(), | 230 | Assist::Resolved { assist } => assist.get_first_action(), |
@@ -232,17 +237,13 @@ mod helpers { | |||
232 | assert_eq_text!(after, &actual); | 237 | assert_eq_text!(after, &actual); |
233 | } | 238 | } |
234 | 239 | ||
235 | pub(crate) fn check_assist_target( | 240 | pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) { |
236 | assist: fn(AssistCtx) -> Option<Assist>, | ||
237 | before: &str, | ||
238 | target: &str, | ||
239 | ) { | ||
240 | let (before_cursor_pos, before) = extract_offset(before); | 241 | let (before_cursor_pos, before) = extract_offset(before); |
241 | let (db, file_id) = with_single_file(&before); | 242 | let (db, file_id) = with_single_file(&before); |
242 | let frange = | 243 | let frange = |
243 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 244 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
244 | let assist = | 245 | let assist = |
245 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 246 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); |
246 | let action = match assist { | 247 | let action = match assist { |
247 | Assist::Unresolved { .. } => unreachable!(), | 248 | Assist::Unresolved { .. } => unreachable!(), |
248 | Assist::Resolved { assist } => assist.get_first_action(), | 249 | Assist::Resolved { assist } => assist.get_first_action(), |
@@ -252,16 +253,12 @@ mod helpers { | |||
252 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | 253 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); |
253 | } | 254 | } |
254 | 255 | ||
255 | pub(crate) fn check_assist_range_target( | 256 | pub(crate) fn check_assist_range_target(assist: AssistHandler, before: &str, target: &str) { |
256 | assist: fn(AssistCtx) -> Option<Assist>, | ||
257 | before: &str, | ||
258 | target: &str, | ||
259 | ) { | ||
260 | let (range, before) = extract_range(before); | 257 | let (range, before) = extract_range(before); |
261 | let (db, file_id) = with_single_file(&before); | 258 | let (db, file_id) = with_single_file(&before); |
262 | let frange = FileRange { file_id, range }; | 259 | let frange = FileRange { file_id, range }; |
263 | let assist = | 260 | let assist = |
264 | AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); | 261 | assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable"); |
265 | let action = match assist { | 262 | let action = match assist { |
266 | Assist::Unresolved { .. } => unreachable!(), | 263 | Assist::Unresolved { .. } => unreachable!(), |
267 | Assist::Resolved { assist } => assist.get_first_action(), | 264 | Assist::Resolved { assist } => assist.get_first_action(), |
@@ -271,26 +268,20 @@ mod helpers { | |||
271 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); | 268 | assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target); |
272 | } | 269 | } |
273 | 270 | ||
274 | pub(crate) fn check_assist_not_applicable( | 271 | pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) { |
275 | assist: fn(AssistCtx) -> Option<Assist>, | ||
276 | before: &str, | ||
277 | ) { | ||
278 | let (before_cursor_pos, before) = extract_offset(before); | 272 | let (before_cursor_pos, before) = extract_offset(before); |
279 | let (db, file_id) = with_single_file(&before); | 273 | let (db, file_id) = with_single_file(&before); |
280 | let frange = | 274 | let frange = |
281 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; | 275 | FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; |
282 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); | 276 | let assist = assist(AssistCtx::new(&db, frange, true)); |
283 | assert!(assist.is_none()); | 277 | assert!(assist.is_none()); |
284 | } | 278 | } |
285 | 279 | ||
286 | pub(crate) fn check_assist_range_not_applicable( | 280 | pub(crate) fn check_assist_range_not_applicable(assist: AssistHandler, before: &str) { |
287 | assist: fn(AssistCtx) -> Option<Assist>, | ||
288 | before: &str, | ||
289 | ) { | ||
290 | let (range, before) = extract_range(before); | 281 | let (range, before) = extract_range(before); |
291 | let (db, file_id) = with_single_file(&before); | 282 | let (db, file_id) = with_single_file(&before); |
292 | let frange = FileRange { file_id, range }; | 283 | let frange = FileRange { file_id, range }; |
293 | let assist = AssistCtx::with_ctx(&db, frange, true, assist); | 284 | let assist = assist(AssistCtx::new(&db, frange, true)); |
294 | assert!(assist.is_none()); | 285 | assert!(assist.is_none()); |
295 | } | 286 | } |
296 | } | 287 | } |