Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: gdb-patches@sourceware.org
Cc: Andrew Burgess <aburgess@redhat.com>
Subject: [PATCHv4 1/4] gdb: allow gdb.Color to work correctly with pagination
Date: Wed, 18 Jun 2025 20:30:01 +0100	[thread overview]
Message-ID: <c13b7f8cc6a55c400f749ef46353d85a509c574a.1750274641.git.aburgess@redhat.com> (raw)
In-Reply-To: <cover.1750274641.git.aburgess@redhat.com>

This commit allows gdb.Color objects to be used to style output from
GDB command, and that styled output should then work correctly with
pagination.

There are two parts to fixing this functionality.

First, GDB needs to be able to track the currently applied style
within the page_file class.  This means that style changes need to be
achieved with calls to pager_file::emit_style_escape.

Now usually, GDB does this by calling something like fprintf_styled,
which takes care to apply the style for us.  However, that's not
really an option here as a gdb.Color isn't a full style, and as the
gdb.Color object is designed to be converted directly into escape
sequences that can then be printed, we really need a solution that
works with that approach.

However pager_file::puts already has code in place to handle escape
sequences.  Right now all this code does is spot the escape sequence
and append it to the m_wrap_buffer.  But in this commit I propose that
we go one step further.  GDB already has code in place to parse an
escape sequence back into a ui_file_style object, and so, in
pager_file::puts, if we see an escape sequence, we can try to parse it
back into a ui_file_style, if that works then we can call
pager_file::emit_style_escape.  If the parsing doesn't work then we
can just add the escape sequence to m_wrap_buffer as we did before.

Ideally this would be all that was needed.  But unfortunately this
doesn't work because....

... the implementation of the Python function gdb.write() calls
gdb_printf(), which calls gdb_vprintf(), which calls ui_file::vprintf,
which calls ui_out::vmessage, which calls ui_out::call_do_message, and
finally we reach cli_ui_out::do_message.  This final do_message
function does this:

  ui_file *stream = m_streams.back ();
  stream->emit_style_escape (style);
  stream->puts (str.c_str ());
  stream->emit_style_escape (ui_file_style ());

So STR here holds the escape sequence we are writing.  The STYLE will
be the default style.

After the above change, when we call pager_file::puts we now modify
the stream's m_applied_style.  And now, when we run the final line,
which restores the default style, GDB immediately overwrites our
updated style.

The fix for this is to avoid the high level gdb_printf in the
gdb.write() call, and instead use gdb_puts instead.  The gdb_puts
function doesn't try to restore the default style, which means our
style modification survives.

The test includes what appears like a pointless extra loop (looping
over a single value), but later commits will add more values to this
list.
---
 gdb/python/python.c                           |  18 +--
 .../gdb.python/py-color-pagination.exp        | 116 ++++++++++++++++++
 .../gdb.python/py-color-pagination.py         |  46 +++++++
 gdb/utils.c                                   |  21 +++-
 4 files changed, 189 insertions(+), 12 deletions(-)
 create mode 100644 gdb/testsuite/gdb.python/py-color-pagination.exp
 create mode 100644 gdb/testsuite/gdb.python/py-color-pagination.py

diff --git a/gdb/python/python.c b/gdb/python/python.c
index 7c3d0d104b5..7a3f688e5aa 100644
--- a/gdb/python/python.c
+++ b/gdb/python/python.c
@@ -1570,21 +1570,21 @@ gdbpy_write (PyObject *self, PyObject *args, PyObject *kw)
 
   try
     {
+      ui_file *stream;
       switch (stream_type)
 	{
 	case 1:
-	  {
-	    gdb_printf (gdb_stderr, "%s", arg);
-	    break;
-	  }
+	  stream = gdb_stderr;
+	  break;
 	case 2:
-	  {
-	    gdb_printf (gdb_stdlog, "%s", arg);
-	    break;
-	  }
+	  stream = gdb_stdlog;
+	  break;
 	default:
-	  gdb_printf (gdb_stdout, "%s", arg);
+	  stream = gdb_stdout;
+	  break;
 	}
+
+      gdb_puts (arg, stream);
     }
   catch (const gdb_exception &except)
     {
diff --git a/gdb/testsuite/gdb.python/py-color-pagination.exp b/gdb/testsuite/gdb.python/py-color-pagination.exp
new file mode 100644
index 00000000000..cb410da2274
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color-pagination.exp
@@ -0,0 +1,116 @@
+# Copyright (C) 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# This file is part of the GDB testsuite.  It tests gdb.Color and how this
+# interacts with GDB's pagination system.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+set str "<[string repeat - 78]>"
+
+set black "(?:\033\\\[30;49;22;27m)"
+set red "(?:\033\\\[31;49;22;27m)"
+set green "(?:\033\\\[32;49;22;27m)"
+set yellow "(?:\033\\\[33;49;22;27m)"
+set blue "(?:\033\\\[34;49;22;27m)"
+set magenta "(?:\033\\\[35;49;22;27m)"
+set cyan "(?:\033\\\[36;49;22;27m)"
+set white "(?:\033\\\[37;49;22;27m)"
+
+set any_color "(?:${black}|${red}|${green}|${yellow}|${blue}|${magenta}|${cyan}|${white})"
+
+# Run the command 'TYPE-fill MODE' which fills the screen with output and
+# triggers the pagination prompt.  Check that styling is applied correctly
+# to the output.
+proc test_pagination { type mode } {
+
+    # Start with a fresh GDB, but enable color support.
+    with_ansi_styling_terminal {
+	clean_restart
+    }
+
+    gdb_test_no_output "source $::pyfile" "source the script"
+
+    gdb_test_no_output "set width 80"
+    gdb_test_no_output "set height 15"
+
+    set saw_bad_color_handling false
+    set expected_restore_color ""
+    set last_color ""
+    gdb_test_multiple "$type-fill $mode" "" {
+	-re "^$type-fill $mode\r\n" {
+	    exp_continue
+	}
+
+	-re "^(${::any_color}?)(${::any_color})$::str" {
+	    # After a continuation prompt GDB will restore the previous
+	    # color, and then we immediately switch to a new color.
+	    set restored_color $expect_out(1,string)
+	    if { $restored_color ne ""
+		 && $restored_color ne $expected_restore_color } {
+		set saw_bad_color_handling true
+	    }
+	    set last_color $expect_out(2,string)
+	    exp_continue
+	}
+
+	-re "^\033\\\[${::decimal}m$::str" {
+	    # This catches the case where the color's escape sequence has
+	    # not been converted back into a full style.  This indicates
+	    # something went wrong in the pager_file::puts function.
+	    set saw_bad_color_handling true
+	    exp_continue
+	}
+
+	-re "^((?:\033\\\[m)?)$::pagination_prompt$" {
+	    # After a pagination prompt we expect GDB to restore the last
+	    # color.
+	    set expected_restore_color $last_color
+
+	    # If we didn't see a color reset sequence then the pagination
+	    # prompt will have been printed in the wrong color, this is a
+	    # GDB bug.
+	    set color_reset $expect_out(1,string)
+	    if { $color_reset eq "" } {
+		set saw_bad_color_handling true
+	    }
+
+	    # Send '\n' to view more output.
+	    send_gdb "\n"
+	    exp_continue
+	}
+
+	-re "^\r\n" {
+	    # The matches the newline sent to the continuation prompt.
+	    exp_continue
+	}
+
+	-re "^\033\\\[m\r\n$::gdb_prompt $" {
+	    gdb_assert { !$saw_bad_color_handling } $gdb_test_name
+	}
+    }
+}
+
+foreach_with_prefix type { color } {
+    foreach_with_prefix mode { write print } {
+	test_pagination $type $mode
+    }
+}
diff --git a/gdb/testsuite/gdb.python/py-color-pagination.py b/gdb/testsuite/gdb.python/py-color-pagination.py
new file mode 100644
index 00000000000..efd501eedf5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color-pagination.py
@@ -0,0 +1,46 @@
+# Copyright (C) 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import gdb
+
+basic_colors = ["black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"]
+
+
+def write(mode, text):
+    if mode == "write":
+        gdb.write(text)
+    else:
+        print(text, end="")
+
+
+class ColorTester(gdb.Command):
+    def __init__(self):
+        super().__init__("color-fill", gdb.COMMAND_USER)
+
+    def invoke(self, args, from_tty):
+        mode = args
+        str = "<" + "-" * 78 + ">"
+        for i in range(0, 20):
+            for color_name in basic_colors:
+                c = gdb.Color(color_name)
+                write(mode, c.escape_sequence(True))
+                write(mode, str)
+
+        default = gdb.Color("none")
+        write(mode, default.escape_sequence(True))
+        write(mode, "\n")
+
+
+ColorTester()
diff --git a/gdb/utils.c b/gdb/utils.c
index 4f48e15b7eb..3119ac74260 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -1682,10 +1682,25 @@ pager_file::puts (const char *linebuffer)
 	  else if (*linebuffer == '\033'
 		   && skip_ansi_escape (linebuffer, &skip_bytes))
 	    {
-	      m_wrap_buffer.append (linebuffer, skip_bytes);
-	      /* Note that we don't consider this a character, so we
+	      /* We don't consider escape sequences as characters, so we
 		 don't increment chars_printed here.  */
-	      linebuffer += skip_bytes;
+
+	      size_t style_len;
+	      ui_file_style style;
+	      if (style.parse (linebuffer, &style_len)
+		  && style_len <= skip_bytes)
+		{
+		  this->emit_style_escape (style);
+
+		  linebuffer += style_len;
+		  skip_bytes -= style_len;
+		}
+
+	      if (skip_bytes > 0)
+		{
+		  m_wrap_buffer.append (linebuffer, skip_bytes);
+		  linebuffer += skip_bytes;
+		}
 	    }
 	  else if (*linebuffer == '\r')
 	    {
-- 
2.47.1


  reply	other threads:[~2025-06-18 19:33 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-04  9:39 [PATCH 0/2] Python Style API Andrew Burgess
2025-06-04  9:39 ` [PATCH 1/2] gdb/python: add gdb.Style class Andrew Burgess
2025-06-04  9:39 ` [PATCH 2/2] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-06-06 13:38 ` [PATCHv2 0/2] Python Style API Andrew Burgess
2025-06-06 13:38   ` [PATCHv2 1/2] gdb/python: add gdb.Style class Andrew Burgess
2025-06-06 14:21     ` Eli Zaretskii
2025-06-06 13:38   ` [PATCHv2 2/2] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-06-07 10:44   ` [PATCHv3 0/2] Python Style API Andrew Burgess
2025-06-07 10:44     ` [PATCHv3 1/2] gdb/python: add gdb.Style class Andrew Burgess
2025-06-07 10:44     ` [PATCHv3 2/2] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-06-09 15:54     ` [PATCHv3 0/2] Python Style API Andrew Burgess
2025-06-18 19:30     ` [PATCHv4 0/4] " Andrew Burgess
2025-06-18 19:30       ` Andrew Burgess [this message]
2025-06-18 19:30       ` [PATCHv4 2/4] gdb/python: add gdb.Style class Andrew Burgess
2025-06-18 19:30       ` [PATCHv4 3/4] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-06-18 19:30       ` [PATCHv4 4/4] gdb: extend gdb.write to support styled output Andrew Burgess
2025-06-25 14:42       ` [PATCHv5 0/4] Python Style API Andrew Burgess
2025-06-25 14:42         ` [PATCHv5 1/4] gdb: allow gdb.Color to work correctly with pagination Andrew Burgess
2025-06-25 14:42         ` [PATCHv5 2/4] gdb/python: add gdb.Style class Andrew Burgess
2025-06-25 14:42         ` [PATCHv5 3/4] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-06-25 14:42         ` [PATCHv5 4/4] gdb/python: extend gdb.write to support styled output Andrew Burgess
2025-07-07 14:43         ` [PATCHv6 0/4] Python Style API Andrew Burgess
2025-07-07 14:43           ` [PATCHv6 1/4] gdb: allow gdb.Color to work correctly with pagination Andrew Burgess
2025-07-07 14:43           ` [PATCHv6 2/4] gdb/python: add gdb.Style class Andrew Burgess
2025-07-07 14:43           ` [PATCHv6 3/4] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-07-07 14:43           ` [PATCHv6 4/4] gdb/python: extend gdb.write to support styled output Andrew Burgess
2025-08-13 10:25           ` [PATCHv7 0/4] Fix for gdb.Color + pagination AND new gdb.Style API Andrew Burgess
2025-08-13 10:25             ` [PATCHv7 1/4] gdb: allow gdb.Color to work correctly with pagination Andrew Burgess
2025-08-24 16:13               ` Andrew Burgess
2025-08-25 10:21                 ` Tom de Vries
2025-08-26 14:35                   ` [PATCH] gdb/testsuite: work around empty substring bug in expect Andrew Burgess
2025-08-27  6:24                     ` Tom de Vries
2025-08-13 10:29             ` [PATCHv7 2/4] gdb/python: add gdb.Style class Andrew Burgess
2025-08-13 10:30             ` [PATCHv7 4/4] gdb/python: extend gdb.write to support styled output Andrew Burgess
2025-08-13 12:37               ` Eli Zaretskii
2025-08-13 10:34             ` [PATCHv7 3/4] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-08-13 13:05               ` Eli Zaretskii
2025-08-13 13:05               ` Eli Zaretskii
2025-08-27 11:34             ` [PATCHv8 0/3] new gdb.Style API Andrew Burgess
2025-08-27 11:34               ` [PATCHv8 1/3] gdb/python: add gdb.Style class Andrew Burgess
2025-09-16 16:47                 ` Tom Tromey
2025-09-23 16:59                   ` Andrew Burgess
2025-08-27 11:34               ` [PATCHv8 2/3] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-09-16 16:50                 ` Tom Tromey
2025-08-27 11:34               ` [PATCHv8 3/3] gdb/python: extend gdb.write to support styled output Andrew Burgess
2025-09-16 16:56                 ` Tom Tromey
2025-09-23 17:05                   ` Andrew Burgess
2025-09-23 18:38                     ` Tom Tromey
2025-09-23 16:54               ` [PATCHv9 0/3] new gdb.Style API Andrew Burgess
2025-09-23 16:54                 ` [PATCHv9 1/3] gdb/python: add gdb.Style class Andrew Burgess
2025-09-23 16:54                 ` [PATCHv9 2/3] gdb/python: new class gdb.StyleParameterSet Andrew Burgess
2025-09-23 16:54                 ` [PATCHv9 3/3] gdb/python: extend gdb.write to support styled output Andrew Burgess
2025-10-03 19:27                 ` [PATCHv9 0/3] new gdb.Style API Tom Tromey
2025-10-05 12:51                   ` Andrew Burgess
2025-10-05 15:16                     ` Tom de Vries
2025-10-05 17:59                       ` Tom Tromey
2025-10-06  9:01                         ` Andrew Burgess
2025-10-05 18:03                       ` Andrew Burgess
2025-10-05 18:41                         ` Tom de Vries
2025-10-06  9:01                           ` Andrew Burgess
2025-10-06 12:29                             ` Tom de Vries

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=c13b7f8cc6a55c400f749ef46353d85a509c574a.1750274641.git.aburgess@redhat.com \
    --to=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox