From 2234b680fb3816a8b9ebccefd7cbb4b90b98553d Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Thu, 12 Mar 2026 11:50:01 -0600 Subject: [PATCH 1/5] test: Add tests for SGR off codes, underline subparams, and underline color reset --- crates/anstyle-svg/tests/sgr_off_codes.html | 49 ++++++++++++++++ crates/anstyle-svg/tests/sgr_off_codes.svg | 57 +++++++++++++++++++ crates/anstyle-svg/tests/sgr_off_codes.vte | 8 +++ crates/anstyle-svg/tests/term.rs | 54 ++++++++++++++++++ .../tests/underline_color_reset.html | 35 ++++++++++++ .../tests/underline_color_reset.svg | 33 +++++++++++ .../tests/underline_color_reset.vte | 3 + .../tests/underline_subparams.html | 38 +++++++++++++ .../anstyle-svg/tests/underline_subparams.svg | 38 +++++++++++++ .../anstyle-svg/tests/underline_subparams.vte | 5 ++ 10 files changed, 320 insertions(+) create mode 100644 crates/anstyle-svg/tests/sgr_off_codes.html create mode 100644 crates/anstyle-svg/tests/sgr_off_codes.svg create mode 100644 crates/anstyle-svg/tests/sgr_off_codes.vte create mode 100644 crates/anstyle-svg/tests/underline_color_reset.html create mode 100644 crates/anstyle-svg/tests/underline_color_reset.svg create mode 100644 crates/anstyle-svg/tests/underline_color_reset.vte create mode 100644 crates/anstyle-svg/tests/underline_subparams.html create mode 100644 crates/anstyle-svg/tests/underline_subparams.svg create mode 100644 crates/anstyle-svg/tests/underline_subparams.vte diff --git a/crates/anstyle-svg/tests/sgr_off_codes.html b/crates/anstyle-svg/tests/sgr_off_codes.html new file mode 100644 index 00000000..e42d348d --- /dev/null +++ b/crates/anstyle-svg/tests/sgr_off_codes.html @@ -0,0 +1,49 @@ + + + + + + + + + + + +
+bold normal
+dim normal
+italic normal
+underline normal
+█████████████
+invert normal
+█████████████
+
+████████████████████
+
+█████████████████████████████████
+
+
+
+
+ + + diff --git a/crates/anstyle-svg/tests/sgr_off_codes.svg b/crates/anstyle-svg/tests/sgr_off_codes.svg new file mode 100644 index 00000000..e4ef4a5e --- /dev/null +++ b/crates/anstyle-svg/tests/sgr_off_codes.svg @@ -0,0 +1,57 @@ + + + + + + + bold normal + + dim normal + + italic normal + + underline normal + + █████████████ + + invert normal + + █████████████ + + + + ████████████████████ + + + + █████████████████████████████████ + + + + + + + + + + diff --git a/crates/anstyle-svg/tests/sgr_off_codes.vte b/crates/anstyle-svg/tests/sgr_off_codes.vte new file mode 100644 index 00000000..16011406 --- /dev/null +++ b/crates/anstyle-svg/tests/sgr_off_codes.vte @@ -0,0 +1,8 @@ +bold normal +dim normal +italic normal +underline normal +invert normal +hidden normal +strikethrough normal +all three no bold no italic plain diff --git a/crates/anstyle-svg/tests/term.rs b/crates/anstyle-svg/tests/term.rs index df244827..7e934a3c 100644 --- a/crates/anstyle-svg/tests/term.rs +++ b/crates/anstyle-svg/tests/term.rs @@ -73,3 +73,57 @@ fn custom_background_color_html() { snapbox::file!["custom_background_color.html": Text].raw() ); } + +#[test] +fn sgr_off_codes() { + let input = std::fs::read_to_string("tests/sgr_off_codes.vte").unwrap(); + let actual = anstyle_svg::Term::new().render_svg(&input); + snapbox::assert_data_eq!(actual, snapbox::file!["sgr_off_codes.svg": Text].raw()); +} + +#[test] +fn sgr_off_codes_html() { + let input = std::fs::read_to_string("tests/sgr_off_codes.vte").unwrap(); + let actual = anstyle_svg::Term::new().render_html(&input); + snapbox::assert_data_eq!(actual, snapbox::file!["sgr_off_codes.html": Text].raw()); +} + +#[test] +fn underline_subparams() { + let input = std::fs::read_to_string("tests/underline_subparams.vte").unwrap(); + let actual = anstyle_svg::Term::new().render_svg(&input); + snapbox::assert_data_eq!( + actual, + snapbox::file!["underline_subparams.svg": Text].raw() + ); +} + +#[test] +fn underline_subparams_html() { + let input = std::fs::read_to_string("tests/underline_subparams.vte").unwrap(); + let actual = anstyle_svg::Term::new().render_html(&input); + snapbox::assert_data_eq!( + actual, + snapbox::file!["underline_subparams.html": Text].raw() + ); +} + +#[test] +fn underline_color_reset() { + let input = std::fs::read_to_string("tests/underline_color_reset.vte").unwrap(); + let actual = anstyle_svg::Term::new().render_svg(&input); + snapbox::assert_data_eq!( + actual, + snapbox::file!["underline_color_reset.svg": Text].raw() + ); +} + +#[test] +fn underline_color_reset_html() { + let input = std::fs::read_to_string("tests/underline_color_reset.vte").unwrap(); + let actual = anstyle_svg::Term::new().render_html(&input); + snapbox::assert_data_eq!( + actual, + snapbox::file!["underline_color_reset.html": Text].raw() + ); +} diff --git a/crates/anstyle-svg/tests/underline_color_reset.html b/crates/anstyle-svg/tests/underline_color_reset.html new file mode 100644 index 00000000..4d94d30a --- /dev/null +++ b/crates/anstyle-svg/tests/underline_color_reset.html @@ -0,0 +1,35 @@ + + + + + + + + + + + +
+red underline default color underline normal
+orange underline default color underline normal
+blue underline default color underline normal
+
+
+ + + diff --git a/crates/anstyle-svg/tests/underline_color_reset.svg b/crates/anstyle-svg/tests/underline_color_reset.svg new file mode 100644 index 00000000..60f7f88a --- /dev/null +++ b/crates/anstyle-svg/tests/underline_color_reset.svg @@ -0,0 +1,33 @@ + + + + + + + red underline default color underline normal + + orange underline default color underline normal + + blue underline default color underline normal + + + + + + diff --git a/crates/anstyle-svg/tests/underline_color_reset.vte b/crates/anstyle-svg/tests/underline_color_reset.vte new file mode 100644 index 00000000..e9d85bdc --- /dev/null +++ b/crates/anstyle-svg/tests/underline_color_reset.vte @@ -0,0 +1,3 @@ +red underline default color underline normal +orange underline default color underline normal +[58:2:0:128:255mblue underline default color underline normal diff --git a/crates/anstyle-svg/tests/underline_subparams.html b/crates/anstyle-svg/tests/underline_subparams.html new file mode 100644 index 00000000..0b3e20b6 --- /dev/null +++ b/crates/anstyle-svg/tests/underline_subparams.html @@ -0,0 +1,38 @@ + + + + + + + + + + + +
+underline normal
+double normal
+curly normal
+dotted normal
+dashed normal
+
+
+ + + diff --git a/crates/anstyle-svg/tests/underline_subparams.svg b/crates/anstyle-svg/tests/underline_subparams.svg new file mode 100644 index 00000000..fd507308 --- /dev/null +++ b/crates/anstyle-svg/tests/underline_subparams.svg @@ -0,0 +1,38 @@ + + + + + + + underline normal + + double normal + + curly normal + + dotted normal + + dashed normal + + + + + + diff --git a/crates/anstyle-svg/tests/underline_subparams.vte b/crates/anstyle-svg/tests/underline_subparams.vte new file mode 100644 index 00000000..34bf43be --- /dev/null +++ b/crates/anstyle-svg/tests/underline_subparams.vte @@ -0,0 +1,5 @@ +underline[4:0m normal +[4:2mdouble[4:0m normal +[4:3mcurly[4:0m normal +[4:4mdotted[4:0m normal +[4:5mdashed[4:0m normal From ac5c23b73200b8bfa6b016736a951d413fc0536d Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 13 Mar 2026 10:09:57 -0600 Subject: [PATCH 2/5] refactor: Reorder SGR match arms to follow numerical order --- crates/anstyle-svg/src/adapter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/anstyle-svg/src/adapter.rs b/crates/anstyle-svg/src/adapter.rs index 4cb6467b..5f98c120 100644 --- a/crates/anstyle-svg/src/adapter.rs +++ b/crates/anstyle-svg/src/adapter.rs @@ -138,10 +138,6 @@ impl anstyle_parse::Perform for AnsiCapture { style = style.underline(); state = CsiState::Underline; } - (CsiState::Normal, 21) => { - style |= anstyle::Effects::DOUBLE_UNDERLINE; - break; - } (CsiState::Normal, 7) => { style = style.invert(); break; @@ -154,6 +150,10 @@ impl anstyle_parse::Perform for AnsiCapture { style = style.strikethrough(); break; } + (CsiState::Normal, 21) => { + style |= anstyle::Effects::DOUBLE_UNDERLINE; + break; + } (CsiState::Normal, 30..=37) => { let color = to_ansi_color(value - 30).expect("within 4-bit range"); style = style.fg_color(Some(color.into())); From 85c9a86d4cb8bb2fb1c4a7729e2c70965ad38688 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 13 Mar 2026 10:16:26 -0600 Subject: [PATCH 3/5] feat: Support SGR 22-29 (off codes) --- crates/anstyle-svg/src/adapter.rs | 38 +++++++++++++++++++ crates/anstyle-svg/tests/sgr_off_codes.html | 22 +++++------ crates/anstyle-svg/tests/sgr_off_codes.svg | 26 +++++-------- .../tests/underline_color_reset.html | 6 +-- .../tests/underline_color_reset.svg | 6 +-- 5 files changed, 62 insertions(+), 36 deletions(-) diff --git a/crates/anstyle-svg/src/adapter.rs b/crates/anstyle-svg/src/adapter.rs index 5f98c120..7b66d1b0 100644 --- a/crates/anstyle-svg/src/adapter.rs +++ b/crates/anstyle-svg/src/adapter.rs @@ -154,6 +154,44 @@ impl anstyle_parse::Perform for AnsiCapture { style |= anstyle::Effects::DOUBLE_UNDERLINE; break; } + (CsiState::Normal, 22) => { + style = style.effects( + style + .get_effects() + .remove(anstyle::Effects::BOLD) + .remove(anstyle::Effects::DIMMED), + ); + break; + } + (CsiState::Normal, 23) => { + style = style.effects(style.get_effects().remove(anstyle::Effects::ITALIC)); + break; + } + (CsiState::Normal, 24) => { + style = style.effects( + style + .get_effects() + .remove(anstyle::Effects::UNDERLINE) + .remove(anstyle::Effects::DOUBLE_UNDERLINE) + .remove(anstyle::Effects::CURLY_UNDERLINE) + .remove(anstyle::Effects::DOTTED_UNDERLINE) + .remove(anstyle::Effects::DASHED_UNDERLINE), + ); + break; + } + (CsiState::Normal, 27) => { + style = style.effects(style.get_effects().remove(anstyle::Effects::INVERT)); + break; + } + (CsiState::Normal, 28) => { + style = style.effects(style.get_effects().remove(anstyle::Effects::HIDDEN)); + break; + } + (CsiState::Normal, 29) => { + style = style + .effects(style.get_effects().remove(anstyle::Effects::STRIKETHROUGH)); + break; + } (CsiState::Normal, 30..=37) => { let color = to_ansi_color(value - 30).expect("within 4-bit range"); style = style.fg_color(Some(color.into())); diff --git a/crates/anstyle-svg/tests/sgr_off_codes.html b/crates/anstyle-svg/tests/sgr_off_codes.html index e42d348d..c453d43e 100644 --- a/crates/anstyle-svg/tests/sgr_off_codes.html +++ b/crates/anstyle-svg/tests/sgr_off_codes.html @@ -29,19 +29,15 @@
-bold normal
-dim normal
-italic normal
-underline normal
-█████████████
-invert normal
-█████████████
-
-████████████████████
-
-█████████████████████████████████
-
-
+bold normal
+dim normal
+italic normal
+underline normal
+██████
+invert normal
+ normal
+strikethrough normal
+all three no bold no italic plain

diff --git a/crates/anstyle-svg/tests/sgr_off_codes.svg b/crates/anstyle-svg/tests/sgr_off_codes.svg index e4ef4a5e..703f4d97 100644 --- a/crates/anstyle-svg/tests/sgr_off_codes.svg +++ b/crates/anstyle-svg/tests/sgr_off_codes.svg @@ -24,31 +24,23 @@ - bold normal + bold normal - dim normal + dim normal - italic normal + italic normal - underline normal + underline normal - █████████████ + ██████ - invert normal + invert normal - █████████████ + normal - + strikethrough normal - ████████████████████ - - - - █████████████████████████████████ - - - - + all three no bold no italic plain diff --git a/crates/anstyle-svg/tests/underline_color_reset.html b/crates/anstyle-svg/tests/underline_color_reset.html index 4d94d30a..1e5269d0 100644 --- a/crates/anstyle-svg/tests/underline_color_reset.html +++ b/crates/anstyle-svg/tests/underline_color_reset.html @@ -25,9 +25,9 @@
-red underline default color underline normal
-orange underline default color underline normal
-blue underline default color underline normal
+red underline default color underline normal
+orange underline default color underline normal
+blue underline default color underline normal

diff --git a/crates/anstyle-svg/tests/underline_color_reset.svg b/crates/anstyle-svg/tests/underline_color_reset.svg index 60f7f88a..2579f48d 100644 --- a/crates/anstyle-svg/tests/underline_color_reset.svg +++ b/crates/anstyle-svg/tests/underline_color_reset.svg @@ -20,11 +20,11 @@ - red underline default color underline normal + red underline default color underline normal - orange underline default color underline normal + orange underline default color underline normal - blue underline default color underline normal + blue underline default color underline normal From d82d083aa05ee4ddc059631f083047cbf32cc9ae Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Fri, 13 Mar 2026 10:43:31 -0600 Subject: [PATCH 4/5] feat: Support SGR 59 (reset underline color to default) --- crates/anstyle-svg/src/adapter.rs | 4 ++++ crates/anstyle-svg/tests/underline_color_reset.html | 6 +++--- crates/anstyle-svg/tests/underline_color_reset.svg | 6 +++--- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/anstyle-svg/src/adapter.rs b/crates/anstyle-svg/src/adapter.rs index 7b66d1b0..4a724001 100644 --- a/crates/anstyle-svg/src/adapter.rs +++ b/crates/anstyle-svg/src/adapter.rs @@ -222,6 +222,10 @@ impl anstyle_parse::Perform for AnsiCapture { color_target = ColorTarget::Underline; state = CsiState::PrepareCustomColor; } + (CsiState::Normal, 59) => { + style = style.underline_color(None); + break; + } (CsiState::Normal, 90..=97) => { let color = to_ansi_color(value - 90) .expect("within 4-bit range") diff --git a/crates/anstyle-svg/tests/underline_color_reset.html b/crates/anstyle-svg/tests/underline_color_reset.html index 1e5269d0..b4360bf2 100644 --- a/crates/anstyle-svg/tests/underline_color_reset.html +++ b/crates/anstyle-svg/tests/underline_color_reset.html @@ -25,9 +25,9 @@
-red underline default color underline normal
-orange underline default color underline normal
-blue underline default color underline normal
+red underline default color underline normal
+orange underline default color underline normal
+blue underline default color underline normal

diff --git a/crates/anstyle-svg/tests/underline_color_reset.svg b/crates/anstyle-svg/tests/underline_color_reset.svg index 2579f48d..5ce70e8c 100644 --- a/crates/anstyle-svg/tests/underline_color_reset.svg +++ b/crates/anstyle-svg/tests/underline_color_reset.svg @@ -20,11 +20,11 @@ - red underline default color underline normal + red underline default color underline normal - orange underline default color underline normal + orange underline default color underline normal - blue underline default color underline normal + blue underline default color underline normal From e2d2fb53a71cd95ed0c2f50bc8a73f86c9e6bb37 Mon Sep 17 00:00:00 2001 From: Scott Schafer Date: Thu, 12 Mar 2026 11:53:48 -0600 Subject: [PATCH 5/5] fix: Make 4:0 removes all underline styles --- crates/anstyle-svg/src/adapter.rs | 11 +++++++++-- crates/anstyle-svg/tests/underline_subparams.html | 8 ++++---- crates/anstyle-svg/tests/underline_subparams.svg | 8 ++++---- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/crates/anstyle-svg/src/adapter.rs b/crates/anstyle-svg/src/adapter.rs index 4a724001..0c124fd7 100644 --- a/crates/anstyle-svg/src/adapter.rs +++ b/crates/anstyle-svg/src/adapter.rs @@ -275,8 +275,15 @@ impl anstyle_parse::Perform for AnsiCapture { } }, (CsiState::Underline, 0) => { - style = - style.effects(style.get_effects().remove(anstyle::Effects::UNDERLINE)); + style = style.effects( + style + .get_effects() + .remove(anstyle::Effects::UNDERLINE) + .remove(anstyle::Effects::DOUBLE_UNDERLINE) + .remove(anstyle::Effects::CURLY_UNDERLINE) + .remove(anstyle::Effects::DOTTED_UNDERLINE) + .remove(anstyle::Effects::DASHED_UNDERLINE), + ); } (CsiState::Underline, 1) => { // underline already set diff --git a/crates/anstyle-svg/tests/underline_subparams.html b/crates/anstyle-svg/tests/underline_subparams.html index 0b3e20b6..710977cc 100644 --- a/crates/anstyle-svg/tests/underline_subparams.html +++ b/crates/anstyle-svg/tests/underline_subparams.html @@ -27,10 +27,10 @@
underline normal
-double normal
-curly normal
-dotted normal
-dashed normal
+double normal
+curly normal
+dotted normal
+dashed normal

diff --git a/crates/anstyle-svg/tests/underline_subparams.svg b/crates/anstyle-svg/tests/underline_subparams.svg index fd507308..02d3c87c 100644 --- a/crates/anstyle-svg/tests/underline_subparams.svg +++ b/crates/anstyle-svg/tests/underline_subparams.svg @@ -23,13 +23,13 @@ underline normal - double normal + double normal - curly normal + curly normal - dotted normal + dotted normal - dashed normal + dashed normal