]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
[gdb/cli] Use debug info language to pick pygments lexer
authorTom de Vries <tdevries@suse.de>
Mon, 7 Apr 2025 20:40:04 +0000 (22:40 +0200)
committerTom de Vries <tdevries@suse.de>
Mon, 7 Apr 2025 20:40:04 +0000 (22:40 +0200)
Consider the following scenario:
...
$ cat hello

int
main (void)
{
  printf ("hello\n");
  return 0;
}
$ gcc -x c hello -g
$ gdb -q -iex "maint set gnu-source-highlight enabled off" a.out
Reading symbols from a.out...
(gdb) start
Temporary breakpoint 1 at 0x4005db: file hello, line 6.
Starting program: /data/vries/gdb/a.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".

Temporary breakpoint 1, main () at hello:6
6   printf ("hello\n");
...

This doesn't produce highlighting for line 6, because:
- pygments is used for highlighting instead of source-highlight, and
- pygments guesses the language for highlighting only based on the filename,
  which in this case doesn't give a clue.

Fix this by:
- adding a language parameter to the extension_language_ops.colorize interface,
- passing the language as found in the debug info, and
- using it in gdb.styling.colorize to pick the pygments lexer.

The new test-case gdb.python/py-source-styling-2.exp excercises a slightly
different scenario: it compiles a c++ file with a .c extension, and checks
that c++ highlighting is done instead of c highlighting.

Tested on x86_64-linux.

Approved-By: Tom Tromey <tom@tromey.com>
PR cli/30966
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30966

gdb/extension-priv.h
gdb/extension.c
gdb/extension.h
gdb/python/lib/gdb/styling.py
gdb/python/python.c
gdb/source-cache.c
gdb/testsuite/gdb.python/py-source-styling-2.c [new file with mode: 0644]
gdb/testsuite/gdb.python/py-source-styling-2.exp [new file with mode: 0644]

index a38f104d949551214259ebe6c6225d62cbfeac9a..4bec129bbba98ee5b37f2facaf0bd6fc2668b386 100644 (file)
@@ -262,12 +262,13 @@ struct extension_language_ops
      const char *method_name,
      std::vector<xmethod_worker_up> *dm_vec);
 
-  /* Colorize a source file.  NAME is the source file's name, and
-     CONTENTS is the contents of the file.  This should either return
-     colorized (using ANSI terminal escapes) version of the contents,
+  /* Colorize a source file.  NAME is the source file's name, CONTENTS is the
+     contents of the file, and LANG is the language.  This should either
+     return colorized (using ANSI terminal escapes) version of the contents,
      or an empty option.  */
   std::optional<std::string> (*colorize) (const std::string &name,
-                                         const std::string &contents);
+                                         const std::string &contents,
+                                         enum language lang);
 
   /* Colorize a single line of disassembler output, CONTENT.  This should
      either return colorized (using ANSI terminal escapes) version of the
index b78ea4f27160745587bbf8233f1df86e2dfd1376..f8fef0c04db41a4722c9c555707cc8a40f096d8e 100644 (file)
@@ -974,7 +974,8 @@ xmethod_worker::get_result_type (value *object, gdb::array_view<value *> args)
 /* See extension.h.  */
 
 std::optional<std::string>
-ext_lang_colorize (const std::string &filename, const std::string &contents)
+ext_lang_colorize (const std::string &filename, const std::string &contents,
+                  enum language lang)
 {
   std::optional<std::string> result;
 
@@ -983,7 +984,7 @@ ext_lang_colorize (const std::string &filename, const std::string &contents)
       if (extlang->ops == nullptr
          || extlang->ops->colorize == nullptr)
        continue;
-      result = extlang->ops->colorize (filename, contents);
+      result = extlang->ops->colorize (filename, contents, lang);
       if (result.has_value ())
        return result;
     }
index 957642a99dc8b55d0bac0c766f34fb91492721bd..5763e41148e4c342e9f5d81636aafec49f4747c9 100644 (file)
@@ -325,12 +325,14 @@ extern void get_matching_xmethod_workers
    std::vector<xmethod_worker_up> *workers);
 
 /* Try to colorize some source code.  FILENAME is the name of the file
-   holding the code.  CONTENTS is the source code itself.  This will
-   either a colorized (using ANSI terminal escapes) version of the
-   source code, or an empty value if colorizing could not be done.  */
+   holding the code.  CONTENTS is the source code itself.  LANG is the
+   language of the CONTENTS.  This will either a colorized (using ANSI
+   terminal escapes) version of the source code, or an empty value if
+   colorizing could not be done.  */
 
 extern std::optional<std::string> ext_lang_colorize
-  (const std::string &filename, const std::string &contents);
+  (const std::string &filename, const std::string &contents,
+   enum language lang);
 
 /* Try to colorize a single line of disassembler output, CONTENT for
    GDBARCH.  This will return either a colorized (using ANSI terminal
index 1c5394e479b1d2d573206a272ca0c3233a0c8c09..2efaf4cb5e04bd3b7367bb2fb0327c48790665b4 100644 (file)
@@ -22,6 +22,7 @@ try:
     from pygments import formatters, highlight, lexers
     from pygments.filters import TokenMergeFilter
     from pygments.token import Comment, Error, Text
+    from pygments.util import ClassNotFound
 
     _formatter = None
 
@@ -31,10 +32,13 @@ try:
             _formatter = formatters.TerminalFormatter()
         return _formatter
 
-    def colorize(filename, contents):
+    def colorize(filename, contents, lang):
         # Don't want any errors.
         try:
-            lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
+            try:
+                lexer = lexers.get_lexer_by_name(lang, stripnl=False)
+            except ClassNotFound:
+                lexer = lexers.get_lexer_for_filename(filename, stripnl=False)
             formatter = get_formatter()
             return highlight(contents, lexer, formatter).encode(
                 gdb.host_charset(), "backslashreplace"
@@ -94,7 +98,7 @@ try:
 
 except ImportError:
 
-    def colorize(filename, contents):
+    def colorize(filename, contents, lang):
         return None
 
     def colorize_disasm(content, gdbarch):
index 2aaa30c7d8eeadb10ffbc9aa1a4552a195cf4f4e..e32b776549f0f9ae48a5031e3be43cae4737f5ec 100644 (file)
@@ -128,7 +128,8 @@ static bool gdbpy_check_quit_flag (const struct extension_language_defn *);
 static enum ext_lang_rc gdbpy_before_prompt_hook
   (const struct extension_language_defn *, const char *current_gdb_prompt);
 static std::optional<std::string> gdbpy_colorize
-  (const std::string &filename, const std::string &contents);
+  (const std::string &filename, const std::string &contents,
+   enum language lang);
 static std::optional<std::string> gdbpy_colorize_disasm
 (const std::string &content, gdbarch *gdbarch);
 static ext_lang_missing_file_result gdbpy_handle_missing_debuginfo
@@ -1295,7 +1296,8 @@ gdbpy_before_prompt_hook (const struct extension_language_defn *extlang,
 /* This is the extension_language_ops.colorize "method".  */
 
 static std::optional<std::string>
-gdbpy_colorize (const std::string &filename, const std::string &contents)
+gdbpy_colorize (const std::string &filename, const std::string &contents,
+               enum language lang)
 {
   if (!gdb_python_initialized)
     return {};
@@ -1329,6 +1331,13 @@ gdbpy_colorize (const std::string &filename, const std::string &contents)
       return {};
     }
 
+  gdbpy_ref<> lang_arg (PyUnicode_FromString (language_str (lang)));
+  if (lang_arg == nullptr)
+    {
+      gdbpy_print_stack ();
+      return {};
+    }
+
   /* The pygments library, which is what we currently use for applying
      styling, is happy to take input as a bytes object, and to figure out
      the encoding for itself.  This removes the need for us to figure out
@@ -1349,6 +1358,7 @@ gdbpy_colorize (const std::string &filename, const std::string &contents)
   gdbpy_ref<> result (PyObject_CallFunctionObjArgs (hook.get (),
                                                    fname_arg.get (),
                                                    contents_arg.get (),
+                                                   lang_arg.get (),
                                                    nullptr));
   if (result == nullptr)
     {
index 30c9e619dae0ff8c3574e6906c91a791fa622b4c..2c3ad34c23325f8b2f52bd45c45d66a9f30241eb 100644 (file)
@@ -364,7 +364,8 @@ source_cache::ensure (struct symtab *s)
       if (!styled_p)
        {
          std::optional<std::string> ext_contents;
-         ext_contents = ext_lang_colorize (fullname, contents);
+         ext_contents = ext_lang_colorize (fullname, contents,
+                                           s->language ());
          if (ext_contents.has_value ())
            {
              contents = std::move (*ext_contents);
diff --git a/gdb/testsuite/gdb.python/py-source-styling-2.c b/gdb/testsuite/gdb.python/py-source-styling-2.c
new file mode 100644 (file)
index 0000000..aaa3d69
--- /dev/null
@@ -0,0 +1,26 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 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/>.  */
+
+int
+main ()
+{ /* List this line.  */
+  try
+    {}
+  catch (...)
+    {}
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-source-styling-2.exp b/gdb/testsuite/gdb.python/py-source-styling-2.exp
new file mode 100644 (file)
index 0000000..b13ee1f
--- /dev/null
@@ -0,0 +1,55 @@
+# 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/>.
+
+# Compile a c++ file using a .c extension, and check that pygments uses c++
+# highlighting instead of c highlighting.
+
+require allow_python_tests
+
+load_lib gdb-python.exp
+
+standard_testfile py-source-styling-2.c
+
+set line_number [gdb_get_line_number "List this line."]
+
+set opts {}
+lappend opts debug
+lappend opts c++
+
+if { [build_executable "failed to build" $testfile $srcfile $opts] == -1 } {
+    return
+}
+
+clean_restart
+
+gdb_test_no_output "maint set gnu-source-highlight enabled off"
+
+gdb_load $binfile
+
+require {gdb_py_module_available pygments}
+
+with_ansi_styling_terminal {
+    gdb_test_no_output "set style enabled on"
+
+    gdb_test_multiple "list $line_number" "Styling of c++ keyword try" {
+       -re -wrap "  try\r\n.*" {
+           # Unstyled.
+           fail $gdb_test_name
+       }
+       -re -wrap "" {
+           pass $gdb_test_name
+       }
+    }
+}