diff --git a/crates/agent/src/context_picker/completion_provider.rs b/crates/agent/src/context_picker/completion_provider.rs index 9ceb960ec7..67e9f801fc 100644 --- a/crates/agent/src/context_picker/completion_provider.rs +++ b/crates/agent/src/context_picker/completion_provider.rs @@ -214,6 +214,7 @@ fn search( &entry_candidates, &query, false, + true, 100, &Arc::new(AtomicBool::default()), executor, diff --git a/crates/agent/src/context_picker/symbol_context_picker.rs b/crates/agent/src/context_picker/symbol_context_picker.rs index e631cb9bc3..f031353628 100644 --- a/crates/agent/src/context_picker/symbol_context_picker.rs +++ b/crates/agent/src/context_picker/symbol_context_picker.rs @@ -307,6 +307,7 @@ pub(crate) fn search_symbols( &visible_match_candidates, &query, false, + true, MAX_MATCHES, &cancellation_flag, cx.background_executor().clone(), @@ -315,6 +316,7 @@ pub(crate) fn search_symbols( &external_match_candidates, &query, false, + true, MAX_MATCHES - visible_matches.len().min(MAX_MATCHES), &cancellation_flag, cx.background_executor().clone(), diff --git a/crates/agent/src/context_picker/thread_context_picker.rs b/crates/agent/src/context_picker/thread_context_picker.rs index 221e931881..ee9608a8d8 100644 --- a/crates/agent/src/context_picker/thread_context_picker.rs +++ b/crates/agent/src/context_picker/thread_context_picker.rs @@ -342,6 +342,7 @@ pub(crate) fn search_threads( &candidates, &query, false, + true, 100, &cancellation_flag, executor, diff --git a/crates/agent/src/thread_history.rs b/crates/agent/src/thread_history.rs index be2709a535..dd77dd7cb1 100644 --- a/crates/agent/src/thread_history.rs +++ b/crates/agent/src/thread_history.rs @@ -224,6 +224,7 @@ impl ThreadHistory { &candidates, &query, false, + true, MAX_MATCHES, &Default::default(), executor, diff --git a/crates/assistant_context_editor/src/context_store.rs b/crates/assistant_context_editor/src/context_store.rs index 30268420dd..3400913eb8 100644 --- a/crates/assistant_context_editor/src/context_store.rs +++ b/crates/assistant_context_editor/src/context_store.rs @@ -745,6 +745,7 @@ impl ContextStore { &candidates, &query, false, + true, 100, &Default::default(), executor, diff --git a/crates/assistant_context_editor/src/language_model_selector.rs b/crates/assistant_context_editor/src/language_model_selector.rs index 128d801c0c..b1cf3a050c 100644 --- a/crates/assistant_context_editor/src/language_model_selector.rs +++ b/crates/assistant_context_editor/src/language_model_selector.rs @@ -310,6 +310,7 @@ impl ModelMatcher { &self.candidates, &query, false, + true, 100, &Default::default(), self.bg_executor.clone(), diff --git a/crates/assistant_context_editor/src/slash_command.rs b/crates/assistant_context_editor/src/slash_command.rs index 726e74297b..9cac5ec541 100644 --- a/crates/assistant_context_editor/src/slash_command.rs +++ b/crates/assistant_context_editor/src/slash_command.rs @@ -62,6 +62,7 @@ impl SlashCommandCompletionProvider { &candidates, &command_name, true, + true, usize::MAX, &Default::default(), cx.background_executor().clone(), diff --git a/crates/assistant_slash_commands/src/diagnostics_command.rs b/crates/assistant_slash_commands/src/diagnostics_command.rs index e7349c818e..2feabd8b1e 100644 --- a/crates/assistant_slash_commands/src/diagnostics_command.rs +++ b/crates/assistant_slash_commands/src/diagnostics_command.rs @@ -147,6 +147,7 @@ impl SlashCommand for DiagnosticsSlashCommand { &Options::match_candidates_for_args(), &query, false, + true, 10, &cancellation_flag, executor, diff --git a/crates/assistant_slash_commands/src/tab_command.rs b/crates/assistant_slash_commands/src/tab_command.rs index 49e93f0ad8..ca7601bc4c 100644 --- a/crates/assistant_slash_commands/src/tab_command.rs +++ b/crates/assistant_slash_commands/src/tab_command.rs @@ -261,6 +261,7 @@ fn tab_items_for_queries( &match_candidates, query, true, + true, usize::MAX, &cancel, background_executor.clone(), diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index a0df4cd536..03d39cb8ce 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -293,6 +293,7 @@ impl MessageEditor { candidates, query, true, + true, LIMIT, &Default::default(), cx.background_executor().clone(), diff --git a/crates/collab_ui/src/collab_panel.rs b/crates/collab_ui/src/collab_panel.rs index 8ec1395903..d45ce2f88d 100644 --- a/crates/collab_ui/src/collab_panel.rs +++ b/crates/collab_ui/src/collab_panel.rs @@ -499,6 +499,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -542,6 +543,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -593,6 +595,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -623,6 +626,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -699,6 +703,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -734,6 +739,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -758,6 +764,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), @@ -791,6 +798,7 @@ impl CollabPanel { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), executor.clone(), diff --git a/crates/collab_ui/src/collab_panel/channel_modal.rs b/crates/collab_ui/src/collab_panel/channel_modal.rs index 1c22fc35c6..9af7baa160 100644 --- a/crates/collab_ui/src/collab_panel/channel_modal.rs +++ b/crates/collab_ui/src/collab_panel/channel_modal.rs @@ -295,6 +295,7 @@ impl PickerDelegate for ChannelModalDelegate { &self.match_candidates, &query, true, + true, usize::MAX, &Default::default(), cx.background_executor().clone(), diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index bafe611791..2e411fd139 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -327,6 +327,7 @@ impl PickerDelegate for CommandPaletteDelegate { &candidates, &query, true, + true, 10000, &Default::default(), executor, diff --git a/crates/debugger_ui/src/attach_modal.rs b/crates/debugger_ui/src/attach_modal.rs index b8b46c33fa..aa4ca9e868 100644 --- a/crates/debugger_ui/src/attach_modal.rs +++ b/crates/debugger_ui/src/attach_modal.rs @@ -183,6 +183,7 @@ impl PickerDelegate for AttachModalDelegate { .collect::>(), &query, true, + true, 100, &Default::default(), cx.background_executor().clone(), diff --git a/crates/debugger_ui/src/new_process_modal.rs b/crates/debugger_ui/src/new_process_modal.rs index ffd25e610f..3351b1e6df 100644 --- a/crates/debugger_ui/src/new_process_modal.rs +++ b/crates/debugger_ui/src/new_process_modal.rs @@ -1279,6 +1279,7 @@ impl PickerDelegate for DebugDelegate { &candidates, &query, true, + true, 1000, &Default::default(), cx.background_executor().clone(), diff --git a/crates/debugger_ui/src/session/running/console.rs b/crates/debugger_ui/src/session/running/console.rs index 06cf408316..34f9a3bf71 100644 --- a/crates/debugger_ui/src/session/running/console.rs +++ b/crates/debugger_ui/src/session/running/console.rs @@ -527,6 +527,7 @@ impl ConsoleQueryBarCompletionProvider { &string_matches, &query, true, + true, LIMIT, &Default::default(), cx.background_executor().clone(), diff --git a/crates/editor/src/code_completion_tests.rs b/crates/editor/src/code_completion_tests.rs index 22ab28174d..83e8ed0927 100644 --- a/crates/editor/src/code_completion_tests.rs +++ b/crates/editor/src/code_completion_tests.rs @@ -1,5 +1,5 @@ use crate::{code_context_menus::CompletionsMenu, editor_settings::SnippetSortOrder}; -use fuzzy::StringMatchCandidate; +use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::TestAppContext; use language::CodeLabel; use lsp::{CompletionItem, CompletionItemKind, LanguageServerId}; @@ -9,432 +9,258 @@ use std::sync::atomic::AtomicBool; use text::Anchor; #[gpui::test] -async fn test_sort_matches_local_variable_over_global_variable(cx: &mut TestAppContext) { - // Case 1: "foo" +async fn test_sort_kind(cx: &mut TestAppContext) { let completions = vec![ - CompletionBuilder::constant("foo_bar_baz", "7fffffff"), - CompletionBuilder::variable("foo_bar_qux", "7ffffffe"), - CompletionBuilder::constant("floorf64", "80000000"), - CompletionBuilder::constant("floorf32", "80000000"), - CompletionBuilder::constant("floorf16", "80000000"), - CompletionBuilder::constant("floorf128", "80000000"), + CompletionBuilder::function("floorf128", None, "80000000"), + CompletionBuilder::constant("foo_bar_baz", None, "80000000"), + CompletionBuilder::variable("foo_bar_qux", None, "80000000"), ]; - let matches = sort_matches("foo", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "foo_bar_qux"); - assert_eq!(matches[1], "foo_bar_baz"); - assert_eq!(matches[2], "floorf16"); - assert_eq!(matches[3], "floorf32"); + let matches = + filter_and_sort_matches("foo", &completions, SnippetSortOrder::default(), cx).await; - // Case 2: "foobar" - let completions = vec![ - CompletionBuilder::constant("foo_bar_baz", "7fffffff"), - CompletionBuilder::variable("foo_bar_qux", "7ffffffe"), - ]; - let matches = sort_matches("foobar", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "foo_bar_qux"); - assert_eq!(matches[1], "foo_bar_baz"); + // variable takes precedence over constant + // constant take precedence over function + assert_eq!( + matches + .iter() + .map(|m| m.string.as_str()) + .collect::>(), + vec!["foo_bar_qux", "foo_bar_baz", "floorf128"] + ); + + // fuzzy score should match for first two items as query is common prefix + assert_eq!(matches[0].score, matches[1].score); } #[gpui::test] -async fn test_sort_matches_local_variable_over_global_enum(cx: &mut TestAppContext) { - // Case 1: "ele" - let completions = vec![ - CompletionBuilder::constant("ElementType", "7fffffff"), - CompletionBuilder::variable("element_type", "7ffffffe"), - CompletionBuilder::constant("simd_select", "80000000"), - CompletionBuilder::keyword("while let", "7fffffff"), - ]; - let matches = sort_matches("ele", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "element_type"); - assert_eq!(matches[1], "ElementType"); +async fn test_fuzzy_score(cx: &mut TestAppContext) { + // first character sensitive over sort_text and sort_kind + { + let completions = vec![ + CompletionBuilder::variable("element_type", None, "7ffffffe"), + CompletionBuilder::constant("ElementType", None, "7fffffff"), + ]; + let matches = + filter_and_sort_matches("Elem", &completions, SnippetSortOrder::default(), cx).await; + assert_eq!( + matches + .iter() + .map(|m| m.string.as_str()) + .collect::>(), + vec!["ElementType", "element_type"] + ); + assert!(matches[0].score > matches[1].score); + } - // Case 2: "eleme" - let completions = vec![ - CompletionBuilder::constant("ElementType", "7fffffff"), - CompletionBuilder::variable("element_type", "7ffffffe"), - CompletionBuilder::constant("REPLACEMENT_CHARACTER", "80000000"), - ]; - let matches = sort_matches("eleme", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "element_type"); - assert_eq!(matches[1], "ElementType"); + // fuzzy takes over sort_text and sort_kind + { + let completions = vec![ + CompletionBuilder::function("onAbort?", None, "12"), + CompletionBuilder::function("onAuxClick?", None, "12"), + CompletionBuilder::variable("onPlay?", None, "12"), + CompletionBuilder::variable("onLoad?", None, "12"), + CompletionBuilder::variable("onDrag?", None, "12"), + CompletionBuilder::function("onPause?", None, "10"), + CompletionBuilder::function("onPaste?", None, "10"), + CompletionBuilder::function("onAnimationEnd?", None, "12"), + CompletionBuilder::function("onAbortCapture?", None, "12"), + CompletionBuilder::constant("onChange?", None, "12"), + CompletionBuilder::constant("onWaiting?", None, "12"), + CompletionBuilder::function("onCanPlay?", None, "12"), + ]; + let matches = + filter_and_sort_matches("ona", &completions, SnippetSortOrder::default(), cx).await; + for i in 0..4 { + assert!(matches[i].string.to_lowercase().starts_with("ona")); + } + } + + // plain fuzzy prefix match + { + let completions = vec![ + CompletionBuilder::function("set_text", None, "7fffffff"), + CompletionBuilder::function("set_placeholder_text", None, "7fffffff"), + CompletionBuilder::function("set_text_style_refinement", None, "7fffffff"), + CompletionBuilder::function("set_context_menu_options", None, "7fffffff"), + CompletionBuilder::function("select_to_next_word_end", None, "7fffffff"), + CompletionBuilder::function("select_to_next_subword_end", None, "7fffffff"), + CompletionBuilder::function("set_custom_context_menu", None, "7fffffff"), + CompletionBuilder::function("select_to_end_of_excerpt", None, "7fffffff"), + CompletionBuilder::function("select_to_start_of_excerpt", None, "7fffffff"), + CompletionBuilder::function("select_to_start_of_next_excerpt", None, "7fffffff"), + CompletionBuilder::function("select_to_end_of_previous_excerpt", None, "7fffffff"), + ]; + let matches = + filter_and_sort_matches("set_text", &completions, SnippetSortOrder::Top, cx).await; + assert_eq!(matches[0].string, "set_text"); + assert_eq!(matches[1].string, "set_text_style_refinement"); + assert_eq!(matches[2].string, "set_context_menu_options"); + } + + // fuzzy filter text over label, sort_text and sort_kind + { + // Case 1: "awa" + let completions = vec![ + CompletionBuilder::method("await", Some("await"), "7fffffff"), + CompletionBuilder::method("await.ne", Some("ne"), "80000010"), + CompletionBuilder::method("await.eq", Some("eq"), "80000010"), + CompletionBuilder::method("await.or", Some("or"), "7ffffff8"), + CompletionBuilder::method("await.zip", Some("zip"), "80000006"), + CompletionBuilder::method("await.xor", Some("xor"), "7ffffff8"), + CompletionBuilder::method("await.and", Some("and"), "80000006"), + CompletionBuilder::method("await.map", Some("map"), "80000006"), + ]; + + test_for_each_prefix("await", &completions, cx, |matches| { + // for each prefix, first item should always be one with lower sort_text + assert_eq!(matches[0].string, "await"); + }) + .await; + } } #[gpui::test] -async fn test_sort_matches_capitalization(cx: &mut TestAppContext) { - // Case 1: "Elem" - let completions = vec![ - CompletionBuilder::constant("ElementType", "7fffffff"), - CompletionBuilder::variable("element_type", "7ffffffe"), - ]; - let matches = sort_matches("Elem", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "ElementType"); - assert_eq!(matches[1], "element_type"); +async fn test_sort_text(cx: &mut TestAppContext) { + // sort text takes precedance over sort_kind, when fuzzy is same + { + let completions = vec![ + CompletionBuilder::variable("unreachable", None, "80000000"), + CompletionBuilder::function("unreachable!(…)", None, "7fffffff"), + CompletionBuilder::function("unchecked_rem", None, "80000010"), + CompletionBuilder::function("unreachable_unchecked", None, "80000020"), + ]; + + test_for_each_prefix("unreachabl", &completions, cx, |matches| { + // for each prefix, first item should always be one with lower sort_text + assert_eq!(matches[0].string, "unreachable!(…)"); + assert_eq!(matches[1].string, "unreachable"); + + // fuzzy score should match for first two items as query is common prefix + assert_eq!(matches[0].score, matches[1].score); + }) + .await; + + let matches = + filter_and_sort_matches("unreachable", &completions, SnippetSortOrder::Top, cx).await; + // exact match comes first + assert_eq!(matches[0].string, "unreachable"); + assert_eq!(matches[1].string, "unreachable!(…)"); + + // fuzzy score should match for first two items as query is common prefix + assert_eq!(matches[0].score, matches[1].score); + } } #[gpui::test] -async fn test_sort_matches_for_unreachable(cx: &mut TestAppContext) { - // Case 1: "unre" +async fn test_sort_snippet(cx: &mut TestAppContext) { let completions = vec![ - CompletionBuilder::function("unreachable", "80000000"), - CompletionBuilder::function("unreachable!(…)", "7fffffff"), - CompletionBuilder::function("unchecked_rem", "80000000"), - CompletionBuilder::function("unreachable_unchecked", "80000000"), + CompletionBuilder::constant("println", None, "7fffffff"), + CompletionBuilder::snippet("println!(…)", None, "80000000"), ]; - let matches = sort_matches("unre", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "unreachable!(…)"); + let matches = filter_and_sort_matches("prin", &completions, SnippetSortOrder::Top, cx).await; - // Case 2: "unrea" - let completions = vec![ - CompletionBuilder::function("unreachable", "80000000"), - CompletionBuilder::function("unreachable!(…)", "7fffffff"), - CompletionBuilder::function("unreachable_unchecked", "80000000"), - ]; - let matches = sort_matches("unrea", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "unreachable!(…)"); - - // Case 3: "unreach" - let completions = vec![ - CompletionBuilder::function("unreachable", "80000000"), - CompletionBuilder::function("unreachable!(…)", "7fffffff"), - CompletionBuilder::function("unreachable_unchecked", "80000000"), - ]; - let matches = sort_matches("unreach", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "unreachable!(…)"); - - // Case 4: "unreachabl" - let completions = vec![ - CompletionBuilder::function("unreachable", "80000000"), - CompletionBuilder::function("unreachable!(…)", "7fffffff"), - CompletionBuilder::function("unreachable_unchecked", "80000000"), - ]; - let matches = sort_matches("unreachable", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "unreachable!(…)"); - - // Case 5: "unreachable" - let completions = vec![ - CompletionBuilder::function("unreachable", "80000000"), - CompletionBuilder::function("unreachable!(…)", "7fffffff"), - CompletionBuilder::function("unreachable_unchecked", "80000000"), - ]; - let matches = sort_matches("unreachable", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "unreachable!(…)"); + // snippet take precedence over sort_text and sort_kind + assert_eq!(matches[0].string, "println!(…)"); } #[gpui::test] -async fn test_sort_matches_variable_and_constants_over_function(cx: &mut TestAppContext) { - // Case 1: "var" as variable +async fn test_sort_exact(cx: &mut TestAppContext) { + // sort_text takes over if no exact match let completions = vec![ - CompletionBuilder::function("var", "7fffffff"), - CompletionBuilder::variable("var", "7fffffff"), + CompletionBuilder::function("into", None, "80000004"), + CompletionBuilder::function("try_into", None, "80000004"), + CompletionBuilder::snippet("println", None, "80000004"), + CompletionBuilder::function("clone_into", None, "80000004"), + CompletionBuilder::function("into_searcher", None, "80000000"), + CompletionBuilder::snippet("eprintln", None, "80000004"), ]; - let matches = sort_matches("var", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "var"); - assert_eq!(matches[1], "var"); + let matches = + filter_and_sort_matches("int", &completions, SnippetSortOrder::default(), cx).await; + assert_eq!(matches[0].string, "into_searcher"); - // Case 2: "var" as constant + // exact match takes over sort_text let completions = vec![ - CompletionBuilder::function("var", "7fffffff"), - CompletionBuilder::constant("var", "7fffffff"), + CompletionBuilder::function("into", None, "80000004"), + CompletionBuilder::function("try_into", None, "80000004"), + CompletionBuilder::function("clone_into", None, "80000004"), + CompletionBuilder::function("into_searcher", None, "80000000"), + CompletionBuilder::function("split_terminator", None, "7fffffff"), + CompletionBuilder::function("rsplit_terminator", None, "7fffffff"), ]; - let matches = sort_matches("var", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "var"); - assert_eq!(matches[1], "var"); + let matches = + filter_and_sort_matches("into", &completions, SnippetSortOrder::default(), cx).await; + assert_eq!(matches[0].string, "into"); } #[gpui::test] -async fn test_sort_matches_for_jsx_event_handler(cx: &mut TestAppContext) { - // Case 1: "on" +async fn test_sort_positions(cx: &mut TestAppContext) { + // positions take precedence over fuzzy score and sort_text let completions = vec![ - CompletionBuilder::function("onCut?", "12"), - CompletionBuilder::function("onPlay?", "12"), - CompletionBuilder::function("color?", "12"), - CompletionBuilder::function("defaultValue?", "12"), - CompletionBuilder::function("style?", "12"), - CompletionBuilder::function("className?", "12"), + CompletionBuilder::function("rounded-full", None, "15788"), + CompletionBuilder::variable("rounded-t-full", None, "15846"), + CompletionBuilder::variable("rounded-b-full", None, "15731"), + CompletionBuilder::function("rounded-tr-full", None, "15866"), ]; - let matches = sort_matches("on", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "onCut?"); - assert_eq!(matches[1], "onPlay?"); - // Case 2: "ona" - let completions = vec![ - CompletionBuilder::function("onAbort?", "12"), - CompletionBuilder::function("onAuxClick?", "12"), - CompletionBuilder::function("onPlay?", "12"), - CompletionBuilder::function("onLoad?", "12"), - CompletionBuilder::function("onDrag?", "12"), - CompletionBuilder::function("onPause?", "12"), - CompletionBuilder::function("onPaste?", "12"), - CompletionBuilder::function("onAnimationEnd?", "12"), - CompletionBuilder::function("onAbortCapture?", "12"), - CompletionBuilder::function("onChange?", "12"), - CompletionBuilder::function("onWaiting?", "12"), - CompletionBuilder::function("onCanPlay?", "12"), - ]; - let matches = sort_matches("ona", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "onAbort?"); - assert_eq!(matches[1], "onAuxClick?"); -} - -#[gpui::test] -async fn test_sort_matches_for_snippets(cx: &mut TestAppContext) { - // Case 1: "prin" - let completions = vec![ - CompletionBuilder::constant("println", "80000000"), - CompletionBuilder::snippet("println!(…)", "80000000"), - ]; - let matches = sort_matches("prin", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "println!(…)"); -} - -#[gpui::test] -async fn test_sort_matches_for_exact_match(cx: &mut TestAppContext) { - // Case 1: "set_text" - let completions = vec![ - CompletionBuilder::function("set_text", "7fffffff"), - CompletionBuilder::function("set_placeholder_text", "7fffffff"), - CompletionBuilder::function("set_text_style_refinement", "7fffffff"), - CompletionBuilder::function("set_context_menu_options", "7fffffff"), - CompletionBuilder::function("select_to_next_word_end", "7fffffff"), - CompletionBuilder::function("select_to_next_subword_end", "7fffffff"), - CompletionBuilder::function("set_custom_context_menu", "7fffffff"), - CompletionBuilder::function("select_to_end_of_excerpt", "7fffffff"), - CompletionBuilder::function("select_to_start_of_excerpt", "7fffffff"), - CompletionBuilder::function("select_to_start_of_next_excerpt", "7fffffff"), - CompletionBuilder::function("select_to_end_of_previous_excerpt", "7fffffff"), - ]; - let matches = sort_matches("set_text", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "set_text"); - assert_eq!(matches[1], "set_text_style_refinement"); - assert_eq!(matches[2], "set_context_menu_options"); -} - -#[gpui::test] -async fn test_sort_matches_for_prefix_matches(cx: &mut TestAppContext) { - // Case 1: "set" - let completions = vec![ - CompletionBuilder::function("select_to_beginning", "7fffffff"), - CompletionBuilder::function("set_collapse_matches", "7fffffff"), - CompletionBuilder::function("set_autoindent", "7fffffff"), - CompletionBuilder::function("set_all_diagnostics_active", "7fffffff"), - CompletionBuilder::function("select_to_end_of_line", "7fffffff"), - CompletionBuilder::function("select_all", "7fffffff"), - CompletionBuilder::function("select_line", "7fffffff"), - CompletionBuilder::function("select_left", "7fffffff"), - CompletionBuilder::function("select_down", "7fffffff"), - ]; - let matches = sort_matches("set", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "set_autoindent"); - assert_eq!(matches[1], "set_collapse_matches"); - assert_eq!(matches[2], "set_all_diagnostics_active"); -} - -#[gpui::test] -async fn test_sort_matches_for_await(cx: &mut TestAppContext) { - // Case 1: "awa" - let completions = vec![ - CompletionBuilder::keyword("await", "7fffffff"), - CompletionBuilder::function("await.ne", "80000010"), - CompletionBuilder::function("await.eq", "80000010"), - CompletionBuilder::function("await.or", "7ffffff8"), - CompletionBuilder::function("await.zip", "80000006"), - CompletionBuilder::function("await.xor", "7ffffff8"), - CompletionBuilder::function("await.and", "80000006"), - CompletionBuilder::function("await.map", "80000006"), - CompletionBuilder::function("await.take", "7ffffff8"), - ]; - let matches = sort_matches("awa", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "await"); - - // Case 2: "await" - let completions = vec![ - CompletionBuilder::keyword("await", "7fffffff"), - CompletionBuilder::function("await.ne", "80000010"), - CompletionBuilder::function("await.eq", "80000010"), - CompletionBuilder::function("await.or", "7ffffff8"), - CompletionBuilder::function("await.zip", "80000006"), - CompletionBuilder::function("await.xor", "7ffffff8"), - CompletionBuilder::function("await.and", "80000006"), - CompletionBuilder::function("await.map", "80000006"), - CompletionBuilder::function("await.take", "7ffffff8"), - ]; - let matches = sort_matches("await", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "await"); -} - -#[gpui::test] -async fn test_sort_matches_for_python_init(cx: &mut TestAppContext) { - // Case 1: "__in" - let completions = vec![ - CompletionBuilder::function("__init__", "05.0003.__init__"), - CompletionBuilder::function("__init__", "05.0003"), - CompletionBuilder::function("__instancecheck__", "05.0005.__instancecheck__"), - CompletionBuilder::function("__init_subclass__", "05.0004.__init_subclass__"), - CompletionBuilder::function("__instancecheck__", "05.0005"), - CompletionBuilder::function("__init_subclass__", "05.0004"), - ]; - let matches = sort_matches("__in", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "__init__"); - assert_eq!(matches[1], "__init__"); - - // Case 2: "__ini" - let completions = vec![ - CompletionBuilder::function("__init__", "05.0004.__init__"), - CompletionBuilder::function("__init__", "05.0004"), - CompletionBuilder::function("__init_subclass__", "05.0003.__init_subclass__"), - CompletionBuilder::function("__init_subclass__", "05.0003"), - ]; - let matches = sort_matches("__ini", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "__init__"); - assert_eq!(matches[1], "__init__"); - - // Case 3: "__init" - let completions = vec![ - CompletionBuilder::function("__init__", "05.0000.__init__"), - CompletionBuilder::function("__init__", "05.0000"), - CompletionBuilder::function("__init_subclass__", "05.0001.__init_subclass__"), - CompletionBuilder::function("__init_subclass__", "05.0001"), - ]; - let matches = sort_matches("__init", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "__init__"); - assert_eq!(matches[1], "__init__"); - - // Case 4: "__init_" - let completions = vec![ - CompletionBuilder::function("__init__", "11.9999.__init__"), - CompletionBuilder::function("__init__", "11.9999"), - CompletionBuilder::function("__init_subclass__", "05.0000.__init_subclass__"), - CompletionBuilder::function("__init_subclass__", "05.0000"), - ]; - let matches = sort_matches("__init_", &completions, SnippetSortOrder::Top, cx).await; - assert_eq!(matches[0], "__init__"); - assert_eq!(matches[1], "__init__"); -} - -#[gpui::test] -async fn test_sort_matches_for_rust_into(cx: &mut TestAppContext) { - // Case 1: "int" - let completions = vec![ - CompletionBuilder::function("into", "80000004"), - CompletionBuilder::function("try_into", "80000004"), - CompletionBuilder::snippet("println", "80000004"), - CompletionBuilder::function("clone_into", "80000004"), - CompletionBuilder::function("into_searcher", "80000000"), - CompletionBuilder::snippet("eprintln", "80000004"), - ]; - let matches = sort_matches("int", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "into"); - - // Case 2: "into" - let completions = vec![ - CompletionBuilder::function("into", "80000004"), - CompletionBuilder::function("try_into", "80000004"), - CompletionBuilder::function("clone_into", "80000004"), - CompletionBuilder::function("into_searcher", "80000000"), - CompletionBuilder::function("split_terminator", "7fffffff"), - CompletionBuilder::function("rsplit_terminator", "7fffffff"), - ]; - let matches = sort_matches("into", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "into"); -} - -#[gpui::test] -async fn test_sort_matches_for_variable_over_function(cx: &mut TestAppContext) { - // Case 1: "serial" - let completions = vec![ - CompletionBuilder::function("serialize", "80000000"), - CompletionBuilder::function("serialize", "80000000"), - CompletionBuilder::variable("serialization_key", "7ffffffe"), - CompletionBuilder::function("serialize_version", "80000000"), - CompletionBuilder::function("deserialize", "80000000"), - ]; - let matches = sort_matches("serial", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "serialization_key"); - assert_eq!(matches[1], "serialize"); - assert_eq!(matches[2], "serialize"); - assert_eq!(matches[3], "serialize_version"); - assert_eq!(matches[4], "deserialize"); -} - -#[gpui::test] -async fn test_sort_matches_for_local_methods_over_library(cx: &mut TestAppContext) { - // Case 1: "setis" - let completions = vec![ - CompletionBuilder::variable("setISODay", "16"), - CompletionBuilder::variable("setISOWeek", "16"), - CompletionBuilder::variable("setISOWeekYear", "16"), - CompletionBuilder::function("setISOWeekYear", "16"), - CompletionBuilder::variable("setIsRefreshing", "11"), - CompletionBuilder::function("setFips", "16"), - ]; - let matches = sort_matches("setis", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "setIsRefreshing"); - assert_eq!(matches[1], "setISODay"); - assert_eq!(matches[2], "setISOWeek"); -} - -#[gpui::test] -async fn test_sort_matches_for_prioritize_not_exact_match(cx: &mut TestAppContext) { - // Case 1: "item" - let completions = vec![ - CompletionBuilder::function("Item", "16"), - CompletionBuilder::variable("Item", "16"), - CompletionBuilder::variable("items", "11"), - CompletionBuilder::function("ItemText", "16"), - ]; - let matches = sort_matches("item", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "items"); - assert_eq!(matches[1], "Item"); - assert_eq!(matches[2], "Item"); - assert_eq!(matches[3], "ItemText"); -} - -#[gpui::test] -async fn test_sort_matches_for_tailwind_classes(cx: &mut TestAppContext) { - let completions = vec![ - CompletionBuilder::function("rounded-full", "15788"), - CompletionBuilder::variable("rounded-t-full", "15846"), - CompletionBuilder::variable("rounded-b-full", "15731"), - CompletionBuilder::function("rounded-tr-full", "15866"), - ]; - // Case 1: "rounded-full" - let matches = sort_matches( + let matches = filter_and_sort_matches( "rounded-full", &completions, SnippetSortOrder::default(), cx, ) .await; - assert_eq!(matches[0], "rounded-full"); - // Case 2: "roundedfull" - let matches = sort_matches("roundedfull", &completions, SnippetSortOrder::default(), cx).await; - assert_eq!(matches[0], "rounded-full"); + assert_eq!(matches[0].string, "rounded-full"); + + let matches = + filter_and_sort_matches("roundedfull", &completions, SnippetSortOrder::default(), cx).await; + assert_eq!(matches[0].string, "rounded-full"); +} + +async fn test_for_each_prefix( + target: &str, + completions: &Vec, + cx: &mut TestAppContext, + mut test_fn: F, +) where + F: FnMut(Vec), +{ + for i in 1..=target.len() { + let prefix = &target[..i]; + let matches = + filter_and_sort_matches(prefix, completions, SnippetSortOrder::default(), cx).await; + test_fn(matches); + } } struct CompletionBuilder; impl CompletionBuilder { - fn constant(label: &str, sort_text: &str) -> Completion { - Self::new(label, sort_text, CompletionItemKind::CONSTANT) + fn constant(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion { + Self::new(label, filter_text, sort_text, CompletionItemKind::CONSTANT) } - fn function(label: &str, sort_text: &str) -> Completion { - Self::new(label, sort_text, CompletionItemKind::FUNCTION) + fn function(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion { + Self::new(label, filter_text, sort_text, CompletionItemKind::FUNCTION) } - fn variable(label: &str, sort_text: &str) -> Completion { - Self::new(label, sort_text, CompletionItemKind::VARIABLE) + fn method(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion { + Self::new(label, filter_text, sort_text, CompletionItemKind::METHOD) } - fn keyword(label: &str, sort_text: &str) -> Completion { - Self::new(label, sort_text, CompletionItemKind::KEYWORD) + fn variable(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion { + Self::new(label, filter_text, sort_text, CompletionItemKind::VARIABLE) } - fn snippet(label: &str, sort_text: &str) -> Completion { - Self::new(label, sort_text, CompletionItemKind::SNIPPET) + fn snippet(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion { + Self::new(label, filter_text, sort_text, CompletionItemKind::SNIPPET) } - fn new(label: &str, sort_text: &str, kind: CompletionItemKind) -> Completion { + fn new( + label: &str, + filter_text: Option<&str>, + sort_text: &str, + kind: CompletionItemKind, + ) -> Completion { Completion { replace_range: Anchor::MIN..Anchor::MAX, new_text: label.to_string(), @@ -451,6 +277,7 @@ impl CompletionBuilder { label: label.to_string(), kind: Some(kind), sort_text: Some(sort_text.to_string()), + filter_text: filter_text.map(|text| text.to_string()), ..Default::default() }), lsp_defaults: None, @@ -463,16 +290,16 @@ impl CompletionBuilder { } } -async fn sort_matches( +async fn filter_and_sort_matches( query: &str, completions: &Vec, snippet_sort_order: SnippetSortOrder, cx: &mut TestAppContext, -) -> Vec { +) -> Vec { let candidates: Arc<[StringMatchCandidate]> = completions .iter() .enumerate() - .map(|(id, completion)| StringMatchCandidate::new(id, &completion.label.text)) + .map(|(id, completion)| StringMatchCandidate::new(id, &completion.filter_text())) .collect(); let cancel_flag = Arc::new(AtomicBool::new(false)); let background_executor = cx.executor(); @@ -480,16 +307,11 @@ async fn sort_matches( &candidates, query, query.chars().any(|c| c.is_uppercase()), + false, 100, &cancel_flag, background_executor, ) .await; - let sorted_matches = CompletionsMenu::sort_string_matches( - matches, - Some(query), - snippet_sort_order, - &completions, - ); - sorted_matches.into_iter().map(|m| m.string).collect() + CompletionsMenu::sort_string_matches(matches, Some(query), snippet_sort_order, &completions) } diff --git a/crates/editor/src/code_context_menus.rs b/crates/editor/src/code_context_menus.rs index 542e3689c9..85b8aa005e 100644 --- a/crates/editor/src/code_context_menus.rs +++ b/crates/editor/src/code_context_menus.rs @@ -260,7 +260,7 @@ impl CompletionsMenu { let match_candidates = completions .iter() .enumerate() - .map(|(id, completion)| StringMatchCandidate::new(id, &completion.label.filter_text())) + .map(|(id, completion)| StringMatchCandidate::new(id, completion.filter_text())) .collect(); let completions_menu = Self { @@ -979,7 +979,8 @@ impl CompletionsMenu { &match_candidates, &query, query.chars().any(|c| c.is_uppercase()), - 100, + false, + 1000, &cancel_filter, background_executor, ) @@ -1055,13 +1056,12 @@ impl CompletionsMenu { #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] enum MatchTier<'a> { WordStartMatch { - sort_capitalize: Reverse, + sort_exact: Reverse, sort_positions: Vec, sort_snippet: Reverse, - sort_kind: usize, - sort_fuzzy_bracket: Reverse, - sort_text: Option<&'a str>, sort_score: Reverse>, + sort_text: Option<&'a str>, + sort_kind: usize, sort_label: &'a str, }, OtherMatch { @@ -1069,14 +1069,6 @@ impl CompletionsMenu { }, } - // Our goal here is to intelligently sort completion suggestions. We want to - // balance the raw fuzzy match score with hints from the language server - - // In a fuzzy bracket, matches with a score of 1.0 are prioritized. - // The remaining matches are partitioned into two groups at 3/5 of the max_score. - let max_score = matches.iter().map(|mat| mat.score).fold(0.0, f64::max); - let fuzzy_bracket_threshold = max_score * (3.0 / 5.0); - let query_start_lower = query .as_ref() .and_then(|q| q.chars().next()) @@ -1117,34 +1109,25 @@ impl CompletionsMenu { if query_start_doesnt_match_split_words { MatchTier::OtherMatch { sort_score } } else { - let sort_fuzzy_bracket = Reverse(if score >= fuzzy_bracket_threshold { - 1 - } else { - 0 - }); let sort_snippet = match snippet_sort_order { SnippetSortOrder::Top => Reverse(if is_snippet { 1 } else { 0 }), SnippetSortOrder::Bottom => Reverse(if is_snippet { 0 } else { 1 }), SnippetSortOrder::Inline => Reverse(0), }; - let sort_capitalize = Reverse( - query - .as_ref() - .and_then(|q| q.chars().next()) - .zip(string_match.string.chars().next()) - .map(|(q_char, s_char)| if q_char == s_char { 1 } else { 0 }) - .unwrap_or(0), - ); let sort_positions = string_match.positions.clone(); + let sort_exact = Reverse(if Some(completion.filter_text()) == query { + 1 + } else { + 0 + }); MatchTier::WordStartMatch { - sort_capitalize, + sort_exact, sort_positions, sort_snippet, - sort_kind, - sort_fuzzy_bracket, - sort_text, sort_score, + sort_text, + sort_kind, sort_label, } } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index c02095794c..dadbe6fbfa 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -21221,6 +21221,7 @@ fn snippet_completions( &candidates, &last_word, last_word.chars().any(|c| c.is_uppercase()), + true, MAX_RESULTS, &Default::default(), executor.clone(), diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index d53b75a21a..1083ae9cf8 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -11950,7 +11950,7 @@ async fn test_completion(cx: &mut TestAppContext) { .confirm_completion(&ConfirmCompletion::default(), window, cx) .unwrap() }); - cx.assert_editor_state("editor.closeˇ"); + cx.assert_editor_state("editor.clobberˇ"); handle_resolve_completion_request(&mut cx, None).await; apply_additional_edits.await.unwrap(); } @@ -15468,7 +15468,7 @@ async fn test_completions_in_languages_with_extra_word_characters(cx: &mut TestA { assert_eq!( completion_menu_entries(&menu), - &["bg-red", "bg-blue", "bg-yellow"] + &["bg-blue", "bg-red", "bg-yellow"] ); } else { panic!("expected completion menu to be open"); diff --git a/crates/extensions_ui/src/extension_version_selector.rs b/crates/extensions_ui/src/extension_version_selector.rs index 3cb3f0e424..aaf5d5e8eb 100644 --- a/crates/extensions_ui/src/extension_version_selector.rs +++ b/crates/extensions_ui/src/extension_version_selector.rs @@ -146,6 +146,7 @@ impl PickerDelegate for ExtensionVersionSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background_executor, diff --git a/crates/extensions_ui/src/extensions_ui.rs b/crates/extensions_ui/src/extensions_ui.rs index c6525a6ccc..72b0b31b47 100644 --- a/crates/extensions_ui/src/extensions_ui.rs +++ b/crates/extensions_ui/src/extensions_ui.rs @@ -474,6 +474,7 @@ impl ExtensionsPage { &match_candidates, &search, false, + true, match_candidates.len(), &Default::default(), cx.background_executor().clone(), diff --git a/crates/file_finder/src/open_path_prompt.rs b/crates/file_finder/src/open_path_prompt.rs index 71b345a536..d7428f0068 100644 --- a/crates/file_finder/src/open_path_prompt.rs +++ b/crates/file_finder/src/open_path_prompt.rs @@ -418,6 +418,7 @@ impl PickerDelegate for OpenPathDelegate { candidates.as_slice(), &suffix, false, + true, 100, &cancel_flag, cx.background_executor().clone(), diff --git a/crates/fuzzy/src/matcher.rs b/crates/fuzzy/src/matcher.rs index ba27be1505..aff6390534 100644 --- a/crates/fuzzy/src/matcher.rs +++ b/crates/fuzzy/src/matcher.rs @@ -17,6 +17,7 @@ pub struct Matcher<'a> { lowercase_query: &'a [char], query_char_bag: CharBag, smart_case: bool, + penalize_length: bool, min_score: f64, match_positions: Vec, last_positions: Vec, @@ -35,6 +36,7 @@ impl<'a> Matcher<'a> { lowercase_query: &'a [char], query_char_bag: CharBag, smart_case: bool, + penalize_length: bool, ) -> Self { Self { query, @@ -46,6 +48,7 @@ impl<'a> Matcher<'a> { score_matrix: Vec::new(), best_position_matrix: Vec::new(), smart_case, + penalize_length, } } @@ -294,7 +297,7 @@ impl<'a> Matcher<'a> { let mut multiplier = char_score; // Scale the score based on how deep within the path we found the match. - if query_idx == 0 { + if self.penalize_length && query_idx == 0 { multiplier /= ((prefix.len() + path.len()) - last_slash) as f64; } @@ -355,18 +358,18 @@ mod tests { #[test] fn test_get_last_positions() { let mut query: &[char] = &['d', 'c']; - let mut matcher = Matcher::new(query, query, query.into(), false); + let mut matcher = Matcher::new(query, query, query.into(), false, true); let result = matcher.find_last_positions(&['a', 'b', 'c'], &['b', 'd', 'e', 'f']); assert!(!result); query = &['c', 'd']; - let mut matcher = Matcher::new(query, query, query.into(), false); + let mut matcher = Matcher::new(query, query, query.into(), false, true); let result = matcher.find_last_positions(&['a', 'b', 'c'], &['b', 'd', 'e', 'f']); assert!(result); assert_eq!(matcher.last_positions, vec![2, 4]); query = &['z', '/', 'z', 'f']; - let mut matcher = Matcher::new(query, query, query.into(), false); + let mut matcher = Matcher::new(query, query, query.into(), false, true); let result = matcher.find_last_positions(&['z', 'e', 'd', '/'], &['z', 'e', 'd', '/', 'f']); assert!(result); assert_eq!(matcher.last_positions, vec![0, 3, 4, 8]); @@ -613,7 +616,7 @@ mod tests { }); } - let mut matcher = Matcher::new(&query, &lowercase_query, query_chars, smart_case); + let mut matcher = Matcher::new(&query, &lowercase_query, query_chars, smart_case, true); let cancel_flag = AtomicBool::new(false); let mut results = Vec::new(); diff --git a/crates/fuzzy/src/paths.rs b/crates/fuzzy/src/paths.rs index 79628240eb..78030d5f96 100644 --- a/crates/fuzzy/src/paths.rs +++ b/crates/fuzzy/src/paths.rs @@ -95,7 +95,7 @@ pub fn match_fixed_path_set( let query = query.chars().collect::>(); let query_char_bag = CharBag::from(&lowercase_query[..]); - let mut matcher = Matcher::new(&query, &lowercase_query, query_char_bag, smart_case); + let mut matcher = Matcher::new(&query, &lowercase_query, query_char_bag, smart_case, true); let mut results = Vec::new(); matcher.match_candidates( @@ -153,7 +153,7 @@ pub async fn match_path_sets<'a, Set: PathMatchCandidateSet<'a>>( let segment_start = segment_idx * segment_size; let segment_end = segment_start + segment_size; let mut matcher = - Matcher::new(query, lowercase_query, query_char_bag, smart_case); + Matcher::new(query, lowercase_query, query_char_bag, smart_case, true); let mut tree_start = 0; for candidate_set in candidate_sets { diff --git a/crates/fuzzy/src/strings.rs b/crates/fuzzy/src/strings.rs index 2180a9b7b6..5bd7b66c0b 100644 --- a/crates/fuzzy/src/strings.rs +++ b/crates/fuzzy/src/strings.rs @@ -117,6 +117,7 @@ pub async fn match_strings( candidates: &[T], query: &str, smart_case: bool, + penalize_length: bool, max_results: usize, cancel_flag: &AtomicBool, executor: BackgroundExecutor, @@ -160,8 +161,13 @@ where scope.spawn(async move { let segment_start = cmp::min(segment_idx * segment_size, candidates.len()); let segment_end = cmp::min(segment_start + segment_size, candidates.len()); - let mut matcher = - Matcher::new(query, lowercase_query, query_char_bag, smart_case); + let mut matcher = Matcher::new( + query, + lowercase_query, + query_char_bag, + smart_case, + penalize_length, + ); matcher.match_candidates( &[], diff --git a/crates/git_ui/src/branch_picker.rs b/crates/git_ui/src/branch_picker.rs index 5f51f8e2db..a4b77eff74 100644 --- a/crates/git_ui/src/branch_picker.rs +++ b/crates/git_ui/src/branch_picker.rs @@ -305,6 +305,7 @@ impl PickerDelegate for BranchListDelegate { &candidates, &query, true, + true, 10000, &Default::default(), cx.background_executor().clone(), diff --git a/crates/git_ui/src/picker_prompt.rs b/crates/git_ui/src/picker_prompt.rs index 74b2a63c31..4077e0f362 100644 --- a/crates/git_ui/src/picker_prompt.rs +++ b/crates/git_ui/src/picker_prompt.rs @@ -174,6 +174,7 @@ impl PickerDelegate for PickerPromptDelegate { &candidates, &query, true, + true, 10000, &Default::default(), cx.background_executor().clone(), diff --git a/crates/indexed_docs/src/store.rs b/crates/indexed_docs/src/store.rs index 454971ad26..1407078efa 100644 --- a/crates/indexed_docs/src/store.rs +++ b/crates/indexed_docs/src/store.rs @@ -215,6 +215,7 @@ impl IndexedDocsStore { &candidates, &query, false, + true, 100, &AtomicBool::default(), executor, diff --git a/crates/jj_ui/src/bookmark_picker.rs b/crates/jj_ui/src/bookmark_picker.rs index 8459112747..f6121fb9fc 100644 --- a/crates/jj_ui/src/bookmark_picker.rs +++ b/crates/jj_ui/src/bookmark_picker.rs @@ -144,6 +144,7 @@ impl PickerDelegate for BookmarkPickerDelegate { &candidates, &query, false, + true, 100, &Default::default(), background, diff --git a/crates/language/src/outline.rs b/crates/language/src/outline.rs index 2b700fd69c..d96cd90e03 100644 --- a/crates/language/src/outline.rs +++ b/crates/language/src/outline.rs @@ -122,6 +122,7 @@ impl Outline { }, query, smart_case, + true, 100, &Default::default(), executor.clone(), diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 6725ffc8ec..2a4b6de655 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -247,6 +247,7 @@ impl PickerDelegate for LanguageSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background, diff --git a/crates/outline_panel/src/outline_panel.rs b/crates/outline_panel/src/outline_panel.rs index 046ffd1284..ce410c6b24 100644 --- a/crates/outline_panel/src/outline_panel.rs +++ b/crates/outline_panel/src/outline_panel.rs @@ -3815,6 +3815,7 @@ impl OutlinePanel { &generation_state.match_candidates, &query, true, + true, usize::MAX, &AtomicBool::default(), cx.background_executor().clone(), diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index cb636ba71e..5bc045ef9a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5303,6 +5303,16 @@ impl ProjectItem for Buffer { } impl Completion { + pub fn filter_text(&self) -> &str { + match &self.source { + CompletionSource::Lsp { lsp_completion, .. } => lsp_completion + .filter_text + .as_deref() + .unwrap_or_else(|| self.label.filter_text()), + _ => self.label.filter_text(), + } + } + pub fn kind(&self) -> Option { self.source // `lsp::CompletionListItemDefaults` has no `kind` field @@ -5319,17 +5329,18 @@ impl Completion { /// A key that can be used to sort completions when displaying /// them to the user. pub fn sort_key(&self) -> (usize, &str) { - const DEFAULT_KIND_KEY: usize = 3; + const DEFAULT_KIND_KEY: usize = 4; let kind_key = self .kind() .and_then(|lsp_completion_kind| match lsp_completion_kind { lsp::CompletionItemKind::KEYWORD => Some(0), lsp::CompletionItemKind::VARIABLE => Some(1), lsp::CompletionItemKind::CONSTANT => Some(2), + lsp::CompletionItemKind::PROPERTY => Some(3), _ => None, }) .unwrap_or(DEFAULT_KIND_KEY); - (kind_key, &self.label.text[self.label.filter_range.clone()]) + (kind_key, &self.label.filter_text()) } /// Whether this completion is a snippet. diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 704123d2b3..a9ba14264f 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -66,6 +66,7 @@ impl ProjectSymbolsDelegate { &self.visible_match_candidates, query, false, + true, MAX_MATCHES, &Default::default(), cx.background_executor().clone(), @@ -74,6 +75,7 @@ impl ProjectSymbolsDelegate { &self.external_match_candidates, query, false, + true, MAX_MATCHES - visible_matches.len().min(MAX_MATCHES), &Default::default(), cx.background_executor().clone(), @@ -342,6 +344,7 @@ mod tests { &candidates, ¶ms.query, true, + true, 100, &Default::default(), executor.clone(), diff --git a/crates/prompt_store/src/prompt_store.rs b/crates/prompt_store/src/prompt_store.rs index ff60c26323..f9cb26ed9a 100644 --- a/crates/prompt_store/src/prompt_store.rs +++ b/crates/prompt_store/src/prompt_store.rs @@ -356,6 +356,7 @@ impl PromptStore { &candidates, &query, false, + true, 100, &cancellation_flag, executor, diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 2400151324..5dbde6496d 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -251,6 +251,7 @@ impl PickerDelegate for RecentProjectsDelegate { candidates.as_slice(), query, smart_case, + true, 100, &Default::default(), cx.background_executor().clone(), diff --git a/crates/snippets_ui/src/snippets_ui.rs b/crates/snippets_ui/src/snippets_ui.rs index f2e1b5cb5b..ecd1143c36 100644 --- a/crates/snippets_ui/src/snippets_ui.rs +++ b/crates/snippets_ui/src/snippets_ui.rs @@ -277,6 +277,7 @@ impl PickerDelegate for ScopeSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background, diff --git a/crates/storybook/src/stories/picker.rs b/crates/storybook/src/stories/picker.rs index 723829d168..d2d9a854a1 100644 --- a/crates/storybook/src/stories/picker.rs +++ b/crates/storybook/src/stories/picker.rs @@ -98,6 +98,7 @@ impl PickerDelegate for Delegate { &candidates, &query, true, + true, 100, &Default::default(), cx.background_executor().clone(), diff --git a/crates/tab_switcher/src/tab_switcher.rs b/crates/tab_switcher/src/tab_switcher.rs index 3474383f9d..7ba0d8d4c4 100644 --- a/crates/tab_switcher/src/tab_switcher.rs +++ b/crates/tab_switcher/src/tab_switcher.rs @@ -318,6 +318,7 @@ impl TabSwitcherDelegate { &candidates, &query, true, + true, 10000, &Default::default(), cx.background_executor().clone(), diff --git a/crates/tasks_ui/src/modal.rs b/crates/tasks_ui/src/modal.rs index 5f2460451f..c4b0c48a5b 100644 --- a/crates/tasks_ui/src/modal.rs +++ b/crates/tasks_ui/src/modal.rs @@ -358,6 +358,7 @@ impl PickerDelegate for TasksModalDelegate { &candidates, &query, true, + true, 1000, &Default::default(), cx.background_executor().clone(), diff --git a/crates/theme_selector/src/icon_theme_selector.rs b/crates/theme_selector/src/icon_theme_selector.rs index 9b3509d777..40ba7bd5a6 100644 --- a/crates/theme_selector/src/icon_theme_selector.rs +++ b/crates/theme_selector/src/icon_theme_selector.rs @@ -244,6 +244,7 @@ impl PickerDelegate for IconThemeSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background, diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index e6cc08483f..e7a3f32909 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -296,6 +296,7 @@ impl PickerDelegate for ThemeSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background, diff --git a/crates/toolchain_selector/src/toolchain_selector.rs b/crates/toolchain_selector/src/toolchain_selector.rs index 88b5b82b45..0bb4de4f43 100644 --- a/crates/toolchain_selector/src/toolchain_selector.rs +++ b/crates/toolchain_selector/src/toolchain_selector.rs @@ -355,6 +355,7 @@ impl PickerDelegate for ToolchainSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background, diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs index c0c7c1999e..06cda8638a 100644 --- a/crates/welcome/src/base_keymap_picker.rs +++ b/crates/welcome/src/base_keymap_picker.rs @@ -147,6 +147,7 @@ impl PickerDelegate for BaseKeymapSelectorDelegate { &candidates, &query, false, + true, 100, &Default::default(), background,