Show more possible matches in code context completion (#27199)

Smit Barmase and Peter Tripp created

Closes #24794

We now don't filter matches provided by the fuzzy matcher, as it already
performs most of the filtering for us. Instead, the custom logic we
previously used for filtering is now used to partition, where before
discarded matches will be appended at end of list.

Before - Filtering out matches with higher fuzzy score
<img width="400" alt="image"
src="https://github.com/user-attachments/assets/7f9d66a2-0921-499c-af8a-f1e530da50b1"
/>

After - Changing filter to partition instead, and appending remaining
items at the end
<img width="400" alt="image"
src="https://github.com/user-attachments/assets/45848f70-ed51-4935-976c-6c16c5b5777b"
/>


Release Notes:

- Improved LSP auto complete to show more possible matches.

---------

Co-authored-by: Peter Tripp <petertripp@gmail.com>

Change summary

crates/editor/src/code_context_menus.rs | 9 +++++++--
crates/editor/src/editor_tests.rs       | 4 ++--
2 files changed, 9 insertions(+), 4 deletions(-)

Detailed changes

crates/editor/src/code_context_menus.rs 🔗

@@ -665,10 +665,11 @@ impl CompletionsMenu {
                 .collect()
         };
 
-        // Remove all candidates where the query's start does not match the start of any word in the candidate
+        let mut additional_matches = Vec::new();
+        // Deprioritize all candidates where the query's start does not match the start of any word in the candidate
         if let Some(query) = query {
             if let Some(query_start) = query.chars().next() {
-                matches.retain(|string_match| {
+                let (primary, secondary) = matches.into_iter().partition(|string_match| {
                     split_words(&string_match.string).any(|word| {
                         // Check that the first codepoint of the word as lowercase matches the first
                         // codepoint of the query as lowercase
@@ -678,6 +679,8 @@ impl CompletionsMenu {
                             .all(|(word_cp, query_cp)| word_cp == query_cp)
                     })
                 });
+                matches = primary;
+                additional_matches = secondary;
             }
         }
 
@@ -740,6 +743,8 @@ impl CompletionsMenu {
         }
         drop(completions);
 
+        matches.extend(additional_matches);
+
         *self.entries.borrow_mut() = matches;
         self.selected_item = 0;
         // This keeps the display consistent when y_flipped.

crates/editor/src/editor_tests.rs 🔗

@@ -9492,7 +9492,7 @@ async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
         }
     });
 
-    cx.simulate_keystroke("s");
+    cx.simulate_keystroke("l");
     cx.executor().run_until_parked();
     cx.condition(|editor, _| editor.context_menu_visible())
         .await;
@@ -9501,7 +9501,7 @@ async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
         {
             assert_eq!(
                 completion_menu_entries(&menu),
-                &["second"],
+                &["last"],
                 "After showing word completions, further editing should filter them and not query the LSP"
             );
         } else {