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.rs100
1 files changed, 96 insertions, 4 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 881db6347..7928b4983 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -8,7 +8,7 @@
8mod assist_ctx; 8mod assist_ctx;
9 9
10use ra_text_edit::TextEdit; 10use ra_text_edit::TextEdit;
11use ra_syntax::{TextUnit, SyntaxNode, Direction}; 11use ra_syntax::{TextRange, TextUnit, SyntaxNode, Direction};
12use ra_db::FileRange; 12use ra_db::FileRange;
13use hir::db::HirDatabase; 13use hir::db::HirDatabase;
14 14
@@ -23,6 +23,7 @@ pub struct AssistLabel {
23pub struct AssistAction { 23pub struct AssistAction {
24 pub edit: TextEdit, 24 pub edit: TextEdit,
25 pub cursor_position: Option<TextUnit>, 25 pub cursor_position: Option<TextUnit>,
26 pub target: Option<TextRange>,
26} 27}
27 28
28/// Return all the assists applicable at the given position. 29/// Return all the assists applicable at the given position.
@@ -53,15 +54,24 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<(AssistLabel, AssistAction)>
53where 54where
54 H: HirDatabase + 'static, 55 H: HirDatabase + 'static,
55{ 56{
57 use std::cmp::Ordering;
58
56 AssistCtx::with_ctx(db, range, true, |ctx| { 59 AssistCtx::with_ctx(db, range, true, |ctx| {
57 all_assists() 60 let mut a = all_assists()
58 .iter() 61 .iter()
59 .filter_map(|f| f(ctx.clone())) 62 .filter_map(|f| f(ctx.clone()))
60 .map(|a| match a { 63 .map(|a| match a {
61 Assist::Resolved(label, action) => (label, action), 64 Assist::Resolved(label, action) => (label, action),
62 Assist::Unresolved(..) => unreachable!(), 65 Assist::Unresolved(..) => unreachable!(),
63 }) 66 })
64 .collect() 67 .collect::<Vec<(AssistLabel, AssistAction)>>();
68 a.sort_by(|a, b| match (a.1.target, b.1.target) {
69 (Some(a), Some(b)) => a.len().cmp(&b.len()),
70 (Some(_), None) => Ordering::Less,
71 (None, Some(_)) => Ordering::Greater,
72 (None, None) => Ordering::Equal,
73 });
74 a
65 }) 75 })
66} 76}
67 77
@@ -97,7 +107,7 @@ mod helpers {
97 use hir::mock::MockDatabase; 107 use hir::mock::MockDatabase;
98 use ra_syntax::TextRange; 108 use ra_syntax::TextRange;
99 use ra_db::FileRange; 109 use ra_db::FileRange;
100 use test_utils::{extract_offset, assert_eq_text, add_cursor, extract_range}; 110 use test_utils::{extract_offset, extract_range, assert_eq_text, add_cursor};
101 111
102 use crate::{AssistCtx, Assist}; 112 use crate::{AssistCtx, Assist};
103 113
@@ -151,6 +161,45 @@ mod helpers {
151 assert_eq_text!(after, &actual); 161 assert_eq_text!(after, &actual);
152 } 162 }
153 163
164 pub(crate) fn check_assist_target(
165 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
166 before: &str,
167 target: &str,
168 ) {
169 let (before_cursor_pos, before) = extract_offset(before);
170 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
171 let frange =
172 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
173 let assist =
174 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
175 let action = match assist {
176 Assist::Unresolved(_) => unreachable!(),
177 Assist::Resolved(_, it) => it,
178 };
179
180 let range = action.target.expect("expected target on action");
181 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
182 }
183
184 pub(crate) fn check_assist_range_target(
185 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
186 before: &str,
187 target: &str,
188 ) {
189 let (range, before) = extract_range(before);
190 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
191 let frange = FileRange { file_id, range };
192 let assist =
193 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
194 let action = match assist {
195 Assist::Unresolved(_) => unreachable!(),
196 Assist::Resolved(_, it) => it,
197 };
198
199 let range = action.target.expect("expected target on action");
200 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
201 }
202
154 pub(crate) fn check_assist_not_applicable( 203 pub(crate) fn check_assist_not_applicable(
155 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>, 204 assist: fn(AssistCtx<MockDatabase>) -> Option<Assist>,
156 before: &str, 205 before: &str,
@@ -162,5 +211,48 @@ mod helpers {
162 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 211 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
163 assert!(assist.is_none()); 212 assert!(assist.is_none());
164 } 213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use hir::mock::MockDatabase;
219 use ra_syntax::TextRange;
220 use ra_db::FileRange;
221 use test_utils::{extract_offset};
222
223 #[test]
224 fn assist_order_field_struct() {
225 let before = "struct Foo { <|>bar: u32 }";
226 let (before_cursor_pos, before) = extract_offset(before);
227 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
228 let frange =
229 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
230 let assists = super::assists(&db, frange);
231 let mut assists = assists.iter();
232
233 assert_eq!(assists.next().expect("expected assist").0.label, "make pub(crate)");
234 assert_eq!(assists.next().expect("expected assist").0.label, "add `#[derive]`");
235 }
236
237 #[test]
238 fn assist_order_if_expr() {
239 let before = "
240 pub fn test_some_range(a: int) -> bool {
241 if let 2..6 = 5<|> {
242 true
243 } else {
244 false
245 }
246 }";
247 let (before_cursor_pos, before) = extract_offset(before);
248 let (db, _source_root, file_id) = MockDatabase::with_single_file(&before);
249 let frange =
250 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
251 let assists = super::assists(&db, frange);
252 let mut assists = assists.iter();
253
254 assert_eq!(assists.next().expect("expected assist").0.label, "introduce variable");
255 assert_eq!(assists.next().expect("expected assist").0.label, "replace with match");
256 }
165 257
166} 258}