From eb68b1266ac833a00ee5a11b3e2d9a9d516d7a3c Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 29 Oct 2026 17:25:50 +0100 Subject: v2.1.2: ignore ANSI when measuring board height --- Cargo.toml | 2 +- src/client/ui.rs | 24 +++++++++++++++++++++++- tests/client_ui.rs | 17 +++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08a4a3b..1725705 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zagan-bet" -version = "2.1.1" +version = "2.1.2" edition = "2024" [dependencies] diff --git a/src/client/ui.rs b/src/client/ui.rs index 11cedc7..1a35db1 100644 --- a/src/client/ui.rs +++ b/src/client/ui.rs @@ -159,7 +159,29 @@ pub fn parse_bet_arg(raw: &str) -> Result { /// ASCII, Latin diacritics, combining marks, CJK, and the common emoji ranges /// without adding a dependency. pub fn display_width(text: &str) -> usize { - text.chars().map(char_width).sum() + let mut width = 0; + let mut chars = text.chars().peekable(); + + while let Some(character) = chars.next() { + if character == '\x1b' { + if chars.peek() == Some(&'[') { + chars.next(); + // Skip CSI parameters until the final byte (@ through ~). + for character in chars.by_ref() { + if ('@'..='~').contains(&character) { + break; + } + } + } else { + // Two-character escape sequence. + chars.next(); + } + continue; + } + width += char_width(character); + } + + width } fn char_width(c: char) -> usize { diff --git a/tests/client_ui.rs b/tests/client_ui.rs index ea432e0..f5e3c0e 100644 --- a/tests/client_ui.rs +++ b/tests/client_ui.rs @@ -98,6 +98,23 @@ fn redraw_moves_up_by_line_count_minus_one() { assert_eq!(lines_to_move_up(70_000), u16::MAX); } +#[test] +fn display_width_ignores_ansi_sequences() { + assert_eq!(display_width("\x1b[31m18\x1b[0m"), 2); + assert_eq!(display_width("\x1b[1;31m[ 18 ]\x1b[0m"), 6); + assert_eq!(display_width("plain"), 5); +} + +#[test] +fn visual_line_count_ignores_ansi_sequences() { + let styled = format!("\x1b[31m{}\x1b[0m", "x".repeat(75)); + assert_eq!(visual_line_count(&[styled], 72), 2); + assert_eq!( + visual_line_count(&["\x1b[31m18\x1b[0m".to_string()], 80), + 1 + ); +} + #[test] fn visual_line_count_accounts_for_terminal_wrapping() { let lines = vec![ -- cgit v1.2.3