]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[UI/TUI] Add support for italic and underline ANSI escape sequences
authorJannik Hartung <peacemakerctf@gmail.com>
Tue, 5 Aug 2025 11:50:58 +0000 (13:50 +0200)
committerJannik Hartung <peacemakerctf@gmail.com>
Tue, 5 Aug 2025 11:52:15 +0000 (13:52 +0200)
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 <tom@tromey.com>
gdb/testsuite/gdb.base/style.exp
gdb/testsuite/lib/gdb-utils.exp
gdb/tui/tui-io.c
gdb/ui-style.c
gdb/ui-style.h
gdb/unittests/style-selftests.c

index 47efb646f8539dc459ccd670183d4fc10996c17d..a6c18d34269353decd03a25301ac436d6c38b76d 100644 (file)
@@ -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
        }
 
index fe2cfcab10c7c9d70b9bcd5cdd6c7a0cb6eca72b..c24e7ed407a74071d259d38fc79269c3f711aeb1 100644 (file)
@@ -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
index 1b4cc82cce8af85ee3b729ec9c7eb21de1d6255b..c97e8fd17174c068fd4d805ba01887f3fed85652 100644 (file)
@@ -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);
 
index c8f2e11759a89a01ba0dbc3d687f580412107aea..9a58e4dd2aeea3ccb2c548d9876589a26b3edb82 100644 (file)
@@ -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;
index f61152f75171c308fcd152b4a5370fb2f95485e8..1ea3556869d8e711cdce6bb86c32c320c134607f 100644 (file)
@@ -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;
 };
 
index f2a37bb72199e8859e7c318cc3bb2f59d9c79c8f..7cb42a1eff4eb264efa1acd0a86392f913e1f057 100644 (file)
@@ -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 */