| author | |
| committer | |
| log | 2b462d10dda0898b5f553701b46982d68bfc8661 |
| tree | 13a86b761214cfe7afe0d3280bfa36d75c71fab0 |
| parent | d92401a36cb3a706f3f80bd0b6aeb1f5cb789375 |
| signature | Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU |
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.52 files changed, 38 insertions(+), 4 deletions(-)
crates/ui/src/popup.rs+6-4| ... | ... | @@ -502,12 +502,13 @@ fn choose( |
| 502 | 502 | } |
| 503 | 503 | let typed = &query[prefix.len()..]; |
| 504 | 504 | let matches = Matches::new(items, typed, style.rule_band); |
| 505 | // Unfiltered, the current item starts highlighted; filtered, the best match. | |
| 505 | // Unfiltered, the current item starts highlighted; filtered, the best match, unless it | |
| 506 | // is disabled, so Enter never runs a worse one. | |
| 506 | 507 | highlight = highlight.or_else(|| { |
| 507 | 508 | let first = if typed.is_empty() { |
| 508 | 509 | matches.order.iter().position(|index| items[*index].current) |
| 509 | 510 | } else { |
| 510 | (0..matches.count()).find(|row| matches.selectable(*row)) | |
| 511 | Some(0).filter(|row| *row < matches.count() && matches.selectable(*row)) | |
| 511 | 512 | }; |
| 512 | 513 | first.map(|row| matches.key(row)) |
| 513 | 514 | }); |
| ... | ... | @@ -1532,8 +1533,9 @@ impl<'a> Matches<'a> { |
| 1532 | 1533 | }) |
| 1533 | 1534 | .collect() |
| 1534 | 1535 | }); |
| 1535 | // Stable, so equal matches keep the items' order. | |
| 1536 | ranked.sort_by_key(|(score, _)| std::cmp::Reverse(*score)); | |
| 1536 | // Stable, so equal matches keep the items' order; the text itself typed leads. | |
| 1537 | let typed = |index: usize| items[index].text.eq_ignore_ascii_case(query.trim()); | |
| 1538 | ranked.sort_by_key(|(score, index)| (!typed(*index), std::cmp::Reverse(*score))); | |
| 1537 | 1539 | ranked.into_iter().map(|(_, index)| index).collect() |
| 1538 | 1540 | }; |
| 1539 | 1541 | if order.is_empty() && !pattern.atoms.is_empty() { |
crates/ui/src/tests.rs+32| ... | ... | @@ -1591,6 +1591,38 @@ fn the_palette_centres_near_the_windows_top_and_chooses_the_best_match() { |
| 1591 | 1591 | ); |
| 1592 | 1592 | } |
| 1593 | 1593 | |
| 1594 | #[test] | |
| 1595 | fn enter_runs_no_worse_match_past_a_disabled_best_one() { | |
| 1596 | let mut ui = Ui::new(Theme::dark(), DOUBLE_CLICK); | |
| 1597 | let palette = Id::ROOT.child("palette"); | |
| 1598 | let commands = [ | |
| 1599 | popup::Item { | |
| 1600 | text: "Hide Spelling Errors", | |
| 1601 | ..popup::Item::default() | |
| 1602 | }, | |
| 1603 | popup::Item { | |
| 1604 | text: "Spelling", | |
| 1605 | disabled: true, | |
| 1606 | ..popup::Item::default() | |
| 1607 | }, | |
| 1608 | ]; | |
| 1609 | let build = |ui: &mut Ui| { | |
| 1610 | let mut chosen = None; | |
| 1611 | frame(ui, |ui| { | |
| 1612 | chosen = popup::palette(ui, palette, &[("", &commands)], "Search commands") | |
| 1613 | }); | |
| 1614 | chosen | |
| 1615 | }; | |
| 1616 | build(&mut ui); | |
| 1617 | ui.open_popup(palette); | |
| 1618 | build(&mut ui); | |
| 1619 | ui.event(typed("Spelling")); | |
| 1620 | build(&mut ui); | |
| 1621 | ui.event(key(NamedKey::Enter)); | |
| 1622 | assert_eq!(build(&mut ui), None); | |
| 1623 | assert!(ui.popup_open(palette)); | |
| 1624 | } | |
| 1625 | ||
| 1594 | 1626 | #[test] |
| 1595 | 1627 | fn a_prefix_typed_in_the_palette_switches_what_it_lists() { |
| 1596 | 1628 | let mut ui = Ui::new(Theme::dark(), DOUBLE_CLICK); |