aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src')
-rw-r--r--crates/hir_def/src/import_map.rs96
1 files changed, 62 insertions, 34 deletions
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index c0f108848..64d0ec471 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -156,8 +156,7 @@ 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_path(&importables[start].1.path); 159 let key = fst_string(&importables[start].1.path);
160
161 builder.insert(key, start as u64).unwrap(); 160 builder.insert(key, start as u64).unwrap();
162 } 161 }
163 162
@@ -213,15 +212,15 @@ impl fmt::Debug for ImportMap {
213 } 212 }
214} 213}
215 214
216fn fst_path(path: &ImportPath) -> String { 215fn fst_string<T: ToString>(t: &T) -> String {
217 let mut s = path.to_string(); 216 let mut s = t.to_string();
218 s.make_ascii_lowercase(); 217 s.make_ascii_lowercase();
219 s 218 s
220} 219}
221 220
222fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering { 221fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
223 let lhs_str = fst_path(&lhs.path); 222 let lhs_str = fst_string(&lhs.path);
224 let rhs_str = fst_path(&rhs.path); 223 let rhs_str = fst_string(&rhs.path);
225 lhs_str.cmp(&rhs_str) 224 lhs_str.cmp(&rhs_str)
226} 225}
227 226
@@ -242,7 +241,10 @@ pub enum ImportKind {
242pub struct Query { 241pub struct Query {
243 query: String, 242 query: String,
244 lowercased: String, 243 lowercased: String,
245 anchor_end: bool, 244 // TODO kb use enums instead?
245 name_only: bool,
246 name_end: bool,
247 strict_include: bool,
246 case_sensitive: bool, 248 case_sensitive: bool,
247 limit: usize, 249 limit: usize,
248 exclude_import_kinds: FxHashSet<ImportKind>, 250 exclude_import_kinds: FxHashSet<ImportKind>,
@@ -253,17 +255,27 @@ impl Query {
253 Self { 255 Self {
254 lowercased: query.to_lowercase(), 256 lowercased: query.to_lowercase(),
255 query: query.to_string(), 257 query: query.to_string(),
256 anchor_end: false, 258 name_only: false,
259 name_end: false,
260 strict_include: false,
257 case_sensitive: false, 261 case_sensitive: false,
258 limit: usize::max_value(), 262 limit: usize::max_value(),
259 exclude_import_kinds: FxHashSet::default(), 263 exclude_import_kinds: FxHashSet::default(),
260 } 264 }
261 } 265 }
262 266
263 /// Only returns items whose paths end with the (case-insensitive) query string as their last 267 pub fn name_end(self) -> Self {
264 /// segment. 268 Self { name_end: true, ..self }
265 pub fn anchor_end(self) -> Self { 269 }
266 Self { anchor_end: true, ..self } 270
271 /// todo kb
272 pub fn name_only(self) -> Self {
273 Self { name_only: true, ..self }
274 }
275
276 /// todo kb
277 pub fn strict_include(self) -> Self {
278 Self { strict_include: true, ..self }
267 } 279 }
268 280
269 /// Limits the returned number of items to `limit`. 281 /// Limits the returned number of items to `limit`.
@@ -283,6 +295,35 @@ impl Query {
283 } 295 }
284} 296}
285 297
298// TODO kb: ugly with a special `return true` case and the `enforce_lowercase` one.
299fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool {
300 let mut input = if query.name_only {
301 input_path.segments.last().unwrap().to_string()
302 } else {
303 input_path.to_string()
304 };
305 if enforce_lowercase || !query.case_sensitive {
306 input.make_ascii_lowercase();
307 }
308
309 let query_string =
310 if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased };
311
312 if query.strict_include {
313 if query.name_end {
314 &input == query_string
315 } else {
316 input.contains(query_string)
317 }
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 }
325}
326
286/// Searches dependencies of `krate` for an importable path matching `query`. 327/// Searches dependencies of `krate` for an importable path matching `query`.
287/// 328///
288/// This returns a list of items that could be imported from dependencies of `krate`. 329/// This returns a list of items that could be imported from dependencies of `krate`.
@@ -312,39 +353,26 @@ pub fn search_dependencies<'a>(
312 let importables = &import_map.importables[indexed_value.value as usize..]; 353 let importables = &import_map.importables[indexed_value.value as usize..];
313 354
314 // Path shared by the importable items in this group. 355 // Path shared by the importable items in this group.
315 let path = &import_map.map[&importables[0]].path; 356 let common_importables_path = &import_map.map[&importables[0]].path;
316 357 if !contains_query(&query, common_importables_path, true) {
317 if query.anchor_end { 358 continue;
318 // Last segment must match query.
319 let last = path.segments.last().unwrap().to_string();
320 if last.to_lowercase() != query.lowercased {
321 continue;
322 }
323 } 359 }
324 360
361 let common_importables_path_fst = fst_string(common_importables_path);
325 // Add the items from this `ModPath` group. Those are all subsequent items in 362 // Add the items from this `ModPath` group. Those are all subsequent items in
326 // `importables` whose paths match `path`. 363 // `importables` whose paths match `path`.
327 let iter = importables 364 let iter = importables
328 .iter() 365 .iter()
329 .copied() 366 .copied()
330 .take_while(|item| { 367 .take_while(|item| {
331 let item_path = &import_map.map[item].path; 368 common_importables_path_fst == fst_string(&import_map.map[item].path)
332 fst_path(item_path) == fst_path(path)
333 }) 369 })
334 .filter(|&item| match item_import_kind(item) { 370 .filter(|&item| match item_import_kind(item) {
335 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), 371 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
336 None => true, 372 None => true,
337 }); 373 })
338 374 .filter(|item| contains_query(&query, &import_map.map[item].path, false));
339 if query.case_sensitive { 375 res.extend(iter);
340 // FIXME: This does not do a subsequence match.
341 res.extend(iter.filter(|item| {
342 let item_path = &import_map.map[item].path;
343 item_path.to_string().contains(&query.query)
344 }));
345 } else {
346 res.extend(iter);
347 }
348 376
349 if res.len() >= query.limit { 377 if res.len() >= query.limit {
350 res.truncate(query.limit); 378 res.truncate(query.limit);
@@ -728,7 +756,7 @@ mod tests {
728 check_search( 756 check_search(
729 ra_fixture, 757 ra_fixture,
730 "main", 758 "main",
731 Query::new("fmt").anchor_end(), 759 Query::new("fmt").name_only().strict_include(),
732 expect![[r#" 760 expect![[r#"
733 dep::fmt (t) 761 dep::fmt (t)
734 dep::Fmt (t) 762 dep::Fmt (t)