diff options
author | Kirill Bulatov <[email protected]> | 2020-12-19 10:54:17 +0000 |
---|---|---|
committer | Kirill Bulatov <[email protected]> | 2020-12-19 10:54:17 +0000 |
commit | 5fa078f26fed1d951e949df09da82567e9e12404 (patch) | |
tree | 746a7cfa285577927f1c4f3a6a4235232227eab4 | |
parent | bd270cbc024f56596c315061d10732ebbe16eef9 (diff) |
Add a slightly better fuzzy search heuristics
-rw-r--r-- | crates/completion/src/completions/unqualified_path.rs | 72 |
1 files changed, 62 insertions, 10 deletions
diff --git a/crates/completion/src/completions/unqualified_path.rs b/crates/completion/src/completions/unqualified_path.rs index 93869f92e..5a7a6e83d 100644 --- a/crates/completion/src/completions/unqualified_path.rs +++ b/crates/completion/src/completions/unqualified_path.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | //! Completion of names from the current scope, e.g. locals and imported items. | 1 | //! Completion of names from the current scope, e.g. locals and imported items. |
2 | 2 | ||
3 | use either::Either; | 3 | use either::Either; |
4 | use hir::{Adt, ModuleDef, ScopeDef, Type}; | 4 | use hir::{Adt, ModPath, ModuleDef, ScopeDef, Type}; |
5 | use ide_db::helpers::insert_use::ImportScope; | 5 | use ide_db::helpers::insert_use::ImportScope; |
6 | use ide_db::imports_locator; | 6 | use ide_db::imports_locator; |
7 | use syntax::AstNode; | 7 | use syntax::AstNode; |
@@ -147,36 +147,49 @@ fn fuzzy_completion(acc: &mut Completions, ctx: &CompletionContext) -> Option<() | |||
147 | .collect::<Vec<_>>(); | 147 | .collect::<Vec<_>>(); |
148 | 148 | ||
149 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { | 149 | all_mod_paths.sort_by_cached_key(|(mod_path, _)| { |
150 | if let Some(name) = mod_path.segments.last().map(|name| name.to_string().to_lowercase()) { | 150 | compute_fuzzy_completion_order_key(mod_path, &potential_import_name) |
151 | if name.contains(&potential_import_name.to_lowercase()) { | ||
152 | return 0; | ||
153 | } | ||
154 | } | ||
155 | 1 | ||
156 | }); | 151 | }); |
157 | 152 | ||
158 | acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { | 153 | acc.add_all(all_mod_paths.into_iter().filter_map(|(import_path, definition)| { |
159 | render_resolution_with_import( | 154 | render_resolution_with_import( |
160 | RenderContext::new(ctx), | 155 | RenderContext::new(ctx), |
161 | ImportEdit { import_path, import_scope: import_scope.clone() }, | 156 | ImportEdit { import_path: import_path, import_scope: import_scope.clone() }, |
162 | &definition, | 157 | &definition, |
163 | ) | 158 | ) |
164 | })); | 159 | })); |
165 | Some(()) | 160 | Some(()) |
166 | } | 161 | } |
167 | 162 | ||
163 | // todo kb add tet marks for the completion order test + the sotring description | ||
164 | fn compute_fuzzy_completion_order_key(proposed_mod_path: &ModPath, user_input: &str) -> usize { | ||
165 | let proposed_import_name = match proposed_mod_path.segments.last() { | ||
166 | Some(name) => name.to_string().to_lowercase(), | ||
167 | None => return usize::MAX, | ||
168 | }; | ||
169 | let user_input = user_input.to_lowercase(); | ||
170 | |||
171 | match proposed_import_name.match_indices(&user_input).next() { | ||
172 | Some((first_matching_index, _)) => first_matching_index, | ||
173 | None => usize::MAX, | ||
174 | } | ||
175 | } | ||
176 | |||
168 | #[cfg(test)] | 177 | #[cfg(test)] |
169 | mod tests { | 178 | mod tests { |
170 | use expect_test::{expect, Expect}; | 179 | use expect_test::{expect, Expect}; |
171 | use test_utils::mark; | 180 | use test_utils::mark; |
172 | 181 | ||
173 | use crate::{ | 182 | use crate::{ |
174 | test_utils::{check_edit, check_edit_with_config, completion_list}, | 183 | test_utils::{check_edit, check_edit_with_config, completion_list_with_config}, |
175 | CompletionConfig, CompletionKind, | 184 | CompletionConfig, CompletionKind, |
176 | }; | 185 | }; |
177 | 186 | ||
178 | fn check(ra_fixture: &str, expect: Expect) { | 187 | fn check(ra_fixture: &str, expect: Expect) { |
179 | let actual = completion_list(ra_fixture, CompletionKind::Reference); | 188 | check_with_config(CompletionConfig::default(), ra_fixture, expect); |
189 | } | ||
190 | |||
191 | fn check_with_config(config: CompletionConfig, ra_fixture: &str, expect: Expect) { | ||
192 | let actual = completion_list_with_config(config, ra_fixture, CompletionKind::Reference); | ||
180 | expect.assert_eq(&actual) | 193 | expect.assert_eq(&actual) |
181 | } | 194 | } |
182 | 195 | ||
@@ -877,4 +890,43 @@ fn main() { | |||
877 | "#, | 890 | "#, |
878 | ); | 891 | ); |
879 | } | 892 | } |
893 | |||
894 | #[test] | ||
895 | fn fuzzy_completions_come_in_specific_order() { | ||
896 | let mut completion_config = CompletionConfig::default(); | ||
897 | completion_config | ||
898 | .active_resolve_capabilities | ||
899 | .insert(crate::CompletionResolveCapability::AdditionalTextEdits); | ||
900 | |||
901 | check_with_config( | ||
902 | completion_config, | ||
903 | r#" | ||
904 | //- /lib.rs crate:dep | ||
905 | pub struct FirstStruct; | ||
906 | pub mod some_module { | ||
907 | pub struct SecondStruct; | ||
908 | |||
909 | pub struct ThiiiiiirdStruct; | ||
910 | pub struct AfterThirdStruct; | ||
911 | pub struct ThirdStruct; | ||
912 | } | ||
913 | |||
914 | //- /main.rs crate:main deps:dep | ||
915 | use dep::{FirstStruct, some_module::SecondStruct}; | ||
916 | |||
917 | fn main() { | ||
918 | hir<|> | ||
919 | } | ||
920 | "#, | ||
921 | expect![[r#" | ||
922 | st FirstStruct | ||
923 | st SecondStruct | ||
924 | md dep | ||
925 | st dep::some_module::ThirdStruct | ||
926 | st dep::some_module::AfterThirdStruct | ||
927 | st dep::some_module::ThiiiiiirdStruct | ||
928 | fn main() fn main() | ||
929 | "#]], | ||
930 | ); | ||
931 | } | ||
880 | } | 932 | } |