Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion crates/hir-def/src/import_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,8 @@ enum SearchMode {
/// Import map entry should contain all letters from the query string,
/// in the same order, but not necessary adjacent.
Fuzzy,
/// Import map entry should match the query string by prefix.
Prefix,
}

/// Three possible ways to search for the name in associated and/or other items.
Expand Down Expand Up @@ -324,6 +326,14 @@ impl Query {
Self { search_mode: SearchMode::Fuzzy, ..self }
}

pub fn prefix(self) -> Self {
Self { search_mode: SearchMode::Prefix, ..self }
}

pub fn exact(self) -> Self {
Self { search_mode: SearchMode::Exact, ..self }
}

/// Specifies whether we want to include associated items in the result.
pub fn assoc_search_mode(self, assoc_mode: AssocSearchMode) -> Self {
Self { assoc_mode, ..self }
Expand Down Expand Up @@ -361,7 +371,8 @@ impl Query {
let query_string = if case_insensitive { &self.lowercased } else { &self.query };

match self.search_mode {
SearchMode::Exact => &input == query_string,
SearchMode::Exact => input == *query_string,
SearchMode::Prefix => input.starts_with(query_string),
SearchMode::Fuzzy => {
let mut input_chars = input.chars();
for query_char in query_string.chars() {
Expand Down
12 changes: 7 additions & 5 deletions crates/ide-completion/src/completions/flyimport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ use crate::{
TypeLocation,
},
render::{render_resolution_with_import, render_resolution_with_import_pat, RenderContext},
Completions,
};

use super::Completions;

// Feature: Completion With Autoimport
//
// When completing names in the current scope, proposes additional imports from other modules or crates,
Expand Down Expand Up @@ -377,9 +376,12 @@ fn import_assets_for_path(
&ctx.sema,
ctx.token.parent()?,
)?;
if fuzzy_name_length < 3 {
cov_mark::hit!(flyimport_exact_on_short_path);
assets_for_path.path_fuzzy_name_to_exact(false);
if fuzzy_name_length == 0 {
// nothing matches the empty string exactly, but we still compute assoc items in this case
assets_for_path.path_fuzzy_name_to_exact();
} else if fuzzy_name_length < 3 {
cov_mark::hit!(flyimport_prefix_on_short_path);
assets_for_path.path_fuzzy_name_to_prefix();
}
Some(assets_for_path)
}
Expand Down
76 changes: 66 additions & 10 deletions crates/ide-completion/src/tests/flyimport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,19 +116,47 @@ fn main() {
}

#[test]
fn short_paths_are_ignored() {
cov_mark::check!(flyimport_exact_on_short_path);
fn short_paths_are_prefix_matched() {
cov_mark::check!(flyimport_prefix_on_short_path);

check(
r#"
//- /lib.rs crate:dep
pub struct Bar;
pub struct Barc;
pub struct Rcar;
pub struct Rc;
pub const RC: () = ();
pub mod some_module {
pub struct Bar;
pub struct Rcar;
pub struct Rc;
pub const RC: () = ();
}

//- /main.rs crate:main deps:dep
fn main() {
Rc$0
}
"#,
expect![[r#"
st Rc (use dep::Rc)
st Rcar (use dep::Rcar)
st Rc (use dep::some_module::Rc)
st Rcar (use dep::some_module::Rcar)
"#]],
);
check(
r#"
//- /lib.rs crate:dep
pub struct Barc;
pub struct Rcar;
pub struct Rc;
pub const RC: () = ();
pub mod some_module {
pub struct Bar;
pub struct Rcar;
pub struct Rc;
pub const RC: () = ();
}

//- /main.rs crate:main deps:dep
Expand All @@ -137,8 +165,36 @@ fn main() {
}
"#,
expect![[r#"
ct RC (use dep::RC)
st Rc (use dep::Rc)
st Rcar (use dep::Rcar)
ct RC (use dep::some_module::RC)
st Rc (use dep::some_module::Rc)
st Rcar (use dep::some_module::Rcar)
"#]],
);
check(
r#"
//- /lib.rs crate:dep
pub struct Barc;
pub struct Rcar;
pub struct Rc;
pub const RC: () = ();
pub mod some_module {
pub struct Bar;
pub struct Rcar;
pub struct Rc;
pub const RC: () = ();
}

//- /main.rs crate:main deps:dep
fn main() {
RC$0
}
"#,
expect![[r#"
ct RC (use dep::RC)
ct RC (use dep::some_module::RC)
"#]],
);
}
Expand Down Expand Up @@ -841,8 +897,8 @@ fn main() {
TES$0
}"#,
expect![[r#"
ct TEST_CONST (use foo::TEST_CONST)
"#]],
ct TEST_CONST (use foo::TEST_CONST)
"#]],
);

check(
Expand All @@ -858,9 +914,9 @@ fn main() {
tes$0
}"#,
expect![[r#"
ct TEST_CONST (use foo::TEST_CONST)
fn test_function() (use foo::test_function) fn() -> i32
"#]],
ct TEST_CONST (use foo::TEST_CONST)
fn test_function() (use foo::test_function) fn() -> i32
"#]],
);

check(
Expand All @@ -873,9 +929,9 @@ mod foo {
}

fn main() {
Te$0
Tes$0
}"#,
expect![[]],
expect![""],
);
}

Expand Down
47 changes: 36 additions & 11 deletions crates/ide-db/src/imports/import_assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,22 +68,29 @@ pub struct FirstSegmentUnresolved {
pub enum NameToImport {
/// Requires items with names that exactly match the given string, bool indicates case-sensitivity.
Exact(String, bool),
/// Requires items with names that case-insensitively contain all letters from the string,
/// Requires items with names that match the given string by prefix, bool indicates case-sensitivity.
Prefix(String, bool),
/// Requires items with names contain all letters from the string,
/// in the same order, but not necessary adjacent.
Fuzzy(String),
Fuzzy(String, bool),
}

impl NameToImport {
pub fn exact_case_sensitive(s: String) -> NameToImport {
NameToImport::Exact(s, true)
}
}

impl NameToImport {
pub fn fuzzy(s: String) -> NameToImport {
// unless all chars are lowercase, we do a case sensitive search
let case_sensitive = s.chars().any(|c| c.is_uppercase());
NameToImport::Fuzzy(s, case_sensitive)
}

pub fn text(&self) -> &str {
match self {
NameToImport::Exact(text, _) => text.as_str(),
NameToImport::Fuzzy(text) => text.as_str(),
NameToImport::Prefix(text, _)
| NameToImport::Exact(text, _)
| NameToImport::Fuzzy(text, _) => text.as_str(),
}
}
}
Expand Down Expand Up @@ -165,7 +172,7 @@ impl ImportAssets {
Some(Self {
import_candidate: ImportCandidate::TraitMethod(TraitImportCandidate {
receiver_ty,
assoc_item_name: NameToImport::Fuzzy(fuzzy_method_name),
assoc_item_name: NameToImport::fuzzy(fuzzy_method_name),
}),
module_with_candidate: module_with_method_call,
candidate_node,
Expand Down Expand Up @@ -228,12 +235,30 @@ impl ImportAssets {
self.search_for(sema, None, prefer_no_std)
}

pub fn path_fuzzy_name_to_exact(&mut self, case_sensitive: bool) {
/// Requires imports to by prefix instead of fuzzily.
pub fn path_fuzzy_name_to_prefix(&mut self) {
if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
&mut self.import_candidate
{
let (name, case_sensitive) = match to_import {
NameToImport::Fuzzy(name, case_sensitive) => {
(std::mem::take(name), *case_sensitive)
}
_ => return,
};
*to_import = NameToImport::Prefix(name, case_sensitive);
}
}

/// Requires imports to match exactly instead of fuzzily.
pub fn path_fuzzy_name_to_exact(&mut self) {
if let ImportCandidate::Path(PathImportCandidate { name: to_import, .. }) =
&mut self.import_candidate
{
let name = match to_import {
NameToImport::Fuzzy(name) => std::mem::take(name),
let (name, case_sensitive) = match to_import {
NameToImport::Fuzzy(name, case_sensitive) => {
(std::mem::take(name), *case_sensitive)
}
_ => return,
};
*to_import = NameToImport::Exact(name, case_sensitive);
Expand Down Expand Up @@ -623,7 +648,7 @@ impl ImportCandidate {
fuzzy_name: String,
sema: &Semantics<'_, RootDatabase>,
) -> Option<Self> {
path_import_candidate(sema, qualifier, NameToImport::Fuzzy(fuzzy_name))
path_import_candidate(sema, qualifier, NameToImport::fuzzy(fuzzy_name))
}
}

Expand Down
30 changes: 19 additions & 11 deletions crates/ide-db/src/items_locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,26 +31,34 @@ pub fn items_with_name<'a>(
)
});

let prefix = matches!(name, NameToImport::Prefix(..));
let (mut local_query, mut external_query) = match name {
NameToImport::Exact(exact_name, case_sensitive) => {
NameToImport::Prefix(exact_name, case_sensitive)
| NameToImport::Exact(exact_name, case_sensitive) => {
let mut local_query = symbol_index::Query::new(exact_name.clone());
local_query.exact();

let external_query = import_map::Query::new(exact_name);

(
local_query,
if case_sensitive { external_query.case_sensitive() } else { external_query },
)
let mut external_query = import_map::Query::new(exact_name);
if prefix {
local_query.prefix();
external_query = external_query.prefix();
} else {
local_query.exact();
external_query = external_query.exact();
}
if case_sensitive {
local_query.case_sensitive();
external_query = external_query.case_sensitive();
}
(local_query, external_query)
}
NameToImport::Fuzzy(fuzzy_search_string) => {
NameToImport::Fuzzy(fuzzy_search_string, case_sensitive) => {
let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
local_query.fuzzy();

let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
.fuzzy()
.assoc_search_mode(assoc_item_search);

if fuzzy_search_string.to_lowercase() != fuzzy_search_string {
if case_sensitive {
local_query.case_sensitive();
external_query = external_query.case_sensitive();
}
Expand Down
43 changes: 34 additions & 9 deletions crates/ide-db/src/symbol_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,20 @@ use triomphe::Arc;

use crate::RootDatabase;

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum SearchMode {
Fuzzy,
Exact,
Prefix,
}

#[derive(Debug)]
pub struct Query {
query: String,
lowercased: String,
only_types: bool,
libs: bool,
exact: bool,
mode: SearchMode,
case_sensitive: bool,
limit: usize,
}
Expand All @@ -62,7 +69,7 @@ impl Query {
lowercased,
only_types: false,
libs: false,
exact: false,
mode: SearchMode::Fuzzy,
case_sensitive: false,
limit: usize::max_value(),
}
Expand All @@ -76,8 +83,16 @@ impl Query {
self.libs = true;
}

pub fn fuzzy(&mut self) {
self.mode = SearchMode::Fuzzy;
}

pub fn exact(&mut self) {
self.exact = true;
self.mode = SearchMode::Exact;
}

pub fn prefix(&mut self) {
self.mode = SearchMode::Prefix;
}

pub fn case_sensitive(&mut self) {
Expand Down Expand Up @@ -329,13 +344,23 @@ impl Query {
{
continue;
}
if self.exact {
if symbol.name != self.query {
continue;
let skip = match self.mode {
SearchMode::Fuzzy => {
self.case_sensitive
&& self.query.chars().any(|c| !symbol.name.contains(c))
}
} else if self.case_sensitive
&& self.query.chars().any(|c| !symbol.name.contains(c))
{
SearchMode::Exact => symbol.name != self.query,
SearchMode::Prefix if self.case_sensitive => {
!symbol.name.starts_with(&self.query)
}
SearchMode::Prefix => symbol
.name
.chars()
.zip(self.lowercased.chars())
.all(|(n, q)| n.to_lowercase().next() == Some(q)),
};

if skip {
continue;
}

Expand Down