aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def')
-rw-r--r--crates/hir_def/Cargo.toml2
-rw-r--r--crates/hir_def/src/import_map.rs224
-rw-r--r--crates/hir_def/src/nameres.rs4
3 files changed, 181 insertions, 49 deletions
diff --git a/crates/hir_def/Cargo.toml b/crates/hir_def/Cargo.toml
index a88b5f57e..e8b581e2f 100644
--- a/crates/hir_def/Cargo.toml
+++ b/crates/hir_def/Cargo.toml
@@ -17,7 +17,7 @@ either = "1.5.3"
17anymap = "0.12.1" 17anymap = "0.12.1"
18drop_bomb = "0.1.4" 18drop_bomb = "0.1.4"
19fst = { version = "0.4", default-features = false } 19fst = { version = "0.4", default-features = false }
20itertools = "0.9.0" 20itertools = "0.10.0"
21indexmap = "1.4.0" 21indexmap = "1.4.0"
22smallvec = "1.4.0" 22smallvec = "1.4.0"
23 23
diff --git a/crates/hir_def/src/import_map.rs b/crates/hir_def/src/import_map.rs
index c0f108848..30b22f51d 100644
--- a/crates/hir_def/src/import_map.rs
+++ b/crates/hir_def/src/import_map.rs
@@ -238,32 +238,53 @@ pub enum ImportKind {
238 BuiltinType, 238 BuiltinType,
239} 239}
240 240
241/// A way to match import map contents against the search query.
242#[derive(Debug)]
243pub enum SearchMode {
244 /// Import map entry should strictly match the query string.
245 Equals,
246 /// Import map entry should contain the query string.
247 Contains,
248 /// Import map entry should contain all letters from the query string,
249 /// in the same order, but not necessary adjacent.
250 Fuzzy,
251}
252
241#[derive(Debug)] 253#[derive(Debug)]
242pub struct Query { 254pub struct Query {
243 query: String, 255 query: String,
244 lowercased: String, 256 lowercased: String,
245 anchor_end: bool, 257 name_only: bool,
258 search_mode: SearchMode,
246 case_sensitive: bool, 259 case_sensitive: bool,
247 limit: usize, 260 limit: usize,
248 exclude_import_kinds: FxHashSet<ImportKind>, 261 exclude_import_kinds: FxHashSet<ImportKind>,
249} 262}
250 263
251impl Query { 264impl Query {
252 pub fn new(query: &str) -> Self { 265 pub fn new(query: String) -> Self {
266 let lowercased = query.to_lowercase();
253 Self { 267 Self {
254 lowercased: query.to_lowercase(), 268 query,
255 query: query.to_string(), 269 lowercased,
256 anchor_end: false, 270 name_only: false,
271 search_mode: SearchMode::Contains,
257 case_sensitive: false, 272 case_sensitive: false,
258 limit: usize::max_value(), 273 limit: usize::max_value(),
259 exclude_import_kinds: FxHashSet::default(), 274 exclude_import_kinds: FxHashSet::default(),
260 } 275 }
261 } 276 }
262 277
263 /// Only returns items whose paths end with the (case-insensitive) query string as their last 278 /// Matches entries' names only, ignoring the rest of
264 /// segment. 279 /// the qualifier.
265 pub fn anchor_end(self) -> Self { 280 /// Example: for `std::marker::PhantomData`, the name is `PhantomData`.
266 Self { anchor_end: true, ..self } 281 pub fn name_only(self) -> Self {
282 Self { name_only: true, ..self }
283 }
284
285 /// Specifies the way to search for the entries using the query.
286 pub fn search_mode(self, search_mode: SearchMode) -> Self {
287 Self { search_mode, ..self }
267 } 288 }
268 289
269 /// Limits the returned number of items to `limit`. 290 /// Limits the returned number of items to `limit`.
@@ -283,6 +304,40 @@ impl Query {
283 } 304 }
284} 305}
285 306
307fn contains_query(query: &Query, input_path: &ImportPath, enforce_lowercase: bool) -> bool {
308 let mut input = if query.name_only {
309 input_path.segments.last().unwrap().to_string()
310 } else {
311 input_path.to_string()
312 };
313 if enforce_lowercase || !query.case_sensitive {
314 input.make_ascii_lowercase();
315 }
316
317 let query_string =
318 if !enforce_lowercase && query.case_sensitive { &query.query } else { &query.lowercased };
319
320 match query.search_mode {
321 SearchMode::Equals => &input == query_string,
322 SearchMode::Contains => input.contains(query_string),
323 SearchMode::Fuzzy => {
324 let mut unchecked_query_chars = query_string.chars();
325 let mut mismatching_query_char = unchecked_query_chars.next();
326
327 for input_char in input.chars() {
328 match mismatching_query_char {
329 None => return true,
330 Some(matching_query_char) if matching_query_char == input_char => {
331 mismatching_query_char = unchecked_query_chars.next();
332 }
333 _ => (),
334 }
335 }
336 mismatching_query_char.is_none()
337 }
338 }
339}
340
286/// Searches dependencies of `krate` for an importable path matching `query`. 341/// Searches dependencies of `krate` for an importable path matching `query`.
287/// 342///
288/// This returns a list of items that could be imported from dependencies of `krate`. 343/// This returns a list of items that could be imported from dependencies of `krate`.
@@ -312,39 +367,29 @@ pub fn search_dependencies<'a>(
312 let importables = &import_map.importables[indexed_value.value as usize..]; 367 let importables = &import_map.importables[indexed_value.value as usize..];
313 368
314 // Path shared by the importable items in this group. 369 // Path shared by the importable items in this group.
315 let path = &import_map.map[&importables[0]].path; 370 let common_importables_path = &import_map.map[&importables[0]].path;
316 371 if !contains_query(&query, common_importables_path, true) {
317 if query.anchor_end { 372 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 } 373 }
324 374
375 let common_importables_path_fst = fst_path(common_importables_path);
325 // Add the items from this `ModPath` group. Those are all subsequent items in 376 // Add the items from this `ModPath` group. Those are all subsequent items in
326 // `importables` whose paths match `path`. 377 // `importables` whose paths match `path`.
327 let iter = importables 378 let iter = importables
328 .iter() 379 .iter()
329 .copied() 380 .copied()
330 .take_while(|item| { 381 .take_while(|item| {
331 let item_path = &import_map.map[item].path; 382 common_importables_path_fst == fst_path(&import_map.map[item].path)
332 fst_path(item_path) == fst_path(path)
333 }) 383 })
334 .filter(|&item| match item_import_kind(item) { 384 .filter(|&item| match item_import_kind(item) {
335 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind), 385 Some(import_kind) => !query.exclude_import_kinds.contains(&import_kind),
336 None => true, 386 None => true,
387 })
388 .filter(|item| {
389 !query.case_sensitive // we've already checked the common importables path case-insensitively
390 || contains_query(&query, &import_map.map[item].path, false)
337 }); 391 });
338 392 res.extend(iter);
339 if query.case_sensitive {
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 393
349 if res.len() >= query.limit { 394 if res.len() >= query.limit {
350 res.truncate(query.limit); 395 res.truncate(query.limit);
@@ -387,8 +432,9 @@ fn item_import_kind(item: ItemInNs) -> Option<ImportKind> {
387mod tests { 432mod tests {
388 use base_db::{fixture::WithFixture, SourceDatabase, Upcast}; 433 use base_db::{fixture::WithFixture, SourceDatabase, Upcast};
389 use expect_test::{expect, Expect}; 434 use expect_test::{expect, Expect};
435 use stdx::format_to;
390 436
391 use crate::{test_db::TestDB, AssocContainerId, Lookup}; 437 use crate::{data::FunctionData, test_db::TestDB, AssocContainerId, Lookup};
392 438
393 use super::*; 439 use super::*;
394 440
@@ -407,14 +453,32 @@ mod tests {
407 .into_iter() 453 .into_iter()
408 .filter_map(|item| { 454 .filter_map(|item| {
409 let mark = match item { 455 let mark = match item {
456 ItemInNs::Types(ModuleDefId::FunctionId(_))
457 | ItemInNs::Values(ModuleDefId::FunctionId(_)) => "f",
410 ItemInNs::Types(_) => "t", 458 ItemInNs::Types(_) => "t",
411 ItemInNs::Values(_) => "v", 459 ItemInNs::Values(_) => "v",
412 ItemInNs::Macros(_) => "m", 460 ItemInNs::Macros(_) => "m",
413 }; 461 };
414 let item = assoc_to_trait(&db, item);
415 item.krate(db.upcast()).map(|krate| { 462 item.krate(db.upcast()).map(|krate| {
416 let map = db.import_map(krate); 463 let map = db.import_map(krate);
417 let path = map.path_of(item).unwrap(); 464
465 let path = match assoc_to_trait(&db, item) {
466 Some(trait_) => {
467 let mut full_path = map.path_of(trait_).unwrap().to_string();
468 if let ItemInNs::Types(ModuleDefId::FunctionId(function_id))
469 | ItemInNs::Values(ModuleDefId::FunctionId(function_id)) = item
470 {
471 format_to!(
472 full_path,
473 "::{}",
474 FunctionData::fn_data_query(&db, function_id).name,
475 );
476 }
477 full_path
478 }
479 None => map.path_of(item).unwrap().to_string(),
480 };
481
418 format!( 482 format!(
419 "{}::{} ({})\n", 483 "{}::{} ({})\n",
420 crate_graph[krate].display_name.as_ref().unwrap(), 484 crate_graph[krate].display_name.as_ref().unwrap(),
@@ -427,15 +491,15 @@ mod tests {
427 expect.assert_eq(&actual) 491 expect.assert_eq(&actual)
428 } 492 }
429 493
430 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> ItemInNs { 494 fn assoc_to_trait(db: &dyn DefDatabase, item: ItemInNs) -> Option<ItemInNs> {
431 let assoc: AssocItemId = match item { 495 let assoc: AssocItemId = match item {
432 ItemInNs::Types(it) | ItemInNs::Values(it) => match it { 496 ItemInNs::Types(it) | ItemInNs::Values(it) => match it {
433 ModuleDefId::TypeAliasId(it) => it.into(), 497 ModuleDefId::TypeAliasId(it) => it.into(),
434 ModuleDefId::FunctionId(it) => it.into(), 498 ModuleDefId::FunctionId(it) => it.into(),
435 ModuleDefId::ConstId(it) => it.into(), 499 ModuleDefId::ConstId(it) => it.into(),
436 _ => return item, 500 _ => return None,
437 }, 501 },
438 _ => return item, 502 _ => return None,
439 }; 503 };
440 504
441 let container = match assoc { 505 let container = match assoc {
@@ -445,8 +509,8 @@ mod tests {
445 }; 509 };
446 510
447 match container { 511 match container {
448 AssocContainerId::TraitId(it) => ItemInNs::Types(it.into()), 512 AssocContainerId::TraitId(it) => Some(ItemInNs::Types(it.into())),
449 _ => item, 513 _ => None,
450 } 514 }
451 } 515 }
452 516
@@ -685,7 +749,7 @@ mod tests {
685 } 749 }
686 750
687 #[test] 751 #[test]
688 fn search() { 752 fn search_mode() {
689 let ra_fixture = r#" 753 let ra_fixture = r#"
690 //- /main.rs crate:main deps:dep 754 //- /main.rs crate:main deps:dep
691 //- /dep.rs crate:dep deps:tdep 755 //- /dep.rs crate:dep deps:tdep
@@ -713,28 +777,96 @@ mod tests {
713 check_search( 777 check_search(
714 ra_fixture, 778 ra_fixture,
715 "main", 779 "main",
716 Query::new("fmt"), 780 Query::new("fmt".to_string()).search_mode(SearchMode::Fuzzy),
717 expect![[r#" 781 expect![[r#"
718 dep::fmt (t) 782 dep::fmt (t)
719 dep::Fmt (t) 783 dep::Fmt (t)
720 dep::Fmt (v) 784 dep::Fmt (v)
721 dep::Fmt (m) 785 dep::Fmt (m)
722 dep::fmt::Display (t) 786 dep::fmt::Display (t)
723 dep::format (v) 787 dep::format (f)
788 dep::fmt::Display::fmt (f)
789 "#]],
790 );
791
792 check_search(
793 ra_fixture,
794 "main",
795 Query::new("fmt".to_string()).search_mode(SearchMode::Equals),
796 expect![[r#"
797 dep::fmt (t)
798 dep::Fmt (t)
799 dep::Fmt (v)
800 dep::Fmt (m)
801 dep::fmt::Display::fmt (f)
802 "#]],
803 );
804
805 check_search(
806 ra_fixture,
807 "main",
808 Query::new("fmt".to_string()).search_mode(SearchMode::Contains),
809 expect![[r#"
810 dep::fmt (t)
811 dep::Fmt (t)
812 dep::Fmt (v)
813 dep::Fmt (m)
724 dep::fmt::Display (t) 814 dep::fmt::Display (t)
815 dep::fmt::Display::fmt (f)
725 "#]], 816 "#]],
726 ); 817 );
818 }
819
820 #[test]
821 fn name_only() {
822 let ra_fixture = r#"
823 //- /main.rs crate:main deps:dep
824 //- /dep.rs crate:dep deps:tdep
825 use tdep::fmt as fmt_dep;
826 pub mod fmt {
827 pub trait Display {
828 fn fmt();
829 }
830 }
831 #[macro_export]
832 macro_rules! Fmt {
833 () => {};
834 }
835 pub struct Fmt;
836
837 pub fn format() {}
838 pub fn no() {}
839
840 //- /tdep.rs crate:tdep
841 pub mod fmt {
842 pub struct NotImportableFromMain;
843 }
844 "#;
727 845
728 check_search( 846 check_search(
729 ra_fixture, 847 ra_fixture,
730 "main", 848 "main",
731 Query::new("fmt").anchor_end(), 849 Query::new("fmt".to_string()),
732 expect![[r#" 850 expect![[r#"
733 dep::fmt (t) 851 dep::fmt (t)
734 dep::Fmt (t) 852 dep::Fmt (t)
735 dep::Fmt (v) 853 dep::Fmt (v)
736 dep::Fmt (m) 854 dep::Fmt (m)
737 dep::fmt::Display (t) 855 dep::fmt::Display (t)
856 dep::fmt::Display::fmt (f)
857 "#]],
858 );
859
860 check_search(
861 ra_fixture,
862 "main",
863 Query::new("fmt".to_string()).name_only(),
864 expect![[r#"
865 dep::fmt (t)
866 dep::Fmt (t)
867 dep::Fmt (v)
868 dep::Fmt (m)
869 dep::fmt::Display::fmt (f)
738 "#]], 870 "#]],
739 ); 871 );
740 } 872 }
@@ -752,7 +884,7 @@ mod tests {
752 check_search( 884 check_search(
753 ra_fixture, 885 ra_fixture,
754 "main", 886 "main",
755 Query::new("FMT"), 887 Query::new("FMT".to_string()),
756 expect![[r#" 888 expect![[r#"
757 dep::fmt (t) 889 dep::fmt (t)
758 dep::fmt (v) 890 dep::fmt (v)
@@ -764,7 +896,7 @@ mod tests {
764 check_search( 896 check_search(
765 ra_fixture, 897 ra_fixture,
766 "main", 898 "main",
767 Query::new("FMT").case_sensitive(), 899 Query::new("FMT".to_string()).case_sensitive(),
768 expect![[r#" 900 expect![[r#"
769 dep::FMT (t) 901 dep::FMT (t)
770 dep::FMT (v) 902 dep::FMT (v)
@@ -793,7 +925,7 @@ mod tests {
793 pub fn no() {} 925 pub fn no() {}
794 "#, 926 "#,
795 "main", 927 "main",
796 Query::new("").limit(2), 928 Query::new("".to_string()).limit(2),
797 expect![[r#" 929 expect![[r#"
798 dep::fmt (t) 930 dep::fmt (t)
799 dep::Fmt (t) 931 dep::Fmt (t)
@@ -814,7 +946,7 @@ mod tests {
814 check_search( 946 check_search(
815 ra_fixture, 947 ra_fixture,
816 "main", 948 "main",
817 Query::new("FMT"), 949 Query::new("FMT".to_string()),
818 expect![[r#" 950 expect![[r#"
819 dep::fmt (t) 951 dep::fmt (t)
820 dep::fmt (v) 952 dep::fmt (v)
@@ -826,7 +958,7 @@ mod tests {
826 check_search( 958 check_search(
827 ra_fixture, 959 ra_fixture,
828 "main", 960 "main",
829 Query::new("FMT").exclude_import_kind(ImportKind::Adt), 961 Query::new("FMT".to_string()).exclude_import_kind(ImportKind::Adt),
830 expect![[r#""#]], 962 expect![[r#""#]],
831 ); 963 );
832 } 964 }
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index ffd0381d4..5682e122d 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -249,7 +249,7 @@ impl CrateDefMap {
249 buf.push_str(" _"); 249 buf.push_str(" _");
250 } 250 }
251 251
252 buf.push_str("\n"); 252 buf.push('\n');
253 } 253 }
254 254
255 for (name, child) in map.modules[module].children.iter() { 255 for (name, child) in map.modules[module].children.iter() {
@@ -454,7 +454,7 @@ mod diagnostics {
454 }); 454 });
455 for token in tokens { 455 for token in tokens {
456 if token.kind() == SyntaxKind::IDENT 456 if token.kind() == SyntaxKind::IDENT
457 && token.to_string() == *name 457 && token.text() == name.as_str()
458 { 458 {
459 precise_location = Some(token.text_range()); 459 precise_location = Some(token.text_range());
460 break 'outer; 460 break 'outer;