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.rs126
1 files changed, 113 insertions, 13 deletions
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 3337805a5..4029962f7 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -14,9 +14,9 @@ mod test_db;
14pub mod ast_transform; 14pub mod ast_transform;
15 15
16use either::Either; 16use either::Either;
17use hir::db::HirDatabase; 17use hir::{db::HirDatabase, InFile, ModPath, Module};
18use ra_db::FileRange; 18use ra_db::FileRange;
19use ra_syntax::{TextRange, TextUnit}; 19use ra_syntax::{ast::NameRef, TextRange, TextUnit};
20use ra_text_edit::TextEdit; 20use ra_text_edit::TextEdit;
21 21
22pub(crate) use crate::assist_ctx::{Assist, AssistCtx}; 22pub(crate) use crate::assist_ctx::{Assist, AssistCtx};
@@ -77,6 +77,55 @@ where
77 }) 77 })
78} 78}
79 79
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<'a> {
87 /// Finds all imports for the given name and the module that contains this name.
88 fn find_imports(
89 &mut self,
90 name_to_import: InFile<&NameRef>,
91 module_with_name_to_import: Module,
92 ) -> Option<Vec<ModPath>>;
93}
94
95/// Return all the assists applicable at the given position
96/// and additional assists that need the imports locator functionality to work.
97///
98/// Assists are returned in the "resolved" state, that is with edit fully
99/// computed.
100pub fn assists_with_imports_locator<'a, H, F: 'a>(
101 db: &H,
102 range: FileRange,
103 mut imports_locator: F,
104) -> Vec<ResolvedAssist>
105where
106 H: HirDatabase + 'static,
107 F: ImportsLocator<'a>,
108{
109 AssistCtx::with_ctx(db, range, true, |ctx| {
110 let mut assists = assists::all()
111 .iter()
112 .map(|f| f(ctx.clone()))
113 .chain(
114 assists::all_with_imports_locator()
115 .iter()
116 .map(|f| f(ctx.clone(), &mut imports_locator)),
117 )
118 .filter_map(std::convert::identity)
119 .map(|a| match a {
120 Assist::Resolved { assist } => assist,
121 Assist::Unresolved { .. } => unreachable!(),
122 })
123 .collect();
124 sort_assists(&mut assists);
125 assists
126 })
127}
128
80/// Return all the assists applicable at the given position. 129/// Return all the assists applicable at the given position.
81/// 130///
82/// Assists are returned in the "resolved" state, that is with edit fully 131/// Assists are returned in the "resolved" state, that is with edit fully
@@ -85,8 +134,6 @@ pub fn assists<H>(db: &H, range: FileRange) -> Vec<ResolvedAssist>
85where 134where
86 H: HirDatabase + 'static, 135 H: HirDatabase + 'static,
87{ 136{
88 use std::cmp::Ordering;
89
90 AssistCtx::with_ctx(db, range, true, |ctx| { 137 AssistCtx::with_ctx(db, range, true, |ctx| {
91 let mut a = assists::all() 138 let mut a = assists::all()
92 .iter() 139 .iter()
@@ -95,19 +142,24 @@ where
95 Assist::Resolved { assist } => assist, 142 Assist::Resolved { assist } => assist,
96 Assist::Unresolved { .. } => unreachable!(), 143 Assist::Unresolved { .. } => unreachable!(),
97 }) 144 })
98 .collect::<Vec<_>>(); 145 .collect();
99 a.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) { 146 sort_assists(&mut a);
100 (Some(a), Some(b)) => a.len().cmp(&b.len()),
101 (Some(_), None) => Ordering::Less,
102 (None, Some(_)) => Ordering::Greater,
103 (None, None) => Ordering::Equal,
104 });
105 a 147 a
106 }) 148 })
107} 149}
108 150
151fn sort_assists(assists: &mut Vec<ResolvedAssist>) {
152 use std::cmp::Ordering;
153 assists.sort_by(|a, b| match (a.get_first_action().target, b.get_first_action().target) {
154 (Some(a), Some(b)) => a.len().cmp(&b.len()),
155 (Some(_), None) => Ordering::Less,
156 (None, Some(_)) => Ordering::Greater,
157 (None, None) => Ordering::Equal,
158 });
159}
160
109mod assists { 161mod assists {
110 use crate::{Assist, AssistCtx}; 162 use crate::{Assist, AssistCtx, ImportsLocator};
111 use hir::db::HirDatabase; 163 use hir::db::HirDatabase;
112 164
113 mod add_derive; 165 mod add_derive;
@@ -116,6 +168,7 @@ mod assists {
116 mod add_custom_impl; 168 mod add_custom_impl;
117 mod add_new; 169 mod add_new;
118 mod apply_demorgan; 170 mod apply_demorgan;
171 mod auto_import;
119 mod invert_if; 172 mod invert_if;
120 mod flip_comma; 173 mod flip_comma;
121 mod flip_binexpr; 174 mod flip_binexpr;
@@ -168,6 +221,11 @@ mod assists {
168 early_return::convert_to_guarded_return, 221 early_return::convert_to_guarded_return,
169 ] 222 ]
170 } 223 }
224
225 pub(crate) fn all_with_imports_locator<'a, DB: HirDatabase, F: ImportsLocator<'a>>(
226 ) -> &'a [fn(AssistCtx<DB>, &mut F) -> Option<Assist>] {
227 &[auto_import::auto_import]
228 }
171} 229}
172 230
173#[cfg(test)] 231#[cfg(test)]
@@ -176,7 +234,7 @@ mod helpers {
176 use ra_syntax::TextRange; 234 use ra_syntax::TextRange;
177 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range}; 235 use test_utils::{add_cursor, assert_eq_text, extract_offset, extract_range};
178 236
179 use crate::{test_db::TestDB, Assist, AssistCtx}; 237 use crate::{test_db::TestDB, Assist, AssistCtx, ImportsLocator};
180 238
181 pub(crate) fn check_assist( 239 pub(crate) fn check_assist(
182 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 240 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
@@ -206,6 +264,35 @@ mod helpers {
206 assert_eq_text!(after, &actual); 264 assert_eq_text!(after, &actual);
207 } 265 }
208 266
267 pub(crate) fn check_assist_with_imports_locator<'a, F: ImportsLocator<'a>>(
268 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
269 imports_locator: &mut F,
270 before: &str,
271 after: &str,
272 ) {
273 let (before_cursor_pos, before) = extract_offset(before);
274 let (db, file_id) = TestDB::with_single_file(&before);
275 let frange =
276 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
277 let assist = AssistCtx::with_ctx(&db, frange, true, |ctx| assist(ctx, imports_locator))
278 .expect("code action is not applicable");
279 let action = match assist {
280 Assist::Unresolved { .. } => unreachable!(),
281 Assist::Resolved { assist } => assist.get_first_action(),
282 };
283
284 let actual = action.edit.apply(&before);
285 let actual_cursor_pos = match action.cursor_position {
286 None => action
287 .edit
288 .apply_to_offset(before_cursor_pos)
289 .expect("cursor position is affected by the edit"),
290 Some(off) => off,
291 };
292 let actual = add_cursor(&actual, actual_cursor_pos);
293 assert_eq_text!(after, &actual);
294 }
295
209 pub(crate) fn check_assist_range( 296 pub(crate) fn check_assist_range(
210 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 297 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
211 before: &str, 298 before: &str,
@@ -279,6 +366,19 @@ mod helpers {
279 assert!(assist.is_none()); 366 assert!(assist.is_none());
280 } 367 }
281 368
369 pub(crate) fn check_assist_with_imports_locator_not_applicable<'a, F: ImportsLocator<'a>>(
370 assist: fn(AssistCtx<TestDB>, &mut F) -> Option<Assist>,
371 imports_locator: &mut F,
372 before: &str,
373 ) {
374 let (before_cursor_pos, before) = extract_offset(before);
375 let (db, file_id) = TestDB::with_single_file(&before);
376 let frange =
377 FileRange { file_id, range: TextRange::offset_len(before_cursor_pos, 0.into()) };
378 let assist = AssistCtx::with_ctx(&db, frange, true, |ctx| assist(ctx, imports_locator));
379 assert!(assist.is_none());
380 }
381
282 pub(crate) fn check_assist_range_not_applicable( 382 pub(crate) fn check_assist_range_not_applicable(
283 assist: fn(AssistCtx<TestDB>) -> Option<Assist>, 383 assist: fn(AssistCtx<TestDB>) -> Option<Assist>,
284 before: &str, 384 before: &str,