From 2b462d10dda0898b5f553701b46982d68bfc8661 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Fri, 2 Oct 2026 22:09:50 -0700 Subject: [PATCH] fix: Enter in the palette never runs another command than the one typed Typing Spelling and Enter toggled Hide Spelling Errors: Spelling was disabled, so the highlight skipped to the next match. A query that is an item's whole text now ranks it first, and a disabled best match is left unhighlighted, so Enter runs nothing rather than something else. Assisted-by: claude-opus-5.5 --- crates/ui/src/popup.rs | 10 ++++++---- crates/ui/src/tests.rs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/crates/ui/src/popup.rs b/crates/ui/src/popup.rs index b34f0e8ace9a93e4de8e325a1220bb3819d9dc0a..40c3bdf67c60ff8ec7993888ad4499982b69c15c 100644 --- a/crates/ui/src/popup.rs +++ b/crates/ui/src/popup.rs @@ -502,12 +502,13 @@ fn choose( } let typed = &query[prefix.len()..]; let matches = Matches::new(items, typed, style.rule_band); - // Unfiltered, the current item starts highlighted; filtered, the best match. + // Unfiltered, the current item starts highlighted; filtered, the best match, unless it + // is disabled, so Enter never runs a worse one. highlight = highlight.or_else(|| { let first = if typed.is_empty() { matches.order.iter().position(|index| items[*index].current) } else { - (0..matches.count()).find(|row| matches.selectable(*row)) + Some(0).filter(|row| *row < matches.count() && matches.selectable(*row)) }; first.map(|row| matches.key(row)) }); @@ -1532,8 +1533,9 @@ impl<'a> Matches<'a> { }) .collect() }); - // Stable, so equal matches keep the items' order. - ranked.sort_by_key(|(score, _)| std::cmp::Reverse(*score)); + // Stable, so equal matches keep the items' order; the text itself typed leads. + let typed = |index: usize| items[index].text.eq_ignore_ascii_case(query.trim()); + ranked.sort_by_key(|(score, index)| (!typed(*index), std::cmp::Reverse(*score))); ranked.into_iter().map(|(_, index)| index).collect() }; if order.is_empty() && !pattern.atoms.is_empty() { diff --git a/crates/ui/src/tests.rs b/crates/ui/src/tests.rs index e5420a71ddd52cfe711b1d50a5fcf9ed5a58491f..8adada16de9172e687cc3144653c3a23539d7e4a 100644 --- a/crates/ui/src/tests.rs +++ b/crates/ui/src/tests.rs @@ -1591,6 +1591,38 @@ fn the_palette_centres_near_the_windows_top_and_chooses_the_best_match() { ); } +#[test] +fn enter_runs_no_worse_match_past_a_disabled_best_one() { + let mut ui = Ui::new(Theme::dark(), DOUBLE_CLICK); + let palette = Id::ROOT.child("palette"); + let commands = [ + popup::Item { + text: "Hide Spelling Errors", + ..popup::Item::default() + }, + popup::Item { + text: "Spelling", + disabled: true, + ..popup::Item::default() + }, + ]; + let build = |ui: &mut Ui| { + let mut chosen = None; + frame(ui, |ui| { + chosen = popup::palette(ui, palette, &[("", &commands)], "Search commands") + }); + chosen + }; + build(&mut ui); + ui.open_popup(palette); + build(&mut ui); + ui.event(typed("Spelling")); + build(&mut ui); + ui.event(key(NamedKey::Enter)); + assert_eq!(build(&mut ui), None); + assert!(ui.popup_open(palette)); +} + #[test] fn a_prefix_typed_in_the_palette_switches_what_it_lists() { let mut ui = Ui::new(Theme::dark(), DOUBLE_CLICK); -- 2.54.0