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.rs123
1 files changed, 56 insertions, 67 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index d7998b0d1..c28a9b92b 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -19,6 +19,7 @@ use ra_text_edit::TextEdit;
19 19
20pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; 20pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler};
21pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; 21pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement;
22use hir::Semantics;
22 23
23/// Unique identifier of the assist, should not be shown to the user 24/// Unique identifier of the assist, should not be shown to the user
24/// directly. 25/// directly.
@@ -63,7 +64,8 @@ pub struct ResolvedAssist {
63/// Assists are returned in the "unresolved" state, that is only labels are 64/// Assists are returned in the "unresolved" state, that is only labels are
64/// returned, without actual edits. 65/// returned, without actual edits.
65pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { 66pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
66 let ctx = AssistCtx::new(db, range, false); 67 let sema = Semantics::new(db);
68 let ctx = AssistCtx::new(&sema, range, false);
67 handlers::all() 69 handlers::all()
68 .iter() 70 .iter()
69 .filter_map(|f| f(ctx.clone())) 71 .filter_map(|f| f(ctx.clone()))
@@ -77,7 +79,8 @@ pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabe
77/// Assists are returned in the "resolved" state, that is with edit fully 79/// Assists are returned in the "resolved" state, that is with edit fully
78/// computed. 80/// computed.
79pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { 81pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
80 let ctx = AssistCtx::new(db, range, true); 82 let sema = Semantics::new(db);
83 let ctx = AssistCtx::new(&sema, range, true);
81 let mut a = handlers::all() 84 let mut a = handlers::all()
82 .iter() 85 .iter()
83 .filter_map(|f| f(ctx.clone())) 86 .filter_map(|f| f(ctx.clone()))
@@ -162,9 +165,10 @@ mod helpers {
162 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt}; 165 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
163 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase}; 166 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
164 use ra_syntax::TextRange; 167 use ra_syntax::TextRange;
165 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 168 use test_utils::{add_cursor, assert_eq_text, extract_range_or_offset, RangeOrOffset};
166 169
167 use crate::{AssistCtx, AssistHandler}; 170 use crate::{AssistCtx, AssistHandler};
171 use hir::Semantics;
168 172
169 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { 173 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
170 let (mut db, file_id) = RootDatabase::with_single_file(text); 174 let (mut db, file_id) = RootDatabase::with_single_file(text);
@@ -176,81 +180,66 @@ mod helpers {
176 } 180 }
177 181
178 pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) { 182 pub(crate) fn check_assist(assist: AssistHandler, before: &str, after: &str) {
179 let (before_cursor_pos, before) = extract_offset(before); 183 check(assist, before, ExpectedResult::After(after));
180 let (db, file_id) = with_single_file(&before);
181 let frange =
182 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
183 let assist =
184 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
185 let action = assist.0[0].action.clone().unwrap();
186
187 let actual = action.edit.apply(&before);
188 let actual_cursor_pos = match action.cursor_position {
189 None => action
190 .edit
191 .apply_to_offset(before_cursor_pos)
192 .expect("cursor position is affected by the edit"),
193 Some(off) => off,
194 };
195 let actual = add_cursor(&actual, actual_cursor_pos);
196 assert_eq_text!(after, &actual);
197 }
198
199 pub(crate) fn check_assist_range(assist: AssistHandler, before: &str, after: &str) {
200 let (range, before) = extract_range(before);
201 let (db, file_id) = with_single_file(&before);
202 let frange = FileRange { file_id, range };
203 let assist =
204 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
205 let action = assist.0[0].action.clone().unwrap();
206
207 let mut actual = action.edit.apply(&before);
208 if let Some(pos) = action.cursor_position {
209 actual = add_cursor(&actual, pos);
210 }
211 assert_eq_text!(after, &actual);
212 } 184 }
213 185
186 // FIXME: instead of having a separate function here, maybe use
187 // `extract_ranges` and mark the target as `<target> </target>` in the
188 // fixuture?
214 pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) { 189 pub(crate) fn check_assist_target(assist: AssistHandler, before: &str, target: &str) {
215 let (before_cursor_pos, before) = extract_offset(before); 190 check(assist, before, ExpectedResult::Target(target));
216 let (db, file_id) = with_single_file(&before);
217 let frange =
218 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
219 let assist =
220 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
221 let action = assist.0[0].action.clone().unwrap();
222
223 let range = action.target.expect("expected target on action");
224 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
225 } 191 }
226 192
227 pub(crate) fn check_assist_range_target(assist: AssistHandler, before: &str, target: &str) { 193 pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) {
228 let (range, before) = extract_range(before); 194 check(assist, before, ExpectedResult::NotApplicable);
229 let (db, file_id) = with_single_file(&before);
230 let frange = FileRange { file_id, range };
231 let assist =
232 assist(AssistCtx::new(&db, frange, true)).expect("code action is not applicable");
233 let action = assist.0[0].action.clone().unwrap();
234
235 let range = action.target.expect("expected target on action");
236 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
237 } 195 }
238 196
239 pub(crate) fn check_assist_not_applicable(assist: AssistHandler, before: &str) { 197 enum ExpectedResult<'a> {
240 let (before_cursor_pos, before) = extract_offset(before); 198 NotApplicable,
241 let (db, file_id) = with_single_file(&before); 199 After(&'a str),
242 let frange = 200 Target(&'a str),
243 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
244 let assist = assist(AssistCtx::new(&db, frange, true));
245 assert!(assist.is_none());
246 } 201 }
247 202
248 pub(crate) fn check_assist_range_not_applicable(assist: AssistHandler, before: &str) { 203 fn check(assist: AssistHandler, before: &str, expected: ExpectedResult) {
249 let (range, before) = extract_range(before); 204 let (range_or_offset, before) = extract_range_or_offset(before);
205 let range: TextRange = range_or_offset.into();
206
250 let (db, file_id) = with_single_file(&before); 207 let (db, file_id) = with_single_file(&before);
251 let frange = FileRange { file_id, range }; 208 let frange = FileRange { file_id, range };
252 let assist = assist(AssistCtx::new(&db, frange, true)); 209 let sema = Semantics::new(&db);
253 assert!(assist.is_none()); 210 let assist_ctx = AssistCtx::new(&sema, frange, true);
211
212 match (assist(assist_ctx), expected) {
213 (Some(assist), ExpectedResult::After(after)) => {
214 let action = assist.0[0].action.clone().unwrap();
215
216 let mut actual = action.edit.apply(&before);
217 match action.cursor_position {
218 None => {
219 if let RangeOrOffset::Offset(before_cursor_pos) = range_or_offset {
220 let off = action
221 .edit
222 .apply_to_offset(before_cursor_pos)
223 .expect("cursor position is affected by the edit");
224 actual = add_cursor(&actual, off)
225 }
226 }
227 Some(off) => actual = add_cursor(&actual, off),
228 };
229
230 assert_eq_text!(after, &actual);
231 }
232 (Some(assist), ExpectedResult::Target(target)) => {
233 let action = assist.0[0].action.clone().unwrap();
234 let range = action.target.expect("expected target on action");
235 assert_eq_text!(&before[range.start().to_usize()..range.end().to_usize()], target);
236 }
237 (Some(_), ExpectedResult::NotApplicable) => panic!("assist should not be applicable!"),
238 (None, ExpectedResult::After(_)) | (None, ExpectedResult::Target(_)) => {
239 panic!("code action is not applicable")
240 }
241 (None, ExpectedResult::NotApplicable) => (),
242 };
254 } 243 }
255} 244}
256 245