aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/lib.rs')
-rw-r--r--crates/ra_assists/src/lib.rs111
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;
9mod marks; 9mod marks;
10#[cfg(test)] 10#[cfg(test)]
11mod doc_tests; 11mod doc_tests;
12mod utils;
12pub mod ast_transform; 13pub mod ast_transform;
13 14
15use std::cmp::Ordering;
16
14use either::Either; 17use either::Either;
15use ra_db::FileRange; 18use ra_db::FileRange;
16use ra_ide_db::RootDatabase; 19use ra_ide_db::RootDatabase;
17use ra_syntax::{TextRange, TextUnit}; 20use ra_syntax::{TextRange, TextUnit};
18use ra_text_edit::TextEdit; 21use ra_text_edit::TextEdit;
19 22
20pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; 23pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
21pub use crate::assists::add_import::auto_import_text_edit; 24pub 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
38impl 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)]
36pub struct AssistAction { 47pub 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.
62pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { 74pub 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.
79pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { 90pub 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
94fn sort_assists(assists: &mut Vec<ResolvedAssist>) { 104fn 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
104mod assists { 113mod 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}