Compare commits

...

7 Commits

Author SHA1 Message Date
Smit Barmase
236309b9ef improve test 2025-11-24 20:16:32 +05:30
Smit Barmase
2febcd9dbf use semver 2025-11-24 20:11:59 +05:30
Smit Barmase
91257b8257 semver match 2025-11-24 20:03:17 +05:30
Smit Barmase
b01f953941 refactor 2025-11-24 20:03:16 +05:30
Smit Barmase
2e550939f2 maybe semver 2025-11-24 20:03:16 +05:30
Smit Barmase
ed739a151a sort semver 2025-11-24 20:03:12 +05:30
Smit Barmase
95fbe27b1b add test 2025-11-24 20:02:38 +05:30
3 changed files with 96 additions and 17 deletions

View File

@@ -66,6 +66,7 @@ regex.workspace = true
rpc.workspace = true
rope.workspace = true
schemars.workspace = true
semver.workspace = true
serde.workspace = true
serde_json.workspace = true
settings.workspace = true

View File

@@ -239,6 +239,48 @@ 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 completions = vec![
CompletionBuilder::new("10.4.22", None, None, None),
CompletionBuilder::new("10.4.2", None, None, None),
CompletionBuilder::new("10.4.20", None, None, None),
CompletionBuilder::new("10.4.21", None, None, None),
CompletionBuilder::new("10.4.12", None, None, None),
// Pre-release versions
CompletionBuilder::new("10.4.22-alpha", None, None, None),
CompletionBuilder::new("10.4.22-alpha.1", None, None, None),
CompletionBuilder::new("10.4.22-alpha.2", None, None, None),
CompletionBuilder::new("10.4.22-beta", None, None, None),
CompletionBuilder::new("10.4.22-beta.1", None, None, None),
CompletionBuilder::new("10.4.22-rc.1", None, None, None),
CompletionBuilder::new("10.4.20-beta.1", None, None, None),
// Build metadata versions
CompletionBuilder::new("10.4.21+build.123", None, None, None),
CompletionBuilder::new("10.4.21+build.456", None, None, None),
CompletionBuilder::new("10.4.20+20210327", None, None, None),
];
let matches = filter_and_sort_matches("2", &completions, SnippetSortOrder::default(), cx).await;
// Within pre-releases: rc.1 > beta.1 > beta > alpha.2 > alpha.1 > alpha
assert_eq!(matches[0].string, "10.4.22");
assert_eq!(matches[1].string, "10.4.22-rc.1");
assert_eq!(matches[2].string, "10.4.22-beta.1");
assert_eq!(matches[3].string, "10.4.22-beta");
assert_eq!(matches[4].string, "10.4.22-alpha.2");
assert_eq!(matches[5].string, "10.4.22-alpha.1");
assert_eq!(matches[6].string, "10.4.22-alpha");
assert_eq!(matches[7].string, "10.4.21+build.456");
assert_eq!(matches[8].string, "10.4.21+build.123");
assert_eq!(matches[9].string, "10.4.21");
assert_eq!(matches[10].string, "10.4.20+20210327");
assert_eq!(matches[11].string, "10.4.20");
assert_eq!(matches[12].string, "10.4.20-beta.1");
assert_eq!(matches[13].string, "10.4.12");
assert_eq!(matches[14].string, "10.4.2");
}
async fn test_for_each_prefix<F>(
target: &str,
completions: &Vec<Completion>,
@@ -259,30 +301,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,
Some(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,
Some(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,
Some(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,
Some(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,
Some(sort_text),
Some(CompletionItemKind::SNIPPET),
)
}
fn new(
label: &str,
filter_text: Option<&str>,
sort_text: &str,
kind: CompletionItemKind,
sort_text: Option<&str>,
kind: Option<CompletionItemKind>,
) -> Completion {
Completion {
replace_range: Anchor::MIN..Anchor::MAX,
@@ -294,8 +361,8 @@ impl CompletionBuilder {
server_id: LanguageServerId(0),
lsp_completion: Box::new(CompletionItem {
label: label.to_string(),
kind: Some(kind),
sort_text: Some(sort_text.to_string()),
kind: kind,
sort_text: sort_text.map(|text| text.to_string()),
filter_text: filter_text.map(|text| text.to_string()),
..Default::default()
}),

View File

@@ -18,6 +18,7 @@ use project::{CompletionDisplayOptions, CompletionSource};
use task::DebugScenario;
use task::TaskContext;
use semver::Version as SemanticVersion;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{
@@ -1168,6 +1169,9 @@ impl CompletionsMenu {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
enum MatchTier<'a> {
SemverMatch {
sort_semver: Reverse<SemanticVersion>,
},
WordStartMatch {
sort_exact: Reverse<i32>,
sort_snippet: Reverse<i32>,
@@ -1192,17 +1196,16 @@ impl CompletionsMenu {
.retain(|string_match| !completions[string_match.candidate_id].is_snippet_kind());
}
matches.sort_unstable_by_key(|string_match| {
matches.sort_by_cached_key(|string_match| {
let completion = &completions[string_match.candidate_id];
let sort_text = match &completion.source {
CompletionSource::Lsp { lsp_completion, .. } => lsp_completion.sort_text.as_deref(),
CompletionSource::Dap { sort_text } => Some(sort_text.as_str()),
_ => None,
};
let (sort_kind, sort_label) = completion.sort_key();
if let Ok(semver) = SemanticVersion::parse(sort_label) {
return MatchTier::SemverMatch {
sort_semver: Reverse(semver),
};
}
let score = string_match.score;
let sort_score = Reverse(OrderedFloat(score));
@@ -1221,6 +1224,14 @@ impl CompletionsMenu {
if query_start_doesnt_match_split_words {
MatchTier::OtherMatch { sort_score }
} else {
let sort_text = match &completion.source {
CompletionSource::Lsp { lsp_completion, .. } => {
lsp_completion.sort_text.as_deref()
}
CompletionSource::Dap { sort_text } => Some(sort_text.as_str()),
_ => None,
};
let sort_snippet = match snippet_sort_order {
SnippetSortOrder::Top => Reverse(if is_snippet { 1 } else { 0 }),
SnippetSortOrder::Bottom => Reverse(if is_snippet { 0 } else { 1 }),