fix(switcher): put the workspace the query names above ones matched by tabs

The list was ordered by current-then-recent whatever the query, so typing
'qa' and Enter reopened this window's workspace (its tabs are under
/tmp/t7qa) instead of the workspace named qa.
This commit is contained in:
l0ng-ai
2026-09-30 00:28:15 +08:00
parent 7e9225e6b0
commit 6f29cac8bf
+34 -2
View File
@@ -405,8 +405,12 @@ fn flatten(groups: &[Group], query: &str) -> Vec<Nav> {
}
nav.sort_by(|&(ga, ra), &(gb, rb)| {
let (a, b) = (&groups[ga].rows[ra], &groups[gb].rows[rb]);
b.current
.cmp(&a.current)
// How a row matched comes before which one is current: "qa" is the
// workspace named qa, not this window's one whose tabs sit in
// `/tmp/t7qa`, and Enter takes the top row.
a.name_match(query)
.cmp(&b.name_match(query))
.then_with(|| b.current.cmp(&a.current))
.then_with(|| b.last_active.cmp(&a.last_active))
.then_with(|| a.name.cmp(&b.name))
});
@@ -3127,6 +3131,22 @@ fn visible_tabs(row: &Row, query: &str) -> Vec<usize> {
}
impl Row {
/// How closely the workspace's own name answers `query`, best first: the
/// name itself, the start of it, anywhere in it, then not at all (the row
/// is listed for its path or a tab). Every row ties on an empty query.
fn name_match(&self, query: &str) -> u8 {
if query.is_empty() {
return 0;
}
let name = self.name.to_lowercase();
match () {
_ if name == query => 0,
_ if name.starts_with(query) => 1,
_ if name.contains(query) => 2,
_ => 3,
}
}
/// A workspace stays in the list when its own name or path matches, and
/// also when any of its tabs does — searching "claude" should surface the
/// workspaces running one.
@@ -3808,6 +3828,18 @@ mod tests {
assert_eq!(groups[first.0].rows[first.1].name, "here");
}
#[test]
fn a_workspace_named_by_the_query_outranks_one_matched_by_its_tabs() {
// This window's workspace has its tabs under `/tmp/t7qa`, so "qa"
// lists it — but the workspace called qa is what was asked for, and
// Enter takes the top row.
let mut here = aged(row("keen-crane", vec![tab("zsh", "/tmp/t7qa/fixture")]), 50);
here.current = true;
let groups = vec![group(vec![here, aged(row("qa", vec![]), 10)])];
let first = flatten(&groups, "qa")[0];
assert_eq!(groups[first.0].rows[first.1].name, "qa");
}
#[test]
fn a_query_matching_a_machines_name_keeps_all_of_its_rows() {
// Searching "devbox" is how the retired per-machine grouping is asked