editor: Prioritize fuzzy score over sort positions in code completion sort (#35229)

Smit Barmase created

We already prioritize matches that come after separators like `_`: 


https://github.com/zed-industries/zed/blob/cef7d53607381975ea00d6302d8a9aab3c40eb1f/crates/fuzzy/src/matcher.rs#L274

and deprioritize non-consecutive matches using distance penalty:


https://github.com/zed-industries/zed/blob/cef7d53607381975ea00d6302d8a9aab3c40eb1f/crates/fuzzy/src/matcher.rs#L281

In completion sort, letting fuzzy score be the primary sort factor and
sort positions be secondary yields better results upon testing. We still
need sort positions because of this kind of test case:


https://github.com/zed-industries/zed/blob/cef7d53607381975ea00d6302d8a9aab3c40eb1f/crates/editor/src/code_completion_tests.rs#L195-L217

Before/After:

<img height="250" alt="image"
src="https://github.com/user-attachments/assets/38495576-add6-4435-93f0-891f48ec9263"
/>
<img height="250" alt="image"
src="https://github.com/user-attachments/assets/0c73b835-0e23-4e30-a3ff-28bb56294239"
/>


Release Notes:

- N/A

Change summary

crates/editor/src/code_completion_tests.rs | 24 +++++++++++++++++++++++-
crates/editor/src/code_context_menus.rs    |  4 ++--
2 files changed, 25 insertions(+), 3 deletions(-)

Detailed changes

crates/editor/src/code_completion_tests.rs 🔗

@@ -94,7 +94,7 @@ async fn test_fuzzy_score(cx: &mut TestAppContext) {
             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");
+        assert_eq!(matches[2].string, "set_placeholder_text");
     }
 
     // fuzzy filter text over label, sort_text and sort_kind
@@ -216,6 +216,28 @@ async fn test_sort_positions(cx: &mut TestAppContext) {
     assert_eq!(matches[0].string, "rounded-full");
 }
 
+#[gpui::test]
+async fn test_fuzzy_over_sort_positions(cx: &mut TestAppContext) {
+    let completions = vec![
+        CompletionBuilder::variable("lsp_document_colors", None, "7fffffff"), // 0.29 fuzzy score
+        CompletionBuilder::function(
+            "language_servers_running_disk_based_diagnostics",
+            None,
+            "7fffffff",
+        ), // 0.168 fuzzy score
+        CompletionBuilder::function("code_lens", None, "7fffffff"),           // 3.2 fuzzy score
+        CompletionBuilder::variable("lsp_code_lens", None, "7fffffff"),       // 3.2 fuzzy score
+        CompletionBuilder::function("fetch_code_lens", None, "7fffffff"),     // 3.2 fuzzy score
+    ];
+
+    let matches =
+        filter_and_sort_matches("lens", &completions, SnippetSortOrder::default(), cx).await;
+
+    assert_eq!(matches[0].string, "code_lens");
+    assert_eq!(matches[1].string, "lsp_code_lens");
+    assert_eq!(matches[2].string, "fetch_code_lens");
+}
+
 async fn test_for_each_prefix<F>(
     target: &str,
     completions: &Vec<Completion>,

crates/editor/src/code_context_menus.rs 🔗

@@ -1057,9 +1057,9 @@ impl CompletionsMenu {
         enum MatchTier<'a> {
             WordStartMatch {
                 sort_exact: Reverse<i32>,
-                sort_positions: Vec<usize>,
                 sort_snippet: Reverse<i32>,
                 sort_score: Reverse<OrderedFloat<f64>>,
+                sort_positions: Vec<usize>,
                 sort_text: Option<&'a str>,
                 sort_kind: usize,
                 sort_label: &'a str,
@@ -1137,9 +1137,9 @@ impl CompletionsMenu {
 
                 MatchTier::WordStartMatch {
                     sort_exact,
-                    sort_positions,
                     sort_snippet,
                     sort_score,
+                    sort_positions,
                     sort_text,
                     sort_kind,
                     sort_label,