From: Jannik Hartung Date: Tue, 5 Aug 2025 11:50:58 +0000 (+0200) Subject: [UI/TUI] Add support for italic and underline ANSI escape sequences X-Git-Tag: gdb-17-branchpoint~381 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=64917bab3caa10d89cb5ed160d20801065b7440b;p=thirdparty%2Fbinutils-gdb.git [UI/TUI] Add support for italic and underline ANSI escape sequences The ANSI escape sequence translation layer in TUI mode strips italic or underlined text modes silently. You cannot output text formatted like that using `TuiWindow.write` in Python at the moment. Parse the ANSI escape sequences for italic and underlined text into the `ui_file_style` structure and apply it to the TUI window when applying styles, similar to preserving the bold/dim state already. A script like this shows italic and underlined text correctly now. ```python import gdb class TestTUIWindow: _tui_window: gdb.TuiWindow def __init__(self, tui_window: gdb.TuiWindow) -> None: self._tui_window = tui_window self._tui_window.title = "colors test" def render(self) -> None: self._tui_window.write(""" \x1b[4mThis is underlined.\x1b[24m And normal text. \x1b[3mThis is italic.\x1b[23m And normal text. """, True) gdb.register_window_type("colortest", TestTUIWindow) ``` And launching it with ``` source the_above_script.py tui new-layout test colortest 1 cmd 1 layout test ``` Approved-By: Tom Tromey --- diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index 47efb646f85..a6c18d34269 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -323,10 +323,10 @@ proc run_style_tests { } { gdb_test_no_output "set style version background 255" gdb_test_no_output "set style version foreground #FED210" gdb_test "show style version background" \ - "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \ + "The \033\\\[38;2;254;210;16;48;5;255;22;23;24;27m.*\".*version.*\".*style.*\033\\\[m background color is: 255" \ "Version's 256-color background style" gdb_test "show style version foreground" \ - "The \033\\\[38;2;254;210;16;48;5;255;22;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \ + "The \033\\\[38;2;254;210;16;48;5;255;22;23;24;27m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \ "Version's TrueColor foreground style" } @@ -782,7 +782,7 @@ proc test_pagination_cmd_after_quit_styling {} { -re "^apropos time\r\n" { exp_continue } - -re "^\033\\\[39;49;1;27m\[^\r\n\]+\r\n" { + -re "^\033\\\[39;49;1;23;24;27m\[^\r\n\]+\r\n" { exp_continue } -re "^$::pagination_prompt$" { @@ -828,7 +828,7 @@ proc test_pagination_cmd_after_quit_styling {} { # non-default style that was in use when pagination kicked in # above. gdb_test "maintenance time" \ - "^\"\033\\\[39;49;1;27mmaintenance time\033\\\[m\" takes a numeric argument\\." + "^\"\033\\\[39;49;1;23;24;27mmaintenance time\033\\\[m\" takes a numeric argument\\." } # Helper for test_pagination_prompt_styling. Return false if STR, a @@ -995,20 +995,20 @@ proc test_pagination_continue_styling_1 { length } { exp_continue } - -re "^The current logfile is \"\033\\\[32;49;22;27m(?:ax)+\033\\\[m" { + -re "^The current logfile is \"\033\\\[32;49;22;23;24;27m(?:ax)+\033\\\[m" { exp_continue } - -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m(?=--)" { + -re "^\r\n\033\\\[32;49;22;23;24;27m(?:ax)+\033\\\[m(?=--)" { exp_continue } - -re "^\r\n\033\\\[32;49;22;27m(?:ax)+(?=--)" { + -re "^\r\n\033\\\[32;49;22;23;24;27m(?:ax)+(?=--)" { set saw_bad_styling true exp_continue } - -re "^\r\n\033\\\[32;49;22;27m(?:ax)+\033\\\[m\"\\.\r\n" { + -re "^\r\n\033\\\[32;49;22;23;24;27m(?:ax)+\033\\\[m\"\\.\r\n" { exp_continue } diff --git a/gdb/testsuite/lib/gdb-utils.exp b/gdb/testsuite/lib/gdb-utils.exp index fe2cfcab10c..c24e7ed407a 100644 --- a/gdb/testsuite/lib/gdb-utils.exp +++ b/gdb/testsuite/lib/gdb-utils.exp @@ -70,6 +70,8 @@ proc style {str style} { set fg 39 set bg 49 set intensity 22 + set italic 23 + set underline 24 set reverse 27 switch -exact -- $style { title { set intensity 1 } @@ -84,7 +86,7 @@ proc style {str style} { line-number { set intensity 2 } none { return $str } } - return "\033\\\[${fg};${bg};${intensity};${reverse}m${str}\033\\\[m" + return "\033\\\[${fg};${bg};${intensity};${italic};${underline};${reverse}m${str}\033\\\[m" } # gdb_get_bp_addr num diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c index 1b4cc82cce8..c97e8fd1717 100644 --- a/gdb/tui/tui-io.c +++ b/gdb/tui/tui-io.c @@ -319,6 +319,8 @@ tui_apply_style (WINDOW *w, ui_file_style style) wattron (w, A_NORMAL); wattroff (w, A_BOLD); wattroff (w, A_DIM); + wattroff (w, A_ITALIC); + wattroff (w, A_UNDERLINE); wattroff (w, A_REVERSE); if (last_color_pair != -1) wattroff (w, COLOR_PAIR (last_color_pair)); @@ -366,6 +368,12 @@ tui_apply_style (WINDOW *w, ui_file_style style) gdb_assert_not_reached ("invalid intensity"); } + if (style.is_italic ()) + wattron (w, A_ITALIC); + + if (style.is_underline ()) + wattron (w, A_UNDERLINE); + if (style.is_reverse ()) wattron (w, A_REVERSE); diff --git a/gdb/ui-style.c b/gdb/ui-style.c index c8f2e11759a..9a58e4dd2ae 100644 --- a/gdb/ui-style.c +++ b/gdb/ui-style.c @@ -290,6 +290,16 @@ ui_file_style::to_ansi () const else result.append (std::to_string (m_intensity)); result.push_back (';'); + if (m_italic) + result.append ("3"); + else + result.append ("23"); + result.push_back (';'); + if (m_underline) + result.append ("4"); + else + result.append ("24"); + result.push_back (';'); if (m_reverse) result.push_back ('7'); else @@ -435,6 +445,14 @@ ui_file_style::parse (const char *buf, size_t *n_read) /* Dim. */ m_intensity = DIM; break; + case 3: + /* Italic. */ + m_italic = true; + break; + case 4: + /* Underline. */ + m_underline = true; + break; case 7: /* Reverse. */ m_reverse = true; @@ -446,6 +464,14 @@ ui_file_style::parse (const char *buf, size_t *n_read) /* Normal. */ m_intensity = NORMAL; break; + case 23: + /* Non-italic. */ + m_italic = false; + break; + case 24: + /* Non-underline. */ + m_underline = false; + break; case 27: /* Inverse off. */ m_reverse = false; diff --git a/gdb/ui-style.h b/gdb/ui-style.h index f61152f7517..1ea3556869d 100644 --- a/gdb/ui-style.h +++ b/gdb/ui-style.h @@ -266,7 +266,9 @@ struct ui_file_style return (m_foreground == other.m_foreground && m_background == other.m_background && m_intensity == other.m_intensity - && m_reverse == other.m_reverse); + && m_reverse == other.m_reverse + && m_italic == other.m_italic + && m_underline == other.m_underline); } bool operator!= (const ui_file_style &other) const @@ -284,7 +286,9 @@ struct ui_file_style return (m_foreground == NONE && m_background == NONE && m_intensity == NORMAL - && !m_reverse); + && !m_reverse + && !m_italic + && !m_underline); } /* Return true if this style specified reverse display; false @@ -330,6 +334,32 @@ struct ui_file_style return m_intensity; } + /* Return true if this style specified italic display; false + otherwise. */ + bool is_italic () const + { + return m_italic; + } + + /* Set/clear the italic display flag. */ + void set_italic (bool italic) + { + m_italic = italic; + } + + /* Return true if this style specified underline display; false + otherwise. */ + bool is_underline () const + { + return m_underline; + } + + /* Set/clear the underline display flag. */ + void set_underline (bool underline) + { + m_underline = underline; + } + /* Parse an ANSI escape sequence in BUF, modifying this style. BUF must begin with an ESC character. Return true if an escape sequence was successfully parsed; false otherwise. In either @@ -351,6 +381,8 @@ private: color m_foreground = NONE; color m_background = NONE; intensity m_intensity = NORMAL; + bool m_italic = false; + bool m_underline = false; bool m_reverse = false; }; diff --git a/gdb/unittests/style-selftests.c b/gdb/unittests/style-selftests.c index f2a37bb7219..7cb42a1eff4 100644 --- a/gdb/unittests/style-selftests.c +++ b/gdb/unittests/style-selftests.c @@ -38,6 +38,8 @@ run_tests () SELF_CHECK (style.get_foreground ().is_none ()); SELF_CHECK (style.get_background ().is_none ()); SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL); + SELF_CHECK (!style.is_italic ()); + SELF_CHECK (!style.is_underline ()); SELF_CHECK (!style.is_reverse ()); SELF_CHECK (style.to_ansi () == "\033[m"); @@ -47,6 +49,8 @@ run_tests () SELF_CHECK (style.get_foreground ().is_none ()); SELF_CHECK (style.get_background ().is_none ()); SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL); + SELF_CHECK (!style.is_italic ()); + SELF_CHECK (!style.is_underline ()); SELF_CHECK (!style.is_reverse ()); /* This particular case does not round-trip identically, but the difference is unimportant. */ @@ -57,8 +61,10 @@ run_tests () SELF_CHECK (style.get_foreground ().is_none ()); SELF_CHECK (style.get_background ().is_none ()); SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL); + SELF_CHECK (!style.is_italic ()); + SELF_CHECK (!style.is_underline ()); SELF_CHECK (style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[39;49;22;7m"); + SELF_CHECK (style.to_ansi () == "\033[39;49;22;23;24;7m"); style = ui_file_style (); SELF_CHECK (style.parse ("\033[32;1m", &n_read)); @@ -67,8 +73,22 @@ run_tests () SELF_CHECK (style.get_foreground ().get_value () == ui_file_style::GREEN); SELF_CHECK (style.get_background ().is_none ()); SELF_CHECK (style.get_intensity () == ui_file_style::BOLD); + SELF_CHECK (!style.is_italic ()); + SELF_CHECK (!style.is_underline ()); SELF_CHECK (!style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[32;49;1;27m"); + SELF_CHECK (style.to_ansi () == "\033[32;49;1;23;24;27m"); + + style = ui_file_style (); + SELF_CHECK (style.parse ("\033[32;2;3;4m", &n_read)); + SELF_CHECK (n_read == 11); + SELF_CHECK (style.get_foreground ().is_basic ()); + SELF_CHECK (style.get_foreground ().get_value () == ui_file_style::GREEN); + SELF_CHECK (style.get_background ().is_none ()); + SELF_CHECK (style.get_intensity () == ui_file_style::DIM); + SELF_CHECK (style.is_italic ()); + SELF_CHECK (style.is_underline ()); + SELF_CHECK (!style.is_reverse ()); + SELF_CHECK (style.to_ansi () == "\033[32;49;2;3;4;27m"); style = ui_file_style (); SELF_CHECK (style.parse ("\033[38;5;112;48;5;249m", &n_read)); @@ -80,8 +100,10 @@ run_tests () style.get_background ().get_rgb (rgb); CHECK_RGB (0xb2, 0xb2, 0xb2); SELF_CHECK (style.get_intensity () == ui_file_style::NORMAL); + SELF_CHECK (!style.is_italic ()); + SELF_CHECK (!style.is_underline ()); SELF_CHECK (!style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;27m"); + SELF_CHECK (style.to_ansi () == "\033[38;5;112;48;5;249;22;23;24;27m"); style = ui_file_style (); SELF_CHECK (style.parse ("\033[38;2;83;84;85;48;2;0;1;254;2;7m", &n_read)); @@ -93,8 +115,10 @@ run_tests () style.get_background ().get_rgb (rgb); CHECK_RGB (0, 1, 254); SELF_CHECK (style.get_intensity () == ui_file_style::DIM); + SELF_CHECK (!style.is_italic ()); + SELF_CHECK (!style.is_underline ()); SELF_CHECK (style.is_reverse ()); - SELF_CHECK (style.to_ansi () == "\033[38;2;83;84;85;48;2;0;1;254;2;7m"); + SELF_CHECK (style.to_ansi () == "\033[38;2;83;84;85;48;2;0;1;254;2;23;24;7m"); } } /* namespace style */