diff options
Diffstat (limited to 'crates/ra_assists/src/lib.rs')
-rw-r--r-- | crates/ra_assists/src/lib.rs | 123 |
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 | ||
20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; | 20 | pub(crate) use crate::assist_ctx::{Assist, AssistCtx, AssistHandler}; |
21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; | 21 | pub use crate::handlers::replace_qualified_name_with_use::insert_use_statement; |
22 | use 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. |
65 | pub fn unresolved_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> { | 66 | pub 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. |
79 | pub fn resolved_assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> { | 81 | pub 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 | ||