@@ -1006,3 +1006,152 @@ fn test_sort_matches_for_snippets(_cx: &mut TestAppContext) {
"Match order not expected"
);
}
+
+#[gpui::test]
+fn test_sort_matches_for_exact_match(_cx: &mut TestAppContext) {
+ // Case 1: "set_text"
+ let query: Option<&str> = Some("set_text");
+ let mut matches: Vec<SortableMatch<'_>> = vec![
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 1.0,
+ positions: vec![],
+ string: "set_text".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "set_text"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.32000000000000006,
+ positions: vec![],
+ string: "set_placeholder_text".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "set_placeholder_text"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.32,
+ positions: vec![],
+ string: "set_text_style_refinement".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "set_text_style_refinement"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.16666666666666666,
+ positions: vec![],
+ string: "set_context_menu_options".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "set_context_menu_options"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.08695652173913043,
+ positions: vec![],
+ string: "select_to_next_word_end".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "select_to_next_word_end"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.07692307692307693,
+ positions: vec![],
+ string: "select_to_next_subword_end".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "select_to_next_subword_end"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.06956521739130435,
+ positions: vec![],
+ string: "set_custom_context_menu".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "set_custom_context_menu"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.06,
+ positions: vec![],
+ string: "select_to_end_of_excerpt".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "select_to_end_of_excerpt"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.055384615384615386,
+ positions: vec![],
+ string: "select_to_start_of_excerpt".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "select_to_start_of_excerpt"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.0464516129032258,
+ positions: vec![],
+ string: "select_to_start_of_next_excerpt".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "select_to_start_of_next_excerpt"),
+ },
+ SortableMatch {
+ string_match: StringMatch {
+ candidate_id: 0,
+ score: 0.04363636363636363,
+ positions: vec![],
+ string: "select_to_end_of_previous_excerpt".to_string(),
+ },
+ is_snippet: false,
+ sort_text: Some("7fffffff"),
+ sort_key: (3, "select_to_end_of_previous_excerpt"),
+ },
+ ];
+ CompletionsMenu::sort_matches(&mut matches, query, SnippetSortOrder::Top);
+ assert_eq!(
+ matches
+ .iter()
+ .map(|m| m.string_match.string.as_str())
+ .collect::<Vec<&str>>(),
+ vec![
+ "set_text",
+ "set_context_menu_options",
+ "set_placeholder_text",
+ "set_text_style_refinement",
+ "select_to_end_of_excerpt",
+ "select_to_end_of_previous_excerpt",
+ "select_to_next_subword_end",
+ "select_to_next_word_end",
+ "select_to_start_of_excerpt",
+ "select_to_start_of_next_excerpt",
+ "set_custom_context_menu"
+ ]
+ );
+}
@@ -671,7 +671,7 @@ impl CompletionsMenu {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum MatchTier<'a> {
WordStartMatch {
- sort_score_int: Reverse<i32>,
+ sort_bucket: Reverse<i32>,
sort_snippet: Reverse<i32>,
sort_text: Option<&'a str>,
sort_key: (usize, &'a str),
@@ -684,11 +684,9 @@ 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
//
- // We first primary sort using fuzzy score by putting matches into two buckets
- // strong one and weak one. Among these buckets matches are then compared by
+ // We first primary sort using fuzzy score by putting matches into multiple
+ // buckets. Among these buckets matches are then compared by
// various criteria like snippet, LSP hints, kind, label text etc.
- //
- const FUZZY_THRESHOLD: f64 = 0.1317;
let query_start_lower = query
.and_then(|q| q.chars().next())
@@ -712,14 +710,20 @@ impl CompletionsMenu {
let sort_score = Reverse(OrderedFloat(score));
MatchTier::OtherMatch { sort_score }
} else {
- let sort_score_int = Reverse(if score >= FUZZY_THRESHOLD { 1 } else { 0 });
+ // Convert fuzzy match score (0.0-1.0) to a priority bucket (0-3)
+ let sort_bucket = Reverse(match (score * 10.0).floor() as i32 {
+ s if s >= 7 => 3,
+ s if s >= 1 => 2,
+ s if s > 0 => 1,
+ _ => 0,
+ });
let sort_snippet = match snippet_sort_order {
SnippetSortOrder::Top => Reverse(if mat.is_snippet { 1 } else { 0 }),
SnippetSortOrder::Bottom => Reverse(if mat.is_snippet { 0 } else { 1 }),
SnippetSortOrder::Inline => Reverse(0),
};
MatchTier::WordStartMatch {
- sort_score_int,
+ sort_bucket,
sort_snippet,
sort_text: mat.sort_text,
sort_key: mat.sort_key,