mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-12-05 15:15:42 +00:00
I noticed that the gdb.Color.escape_sequence() method would produce an
escape sequence even when styling is disabled.
I think this is the wrong choice. Ideally, when styling is
disabled (e.g. with 'set style enabled off'), GDB should not be
producing styled output.
If a GDB extension is using gdb.Color to apply styling to the output,
then currently, the extension should be checking 'show style enabled'
any time Color.escape_sequence() is used. This means lots of code
duplication, and the possibility that some locations will be missed,
which means disabling styling no longer does what it says.
I propose that Color.escape_sequence() should return the empty string
if styling is disabled. A Python extension can then do:
python
c_none = gdb.Color('none')
c_red = gdb.Color('red')
print(c_red.escape_sequence(True)
+ "Text in red."
+ c_none.escape_sequence(True))
end
If styling is enable this will print some red text. And if styling is
disabled, then it will print text in the terminal's default color.
I have applied the same fix to the guile API.
I have extended the tests to cover this case.
Approved-By: Tom Tromey <tom@tromey.com>
346 lines
9.5 KiB
C
346 lines
9.5 KiB
C
/* Python interface to ui_file_style::color objects.
|
|
|
|
Copyright (C) 2008-2025 Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
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/>. */
|
|
|
|
|
|
#include "python-internal.h"
|
|
#include "py-color.h"
|
|
#include "cli/cli-decode.h"
|
|
#include "cli/cli-style.h"
|
|
|
|
/* Colorspace constants and their values. */
|
|
static struct {
|
|
const char *name;
|
|
color_space value;
|
|
} colorspace_constants[] =
|
|
{
|
|
{ "COLORSPACE_MONOCHROME", color_space::MONOCHROME },
|
|
{ "COLORSPACE_ANSI_8COLOR", color_space::ANSI_8COLOR },
|
|
{ "COLORSPACE_AIXTERM_16COLOR", color_space::AIXTERM_16COLOR },
|
|
{ "COLORSPACE_XTERM_256COLOR", color_space::XTERM_256COLOR },
|
|
{ "COLORSPACE_RGB_24BIT", color_space::RGB_24BIT },
|
|
};
|
|
|
|
/* A color. */
|
|
struct colorpy_object
|
|
{
|
|
PyObject_HEAD
|
|
|
|
/* Underlying value. */
|
|
ui_file_style::color color;
|
|
};
|
|
|
|
extern PyTypeObject colorpy_object_type;
|
|
|
|
/* See py-color.h. */
|
|
gdbpy_ref<>
|
|
create_color_object (const ui_file_style::color &color)
|
|
{
|
|
gdbpy_ref<colorpy_object> color_obj (PyObject_New (colorpy_object,
|
|
&colorpy_object_type));
|
|
|
|
if (color_obj == nullptr)
|
|
return nullptr;
|
|
|
|
color_obj->color = color;
|
|
return gdbpy_ref<> ((PyObject *) color_obj.release ());
|
|
}
|
|
|
|
/* See py-color.h. */
|
|
bool
|
|
gdbpy_is_color (PyObject *obj)
|
|
{
|
|
gdb_assert (obj != nullptr);
|
|
return PyObject_TypeCheck (obj, &colorpy_object_type) != 0;
|
|
}
|
|
|
|
/* See py-color.h. */
|
|
const ui_file_style::color &
|
|
gdbpy_get_color (PyObject *obj)
|
|
{
|
|
gdb_assert (gdbpy_is_color (obj));
|
|
colorpy_object *self = (colorpy_object *) obj;
|
|
return self->color;
|
|
}
|
|
|
|
/* Get an attribute. */
|
|
static PyObject *
|
|
get_attr (PyObject *obj, PyObject *attr_name)
|
|
{
|
|
if (!PyUnicode_Check (attr_name))
|
|
return PyObject_GenericGetAttr (obj, attr_name);
|
|
|
|
colorpy_object *self = (colorpy_object *) obj;
|
|
const ui_file_style::color &color = self->color;
|
|
|
|
if (!PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
|
|
{
|
|
int value = static_cast<int> (color.colorspace ());
|
|
return gdb_py_object_from_longest (value).release ();
|
|
}
|
|
|
|
if (!PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
|
|
return PyBool_FromLong (color.is_none ());
|
|
|
|
if (!PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
|
|
return PyBool_FromLong (color.is_indexed ());
|
|
|
|
if (!PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
|
|
return PyBool_FromLong (color.is_direct ());
|
|
|
|
if (color.is_indexed ()
|
|
&& !PyUnicode_CompareWithASCIIString (attr_name, "index"))
|
|
return gdb_py_object_from_longest (color.get_value ()).release ();
|
|
|
|
if (color.is_direct ()
|
|
&& !PyUnicode_CompareWithASCIIString (attr_name, "components"))
|
|
{
|
|
uint8_t rgb[3];
|
|
color.get_rgb (rgb);
|
|
|
|
gdbpy_ref<> rgb_objects[3];
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
rgb_objects[i] = gdb_py_object_from_ulongest (rgb[i]);
|
|
if (rgb_objects[i] == nullptr)
|
|
return nullptr;
|
|
}
|
|
|
|
PyObject *comp = PyTuple_New (3);
|
|
if (comp == nullptr)
|
|
return nullptr;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
PyTuple_SET_ITEM (comp, i, rgb_objects[i].release ());
|
|
|
|
return comp;
|
|
}
|
|
|
|
return PyObject_GenericGetAttr (obj, attr_name);
|
|
}
|
|
|
|
/* Implementation of Color.escape_sequence (self, is_fg) -> str. */
|
|
|
|
static PyObject *
|
|
colorpy_escape_sequence (PyObject *self, PyObject *args, PyObject *kwargs)
|
|
{
|
|
static const char *keywords[] = { "is_foreground", nullptr };
|
|
PyObject *is_fg_obj;
|
|
|
|
/* Parse method arguments. */
|
|
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwargs, "O!", keywords,
|
|
&PyBool_Type, &is_fg_obj))
|
|
return nullptr;
|
|
|
|
/* Python ensures the type of SELF. */
|
|
gdb_assert (gdbpy_is_color (self));
|
|
|
|
/* The argument parsing ensures we have a bool. */
|
|
gdb_assert (PyBool_Check (is_fg_obj));
|
|
|
|
std::string s;
|
|
if (term_cli_styling ())
|
|
{
|
|
bool is_fg = is_fg_obj == Py_True;
|
|
s = gdbpy_get_color (self).to_ansi (is_fg);
|
|
}
|
|
|
|
return host_string_to_python_string (s.c_str ()).release ();
|
|
}
|
|
|
|
/* Object initializer; fills color with value.
|
|
|
|
Use: __init__(VALUE = None, COLORSPACE = None)
|
|
|
|
VALUE is a string, integer, RGB-tuple or None.
|
|
|
|
COLORSPACE is the color space index.
|
|
|
|
Returns -1 on error, with a python exception set. */
|
|
|
|
static int
|
|
colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
colorpy_object *obj = (colorpy_object *) self;
|
|
PyObject *value_obj = nullptr;
|
|
PyObject *colorspace_obj = nullptr;
|
|
color_space colorspace = color_space::MONOCHROME;
|
|
|
|
static const char *keywords[] = { "value", "color_space", nullptr };
|
|
|
|
if (!gdb_PyArg_ParseTupleAndKeywords (args, kwds, "|OO", keywords,
|
|
&value_obj, &colorspace_obj))
|
|
return -1;
|
|
|
|
try
|
|
{
|
|
if (colorspace_obj != nullptr)
|
|
{
|
|
if (PyLong_Check (colorspace_obj))
|
|
{
|
|
long colorspace_id = -1;
|
|
if (!gdb_py_int_as_long (colorspace_obj, &colorspace_id))
|
|
return -1;
|
|
if (!color_space_safe_cast (&colorspace, colorspace_id))
|
|
error (_("colorspace %ld is out of range."), colorspace_id);
|
|
}
|
|
else if (colorspace_obj == Py_None)
|
|
colorspace_obj = nullptr;
|
|
else
|
|
error (_("colorspace must be None or integer"));
|
|
}
|
|
|
|
if (value_obj == nullptr || value_obj == Py_None)
|
|
obj->color = ui_file_style::color (colorspace, -1);
|
|
else if (PyLong_Check (value_obj))
|
|
{
|
|
long value = -1;
|
|
if (!gdb_py_int_as_long (value_obj, &value))
|
|
return -1;
|
|
if (value < 0 || value > INT_MAX)
|
|
error (_("value %ld is out of range."), value);
|
|
if (colorspace_obj != nullptr)
|
|
obj->color = ui_file_style::color (colorspace, value);
|
|
else
|
|
obj->color = ui_file_style::color (value);
|
|
}
|
|
else if (PyTuple_Check (value_obj))
|
|
{
|
|
if (colorspace_obj == nullptr || colorspace != color_space::RGB_24BIT)
|
|
error (_("colorspace must be gdb.COLORSPACE_RGB_24BIT with "
|
|
"value of tuple type."));
|
|
Py_ssize_t tuple_size = PyTuple_Size (value_obj);
|
|
if (tuple_size < 0)
|
|
return -1;
|
|
if (tuple_size != 3)
|
|
error (_("Tuple value with RGB must be of size 3."));
|
|
uint8_t rgb[3];
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
PyObject *item = PyTuple_GetItem (value_obj, i);
|
|
if (!PyLong_Check (item))
|
|
error (_("Item %d of an RGB tuple must be integer."), i);
|
|
long item_value = -1;
|
|
if (!gdb_py_int_as_long (item, &item_value))
|
|
return -1;
|
|
if (item_value < 0 || item_value > UINT8_MAX)
|
|
error (_("RGB item %ld is out of byte range."), item_value);
|
|
rgb[i] = static_cast<uint8_t> (item_value);
|
|
}
|
|
|
|
obj->color = ui_file_style::color (rgb[0], rgb[1], rgb[2]);
|
|
}
|
|
else if (PyUnicode_Check (value_obj))
|
|
{
|
|
gdb::unique_xmalloc_ptr<char>
|
|
str (python_string_to_host_string (value_obj));
|
|
if (str == nullptr)
|
|
return -1;
|
|
obj->color = parse_var_color (str.get());
|
|
|
|
if (colorspace_obj != nullptr
|
|
&& colorspace != obj->color.colorspace ())
|
|
error (_("colorspace doesn't match to the value."));
|
|
}
|
|
else
|
|
error (_("value must be one of None, integer, tuple or str."));
|
|
}
|
|
catch (const gdb_exception &except)
|
|
{
|
|
return gdbpy_handle_gdb_exception (-1, except);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PyObject *
|
|
colorpy_str (PyObject *self)
|
|
{
|
|
colorpy_object *obj = reinterpret_cast<colorpy_object *> (self);
|
|
|
|
return PyUnicode_FromString (obj->color.to_string ().c_str ());
|
|
}
|
|
|
|
/* Initialize the 'color' module. */
|
|
static int
|
|
gdbpy_initialize_color (void)
|
|
{
|
|
for (auto &pair : colorspace_constants)
|
|
if (PyModule_AddIntConstant (gdb_module, pair.name,
|
|
static_cast<long> (pair.value)) < 0)
|
|
return -1;
|
|
|
|
colorpy_object_type.tp_new = PyType_GenericNew;
|
|
return gdbpy_type_ready (&colorpy_object_type, gdb_module);
|
|
}
|
|
|
|
/* Color methods. */
|
|
|
|
static PyMethodDef color_methods[] =
|
|
{
|
|
{ "escape_sequence", (PyCFunction) colorpy_escape_sequence,
|
|
METH_VARARGS | METH_KEYWORDS,
|
|
"escape_sequence (is_foreground) -> str.\n\
|
|
Return the ANSI escape sequence for this color.\n\
|
|
IS_FOREGROUND indicates whether this is a foreground or background color."},
|
|
{nullptr}
|
|
};
|
|
|
|
PyTypeObject colorpy_object_type =
|
|
{
|
|
PyVarObject_HEAD_INIT (nullptr, 0)
|
|
"gdb.Color", /*tp_name*/
|
|
sizeof (colorpy_object), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
0, /*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
0, /*tp_getattr*/
|
|
0, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
0, /*tp_repr*/
|
|
0, /*tp_as_number*/
|
|
0, /*tp_as_sequence*/
|
|
0, /*tp_as_mapping*/
|
|
0, /*tp_hash */
|
|
0, /*tp_call*/
|
|
colorpy_str, /*tp_str*/
|
|
get_attr, /*tp_getattro*/
|
|
0, /*tp_setattro*/
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT, /*tp_flags*/
|
|
"GDB color object", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
color_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
colorpy_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
};
|
|
|
|
GDBPY_INITIALIZE_FILE (gdbpy_initialize_color);
|