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.rs215
1 files changed, 34 insertions, 181 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 625ebc4a2..1343043dd 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -9,13 +9,11 @@ mod assist_ctx;
9mod marks; 9mod marks;
10#[cfg(test)] 10#[cfg(test)]
11mod doc_tests; 11mod doc_tests;
12#[cfg(test)]
13mod test_db;
14pub mod ast_transform; 12pub mod ast_transform;
15 13
16use either::Either; 14use either::Either;
17use hir::{db::HirDatabase, ModuleDef};
18use ra_db::FileRange; 15use ra_db::FileRange;
16use ra_ide_db::RootDatabase;
19use ra_syntax::{TextRange, TextUnit}; 17use ra_syntax::{TextRange, TextUnit};
20use ra_text_edit::TextEdit; 18use ra_text_edit::TextEdit;
21 19
@@ -61,10 +59,7 @@ impl ResolvedAssist {
61/// 59///
62/// Assists are returned in the "unresolved" state, that is only labels are 60/// Assists are returned in the "unresolved" state, that is only labels are
63/// returned, without actual edits. 61/// returned, without actual edits.
64pub fn applicable_assists<H>(db: &H, range: FileRange) -> Vec<AssistLabel> 62pub fn applicable_assists(db: &RootDatabase, range: FileRange) -> Vec<AssistLabel> {
65where
66 H: HirDatabase + 'static,
67{
68 AssistCtx::with_ctx(db, range, false, |ctx| { 63 AssistCtx::with_ctx(db, range, false, |ctx| {
69 assists::all() 64 assists::all()
70 .iter() 65 .iter()
@@ -77,59 +72,11 @@ where
77 }) 72 })
78} 73}
79 74
80/// A functionality for locating imports for the given name.
81///
82/// Currently has to be a trait with the real implementation provided by the ra_ide_api crate,
83/// due to the search functionality located there.
84/// Later, this trait should be removed completely and the search functionality moved to a separate crate,
85/// accessible from the ra_assists crate.
86pub trait ImportsLocator {
87 /// Finds all imports for the given name and the module that contains this name.
88 fn find_imports(&mut self, name_to_import: &str) -> Vec<ModuleDef>;
89}
90
91/// Return all the assists applicable at the given position
92/// and additional assists that need the imports locator functionality to work.
93///
94/// Assists are returned in the "resolved" state, that is with edit fully
95/// computed.
96pub fn assists_with_imports_locator<H, F>(
97 db: &H,
98 range: FileRange,
99 mut imports_locator: F,
100) -> Vec<ResolvedAssist>
101where
102 H: HirDatabase + 'static,
103 F: ImportsLocator,
104{
105 AssistCtx::with_ctx(db, range, true, |ctx| {
106 let mut assists = assists::all()
107 .iter()
108 .map(|f| f(ctx.clone()))
109 .chain(
110 assists::all_with_imports_locator()
111 .iter()
112 .map(|f| f(ctx.clone(), &mut imports_locator)),
113 )
114 .filter_map(std::convert::identity)
115 .map(|a| match a {
116 Assist::Resolved { assist } => assist,
117 Assist::Unresolved { .. } => unreachable!(),
118 })
119 .collect();
120 sort_assists(&mut assists);
121 assists
122 })
123}
124
125/// Return all the assists applicable at the given position. 75/// Return all the assists applicable at the given position.
126/// 76///
127/// Assists are returned in the "resolved" state, that is with edit fully 77/// Assists are returned in the "resolved" state, that is with edit fully
128/// computed. 78/// computed.
129pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist> 79pub fn assists(db: &RootDatabase, range: FileRange) -> Vec<ResolvedAssist> {
130where
131 H: HirDatabase + 'static,
132{
133 AssistCtx::with_ctx(db, range, true, |ctx| { 80 AssistCtx::with_ctx(db, range, true, |ctx| {
134 let mut a = assists::all() 81 let mut a = assists::all()
135 .iter() 82 .iter()
@@ -155,8 +102,7 @@ fn sort_assists(assists: &mut Vec<ResolvedAssist>) {
155} 102}
156 103
157mod assists { 104mod assists {
158 use crate::{Assist, AssistCtx, ImportsLocator}; 105 use crate::{Assist, AssistCtx};
159 use hir::db::HirDatabase;
160 106
161 mod add_derive; 107 mod add_derive;
162 mod add_explicit_type; 108 mod add_explicit_type;
@@ -184,7 +130,7 @@ mod assists {
184 mod move_bounds; 130 mod move_bounds;
185 mod early_return; 131 mod early_return;
186 132
187 pub(crate) fn all<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 133 pub(crate) fn all() -> &'static [fn(AssistCtx) -> Option<Assist>] {
188 &[ 134 &[
189 add_derive::add_derive, 135 add_derive::add_derive,
190 add_explicit_type::add_explicit_type, 136 add_explicit_type::add_explicit_type,
@@ -215,79 +161,34 @@ mod assists {
215 raw_string::make_usual_string, 161 raw_string::make_usual_string,
216 raw_string::remove_hash, 162 raw_string::remove_hash,
217 early_return::convert_to_guarded_return, 163 early_return::convert_to_guarded_return,
164 auto_import::auto_import,
218 ] 165 ]
219 } 166 }
220
221 pub(crate) fn all_with_imports_locator<'a, DB: HirDatabase, F: ImportsLocator>(
222 ) -> &'a [fn(AssistCtx<DB>, &mut F) -> Option<Assist>] {
223 &[auto_import::auto_import]
224 }
225} 167}
226 168
227#[cfg(test)] 169#[cfg(test)]
228mod helpers { 170mod helpers {
229 use hir::db::DefDatabase;
230 use ra_db::{fixture::WithFixture, FileId, FileRange};
231 use ra_syntax::TextRange;
232 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
233
234 use crate::{test_db::TestDB, Assist, AssistCtx, ImportsLocator};
235 use std::sync::Arc; 171 use std::sync::Arc;
236 172
237 // FIXME remove the `ModuleDefId` reexport from `ra_hir` when this gets removed. 173 use ra_db::{fixture::WithFixture, FileId, FileRange, SourceDatabaseExt};
238 pub(crate) struct TestImportsLocator { 174 use ra_ide_db::{symbol_index::SymbolsDatabase, RootDatabase};
239 db: Arc<TestDB>, 175 use ra_syntax::TextRange;
240 test_file_id: FileId, 176 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
241 }
242
243 impl TestImportsLocator {
244 pub(crate) fn new(db: Arc<TestDB>, test_file_id: FileId) -> Self {
245 TestImportsLocator { db, test_file_id }
246 }
247 }
248 177
249 impl ImportsLocator for TestImportsLocator { 178 use crate::{Assist, AssistCtx};
250 fn find_imports(&mut self, name_to_import: &str) -> Vec<hir::ModuleDef> {
251 let crate_def_map = self.db.crate_def_map(self.db.test_crate());
252 let mut findings = Vec::new();
253
254 let mut module_ids_to_process =
255 crate_def_map.modules_for_file(self.test_file_id).collect::<Vec<_>>();
256
257 while !module_ids_to_process.is_empty() {
258 let mut more_ids_to_process = Vec::new();
259 for local_module_id in module_ids_to_process.drain(..) {
260 for (name, namespace_data) in
261 crate_def_map[local_module_id].scope.entries_without_primitives()
262 {
263 let found_a_match = &name.to_string() == name_to_import;
264 vec![namespace_data.types, namespace_data.values]
265 .into_iter()
266 .filter_map(std::convert::identity)
267 .for_each(|(module_def_id, _)| {
268 if found_a_match {
269 findings.push(module_def_id.into());
270 }
271 if let hir::ModuleDefId::ModuleId(module_id) = module_def_id {
272 more_ids_to_process.push(module_id.local_id);
273 }
274 });
275 }
276 }
277 module_ids_to_process = more_ids_to_process;
278 }
279 179
280 findings 180 pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) {
281 } 181 let (mut db, file_id) = RootDatabase::with_single_file(text);
182 // FIXME: ideally, this should be done by the above `RootDatabase::with_single_file`,
183 // but it looks like this might need specialization? :(
184 let local_roots = vec![db.file_source_root(file_id)];
185 db.set_local_roots(Arc::new(local_roots));
186 (db, file_id)
282 } 187 }
283 188
284 pub(crate) fn check_assist( 189 pub(crate) fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) {
285 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
286 before: &str,
287 after: &str,
288 ) {
289 let (before_cursor_pos, before) = extract_offset(before); 190 let (before_cursor_pos, before) = extract_offset(before);
290 let (db, file_id) = TestDB::with_single_file(&before); 191 let (db, file_id) = with_single_file(&before);
291 let frange = 192 let frange =
292 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 193 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
293 let assist = 194 let assist =
@@ -309,45 +210,13 @@ mod helpers {
309 assert_eq_text!(after, &actual); 210 assert_eq_text!(after, &actual);
310 } 211 }
311 212
312 pub(crate) fn check_assist_with_imports_locator<F: ImportsLocator>(
313 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
314 imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F,
315 before: &str,
316 after: &str,
317 ) {
318 let (before_cursor_pos, before) = extract_offset(before);
319 let (db, file_id) = TestDB::with_single_file(&before);
320 let db = Arc::new(db);
321 let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id);
322 let frange =
323 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
324 let assist =
325 AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator))
326 .expect("code action is not applicable");
327 let action = match assist {
328 Assist::Unresolved { .. } => unreachable!(),
329 Assist::Resolved { assist } => assist.get_first_action(),
330 };
331
332 let actual = action.edit.apply(&before);
333 let actual_cursor_pos = match action.cursor_position {
334 None => action
335 .edit
336 .apply_to_offset(before_cursor_pos)
337 .expect("cursor position is affected by the edit"),
338 Some(off) => off,
339 };
340 let actual = add_cursor(&actual, actual_cursor_pos);
341 assert_eq_text!(after, &actual);
342 }
343
344 pub(crate) fn check_assist_range( 213 pub(crate) fn check_assist_range(
345 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 214 assist: fn(AssistCtx) -> Option<Assist>,
346 before: &str, 215 before: &str,
347 after: &str, 216 after: &str,
348 ) { 217 ) {
349 let (range, before) = extract_range(before); 218 let (range, before) = extract_range(before);
350 let (db, file_id) = TestDB::with_single_file(&before); 219 let (db, file_id) = with_single_file(&before);
351 let frange = FileRange { file_id, range }; 220 let frange = FileRange { file_id, range };
352 let assist = 221 let assist =
353 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 222 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -364,12 +233,12 @@ mod helpers {
364 } 233 }
365 234
366 pub(crate) fn check_assist_target( 235 pub(crate) fn check_assist_target(
367 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 236 assist: fn(AssistCtx) -> Option<Assist>,
368 before: &str, 237 before: &str,
369 target: &str, 238 target: &str,
370 ) { 239 ) {
371 let (before_cursor_pos, before) = extract_offset(before); 240 let (before_cursor_pos, before) = extract_offset(before);
372 let (db, file_id) = TestDB::with_single_file(&before); 241 let (db, file_id) = with_single_file(&before);
373 let frange = 242 let frange =
374 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 243 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
375 let assist = 244 let assist =
@@ -384,12 +253,12 @@ mod helpers {
384 } 253 }
385 254
386 pub(crate) fn check_assist_range_target( 255 pub(crate) fn check_assist_range_target(
387 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 256 assist: fn(AssistCtx) -> Option<Assist>,
388 before: &str, 257 before: &str,
389 target: &str, 258 target: &str,
390 ) { 259 ) {
391 let (range, before) = extract_range(before); 260 let (range, before) = extract_range(before);
392 let (db, file_id) = TestDB::with_single_file(&before); 261 let (db, file_id) = with_single_file(&before);
393 let frange = FileRange { file_id, range }; 262 let frange = FileRange { file_id, range };
394 let assist = 263 let assist =
395 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable"); 264 AssistCtx::with_ctx(&db, frange, true, assist).expect("code action is not applicable");
@@ -403,39 +272,23 @@ mod helpers {
403 } 272 }
404 273
405 pub(crate) fn check_assist_not_applicable( 274 pub(crate) fn check_assist_not_applicable(
406 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 275 assist: fn(AssistCtx) -> Option<Assist>,
407 before: &str, 276 before: &str,
408 ) { 277 ) {
409 let (before_cursor_pos, before) = extract_offset(before); 278 let (before_cursor_pos, before) = extract_offset(before);
410 let (db, file_id) = TestDB::with_single_file(&before); 279 let (db, file_id) = with_single_file(&before);
411 let frange = 280 let frange =
412 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 281 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
413 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 282 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
414 assert!(assist.is_none()); 283 assert!(assist.is_none());
415 } 284 }
416 285
417 pub(crate) fn check_assist_with_imports_locator_not_applicable<F: ImportsLocator>(
418 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
419 imports_locator_provider: fn(db: Arc<TestDB>, file_id: FileId) -> F,
420 before: &str,
421 ) {
422 let (before_cursor_pos, before) = extract_offset(before);
423 let (db, file_id) = TestDB::with_single_file(&before);
424 let db = Arc::new(db);
425 let mut imports_locator = imports_locator_provider(Arc::clone(&db), file_id);
426 let frange =
427 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
428 let assist =
429 AssistCtx::with_ctx(db.as_ref(), frange, true, |ctx| assist(ctx, &mut imports_locator));
430 assert!(assist.is_none());
431 }
432
433 pub(crate) fn check_assist_range_not_applicable( 286 pub(crate) fn check_assist_range_not_applicable(
434 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 287 assist: fn(AssistCtx) -> Option<Assist>,
435 before: &str, 288 before: &str,
436 ) { 289 ) {
437 let (range, before) = extract_range(before); 290 let (range, before) = extract_range(before);
438 let (db, file_id) = TestDB::with_single_file(&before); 291 let (db, file_id) = with_single_file(&before);
439 let frange = FileRange { file_id, range }; 292 let frange = FileRange { file_id, range };
440 let assist = AssistCtx::with_ctx(&db, frange, true, assist); 293 let assist = AssistCtx::with_ctx(&db, frange, true, assist);
441 assert!(assist.is_none()); 294 assert!(assist.is_none());
@@ -444,17 +297,17 @@ mod helpers {
444 297
445#[cfg(test)] 298#[cfg(test)]
446mod tests { 299mod tests {
447 use ra_db::{fixture::WithFixture, FileRange}; 300 use ra_db::FileRange;
448 use ra_syntax::TextRange; 301 use ra_syntax::TextRange;
449 use test_utils::{extract_offset, extract_range}; 302 use test_utils::{extract_offset, extract_range};
450 303
451 use crate::test_db::TestDB; 304 use crate::helpers;
452 305
453 #[test] 306 #[test]
454 fn assist_order_field_struct() { 307 fn assist_order_field_struct() {
455 let before = "struct Foo { <|>bar: u32 }"; 308 let before = "struct Foo { <|>bar: u32 }";
456 let (before_cursor_pos, before) = extract_offset(before); 309 let (before_cursor_pos, before) = extract_offset(before);
457 let (db, file_id) = TestDB::with_single_file(&before); 310 let (db, file_id) = helpers::with_single_file(&before);
458 let frange = 311 let frange =
459 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) }; 312 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
460 let assists = super::assists(&db, frange); 313 let assists = super::assists(&db, frange);
@@ -478,7 +331,7 @@ mod tests {
478 } 331 }
479 }"; 332 }";
480 let (range, before) = extract_range(before); 333 let (range, before) = extract_range(before);
481 let (db, file_id) = TestDB::with_single_file(&before); 334 let (db, file_id) = helpers::with_single_file(&before);
482 let frange = FileRange { file_id, range }; 335 let frange = FileRange { file_id, range };
483 let assists = super::assists(&db, frange); 336 let assists = super::assists(&db, frange);
484 let mut assists = assists.iter(); 337 let mut assists = assists.iter();