diff options
author | Kirill Bulatov <[email protected]> | 2020-12-28 12:24:13 +0000 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2020-12-28 13:06:10 +0000 |
commit | c4995cfbd5b265c02d3038d72b8a022cde5f7040 (patch) | |
tree | d2fdb44eedf6c5e806804a1b874643b52586bb44 /crates | |
parent | 0e48cd0c3c712cea0267476de974012b2b05b508 (diff) |
Better query api and fuzzy search
Diffstat (limited to 'crates')
-rw-r--r-- | crates/completion/src/completions/unqualified_path.rs | 2 | ||||
-rw-r--r-- | crates/hir_def/src/import_map.rs | 73 | ||||
-rw-r--r-- | crates/ide_db/src/imports_locator.rs | 13 |
3 files changed, 47 insertions, 41 deletions
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 3d72a08b4..d09849752 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -135,7 +135,7 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
135 | ctx.krate?, | 135 | ctx.krate?, |
136 | Some(100), | 136 | Some(100), |
137 | &potential_import_name, | 137 | &potential_import_name, |
138 | false, | 138 | true, |
139 | ) | 139 | ) |
140 | .filter_map(|import_candidate| { | 140 | .filter_map(|import_candidate| { |
141 | Some(match import_candidate { | 141 | Some(match import_candidate { |
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs index 64d0ec471..ce25e1c6e 100644 --- a/crates/hir_def/src/import_map.rs +++ b/crates/hir_def/src/import_map.rs | |||
@@ -156,7 +156,8 @@ impl ImportMap { | |||
156 | let start = last_batch_start; | 156 | let start = last_batch_start; |
157 | last_batch_start = idx + 1; | 157 | last_batch_start = idx + 1; |
158 | 158 | ||
159 | let key = fst_string(&importables[start].1.path); | 159 | let key = fst_path(&importables[start].1.path); |
160 | |||
160 | builder.insert(key, start as u64).unwrap(); | 161 | builder.insert(key, start as u64).unwrap(); |
161 | } | 162 | } |
162 | 163 | ||
@@ -212,15 +213,15 @@ impl fmt::Debug for ImportMap { | |||
212 | } | 213 | } |
213 | } | 214 | } |
214 | 215 | ||
215 | fn fst_string<T: ToString>(t: &T) -> String { | 216 | fn fst_path(path: &ImportPath) -> String { |
216 | let mut s = t.to_string(); | 217 | let mut s = path.to_string(); |
217 | s.make_ascii_lowercase(); | 218 | s.make_ascii_lowercase(); |
218 | s | 219 | s |
219 | } | 220 | } |
220 | 221 | ||
221 | fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { | 222 | fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { |
222 | let lhs_str = fst_string(&lhs.path); | 223 | let lhs_str = fst_path(&lhs.path); |
223 | let rhs_str = fst_string(&rhs.path); | 224 | let rhs_str = fst_path(&rhs.path); |
224 | lhs_str.cmp(&rhs_str) | 225 | lhs_str.cmp(&rhs_str) |
225 | } | 226 | } |
226 | 227 | ||
@@ -237,14 +238,20 @@ pub enum ImportKind { | |||
237 | BuiltinType, | 238 | BuiltinType, |
238 | } | 239 | } |
239 | 240 | ||
241 | /// todo kb | ||
242 | #[derive(Debug)] | ||
243 | pub enum SearchMode { | ||
244 | Equals, | ||
245 | Contains, | ||
246 | Fuzzy, | ||
247 | } | ||
248 | |||
240 | #[derive(Debug)] | 249 | #[derive(Debug)] |
241 | pub struct Query { | 250 | pub struct Query { |
242 | query: String, | 251 | query: String, |
243 | lowercased: String, | 252 | lowercased: String, |
244 | // TODO kb use enums instead? | ||
245 | name_only: bool, | 253 | name_only: bool, |
246 | name_end: bool, | 254 | search_mode: SearchMode, |
247 | strict_include: bool, | ||
248 | case_sensitive: bool, | 255 | case_sensitive: bool, |
249 | limit: usize, | 256 | limit: usize, |
250 | exclude_import_kinds: FxHashSet<ImportKind>, | 257 | exclude_import_kinds: FxHashSet<ImportKind>, |
@@ -253,29 +260,23 @@ pub struct Query { | |||
253 | impl Query { | 260 | impl Query { |
254 | pub fn new(query: &str) -> Self { | 261 | pub fn new(query: &str) -> Self { |
255 | Self { | 262 | Self { |
256 | lowercased: query.to_lowercase(), | ||
257 | query: query.to_string(), | 263 | query: query.to_string(), |
264 | lowercased: query.to_lowercase(), | ||
258 | name_only: false, | 265 | name_only: false, |
259 | name_end: false, | 266 | search_mode: SearchMode::Contains, |
260 | strict_include: false, | ||
261 | case_sensitive: false, | 267 | case_sensitive: false, |
262 | limit: usize::max_value(), | 268 | limit: usize::max_value(), |
263 | exclude_import_kinds: FxHashSet::default(), | 269 | exclude_import_kinds: FxHashSet::default(), |
264 | } | 270 | } |
265 | } | 271 | } |
266 | 272 | ||
267 | pub fn name_end(self) -> Self { | ||
268 | Self { name_end: true, ..self } | ||
269 | } | ||
270 | |||
271 | /// todo kb | ||
272 | pub fn name_only(self) -> Self { | 273 | pub fn name_only(self) -> Self { |
273 | Self { name_only: true, ..self } | 274 | Self { name_only: true, ..self } |
274 | } | 275 | } |
275 | 276 | ||
276 | /// todo kb | 277 | /// todo kb |
277 | pub fn strict_include(self) -> Self { | 278 | pub fn search_mode(self, search_mode: SearchMode) -> Self { |
278 | Self { strict_include: true, ..self } | 279 | Self { search_mode, ..self } |
279 | } | 280 | } |
280 | 281 | ||
281 | /// Limits the returned number of items to `limit`. | 282 | /// Limits the returned number of items to `limit`. |
@@ -309,18 +310,24 @@ fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: boo | |||
309 | let query_string = | 310 | let query_string = |
310 | if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; | 311 | if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased }; |
311 | 312 | ||
312 | if query.strict_include { | 313 | match query.search_mode { |
313 | if query.name_end { | 314 | SearchMode::Equals => &input == query_string, |
314 | &input == query_string | 315 | SearchMode::Contains => input.contains(query_string), |
315 | } else { | 316 | SearchMode::Fuzzy => { |
316 | input.contains(query_string) | 317 | let mut unchecked_query_chars = query_string.chars(); |
318 | let mut mismatching_query_char = unchecked_query_chars.next(); | ||
319 | |||
320 | for input_char in input.chars() { | ||
321 | match mismatching_query_char { | ||
322 | None => return true, | ||
323 | Some(matching_query_char) if matching_query_char == input_char => { | ||
324 | mismatching_query_char = unchecked_query_chars.next(); | ||
325 | } | ||
326 | _ => (), | ||
327 | } | ||
328 | } | ||
329 | mismatching_query_char.is_none() | ||
317 | } | 330 | } |
318 | } else if query.name_end { | ||
319 | input.ends_with(query_string) | ||
320 | } else { | ||
321 | let input_chars = input.chars().collect::<FxHashSet<_>>(); | ||
322 | // TODO kb actually check for the order and the quantity | ||
323 | query_string.chars().all(|query_char| input_chars.contains(&query_char)) | ||
324 | } | 331 | } |
325 | } | 332 | } |
326 | 333 | ||
@@ -358,14 +365,14 @@ pub fn search_dependencies<'a>( | |||
358 | continue; | 365 | continue; |
359 | } | 366 | } |
360 | 367 | ||
361 | let common_importables_path_fst = fst_string(common_importables_path); | 368 | let common_importables_path_fst = fst_path(common_importables_path); |
362 | // Add the items from this `ModPath` group. Those are all subsequent items in | 369 | // Add the items from this `ModPath` group. Those are all subsequent items in |
363 | // `importables` whose paths match `path`. | 370 | // `importables` whose paths match `path`. |
364 | let iter = importables | 371 | let iter = importables |
365 | .iter() | 372 | .iter() |
366 | .copied() | 373 | .copied() |
367 | .take_while(|item| { | 374 | .take_while(|item| { |
368 | common_importables_path_fst == fst_string(&import_map.map[item].path) | 375 | common_importables_path_fst == fst_path(&import_map.map[item].path) |
369 | }) | 376 | }) |
370 | .filter(|&item| match item_import_kind(item) { | 377 | .filter(|&item| match item_import_kind(item) { |
371 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), | 378 | Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), |
@@ -741,7 +748,7 @@ mod tests { | |||
741 | check_search( | 748 | check_search( |
742 | ra_fixture, | 749 | ra_fixture, |
743 | "main", | 750 | "main", |
744 | Query::new("fmt"), | 751 | Query::new("fmt").search_mode(SearchMode::Fuzzy), |
745 | expect![[r#" | 752 | expect![[r#" |
746 | dep::fmt (t) | 753 | dep::fmt (t) |
747 | dep::Fmt (t) | 754 | dep::Fmt (t) |
@@ -756,7 +763,7 @@ mod tests { | |||
756 | check_search( | 763 | check_search( |
757 | ra_fixture, | 764 | ra_fixture, |
758 | "main", | 765 | "main", |
759 | Query::new("fmt").name_only().strict_include(), | 766 | Query::new("fmt").name_only().search_mode(SearchMode::Equals), |
760 | expect![[r#" | 767 | expect![[r#" |
761 | dep::fmt (t) | 768 | dep::fmt (t) |
762 | dep::Fmt (t) | 769 | dep::Fmt (t) |
diff --git a/crates/ide_db/src/imports_locator.rs b/crates/ide_db/src/imports_locator.rs index 0de949b9a..986cb5b83 100644 --- a/crates/ide_db/src/imports_locator.rs +++ b/crates/ide_db/src/imports_locator.rs | |||
@@ -30,8 +30,7 @@ pub fn find_exact_imports<'a>( | |||
30 | import_map::Query::new(name_to_import) | 30 | import_map::Query::new(name_to_import) |
31 | .limit(40) | 31 | .limit(40) |
32 | .name_only() | 32 | .name_only() |
33 | .name_end() | 33 | .search_mode(import_map::SearchMode::Equals) |
34 | .strict_include() | ||
35 | .case_sensitive(), | 34 | .case_sensitive(), |
36 | ) | 35 | ) |
37 | } | 36 | } |
@@ -41,14 +40,14 @@ pub fn find_similar_imports<'a>( | |||
41 | krate: Crate, | 40 | krate: Crate, |
42 | limit: Option<usize>, | 41 | limit: Option<usize>, |
43 | name_to_import: &str, | 42 | name_to_import: &str, |
44 | // TODO kb change it to search across the whole path or not? | 43 | name_only: bool, |
45 | ignore_modules: bool, | ||
46 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { | 44 | ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> { |
47 | let _p = profile::span("find_similar_imports"); | 45 | let _p = profile::span("find_similar_imports"); |
48 | 46 | ||
49 | let mut external_query = import_map::Query::new(name_to_import).name_only(); | 47 | let mut external_query = |
50 | if ignore_modules { | 48 | import_map::Query::new(name_to_import).search_mode(import_map::SearchMode::Fuzzy); |
51 | external_query = external_query.exclude_import_kind(import_map::ImportKind::Module); | 49 | if name_only { |
50 | external_query = external_query.name_only(); | ||
52 | } | 51 | } |
53 | 52 | ||
54 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); | 53 | let mut local_query = symbol_index::Query::new(name_to_import.to_string()); |