editor: Use fuzzy crate in code completions tests instead of hard coded values (#32565)
This PR makes it a lot cleaner to write code completion tests. It doesn't contain any logical changes, just refactoring. Before, we used to depend on hard-coded values of fuzzy score and its positions for tests. Now we don't need them, as fuzzy crate will handle that for us. This is possible because fuzzy match score isn't dependent on relative candidates or the number of candidates; rather, it's just a one-to-one mapping for each candidate and its score. This also makes it test robust for future purposes if there are changes in fuzzy score logic. Before: ```rs SortableMatch { string_match: StringMatch { // -> whole struct provided by fuzzy crate candidate_id: 1115, score: 1.0, positions: vec![], string: "Item".to_string(), }, is_snippet: false, // -> changed to snippet kind sort_text: Some("16"), sort_kind: 3, // -> changed to function, constant, variable kind sort_label: "Item", }, ``` After: ```rs CompletionBuilder::function("Item", "16") ``` Release Notes: - N/A
This commit is contained in:
parent
7f150f7e0f
commit
65a1d09d24
2 changed files with 394 additions and 2340 deletions
File diff suppressed because it is too large
Load diff
|
@ -1044,55 +1044,14 @@ impl CompletionsMenu {
|
|||
self.handle_selection_changed(provider.as_deref(), window, cx);
|
||||
}
|
||||
|
||||
fn sort_string_matches(
|
||||
pub fn sort_string_matches(
|
||||
matches: Vec<StringMatch>,
|
||||
query: Option<&str>,
|
||||
snippet_sort_order: SnippetSortOrder,
|
||||
completions: &[Completion],
|
||||
) -> Vec<StringMatch> {
|
||||
let mut sortable_items: Vec<SortableMatch<'_>> = matches
|
||||
.into_iter()
|
||||
.map(|string_match| {
|
||||
let completion = &completions[string_match.candidate_id];
|
||||
let mut matches = matches;
|
||||
|
||||
let is_snippet = matches!(
|
||||
&completion.source,
|
||||
CompletionSource::Lsp { lsp_completion, .. }
|
||||
if lsp_completion.kind == Some(CompletionItemKind::SNIPPET)
|
||||
);
|
||||
|
||||
let sort_text =
|
||||
if let CompletionSource::Lsp { lsp_completion, .. } = &completion.source {
|
||||
lsp_completion.sort_text.as_deref()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (sort_kind, sort_label) = completion.sort_key();
|
||||
|
||||
SortableMatch {
|
||||
string_match,
|
||||
is_snippet,
|
||||
sort_text,
|
||||
sort_kind,
|
||||
sort_label,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self::sort_matches(&mut sortable_items, query, snippet_sort_order);
|
||||
|
||||
sortable_items
|
||||
.into_iter()
|
||||
.map(|sortable| sortable.string_match)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn sort_matches(
|
||||
matches: &mut Vec<SortableMatch<'_>>,
|
||||
query: Option<&str>,
|
||||
snippet_sort_order: SnippetSortOrder,
|
||||
) {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum MatchTier<'a> {
|
||||
WordStartMatch {
|
||||
|
@ -1114,10 +1073,7 @@ impl CompletionsMenu {
|
|||
|
||||
// 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.string_match.score)
|
||||
.fold(0.0, f64::max);
|
||||
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
|
||||
|
@ -1125,13 +1081,30 @@ impl CompletionsMenu {
|
|||
.and_then(|q| q.chars().next())
|
||||
.and_then(|c| c.to_lowercase().next());
|
||||
|
||||
matches.sort_unstable_by_key(|mat| {
|
||||
let score = mat.string_match.score;
|
||||
matches.sort_unstable_by_key(|string_match| {
|
||||
let completion = &completions[string_match.candidate_id];
|
||||
|
||||
let is_snippet = matches!(
|
||||
&completion.source,
|
||||
CompletionSource::Lsp { lsp_completion, .. }
|
||||
if lsp_completion.kind == Some(CompletionItemKind::SNIPPET)
|
||||
);
|
||||
|
||||
let sort_text = if let CompletionSource::Lsp { lsp_completion, .. } = &completion.source
|
||||
{
|
||||
lsp_completion.sort_text.as_deref()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (sort_kind, sort_label) = completion.sort_key();
|
||||
|
||||
let score = string_match.score;
|
||||
let sort_score = Reverse(OrderedFloat(score));
|
||||
|
||||
let query_start_doesnt_match_split_words = query_start_lower
|
||||
.map(|query_char| {
|
||||
!split_words(&mat.string_match.string).any(|word| {
|
||||
!split_words(&string_match.string).any(|word| {
|
||||
word.chars()
|
||||
.next()
|
||||
.and_then(|c| c.to_lowercase().next())
|
||||
|
@ -1149,8 +1122,8 @@ impl CompletionsMenu {
|
|||
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::Top => Reverse(if is_snippet { 1 } else { 0 }),
|
||||
SnippetSortOrder::Bottom => Reverse(if is_snippet { 0 } else { 1 }),
|
||||
SnippetSortOrder::Inline => Reverse(0),
|
||||
};
|
||||
let sort_mixed_case_prefix_length = Reverse(
|
||||
|
@ -1158,7 +1131,7 @@ impl CompletionsMenu {
|
|||
.as_ref()
|
||||
.map(|q| {
|
||||
q.chars()
|
||||
.zip(mat.string_match.string.chars())
|
||||
.zip(string_match.string.chars())
|
||||
.enumerate()
|
||||
.take_while(|(i, (q_char, match_char))| {
|
||||
if *i == 0 {
|
||||
|
@ -1176,14 +1149,16 @@ impl CompletionsMenu {
|
|||
MatchTier::WordStartMatch {
|
||||
sort_mixed_case_prefix_length,
|
||||
sort_snippet,
|
||||
sort_kind: mat.sort_kind,
|
||||
sort_kind,
|
||||
sort_fuzzy_bracket,
|
||||
sort_text: mat.sort_text,
|
||||
sort_text,
|
||||
sort_score,
|
||||
sort_label: mat.sort_label,
|
||||
sort_label,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
matches
|
||||
}
|
||||
|
||||
pub fn preserve_markdown_cache(&mut self, prev_menu: CompletionsMenu) {
|
||||
|
@ -1215,15 +1190,6 @@ impl CompletionsMenu {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SortableMatch<'a> {
|
||||
pub string_match: StringMatch,
|
||||
pub is_snippet: bool,
|
||||
pub sort_text: Option<&'a str>,
|
||||
pub sort_kind: usize,
|
||||
pub sort_label: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AvailableCodeAction {
|
||||
pub excerpt_id: ExcerptId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue