editor: Fix package version completion partial accept and improve sorting (#43473)
Closes #41723 This PR fixes an issue with accepting partial semver completions by including `.` in the completion query. This makes the editor treat the entire version string as the query, instead of breaking segment at last `.` . This PR also adds a test for sorting semver completions. The actual sorting fix is handled in the `package-version-server` by having it provide `sort_text`. More: https://github.com/zed-industries/package-version-server/pull/10 <img width="600" alt="image" src="https://github.com/user-attachments/assets/7657912f-c6da-4e05-956b-1c044918304f" /> Release Notes: - Fixed an issue where accepting a completion for a semver version in package.json would append the suggestion to the existing text instead of replacing it. - Improved the sorting of semver completions in package.json so the latest versions appear at the top.
This commit is contained in:
@@ -239,6 +239,89 @@ async fn test_fuzzy_over_sort_positions(cx: &mut TestAppContext) {
|
||||
assert_eq!(matches[2].string, "fetch_code_lens");
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_semver_label_sort_by_latest_version(cx: &mut TestAppContext) {
|
||||
let mut versions = [
|
||||
"10.4.112",
|
||||
"10.4.22",
|
||||
"10.4.2",
|
||||
"10.4.20",
|
||||
"10.4.21",
|
||||
"10.4.12",
|
||||
// Pre-release versions
|
||||
"10.4.22-alpha",
|
||||
"10.4.22-beta.1",
|
||||
"10.4.22-rc.1",
|
||||
// Build metadata versions
|
||||
"10.4.21+build.123",
|
||||
"10.4.20+20210327",
|
||||
];
|
||||
versions.sort_by(|a, b| {
|
||||
match (
|
||||
semver::Version::parse(a).ok(),
|
||||
semver::Version::parse(b).ok(),
|
||||
) {
|
||||
(Some(a_ver), Some(b_ver)) => b_ver.cmp(&a_ver),
|
||||
_ => std::cmp::Ordering::Equal,
|
||||
}
|
||||
});
|
||||
let completions: Vec<_> = versions
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, version)| {
|
||||
// This sort text would come from the LSP
|
||||
let sort_text = format!("{:08}", i);
|
||||
CompletionBuilder::new(version, None, &sort_text, None)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Case 1: User types just the major and minor version
|
||||
let matches =
|
||||
filter_and_sort_matches("10.4.", &completions, SnippetSortOrder::default(), cx).await;
|
||||
// Versions are ordered by recency (latest first)
|
||||
let expected_versions = [
|
||||
"10.4.112",
|
||||
"10.4.22",
|
||||
"10.4.22-rc.1",
|
||||
"10.4.22-beta.1",
|
||||
"10.4.22-alpha",
|
||||
"10.4.21+build.123",
|
||||
"10.4.21",
|
||||
"10.4.20+20210327",
|
||||
"10.4.20",
|
||||
"10.4.12",
|
||||
"10.4.2",
|
||||
];
|
||||
for (match_item, expected) in matches.iter().zip(expected_versions.iter()) {
|
||||
assert_eq!(match_item.string.as_ref() as &str, *expected);
|
||||
}
|
||||
|
||||
// Case 2: User types the major, minor, and patch version
|
||||
let matches =
|
||||
filter_and_sort_matches("10.4.2", &completions, SnippetSortOrder::default(), cx).await;
|
||||
let expected_versions = [
|
||||
// Exact match comes first
|
||||
"10.4.2",
|
||||
// Ordered by recency with exact major, minor, and patch versions
|
||||
"10.4.22",
|
||||
"10.4.22-rc.1",
|
||||
"10.4.22-beta.1",
|
||||
"10.4.22-alpha",
|
||||
"10.4.21+build.123",
|
||||
"10.4.21",
|
||||
"10.4.20+20210327",
|
||||
"10.4.20",
|
||||
// Versions with non-exact patch versions are ordered by fuzzy score
|
||||
// Higher fuzzy score than 112 patch version since "2" appears before "1"
|
||||
// in "12", making it rank higher than "112"
|
||||
"10.4.12",
|
||||
"10.4.112",
|
||||
];
|
||||
for (match_item, expected) in matches.iter().zip(expected_versions.iter()) {
|
||||
assert_eq!(match_item.string.as_ref() as &str, *expected);
|
||||
}
|
||||
}
|
||||
|
||||
async fn test_for_each_prefix<F>(
|
||||
target: &str,
|
||||
completions: &Vec<Completion>,
|
||||
@@ -259,30 +342,55 @@ struct CompletionBuilder;
|
||||
|
||||
impl CompletionBuilder {
|
||||
fn constant(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion {
|
||||
Self::new(label, filter_text, sort_text, CompletionItemKind::CONSTANT)
|
||||
Self::new(
|
||||
label,
|
||||
filter_text,
|
||||
sort_text,
|
||||
Some(CompletionItemKind::CONSTANT),
|
||||
)
|
||||
}
|
||||
|
||||
fn function(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion {
|
||||
Self::new(label, filter_text, sort_text, CompletionItemKind::FUNCTION)
|
||||
Self::new(
|
||||
label,
|
||||
filter_text,
|
||||
sort_text,
|
||||
Some(CompletionItemKind::FUNCTION),
|
||||
)
|
||||
}
|
||||
|
||||
fn method(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion {
|
||||
Self::new(label, filter_text, sort_text, CompletionItemKind::METHOD)
|
||||
Self::new(
|
||||
label,
|
||||
filter_text,
|
||||
sort_text,
|
||||
Some(CompletionItemKind::METHOD),
|
||||
)
|
||||
}
|
||||
|
||||
fn variable(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion {
|
||||
Self::new(label, filter_text, sort_text, CompletionItemKind::VARIABLE)
|
||||
Self::new(
|
||||
label,
|
||||
filter_text,
|
||||
sort_text,
|
||||
Some(CompletionItemKind::VARIABLE),
|
||||
)
|
||||
}
|
||||
|
||||
fn snippet(label: &str, filter_text: Option<&str>, sort_text: &str) -> Completion {
|
||||
Self::new(label, filter_text, sort_text, CompletionItemKind::SNIPPET)
|
||||
Self::new(
|
||||
label,
|
||||
filter_text,
|
||||
sort_text,
|
||||
Some(CompletionItemKind::SNIPPET),
|
||||
)
|
||||
}
|
||||
|
||||
fn new(
|
||||
label: &str,
|
||||
filter_text: Option<&str>,
|
||||
sort_text: &str,
|
||||
kind: CompletionItemKind,
|
||||
kind: Option<CompletionItemKind>,
|
||||
) -> Completion {
|
||||
Completion {
|
||||
replace_range: Anchor::MIN..Anchor::MAX,
|
||||
@@ -294,7 +402,7 @@ impl CompletionBuilder {
|
||||
server_id: LanguageServerId(0),
|
||||
lsp_completion: Box::new(CompletionItem {
|
||||
label: label.to_string(),
|
||||
kind: Some(kind),
|
||||
kind: kind,
|
||||
sort_text: Some(sort_text.to_string()),
|
||||
filter_text: filter_text.map(|text| text.to_string()),
|
||||
..Default::default()
|
||||
|
||||
@@ -11,5 +11,6 @@ brackets = [
|
||||
tab_size = 2
|
||||
prettier_parser_name = "json"
|
||||
debuggers = ["JavaScript"]
|
||||
|
||||
[overrides.string]
|
||||
completion_query_characters = [":", " "]
|
||||
completion_query_characters = [":", " ", "."]
|
||||
|
||||
Reference in New Issue
Block a user