aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/hir_def/src/import_map.rs73
-rw-r--r--crates/ide_db/src/imports_locator.rs13
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
215fn fst_string<T: ToString>(t: &T) -> String { 216fn 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
221fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { 222fn 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)]
243pub enum SearchMode {
244 Equals,
245 Contains,
246 Fuzzy,
247}
248
240#[derive(Debug)] 249#[derive(Debug)]
241pub struct Query { 250pub 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 {
253impl Query { 260impl 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());