From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id BfxKNLyeAmO+JisAWB0awg (envelope-from ) for ; Sun, 21 Aug 2022 17:08:12 -0400 Received: by simark.ca (Postfix, from userid 112) id C88511E4A7; Sun, 21 Aug 2022 17:08:12 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,MAILING_LIST_MULTI, RDNS_DYNAMIC autolearn=ham autolearn_force=no version=3.4.6 Received: from sourceware.org (ip-8-43-85-97.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id 77EB31E21F for ; Sun, 21 Aug 2022 17:08:10 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DEA01385803F for ; Sun, 21 Aug 2022 21:08:09 +0000 (GMT) Received: from mail-lj1-f193.google.com (mail-lj1-f193.google.com [209.85.208.193]) by sourceware.org (Postfix) with ESMTPS id 2E98B3858D3C for ; Sun, 21 Aug 2022 21:07:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 2E98B3858D3C Authentication-Results: sourceware.org; dmarc=none (p=none dis=none) header.from=mail.api.win Authentication-Results: sourceware.org; spf=none smtp.mailfrom=api.win Received: by mail-lj1-f193.google.com with SMTP id z20so9019797ljq.3 for ; Sun, 21 Aug 2022 14:07:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc; bh=ui2ubr/Tzu6544ClP2m/1cIITG2LlQuFBQ+e+GDhKZE=; b=HRCC97lWZO//N/Zf0XpnIFPCJm5WUmX7WGKoD49n5TWq43bntRR5IZDg1S7WAtQROq 9wZNxzz5oJojyixtyaLfctXpx6UE4Jr9Mxh+X6llKqwlxx3LiisGw+At0yA1sWNJC41u 5OR4tetFFlpaMTlvA4N+9VrjaioQSVDCauSo7jZ8a81LB5hdfI6PyZgE6qbj6btFfn1w SFQJQfOgx4XvjOR9gdvOvRw2J9r8y5U/mOxEELoEgmf2IjRtJ3lgpNEkAvLazMySFaOz ibB4zPdzIQanKbhVbhDOfnX4jo38yod2RirtCSFbvqQXTgIU3mKADKoJ+RwQ5F3TNmjH 0HAQ== X-Gm-Message-State: ACgBeo0So52vjwdSWkTRPDSORIm1NG3XwP+ddHnxQyHrK4UQbT67XU3t PF9MjtJACNgHfbM1lqA7ubqNytV5WxHdAISf X-Google-Smtp-Source: AA6agR7y2YLqJxMkWzFcsLoLEqf80DZIe4mk1EEGvjjzzcDLfX3Q9uayfT/RhAbIAJ2C+rzH5tj/Uw== X-Received: by 2002:a2e:be89:0:b0:25f:e95a:7c6e with SMTP id a9-20020a2ebe89000000b0025fe95a7c6emr4744789ljr.468.1661116071577; Sun, 21 Aug 2022 14:07:51 -0700 (PDT) Received: from API.corp.tensor.ru ([2a03:1ac0:b0d6:4d64:ff97:eb92:db9c:f225]) by smtp.gmail.com with ESMTPSA id z15-20020a056512370f00b0048af39ff3edsm1621524lfr.122.2022.08.21.14.07.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 21 Aug 2022 14:07:51 -0700 (PDT) From: Andrei Pikas To: gdb-patches@sourceware.org Subject: [PATCH v3 (documentation fixed)] Add an option with a color type. Date: Mon, 22 Aug 2022 00:07:03 +0300 Message-Id: <20220821210703.22790-1-gdb@mail.api.win> X-Mailer: git-send-email 2.34.1 In-Reply-To: <835yindr0c.fsf@gnu.org> References: <835yindr0c.fsf@gnu.org> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Andrei Pikas Errors-To: gdb-patches-bounces+public-inbox=simark.ca@sourceware.org Sender: "Gdb-patches" Colors can be specified as "none" for terminal's default color, as a name of one of the eight standard colors of ISO/IEC 6429 "black", "red", "green", etc., as an RGB hexadecimal tripplet #RRGGBB for 24-bit TrueColor, or as an integer from 0 to 255. Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are used for the so-called bright colors from the aixterm extended 16-color palette. Integers 16-255 are the indexes into xterm extended 256-color palette (usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal dependent and sometimes can be changed with OSC 4 sequences, e.g. "\033]4;1;rgb:00/FF/00\033\\". It is user's responsibility to provide colors which supported by his terminal. --- gdb/NEWS | 13 ++ gdb/cli/cli-cmds.c | 7 + gdb/cli/cli-decode.c | 173 ++++++++++++++++++++++ gdb/cli/cli-decode.h | 21 +++ gdb/cli/cli-option.c | 42 ++++++ gdb/cli/cli-option.h | 21 +++ gdb/cli/cli-setshow.c | 20 +++ gdb/cli/cli-style.c | 49 ++---- gdb/cli/cli-style.h | 4 +- gdb/command.h | 26 +++- gdb/doc/gdb.texinfo | 44 +++++- gdb/doc/guile.texi | 8 + gdb/doc/python.texi | 10 ++ gdb/guile/scm-param.c | 68 ++++++++- gdb/python/py-param.c | 63 +++++++- gdb/python/python.c | 10 ++ gdb/testsuite/gdb.base/style.exp | 15 ++ gdb/testsuite/gdb.guile/scm-parameter.exp | 26 ++++ gdb/testsuite/gdb.python/py-parameter.exp | 46 ++++++ gdb/ui-style.c | 44 ++++++ gdb/ui-style.h | 23 ++- 21 files changed, 676 insertions(+), 57 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index d2efe2a0a58..c9d25890ed9 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -55,6 +55,9 @@ Python Pygments is still used. For supported targets, libopcodes styling is used by default. + "set style" commands now supports numeric format for basic colors + from 0 to 255 and #RRGGBB format for TrueColor. + * New commands maintenance set ignore-prologue-end-flag on|off @@ -170,6 +173,16 @@ GNU/Linux/LoongArch (gdbserver) loongarch*-*-linux* can be used to request a shorter representation of a value, the way that 'set print frame-arguments scalars' does. + ** New constant gdb.PARAM_COLOR represents color type of a + gdb.Parameter.value. Parameter's value is either an integer + from 0 to 255 or string with color name or #RRGGBB hex triplet. + +* Guile API + + ** New constant PARAM_COLOR represents color type of a value + of a object. Parameter's value is either an integer + from 0 to 255 or string with color name or #RRGGBB hex triplet. + * New features in the GDB remote stub, GDBserver ** GDBserver is now supported on LoongArch GNU/Linux. diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index 18fb6e6d869..1a515b99761 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -2275,6 +2275,12 @@ value_from_setting (const setting &var, struct gdbarch *gdbarch) return value_cstring ("", 1, builtin_type (gdbarch)->builtin_char); } + case var_color: + { + std::string s = var.get ().to_string (); + return value_cstring (s.c_str (), s.size (), + builtin_type (gdbarch)->builtin_char); + } default: gdb_assert_not_reached ("bad var_type"); } @@ -2324,6 +2330,7 @@ str_value_from_setting (const setting &var, struct gdbarch *gdbarch) case var_auto_boolean: case var_uinteger: case var_zuinteger: + case var_color: { std::string cmd_val = get_setshow_command_value_string (var); diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c index fde554c7e6c..b0a4ceeedf6 100644 --- a/gdb/cli/cli-decode.c +++ b/gdb/cli/cli-decode.c @@ -24,6 +24,7 @@ #include "cli/cli-cmds.h" #include "cli/cli-decode.h" #include "cli/cli-style.h" +#include "cli/cli-utils.h" #include "gdbsupport/gdb_optional.h" /* Prototypes for local functions. */ @@ -670,6 +671,87 @@ add_setshow_enum_cmd (const char *name, command_class theclass, return cmds; } +/* See cli-decode.h. */ + +void +complete_on_color (completion_tracker &tracker, + const char *text, const char *word) +{ + complete_on_enum (tracker, ui_file_style::basic_color_enums.data (), + text, word); + if (*text == '\0') + { + /* Convenience to let the user know what the option + can accept. Note there's no common prefix between + the strings on purpose, so that complete_on_enum doesn't do + a partial match. */ + tracker.add_completion (make_unique_xstrdup ("NUMBER")); + tracker.add_completion (make_unique_xstrdup ("#RRGGBB")); + } +} + +/* Completer used in color commands. */ + +static void +color_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word) +{ + complete_on_color (tracker, text, word); +} + + +/* Add element named NAME to command list LIST (the list for set or + some sublist thereof). CLASS is as in add_cmd. VAR is address + of the variable which will contain the color. */ + +set_show_commands +add_setshow_color_cmd (const char *name, + enum command_class theclass, + ui_file_style::color *var, + const char *set_doc, + const char *show_doc, + const char *help_doc, + cmd_func_ftype *set_func, + show_value_ftype *show_func, + struct cmd_list_element **set_list, + struct cmd_list_element **show_list) +{ + set_show_commands commands = add_setshow_cmd_full + (name, theclass, var_color, var, + set_doc, show_doc, help_doc, + nullptr, nullptr, set_func, show_func, + set_list, show_list); + + set_cmd_completer (commands.set, color_completer); + + return commands; +} + +/* Same as above but using a getter and a setter function instead of a pointer + to a global storage buffer. */ + +set_show_commands +add_setshow_color_cmd (const char *name, command_class theclass, + const char *set_doc, const char *show_doc, + const char *help_doc, + setting_func_types::set set_func, + setting_func_types::get get_func, + show_value_ftype *show_func, + cmd_list_element **set_list, + cmd_list_element **show_list) +{ + auto cmds = add_setshow_cmd_full + (name, theclass, var_color, nullptr, + set_doc, show_doc, help_doc, + set_func, get_func, nullptr, show_func, + set_list, show_list); + + set_cmd_completer (cmds.set, color_completer); + + return cmds; +} + /* See cli-decode.h. */ const char * const auto_boolean_enums[] = { "on", "off", "auto", NULL }; @@ -2524,3 +2606,94 @@ cli_user_command_p (struct cmd_list_element *cmd) { return cmd->theclass == class_user && cmd->func == do_simple_func; } + +/* See cli-decode.h. */ + +ui_file_style::color +parse_cli_var_color (const char **args) +{ + /* Do a "set" command. ARG is NULL if no argument, or the + text of the argument. */ + + if (args == nullptr || *args == nullptr || **args == '\0') + { + std::string msg; + + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i) + { + msg.append ("\""); + msg.append (ui_file_style::basic_color_enums[i]); + msg.append ("\", "); + } + + error (_("Requires an argument. Valid arguments are %s integer from -1 " + "to 255 or an RGB hex triplet in a format #RRGGBB"), + msg.c_str ()); + } + + const char *p = skip_to_space (*args); + size_t len = p - *args; + + int nmatches = 0; + ui_file_style::basic_color match = ui_file_style::NONE; + for (int i = 0; ui_file_style::basic_color_enums[i]; ++i) + if (strncmp (*args, ui_file_style::basic_color_enums[i], len) == 0) + { + match = static_cast (i - 1); + if (ui_file_style::basic_color_enums[i][len] == '\0') + { + nmatches = 1; + break; /* Exact match. */ + } + else + nmatches++; + } + + if (nmatches == 1) + { + *args += len; + return ui_file_style::color (match); + } + + if (nmatches > 1) + error (_("Ambiguous item \"%.*s\"."), (int) len, *args); + + if (**args != '#') + { + ULONGEST num = get_ulongest (args); + if (num > 255) + error (_("integer %s out of range"), pulongest (num)); + return ui_file_style::color (static_cast (num)); + } + + /* Try to parse #RRGGBB string. */ + if (len != 7) + error_no_arg (_("invalid RGB hex triplet format")); + + uint8_t r, g, b; + int scanned_chars = 0; + int parsed_args = sscanf (*args, "#%2" SCNx8 "%2" SCNx8 "%2" SCNx8 "%n", + &r, &g, &b, &scanned_chars); + + if (parsed_args != 3 || scanned_chars != 7) + error_no_arg (_("invalid RGB hex triplet format")); + + *args += len; + return ui_file_style::color (r, g, b); +} + +/* See cli-decode.h. */ + +ui_file_style::color +parse_var_color (const char *arg) +{ + const char *end_arg = arg; + ui_file_style::color color = parse_cli_var_color (&end_arg); + + int len = end_arg - arg; + const char *after = skip_spaces (end_arg); + if (*after != '\0') + error (_("Junk after item \"%.*s\": %s"), len, arg, after); + + return color; +} diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h index 18db8822af3..7f1233c87ad 100644 --- a/gdb/cli/cli-decode.h +++ b/gdb/cli/cli-decode.h @@ -306,6 +306,27 @@ extern const char * const boolean_enums[]; /* The enums of auto-boolean commands. */ extern const char * const auto_boolean_enums[]; +/* Add the different possible completions of TEXT with color. + + WORD points in the same buffer as TEXT, and completions should be + returned relative to this position. For example, suppose TEXT is "foo" + and we want to complete to "foobar". If WORD is "oo", return + "oobar"; if WORD is "baz/foo", return "baz/foobar". */ + +extern void complete_on_color (completion_tracker &tracker, + const char *text, const char *word); + +/* Parse ARGS, an option to a var_color variable. + * + Either returns the parsed value on success or throws an error. ARGS may be + one of strings {none, black, red, green, yellow, blue, magenta, + cyan, white}, or color number from 0 to 255, or RGB hex triplet #RRGGBB. + */ +extern ui_file_style::color parse_cli_var_color (const char **args); + +/* Same as above but additionally check that there is no junk in the end. */ +extern ui_file_style::color parse_var_color (const char *arg); + /* Verify whether a given cmd_list_element is a user-defined command. Return 1 if it is user-defined. Return 0 otherwise. */ diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c index b1794ad4b17..ac62c6700fa 100644 --- a/gdb/cli/cli-option.c +++ b/gdb/cli/cli-option.c @@ -46,6 +46,9 @@ union option_value /* For var_string options. This is malloc-allocated. */ std::string *string; + + /* For var_color options. */ + ui_file_style::color color = ui_file_style::NONE; }; /* Holds an options definition and its value. */ @@ -424,6 +427,33 @@ parse_option (gdb::array_view options_group, val.enumeration = parse_cli_var_enum (args, match->enums); return option_def_and_value {*match, match_ctx, val}; } + case var_color: + { + if (completion != nullptr) + { + const char *after_arg = skip_to_space (*args); + if (*after_arg == '\0') + { + complete_on_color (completion->tracker, *args, *args); + + if (completion->tracker.have_completions ()) + return {}; + } + } + + if (check_for_argument (args, "--")) + { + /* Treat e.g., "backtrace -entry-values --" as if there + was no argument after "-entry-values". This makes + parse_cli_var_color throw an error with a suggestion of + what are the valid options. */ + args = nullptr; + } + + option_value val; + val.color = parse_cli_var_color (args); + return option_def_and_value {*match, match_ctx, val}; + } case var_string: { if (check_for_argument (args, "--")) @@ -601,6 +631,10 @@ save_option_value_in_ctx (gdb::optional &ov) *ov->option.var_address.enumeration (ov->option, ov->ctx) = ov->value->enumeration; break; + case var_color: + *ov->option.var_address.color (ov->option, ov->ctx) + = ov->value->color; + break; case var_string: *ov->option.var_address.string (ov->option, ov->ctx) = std::move (*ov->value->string); @@ -677,6 +711,14 @@ get_val_type_str (const option_def &opt, std::string &buffer) } return buffer.c_str (); } + case var_color: + { + buffer = ""; + for (size_t i = 0; ui_file_style::basic_color_enums[i]; ++i) + buffer.append (ui_file_style::basic_color_enums[i]).append ("|"); + buffer += "NUMBER|#RRGGBB"; + return buffer.c_str (); + } case var_string: return "STRING"; default: diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h index 26a8da3a5a4..d30c7024324 100644 --- a/gdb/cli/cli-option.h +++ b/gdb/cli/cli-option.h @@ -87,6 +87,7 @@ struct option_def int *(*integer) (const option_def &, void *ctx); const char **(*enumeration) (const option_def &, void *ctx); std::string *(*string) (const option_def &, void *ctx); + ui_file_style::color *(*color) (const option_def &, void *ctx); } var_address; @@ -282,6 +283,26 @@ struct string_option_def : option_def } }; +/* A var_color command line option. */ + +template +struct color_option_def : option_def +{ + color_option_def (const char *long_option_, + ui_file_style::color *(*get_var_address_cb_) (Context *), + show_value_ftype *show_cmd_cb_, + const char *set_doc_, + const char *show_doc_ = nullptr, + const char *help_doc_ = nullptr) + : option_def (long_option_, var_color, + (erased_get_var_address_ftype *) get_var_address_cb_, + show_cmd_cb_, + set_doc_, show_doc_, help_doc_) + { + var_address.color = detail::get_var_address; + } +}; + /* A group of options that all share the same context pointer to pass to the options' get-current-value callbacks. */ struct option_def_group diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c index 139ebaf8323..4e8e5c7f0f8 100644 --- a/gdb/cli/cli-setshow.c +++ b/gdb/cli/cli-setshow.c @@ -454,6 +454,12 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) option_changed = c->var->set (match); } break; + case var_color: + { + ui_file_style::color color = parse_var_color (arg); + option_changed = c->var->set (color); + } + break; case var_zuinteger_unlimited: option_changed = c->var->set (parse_cli_var_zuinteger_unlimited (&arg, true)); @@ -535,6 +541,14 @@ do_set_command (const char *arg, int from_tty, struct cmd_list_element *c) gdb::observers::command_param_changed.notify (name, c->var->get ()); break; + case var_color: + { + const ui_file_style::color &color + = c->var->get (); + gdb::observers::command_param_changed.notify + (name, color.to_string ().c_str ()); + } + break; case var_boolean: { const char *opt = c->var->get () ? "on" : "off"; @@ -602,6 +616,12 @@ get_setshow_command_value_string (const setting &var) stb.puts (value); } break; + case var_color: + { + const ui_file_style::color &value = var.get (); + stb.puts (value.to_string ().c_str ()); + } + break; case var_boolean: stb.puts (var.get () ? "on" : "off"); break; diff --git a/gdb/cli/cli-style.c b/gdb/cli/cli-style.c index abf685561fa..3123a391763 100644 --- a/gdb/cli/cli-style.c +++ b/gdb/cli/cli-style.c @@ -43,20 +43,6 @@ bool source_styling = true; bool disassembler_styling = true; -/* Name of colors; must correspond to ui_file_style::basic_color. */ -static const char * const cli_colors[] = { - "none", - "black", - "red", - "green", - "yellow", - "blue", - "magenta", - "cyan", - "white", - nullptr -}; - /* Names of intensities; must correspond to ui_file_style::intensity. */ static const char * const cli_intensities[] = { @@ -132,8 +118,8 @@ cli_style_option::cli_style_option (const char *name, ui_file_style::intensity intensity) : changed (name), m_name (name), - m_foreground (cli_colors[fg - ui_file_style::NONE]), - m_background (cli_colors[0]), + m_foreground (fg), + m_background (ui_file_style::NONE), m_intensity (cli_intensities[intensity]) { } @@ -144,32 +130,17 @@ cli_style_option::cli_style_option (const char *name, ui_file_style::intensity i) : changed (name), m_name (name), - m_foreground (cli_colors[0]), - m_background (cli_colors[0]), + m_foreground (ui_file_style::NONE), + m_background (ui_file_style::NONE), m_intensity (cli_intensities[i]) { } -/* Return the color number corresponding to COLOR. */ - -static int -color_number (const char *color) -{ - for (int i = 0; i < ARRAY_SIZE (cli_colors); ++i) - { - if (color == cli_colors[i]) - return i - 1; - } - gdb_assert_not_reached ("color not found"); -} - /* See cli-style.h. */ ui_file_style cli_style_option::style () const { - int fg = color_number (m_foreground); - int bg = color_number (m_background); ui_file_style::intensity intensity = ui_file_style::NORMAL; for (int i = 0; i < ARRAY_SIZE (cli_intensities); ++i) @@ -181,7 +152,7 @@ cli_style_option::style () const } } - return ui_file_style (fg, bg, intensity); + return ui_file_style (m_foreground, m_background, intensity); } /* See cli-style.h. */ @@ -254,9 +225,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass, set_show_commands commands; - commands = add_setshow_enum_cmd - ("foreground", theclass, cli_colors, - &m_foreground, + commands = add_setshow_color_cmd + ("foreground", theclass, &m_foreground, _("Set the foreground color for this property."), _("Show the foreground color for this property."), nullptr, @@ -266,9 +236,8 @@ cli_style_option::add_setshow_commands (enum command_class theclass, commands.set->set_context (this); commands.show->set_context (this); - commands = add_setshow_enum_cmd - ("background", theclass, cli_colors, - &m_background, + commands = add_setshow_color_cmd + ("background", theclass, &m_background, _("Set the background color for this property."), _("Show the background color for this property."), nullptr, diff --git a/gdb/cli/cli-style.h b/gdb/cli/cli-style.h index 4090cf01333..4db86b00359 100644 --- a/gdb/cli/cli-style.h +++ b/gdb/cli/cli-style.h @@ -67,9 +67,9 @@ class cli_style_option const char *m_name; /* The foreground. */ - const char *m_foreground; + ui_file_style::color m_foreground; /* The background. */ - const char *m_background; + ui_file_style::color m_background; /* The intensity. */ const char *m_intensity; diff --git a/gdb/command.h b/gdb/command.h index d901da3c8cb..eb3b2cc20d6 100644 --- a/gdb/command.h +++ b/gdb/command.h @@ -119,7 +119,9 @@ enum var_types /* Enumerated type. Can only have one of the specified values. *VAR is a char pointer to the name of the element that we find. */ - var_enum + var_enum, + /* Color type. *VAR is a ui_file_style::color structure. */ + var_color }; /* Return true if a setting of type VAR_TYPE is backed with type T. @@ -179,6 +181,14 @@ inline bool var_type_uses (var_types t) return t == var_enum; } +/* Return true if a setting of type T is backed by an ui_file_style::color + variable. */ +template<> +inline bool var_type_uses (var_types t) +{ + return t == var_color; +} + template struct setting_func_types_1; template @@ -665,6 +675,20 @@ extern set_show_commands add_setshow_enum_cmd setting_func_types::get get_func, show_value_ftype *show_func, cmd_list_element **set_list, cmd_list_element **show_list); +extern set_show_commands add_setshow_color_cmd + (const char *name, command_class theclass, ui_file_style::color *var, + const char *set_doc, const char *show_doc, const char *help_doc, + cmd_func_ftype *set_func, show_value_ftype *show_func, + cmd_list_element **set_list, cmd_list_element **show_list); + +extern set_show_commands add_setshow_color_cmd + (const char *name, command_class theclass, + const char *set_doc, const char *show_doc, const char *help_doc, + setting_func_types::set set_func, + setting_func_types::get get_func, + show_value_ftype *show_func, cmd_list_element **set_list, + cmd_list_element **show_list); + extern set_show_commands add_setshow_auto_boolean_cmd (const char *name, command_class theclass, auto_boolean *var, const char *set_doc, const char *show_doc, const char *help_doc, diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 382df00ee7d..df007c63752 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -26605,16 +26605,44 @@ For example, the style of file names can be controlled using the @table @code @item set style filename background @var{color} -Set the background to @var{color}. Valid colors are @samp{none} -(meaning the terminal's default color), @samp{black}, @samp{red}, -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, -and@samp{white}. +Set the background to @var{color}. @var{color} can be @samp{none} +(meaning the terminal's default color), a name of one of the eight standard +colors of ISO/IEC 6429, index from 0 to 255 into terminal's color +palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for +24-bit TrueColor. + +Valid color names are @samp{black}, @samp{red}, @samp{green}, +@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and +@samp{white}. + +Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are +used for the so-called bright colors from the aixterm extended 16-color +palette. Integers 16-255 are the indexes into xterm extended 256-color palette +(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal +dependent and sometimes can be changed with OSC 4 sequences, e.g. +"\033]4;1;rgb:00/FF/00\033\\". + +It is user's responsibility to provide colors supported by its terminal. @item set style filename foreground @var{color} -Set the foreground to @var{color}. Valid colors are @samp{none} -(meaning the terminal's default color), @samp{black}, @samp{red}, -@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, -and@samp{white}. +Set the foreground to @var{color}. @var{color} can be @samp{none} +(meaning the terminal's default color), a name of one of the eight standard +colors of ISO/IEC 6429, index from 0 to 255 into terminal's color +palette or a hexadecimal RGB triplet in @samp{#RRGGBB} format for +24-bit TrueColor. + +Valid color names are @samp{black}, @samp{red}, @samp{green}, +@samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, and +@samp{white}. + +Integers 0 to 7 are the synonyms for the standard colors. Integers 8-15 are +used for the so-called bright colors from the aixterm extended 16-color +palette. Integers 16-255 are the indexes into xterm extended 256-color palette +(usually 6x6x6 cube plus gray ramp). In general, 256-color palette is terminal +dependent and sometimes can be changed with OSC 4 sequences, e.g. +"\033]4;1;rgb:00/FF/00\033\\". + +It is user's responsibility to provide colors supported by its terminal. @item set style filename intensity @var{value} Set the intensity to @var{value}. Valid intensities are @samp{normal} diff --git a/gdb/doc/guile.texi b/gdb/doc/guile.texi index 63916eed181..395cad8891a 100644 --- a/gdb/doc/guile.texi +++ b/gdb/doc/guile.texi @@ -2169,6 +2169,14 @@ The value is a filename. This is just like @item PARAM_ENUM The value is a string, which must be one of a collection of string constants provided when the parameter is created. + +@item PARAM_COLOR +The value is either a string or an unsigned integer. Integer from 0 to 255 +means index into terminal's color palette. String can be a hex RGB triplet in +@samp{#RRGGBB} format or one of the following color names: +@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red}, +@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, +or @samp{white}. @end vtable @node Progspaces In Guile diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index eeb847aeaa8..5f7864f3f2a 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -4651,6 +4651,16 @@ except the special value -1 should be interpreted to mean @item gdb.PARAM_ENUM The value is a string, which must be one of a collection string constants provided when the parameter is created. + +@findex PARAM_COLOR +@findex gdb.PARAM_COLOR +@item gdb.PARAM_COLOR +The value is either a string or an unsigned integer. Integer from 0 to 255 +means index into terminal's color palette. String can be a hex RGB triplet in +@samp{#RRGGBB} format or one of the following color names: +@samp{none} (meaning the terminal's default color), @samp{black}, @samp{red}, +@samp{green}, @samp{yellow}, @samp{blue}, @samp{magenta}, @samp{cyan}, +or @samp{white}. @end table @node Functions In Python diff --git a/gdb/guile/scm-param.c b/gdb/guile/scm-param.c index 54c8c27301a..42820dd666d 100644 --- a/gdb/guile/scm-param.c +++ b/gdb/guile/scm-param.c @@ -48,6 +48,9 @@ union pascm_variable /* Hold a string, for enums. */ const char *cstringval; + + /* Hold a color. */ + ui_file_style::color color; }; /* A GDB parameter. @@ -129,6 +132,8 @@ make_setting (param_smob *s) return setting (s->type, s->value.stringval); else if (var_type_uses (s->type)) return setting (s->type, &s->value.cstringval); + else if (var_type_uses (s->type)) + return setting (s->type, &s->value.color); else gdb_assert_not_reached ("unhandled var type"); } @@ -190,10 +195,9 @@ static SCM pascm_make_param_smob (void) { param_smob *p_smob = (param_smob *) - scm_gc_malloc (sizeof (param_smob), param_smob_name); + scm_gc_calloc (sizeof (param_smob), param_smob_name); SCM p_scm; - memset (p_smob, 0, sizeof (*p_smob)); p_smob->cmd_class = no_class; p_smob->type = var_boolean; /* ARI: var_boolean */ p_smob->set_func = SCM_BOOL_F; @@ -466,6 +470,13 @@ add_setshow_generic (enum var_types param_type, enum command_class cmd_class, set_list, show_list); break; + case var_color: + commands = add_setshow_color_cmd (cmd_name, cmd_class, &self->value.color, + set_doc, show_doc, help_doc, + set_func, show_func, + set_list, show_list); + break; + default: gdb_assert_not_reached ("bad param_type value"); } @@ -545,6 +556,7 @@ static const scheme_integer_constant parameter_types[] = { "PARAM_OPTIONAL_FILENAME", var_optional_filename }, { "PARAM_FILENAME", var_filename }, { "PARAM_ENUM", var_enum }, + { "PARAM_COLOR", var_color }, END_INTEGER_CONSTANTS }; @@ -611,6 +623,18 @@ pascm_param_value (const setting &var, int arg_pos, const char *func_name) return gdbscm_scm_from_host_string (str, strlen (str)); } + case var_color: + { + const ui_file_style::color &color = var.get (); + if (color.is_none () || color.is_basic () || !color.is_simple ()) + { + std::string s = color.to_string (); + return gdbscm_scm_from_host_string (s.c_str (), s.size ()); + } + else + return scm_from_int (color.get_value ()); + } + case var_boolean: { if (var.get ()) @@ -716,6 +740,44 @@ pascm_set_param_value_x (param_smob *p_smob, break; } + case var_color: + SCM_ASSERT_TYPE (scm_is_string (value) || scm_is_integer (value), + value, arg_pos, func_name, + _("string or integer")); + + if (scm_is_integer (value)) + { + int i = scm_to_int (value); + if (i < 0 || i > 255) + gdbscm_out_of_range_error (func_name, arg_pos, value, + _("must be in range from 0 to 255")); + var.set (i); + } + else + { + SCM exception; + + gdb::unique_xmalloc_ptr string + = gdbscm_scm_to_host_string (value, nullptr, &exception); + if (string == nullptr) + gdbscm_throw (exception); + + gdbscm_gdb_exception exc {}; + try + { + ui_file_style::color color = parse_var_color (string.get ()); + var.set (color); + } + catch (const gdb_exception &except) + { + exc = unpack (except); + } + + GDBSCM_HANDLE_GDB_EXCEPTION (exc); + } + + break; + case var_boolean: SCM_ASSERT_TYPE (gdbscm_is_bool (value), value, arg_pos, func_name, _("boolean")); @@ -961,6 +1023,8 @@ gdbscm_make_parameter (SCM name_scm, SCM rest) scm_set_smob_free (parameter_smob_tag, pascm_free_parameter_smob); if (var_type_uses (p_smob->type)) p_smob->value.stringval = new std::string; + else if (var_type_uses (p_smob->type)) + p_smob->value.color = ui_file_style::NONE; if (initial_value_arg_pos > 0) { diff --git a/gdb/python/py-param.c b/gdb/python/py-param.c index 5d509ba4658..a2d720490c1 100644 --- a/gdb/python/py-param.c +++ b/gdb/python/py-param.c @@ -46,6 +46,7 @@ static struct { { "PARAM_ZUINTEGER", var_zuinteger }, { "PARAM_ZUINTEGER_UNLIMITED", var_zuinteger_unlimited }, { "PARAM_ENUM", var_enum }, + { "PARAM_COLOR", var_color }, { NULL, 0 } }; @@ -70,6 +71,9 @@ union parmpy_variable /* Hold a string, for enums. */ const char *cstringval; + + /* Hold a color. */ + ui_file_style::color color; }; /* A GDB parameter. */ @@ -108,6 +112,8 @@ make_setting (parmpy_object *s) return setting (s->type, s->value.stringval); else if (var_type_uses (s->type)) return setting (s->type, &s->value.cstringval); + else if (var_type_uses (s->type)) + return setting (s->type, &s->value.color); else gdb_assert_not_reached ("unhandled var type"); } @@ -199,6 +205,49 @@ set_parameter_value (parmpy_object *self, PyObject *value) break; } + case var_color: + { + if (gdbpy_is_string (value)) + { + gdb::unique_xmalloc_ptr + str (python_string_to_host_string (value)); + if (str == NULL) + return -1; + try + { + self->value.color = parse_var_color (str.get()); + } + catch (const gdb_exception &except) + { + gdbpy_convert_exception (except); + return -1; + } + } + else if (PyLong_Check (value)) + { + long l; + if (! gdb_py_int_as_long (value, &l)) + return -1; + if (l < 0 || l > 255) + { + PyErr_SetString (PyExc_RuntimeError, + _("Range exceeded.")); + return -1; + } + self->value.color = ui_file_style::color (l); + } + else if (value == Py_None) + self->value.color = ui_file_style::NONE; + else + { + PyErr_SetString (PyExc_RuntimeError, + _("color arguments must be a string, an integer " + "or None.")); + return -1; + } + } + break; + case var_boolean: if (! PyBool_Check (value)) { @@ -637,6 +686,15 @@ add_setshow_generic (int parmclass, enum command_class cmdclass, get_show_value, set_list, show_list); break; + case var_color: + /* Initialize the value, just in case. */ + self->value.color = ui_file_style::NONE; + commands = add_setshow_color_cmd (cmd_name.get (), cmdclass, + &self->value.color, set_doc, + show_doc, help_doc, get_set_value, + get_show_value, set_list, show_list); + break; + default: gdb_assert_not_reached ("Unhandled parameter class."); } @@ -758,7 +816,8 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds) && parmclass != var_string && parmclass != var_string_noescape && parmclass != var_optional_filename && parmclass != var_filename && parmclass != var_zinteger && parmclass != var_zuinteger - && parmclass != var_zuinteger_unlimited && parmclass != var_enum) + && parmclass != var_zuinteger_unlimited && parmclass != var_enum + && parmclass != var_color) { PyErr_SetString (PyExc_RuntimeError, _("Invalid parameter class argument.")); @@ -779,7 +838,7 @@ parmpy_init (PyObject *self, PyObject *args, PyObject *kwds) else obj->enumeration = NULL; obj->type = (enum var_types) parmclass; - memset (&obj->value, 0, sizeof (obj->value)); + obj->value = {}; /* zeros initialization */ if (var_type_uses (obj->type)) obj->value.stringval = new std::string; diff --git a/gdb/python/python.c b/gdb/python/python.c index c7d4157b70c..141d495c2e2 100644 --- a/gdb/python/python.c +++ b/gdb/python/python.c @@ -484,6 +484,16 @@ gdbpy_parameter_value (const setting &var) return host_string_to_python_string (str).release (); } + case var_color: + { + const ui_file_style::color &color = var.get (); + if (color.is_none () || color.is_basic () || !color.is_simple ()) + return host_string_to_python_string + (color.to_string ().c_str ()).release (); + else + return gdb_py_object_from_longest (color.get_value ()).release (); + } + case var_boolean: { if (var.get ()) diff --git a/gdb/testsuite/gdb.base/style.exp b/gdb/testsuite/gdb.base/style.exp index 2242c5bf743..82458193d8d 100644 --- a/gdb/testsuite/gdb.base/style.exp +++ b/gdb/testsuite/gdb.base/style.exp @@ -298,6 +298,21 @@ proc run_style_tests { } { set url [limited_style "http:.*html" file] gdb_test "show version" "${vers}.*<$url>.*" \ "'show version' is styled" + + if { $currently_disabled_style != "version" } { + # Check that colors in styling can be set as integer and as RGB hex + # triplet. Check that the version string is styled in the output of + # 'show version' according to the set colors. + gdb_test_no_output "set style version intensity normal" + 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;255m.*\".*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;255m.*\".*version.*\".*style.*\033\\\[m foreground color is: #FED210" \ + "Version's TrueColor foreground style" + } } } diff --git a/gdb/testsuite/gdb.guile/scm-parameter.exp b/gdb/testsuite/gdb.guile/scm-parameter.exp index cf6f2834373..8a9cbcab605 100644 --- a/gdb/testsuite/gdb.guile/scm-parameter.exp +++ b/gdb/testsuite/gdb.guile/scm-parameter.exp @@ -91,6 +91,32 @@ with_test_prefix "test-enum-param" { gdb_test "set print test-enum-param three" "Undefined item: \"three\".*" "set invalid enum parameter" } +# Test a color parameter. + +gdb_test_multiline "color gdb parameter" \ + "guile" "" \ + "(define test-color-param" "" \ + " (make-parameter \"print test-color-param\"" "" \ + " #:command-class COMMAND_DATA" "" \ + " #:parameter-type PARAM_COLOR" "" \ + " #:doc \"When set, test param does something useful. When disabled, does nothing.\"" "" \ + " #:show-doc \"Show the state of the test-color-param.\"" "" \ + " #:set-doc \"Set the state of the test-color-param.\"" "" \ + " #:show-func (lambda (self value)" "" \ + " (format #f \"The state of the test-color-param is ~a.\" value))" "" \ + " #:initial-value \"green\"))" "" \ + "(register-parameter! test-color-param)" "" \ + "end" + +with_test_prefix "test-color-param" { + gdb_test "guile (print (parameter-value test-color-param))" "green" "color parameter value (green)" + gdb_test "show print test-color-param" "The state of the test-color-param is green." "show initial value" + gdb_test_no_output "set print test-color-param 255" + gdb_test "show print test-color-param" "The state of the test-color-param is 255." "show new value" + gdb_test "guile (print (parameter-value test-color-param))" "255" "color parameter value (255)" + gdb_test "set print test-color-param 256" "integer 256 out of range.*" "set invalid color parameter" +} + # Test a file parameter. gdb_test_multiline "file gdb parameter" \ diff --git a/gdb/testsuite/gdb.python/py-parameter.exp b/gdb/testsuite/gdb.python/py-parameter.exp index d6db6ac3bb1..0f269dd86a8 100644 --- a/gdb/testsuite/gdb.python/py-parameter.exp +++ b/gdb/testsuite/gdb.python/py-parameter.exp @@ -177,6 +177,51 @@ proc_with_prefix test_enum_parameter { } { "Undefined item: \"three\".*" "set invalid enum parameter" } +# Test an color parameter. +proc_with_prefix test_color_parameter { } { + clean_restart + + gdb_test_multiline "color gdb parameter" \ + "python" "" \ + "class TestColorParam (gdb.Parameter):" "" \ + " \"\"\"When set, test param does something useful. When disabled, does nothing.\"\"\"" "" \ + " show_doc = \"Show the state of the color\"" ""\ + " set_doc = \"Set the state of the color\"" "" \ + " def get_show_string (self, pvalue):" ""\ + " return \"The state of the color is \" + str(pvalue)" ""\ + " def get_set_string (self):" ""\ + " return \"The state of the color has been set to \" + str(self.value)" ""\ + " def __init__ (self, name):" "" \ + " super (TestColorParam, self).__init__ (name, gdb.COMMAND_DATA, gdb.PARAM_COLOR)" "" \ + " self.value = \"green\"" "" \ + "test_color_param = TestColorParam ('print test-color-param')" ""\ + "end" + + gdb_test "python print (test_color_param.value)" "green" \ + "test color parameter value is green" + gdb_test "show print test-color-param" \ + "The state of the color is green.*" \ + "show parameter is initial value" + gdb_test "set print test-color-param 255" \ + "The state of the color has been set to 255" "set color to 255" + gdb_test "show print test-color-param" \ + "The state of the color is 255.*" "show parameter is new value" + gdb_test "python print (test_color_param.value)" "255" \ + "test color parameter value is 255" + gdb_test_no_output "python test_color_param.value = 254" \ + "assign test_color_param.value to 254" + gdb_test "python print (repr (test_color_param.value))" "254" \ + "test color parameter value is integer" + gdb_test_no_output "python test_color_param.value = '#FED210'" \ + "assign test_color_param.value to #FED210" + gdb_test "python print (repr (test_color_param.value))" "'#FED210'" \ + "test color parameter value is string" + gdb_test "set print test-color-param 256" \ + "integer 256 out of range.*" "set invalid color parameter" + gdb_test "python test_color_param.value = 256" \ + "RuntimeError: Range exceeded.*" "set invalid color value" +} + # Test a file parameter. proc_with_prefix test_file_parameter { } { clean_restart @@ -391,6 +436,7 @@ test_directories test_data_directory test_boolean_parameter test_enum_parameter +test_color_parameter test_file_parameter test_undocumented_parameter test_really_undocumented_parameter diff --git a/gdb/ui-style.c b/gdb/ui-style.c index f1a5b8c4101..167550b2c9a 100644 --- a/gdb/ui-style.c +++ b/gdb/ui-style.c @@ -62,6 +62,33 @@ static const uint8_t bright_colors[][3] = { { 255, 255, 255 } /* White. */ }; +/* See ui-style.h. */ +/* Must correspond to ui_file_style::basic_color. */ +const std::vector ui_file_style::basic_color_enums = { + "none", + "black", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white", + nullptr +}; + +/* Returns text representation of a basic COLOR. */ + +static const char * +basic_color_name (int color) +{ + int pos = color - ui_file_style::NONE; + if (0 <= pos && pos < ui_file_style::basic_color_enums.size ()) + if (const char *s = ui_file_style::basic_color_enums[pos]) + return s; + error (_("Basic color %d has no name."), color); +} + /* See ui-style.h. */ bool @@ -93,6 +120,23 @@ ui_file_style::color::append_ansi (bool is_fg, std::string *str) const /* See ui-style.h. */ +std::string +ui_file_style::color::to_string () const +{ + if (!m_simple) + { + char s[64]; + snprintf (s, sizeof s, "#%02X%02X%02X", m_red, m_green, m_blue); + return s; + } + else if (is_none () || is_basic ()) + return basic_color_name (m_value); + else + return std::to_string (get_value ()); +} + +/* See ui-style.h. */ + void ui_file_style::color::get_rgb (uint8_t *rgb) const { diff --git a/gdb/ui-style.h b/gdb/ui-style.h index fe1b2af611d..f002facf706 100644 --- a/gdb/ui-style.h +++ b/gdb/ui-style.h @@ -73,6 +73,11 @@ struct ui_file_style && m_blue == other.m_blue); } + bool operator!= (const color &other) const + { + return ! (*this == other); + } + bool operator< (const color &other) const { if (m_simple != other.m_simple) @@ -104,10 +109,17 @@ struct ui_file_style return m_simple && m_value >= BLACK && m_value <= WHITE; } - /* Return the value of a basic color. */ + /* Return true if this is one of the simple colors, false + otherwise. */ + bool is_simple () const + { + return m_simple; + } + + /* Return the value of a simple color. */ int get_value () const { - gdb_assert (is_basic ()); + gdb_assert (is_simple ()); return m_value; } @@ -123,6 +135,10 @@ struct ui_file_style color). */ bool append_ansi (bool is_fg, std::string *str) const; + /* Returns text representation of this object. + It is "none", name of a basic color, number or a #RRGGBB hex triplet. */ + std::string to_string () const; + private: bool m_simple; @@ -235,6 +251,9 @@ struct ui_file_style return this; } + /* nullptr-terminated list of names corresponding to enum basic_color. */ + static const std::vector basic_color_enums; + private: color m_foreground = NONE; -- 2.34.1