]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/python/guile: user created prefix commands get help list
authorAndrew Burgess <aburgess@redhat.com>
Sat, 12 Apr 2025 08:15:53 +0000 (09:15 +0100)
committerAndrew Burgess <aburgess@redhat.com>
Tue, 3 Jun 2025 14:27:15 +0000 (15:27 +0100)
Consider GDB's builtin prefix set/show prefix sub-commands, if they
are invoked with no sub-command name then they work like this:

  (gdb) show print
  print address:  Printing of addresses is on.
  print array:  Pretty formatting of arrays is off.
  print array-indexes:  Printing of array indexes is off.
  print asm-demangle:  Demangling of C++/ObjC names in disassembly listings is off.
  ... cut lots of lines ...
  (gdb) set print
  List of set print subcommands:

  set print address -- Set printing of addresses.
  set print array -- Set pretty formatting of arrays.
  set print array-indexes -- Set printing of array indexes.
  set print asm-demangle -- Set demangling of C++/ObjC names in disassembly listings.
  ... cut lots of lines ...

  Type "help set print" followed by set print subcommand name for full documentation.
  Type "apropos word" to search for commands related to "word".
  Type "apropos -v word" for full documentation of commands related to "word".
  Command name abbreviations are allowed if unambiguous.
  (gdb)

That is 'show print' lists the values of all settings under the
'print' prefix, and 'set print' lists the help text for all settings
under the 'set print' prefix.

Now, if we try to create something similar using the Python API:

  (gdb) python gdb.ParameterPrefix("my-prefix", gdb.COMMAND_NONE)
  (gdb) python gdb.Parameter("my-prefix foo", gdb.COMMAND_OBSCURE, gdb.PARAM_BOOLEAN)
  (gdb) show my-prefix
  (gdb) set my-prefix

Neither 'show my-prefix' or 'set my-prefix' gives us the same details
relating to the sub-commands that we get with the builtin prefix
commands.

This commit aims to address this.

Currently, in cmdpy_init, when a new command is created, we always set
the commands callback function to cmdpy_function.  It is within
cmdpy_function that we spot that the command is a prefix command, and
that there is no gdb.Command.invoke method, and so return early.

This commit changes things so that the rules are now:

  1. For NON prefix commands, we continue to use cmdpy_function.
  2. For prefix commands that do have a gdb.Command.invoke
     method (i.e. can handle unknown sub-commands), continue to use
     cmdpy_function.
  3. For all other prefix commands, don't use cmdpy_function, instead
     use GDB's normal callback function for set/show prefixes.

This requires splitting the current call to add_prefix_cmd into either
a call to add_prefix_cmd, add_show_prefix_cmd, or
add_basic_prefix_cmd, as appropriate.

After these changes, we now see this:

  (gdb) python gdb.ParameterPrefix("my-prefix", gdb.COMMAND_NONE)     │
  (gdb) python gdb.Parameter("my-prefix foo", gdb.COMMAND_OBSCURE, gdb.PARAM_BOOLEAN)
  (gdb) show my-prefix                                                │
  my-prefix foo:  The current value of 'my-prefix foo' is "off".
  (gdb) set my-prefix
  List of "set my-prefix" subcommands:

  set my-prefix foo -- Set the current value of 'my-prefix foo'.

  Type "help set my-prefix" followed by subcommand name for full documentation.
  Type "apropos word" to search for commands related to "word".
  Type "apropos -v word" for full documentation of commands related to "word".
  Command name abbreviations are allowed if unambiguous.
  (gdb)

Which matches how a prefix defined within GDB would act.

I have made the same changes to the Guile API.

gdb/NEWS
gdb/doc/guile.texi
gdb/doc/python.texi
gdb/guile/scm-cmd.c
gdb/python/py-cmd.c
gdb/testsuite/gdb.guile/scm-parameter.exp
gdb/testsuite/gdb.python/py-cmd.exp
gdb/testsuite/gdb.python/py-parameter-prefix.exp
gdb/testsuite/gdb.python/py-parameter.exp

index 970a527a64cde36ccfbd66bd3fa9f853ae911421..24d00892564686eccf872d89f510c3c0e4929704 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -150,6 +150,12 @@ info threads [-gid] [-stopped] [-running] [ID]...
      and 'show' gdb.Command prefixes, suitable for use with new
      gdb.Parameters.
 
+  ** Prefix commands (gdb.Command sub-classes) that don't have an
+     invoke method will now behave like builtin prefix commands when
+     invoked without a sub-command name.  This means printing the help
+     text for all sub-commands, unless the prefix command is a 'show'
+     command, in which case the value of all sub-commands is printed.
+
 * Guile API
 
   ** New type <gdb:color> for dealing with colors.
@@ -162,6 +168,12 @@ info threads [-gid] [-stopped] [-running] [ID]...
      empty string for make-parameter means GDB will only display the
      #:set_doc or #:show_doc strings in the set/show help output.
 
+  ** Prefix commands (using make-command) that don't have a #:invoke
+     property will now behave like builtin prefix commands when
+     invoked without a sub-command name.  This means printing the help
+     text for all sub-commands, unless the prefix command is a 'show'
+     command, in which case the value of all sub-commands is printed.
+
 * New remote packets
 
 binary-upload in qSupported reply
index 7b3f0a9757b7d19c2e580d53dec4702030cd31b4..96772296b03eba523dfbaa258119b247ffbb91b5 100644 (file)
@@ -1772,6 +1772,16 @@ invoking it interactively.  If this function throws an exception,
 it is turned into a @value{GDBN} @code{error} call.
 Otherwise, the return value is ignored.
 
+For non-prefix commands, @var{invoke} is required.  For prefix
+commands @var{invoke} is optional.  Only prefix commands that need to
+handle unknown sub-commands should supply @var{invoke}.
+
+For prefix commands that don't supply @var{invoke}, if the prefix
+command is used without a sub-command name then @value{GDBN} will
+display the help text for every sub-command, unless the prefix command
+is a @kbd{show} sub-command, in which case @value{GDBN} will list the
+values of all sub-commands.
+
 The argument @var{command-class} is one of the @samp{COMMAND_} constants
 defined below.  This argument tells @value{GDBN} how to categorize the
 new command in the help system.  The default is @code{COMMAND_NONE}.
index 7f801ab3f23810544658a83daf7a79a993c227eb..8f1da6014767e46e385c8e08f4eac5ec8ea3a175 100644 (file)
@@ -4525,6 +4525,7 @@ You can implement new @value{GDBN} CLI commands in Python.  A CLI
 command is implemented using an instance of the @code{gdb.Command}
 class, most commonly using a subclass.
 
+@anchor{Command.__init__}
 @defun Command.__init__ (name, command_class @r{[}, completer_class @r{[}, prefix@r{]]})
 The object initializer for @code{Command} registers the new command
 with @value{GDBN}.  This initializer is normally invoked from the
@@ -4583,6 +4584,17 @@ that the command came from elsewhere.
 If this method throws an exception, it is turned into a @value{GDBN}
 @code{error} call.  Otherwise, the return value is ignored.
 
+For non-prefix commands (@pxref{Command.__init__}), the @code{invoke}
+method is required.  For prefix commands the @code{invoke} method is
+optional.  Only prefix commands that need to handle unknown
+sub-commands should implement the @code{invoke} method.
+
+For prefix commands that don't implement @code{invoke}, if the prefix
+command is used without a sub-command name then @value{GDBN} will
+display the help text for every sub-command, unless the prefix command
+is a @kbd{show} sub-command, in which case @value{GDBN} will list the
+values of all sub-commands.
+
 @findex gdb.string_to_argv
 To break @var{argument} up into an argv-like string use
 @code{gdb.string_to_argv}.  This function behaves identically to
index 453394ccb19fe00cfb2ebf4f719b980a087e9b5f..565d58890b82382756f393bb73130a4dc61199d6 100644 (file)
@@ -753,18 +753,47 @@ gdbscm_register_command_x (SCM self)
     {
       if (c_smob->is_prefix)
        {
-         /* If we have our own "invoke" method, then allow unknown
-            sub-commands.  */
-         int allow_unknown = gdbscm_is_true (c_smob->invoke);
+         bool has_invoke = gdbscm_is_true (c_smob->invoke) == 1;
 
-         cmd = add_prefix_cmd (c_smob->cmd_name, c_smob->cmd_class,
-                               NULL, c_smob->doc, &c_smob->sub_list,
-                               allow_unknown, cmd_list);
+         if (has_invoke)
+           {
+             cmd = add_prefix_cmd (c_smob->cmd_name, c_smob->cmd_class,
+                                   NULL, c_smob->doc, &c_smob->sub_list,
+                                   1 /* allow_unknown */, cmd_list);
+             cmd->func = cmdscm_function;
+           }
+         else
+           {
+             /* If there is no 'invoke' method, then create the prefix
+                using the standard prefix callbacks.  This means that for
+                'set prefix' the user will get the help text listing all
+                of the sub-commands, and for 'show prefix', the user will
+                see all of the sub-command values.  */
+             cmd_list_element *first = *cmd_list;
+             while (first->prefix != nullptr)
+               first = first->prefix;
+
+             bool is_show = first->subcommands == &showlist;
+
+             if (is_show)
+               cmd = add_show_prefix_cmd (c_smob->cmd_name,
+                                          c_smob->cmd_class,
+                                          c_smob->doc,
+                                          &c_smob->sub_list,
+                                          0 /* allow_unknown */, cmd_list);
+             else
+               cmd = add_basic_prefix_cmd (c_smob->cmd_name,
+                                           c_smob->cmd_class,
+                                           c_smob->doc,
+                                           &c_smob->sub_list,
+                                           0 /* allow_unknown */, cmd_list);
+           }
        }
       else
        {
          cmd = add_cmd (c_smob->cmd_name, c_smob->cmd_class,
                         c_smob->doc, cmd_list);
+         cmd->func = cmdscm_function;
        }
     }
   catch (const gdb_exception &except)
@@ -777,7 +806,6 @@ gdbscm_register_command_x (SCM self)
      So no more errors after this point.  */
 
   /* There appears to be no API to set this.  */
-  cmd->func = cmdscm_function;
   cmd->destroyer = cmdscm_destroyer;
 
   c_smob->command = cmd;
index c53138a525f8c4194615c882c7fd9731cce71548..5b4f8138aeade55366f7a1b1391b6f932dc16452 100644 (file)
@@ -105,19 +105,17 @@ cmdpy_function (const char *args, int from_tty, cmd_list_element *command)
 
   gdbpy_enter enter_py;
 
-  if (! obj)
+  if (obj == nullptr)
     error (_("Invalid invocation of Python command object."));
-  if (! PyObject_HasAttr ((PyObject *) obj, invoke_cst))
-    {
-      if (obj->command->is_prefix ())
-       {
-         /* A prefix command does not need an invoke method.  */
-         return;
-       }
-      error (_("Python command object missing 'invoke' method."));
-    }
 
-  if (! args)
+  /* If we get here for a prefix command then the prefix command had an
+     'invoke' method when it was created.  If the 'invoke' method is now
+     missing, then the user has done something weird (like deleting the
+     invoke method, yuck!).  */
+  if (!PyObject_HasAttr ((PyObject *) obj, invoke_cst))
+    error (_("Python command object missing 'invoke' method."));
+
+  if (args == nullptr)
     args = "";
   gdbpy_ref<> argobj (PyUnicode_Decode (args, strlen (args), host_charset (),
                                        NULL));
@@ -507,26 +505,61 @@ cmdpy_init (PyObject *self, PyObject *args, PyObject *kw)
 
       if (is_prefix)
        {
-         int allow_unknown;
-
-         /* If we have our own "invoke" method, then allow unknown
-            sub-commands.  */
-         allow_unknown = PyObject_HasAttr (self, invoke_cst);
-         cmd = add_prefix_cmd (cmd_name.get (),
-                               (enum command_class) cmdtype,
-                               NULL, docstring.release (), &obj->sub_list,
-                               allow_unknown, cmd_list);
+         bool has_invoke = PyObject_HasAttr (self, invoke_cst) == 1;
+         if (has_invoke)
+           {
+             /* If there's an 'invoke' method, then create the prefix
+                command, but call cmdpy_function to dispatch to the invoke
+                method when the user runs the prefix with no sub-command.  */
+             cmd = add_prefix_cmd (cmd_name.get (),
+                                   (enum command_class) cmdtype,
+                                   nullptr,
+                                   docstring.release (), &obj->sub_list,
+                                   1 /* allow_unknown */, cmd_list);
+             cmd->func = cmdpy_function;
+           }
+         else
+           {
+             /* If there is no 'invoke' method, then create the prefix
+                using the standard prefix callbacks.  This means that for
+                'set prefix' the user will get the help text listing all
+                of the sub-commands, and for 'show prefix', the user will
+                see all of the sub-command values.  */
+             cmd_list_element *first = *cmd_list;
+             while (first->prefix != nullptr)
+               first = first->prefix;
+
+             bool is_show = first->subcommands == &showlist;
+
+             if (is_show)
+               cmd = add_show_prefix_cmd (cmd_name.get (),
+                                          (enum command_class) cmdtype,
+                                          docstring.release (),
+                                          &obj->sub_list,
+                                          0 /* allow_unknown */, cmd_list);
+             else
+               cmd = add_basic_prefix_cmd (cmd_name.get (),
+                                           (enum command_class) cmdtype,
+                                           docstring.release (),
+                                           &obj->sub_list,
+                                           0 /* allow_unknown */, cmd_list);
+           }
        }
       else
-       cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
-                      docstring.release (), cmd_list);
+       {
+         /* For non-prefix commands, arrange to call cmdpy_function, which
+            invokes the Python 'invoke' method, or raises an exception if
+            the 'invoke' method is missing.  */
+         cmd = add_cmd (cmd_name.get (), (enum command_class) cmdtype,
+                        docstring.release (), cmd_list);
+         cmd->func = cmdpy_function;
+       }
 
       /* If successful, the above takes ownership of the name, since we set
         name_allocated, so release it.  */
       cmd_name.release ();
 
-      /* There appears to be no API to set this.  */
-      cmd->func = cmdpy_function;
+      /* There appears to be no API to set these member variables.  */
       cmd->destroyer = cmdpy_destroyer;
       cmd->doc_allocated = 1;
       cmd->name_allocated = 1;
index 06eebddf2423a466d06c532bd57309a77e8d4167..bf0d7df5f26aa9fce1f5e1375977199f3c85aad4 100644 (file)
@@ -478,6 +478,79 @@ with_test_prefix "empty doc string" {
     gdb_test "help show empty-doc-string" "^Show doc string\\."
 }
 
+with_test_prefix "set/show parameter" {
+    # This first set/show prefix command doesn't have an invoke
+    # method.  As such, GDB installs the default invoke behaviour; set
+    # prints the full list of sub-commands, and show prints all the
+    # sub-command values.
+    gdb_test_multiline "Setup set/show parameter prefix with no invoke" \
+       "guile" "" \
+       "(register-command! (make-command \"set test-prefix\"" "" \
+       "  #:prefix? #t" "" \
+       "  #:command-class COMMAND_NONE))" ""\
+       "(register-command! (make-command \"show test-prefix\"" "" \
+       "  #:prefix? #t" "" \
+       "  #:command-class COMMAND_NONE))" ""\
+       "(register-parameter! (make-parameter \"test-prefix param-1\"" "" \
+       "  #:command-class COMMAND_NONE" "" \
+       "  #:parameter-type PARAM_BOOLEAN))" "" \
+       "(register-parameter! (make-parameter \"test-prefix param-2\"" "" \
+       "  #:command-class COMMAND_NONE" "" \
+       "  #:parameter-type PARAM_UINTEGER))" "" \
+       "(register-parameter! (make-parameter \"test-prefix param-3\"" "" \
+       "  #:command-class COMMAND_NONE" "" \
+       "  #:parameter-type PARAM_STRING))" "" \
+       "end" ""
+
+    gdb_test "set test-prefix" \
+       [multi_line \
+            "List of \"set test-prefix\" subcommands:" \
+            "" \
+            "set test-prefix param-1 -- Set the current value of 'test-prefix param-1'." \
+            "set test-prefix param-2 -- Set the current value of 'test-prefix param-2'." \
+            "set test-prefix param-3 -- Set the current value of 'test-prefix param-3'." \
+            "" \
+            "Type \"help set test-prefix\" followed by subcommand name for full documentation\\." \
+            "Type \"apropos word\" to search for commands related to \"word\"\\." \
+            "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \
+            "Command name abbreviations are allowed if unambiguous\\."]
+
+    gdb_test "show test-prefix" \
+       [multi_line \
+            "test-prefix param-1:  The current value of 'test-prefix param-1' is off\\." \
+            "test-prefix param-2:  The current value of 'test-prefix param-2' is 0\\." \
+            "test-prefix param-3:  The current value of 'test-prefix param-3' is \"\"\\."]
+
+    # This next set/show prefix has an invoke method, which will be
+    # called instead of the default behaviour tested above.
+    gdb_test_multiline "Setup set/show parameter prefix with invoke" \
+       "guile" "" \
+       "(register-command! (make-command \"set test-prefix-2\"" "" \
+       "  #:prefix? #t" "" \
+       "  #:command-class COMMAND_NONE" ""\
+       "  #:invoke (lambda (self arg from-tty)" "" \
+       "    (display \"invoke -- set\\n\"))))" "" \
+       "(register-command! (make-command \"show test-prefix-2\"" "" \
+       "  #:prefix? #t" "" \
+       "  #:command-class COMMAND_NONE" ""\
+       "  #:invoke (lambda (self arg from-tty)" "" \
+       "    (display \"invoke -- show\\n\"))))" "" \
+       "(register-parameter! (make-parameter \"test-prefix-2 param-1\"" "" \
+       "  #:command-class COMMAND_NONE" "" \
+       "  #:parameter-type PARAM_BOOLEAN))" "" \
+       "(register-parameter! (make-parameter \"test-prefix-2 param-2\"" "" \
+       "  #:command-class COMMAND_NONE" "" \
+       "  #:parameter-type PARAM_UINTEGER))" "" \
+       "(register-parameter! (make-parameter \"test-prefix-2 param-3\"" "" \
+       "  #:command-class COMMAND_NONE" "" \
+       "  #:parameter-type PARAM_STRING))" "" \
+       "end" ""
+
+    gdb_test "set test-prefix-2" "^invoke -- set"
+
+    gdb_test "show test-prefix-2" "^invoke -- show"
+}
+
 rename scm_param_test_maybe_no_output ""
 
 # Test a color parameter.
index 5ed52a2fc67f63f3fc5bef5353d7f48b2d4d1c01..5ac57122f995195d49c92bb3de9c090f099c1cd0 100644 (file)
@@ -354,5 +354,63 @@ proc_with_prefix test_unknown_prefix {} {
     }
 }
 
+# Check what happens if a command object is called without an 'invoke'
+# method.
+proc_with_prefix test_deleting_invoke_methods {} {
+    clean_restart
+
+    gdb_test_multiline "create 'foo' prefix command" \
+       "python" "" \
+       "class test_prefix(gdb.Command):" "" \
+       "  def __init__ (self):" "" \
+       "    super().__init__ (\"foo\", gdb.COMMAND_USER, prefix=True)" "" \
+       "  def invoke (self, arg, from_tty):" "" \
+       "    print(\"In 'foo' invoke: %s\" % arg)" "" \
+       "foo = test_prefix()" "" \
+       "end" ""
+
+    gdb_test_multiline "create 'foo bar' command" \
+       "python" "" \
+       "class test_cmd(gdb.Command):" "" \
+       "  def __init__ (self):" "" \
+       "    super().__init__ (\"foo bar\", gdb.COMMAND_USER)" "" \
+       "  def invoke (self, arg, from_tty):" "" \
+       "    print(\"In 'foo bar' invoke: %s\" % arg)" "" \
+       "foo_bar = test_cmd()" "" \
+       "end" ""
+
+    gdb_test "foo def" "In 'foo' invoke: def" \
+       "call 'foo' with an unknown sub-command"
+
+    gdb_test "foo bar def" "In 'foo bar' invoke: def" \
+       "call 'foo bar' with arguments"
+
+    gdb_test_no_output "python del(foo_bar.__class__.invoke)" \
+       "delete invoke from test_cmd class"
+
+    with_test_prefix "after deleting test_cmd.invoke" {
+       gdb_test "foo def" "In 'foo' invoke: def" \
+           "call 'foo' with an unknown sub-command"
+
+       gdb_test "foo bar def" \
+           "^Python command object missing 'invoke' method\\." \
+           "call 'foo bar' with arguments"
+    }
+
+    gdb_test_no_output "python del(foo.__class__.invoke)" \
+       "delete invoke from test_prefix class"
+
+    with_test_prefix "after deleting test_prefix.invoke" {
+       gdb_test "foo def" \
+           "^Python command object missing 'invoke' method\\." \
+           "call 'foo' with an unknown sub-command"
+
+       gdb_test "foo bar def" \
+           "^Python command object missing 'invoke' method\\." \
+           "call 'foo bar' with arguments"
+    }
+}
+
 test_command_redefining_itself
 test_unknown_prefix
+test_deleting_invoke_methods
index aa2f84a099acdd3f51351795e01a79c57aa85abe..69cbb90ab214aac597d6261679809b5502cdd59f 100644 (file)
@@ -23,25 +23,26 @@ require allow_python_tests
 
 clean_restart
 
-# Helper proc to generate the output of 'help MODE PREFIX', where MODE
-# will be either 'set' or 'show'.  The HELP_TEXT is the expected help
-# text for this prefix command, this should not be a regexp, as this
-# proc converts the text to a regexp.
-#
-# Return a single regexp which should match the output.
-proc make_help_re { mode prefix help_text } {
+# Helper proc to generate the output of 'show PREFIX' commands for the
+# case where the prefix command doesn't handle unknown sub-commands.
+# In this case GDB will list the value of every sub-command under
+# PREFIX.
+proc make_show_prefix_re { prefix } {
+    return "$prefix param-1:\\s+The current value of '$prefix param-1' is \"off\"\\."
+}
+
+# Helper proc to generate the help text that describes all of the sub
+# commands under PREFIX.  The MODE is either 'set' or 'show'.  This
+# output will appear for 'help MODE PREFIX' and also for 'set PREFIX'.
+proc make_sub_cmd_help_re { mode prefix } {
     if { $mode == "set" } {
        set word "Set"
     } else {
        set word "Show"
     }
 
-    set help_re [string_to_regexp $help_text]
-
     return \
        [multi_line \
-            "$help_re" \
-            "" \
             "List of \"$mode $prefix\" subcommands:" \
             "" \
             "$mode $prefix param-1 -- $word the current value of '$prefix param-1'\\." \
@@ -52,6 +53,22 @@ proc make_help_re { mode prefix help_text } {
             "Command name abbreviations are allowed if unambiguous\\."]
 }
 
+# Helper proc to generate the output of 'help MODE PREFIX', where MODE
+# will be either 'set' or 'show'.  The HELP_TEXT is the expected help
+# text for this prefix command, this should not be a regexp, as this
+# proc converts the text to a regexp.
+#
+# Return a single regexp which should match the output.
+proc make_help_re { mode prefix help_text } {
+    set help_re [string_to_regexp $help_text]
+
+    return \
+       [multi_line \
+            "$help_re" \
+            "" \
+            [make_sub_cmd_help_re $mode $prefix]]
+}
+
 # Create gdb.ParameterPrefix without using a sub-class, both with, and
 # without a doc string.  For the doc string case, test single line,
 # and multi-line doc strings.
@@ -92,6 +109,14 @@ proc_with_prefix test_basic_usage {} {
                "^Undefined $mode $prefix command: \"xxx\"\\.  Try \"help $mode $prefix\"\\."
        }
     }
+
+    foreach prefix { prefix-1 prefix-2 prefix-3 } {
+       gdb_test "set $prefix" \
+           [make_sub_cmd_help_re "set" $prefix]
+
+       gdb_test "show $prefix" \
+           [make_show_prefix_re $prefix]
+    }
 }
 
 # Create a sub-class of gdb.ParameterPrefix, but don't do anything
@@ -153,6 +178,14 @@ proc_with_prefix test_simple_sub_class {} {
                "^Undefined $mode $prefix command: \"xxx\"\\.  Try \"help $mode $prefix\"\\."
        }
     }
+
+    foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } {
+       gdb_test "set $prefix" \
+           [make_sub_cmd_help_re "set" $prefix]
+
+       gdb_test "show $prefix" \
+           [make_show_prefix_re $prefix]
+    }
 }
 
 # Create a sub-class of gdb.ParameterPrefix, and make use of
@@ -220,6 +253,24 @@ proc_with_prefix test_prefix_with_invoke {} {
     send_gdb "\n"
     gdb_test "" "^\r\ninvoke_show \\(d\\): \"xxx yyy\" True" \
        "repeat show prefix-10 xxx yyy"
+
+    gdb_test "set prefix-8" \
+       "^invoke_set \\(a\\): \"\" True"
+
+    gdb_test "show prefix-8" \
+       [make_show_prefix_re "prefix-8"]
+
+    gdb_test "set prefix-9" \
+       [make_sub_cmd_help_re "set" "prefix-9"]
+
+    gdb_test "show prefix-9" \
+       "^invoke_show \\(b\\): \"\" True"
+
+    gdb_test "set prefix-10" \
+       "^invoke_set \\(c\\): \"\" True"
+
+    gdb_test "show prefix-10" \
+       "^invoke_show \\(d\\): \"\" True"
 }
 
 # Create ParameterPrefix sub-classes that make use of the
index 2ca56dc83fc6cd615dcc283b43c4c10c9da1500a..214c570724aeda089962f3ee034e57b444288aac 100644 (file)
@@ -793,6 +793,67 @@ proc_with_prefix test_unknown_prefix {} {
     }
 }
 
+# Test the default behaviour of a set/show parameter prefix command.
+proc_with_prefix test_set_show_parameters {} {
+    # This first set/show prefix command doesn't have an invoke
+    # method.  As such, GDB installs the default invoke behaviour; set
+    # prints the full list of sub-commands, and show prints all the
+    # sub-command values.
+    gdb_test_multiline "Setup set/show parameter prefix with no invoke" \
+       "python" "" \
+       "class TestParamPrefix(gdb.Command):" "" \
+       "   \"\"\"TestParamPrefix documentation string.\"\"\"" "" \
+       "   def __init__(self, name):" "" \
+       "      super().__init__(name, gdb.COMMAND_NONE, prefix = True)" "" \
+       "TestParamPrefix('set test-prefix')" "" \
+       "TestParamPrefix('show test-prefix')" "" \
+       "gdb.Parameter('test-prefix param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "gdb.Parameter('test-prefix param-2', gdb.COMMAND_NONE, gdb.PARAM_INTEGER)" "" \
+       "gdb.Parameter('test-prefix param-3', gdb.COMMAND_NONE, gdb.PARAM_STRING)" "" \
+       "end"
+
+    gdb_test "set test-prefix" \
+       [multi_line \
+            "List of \"set test-prefix\" subcommands:" \
+            "" \
+            "set test-prefix param-1 -- Set the current value of 'test-prefix param-1'." \
+            "set test-prefix param-2 -- Set the current value of 'test-prefix param-2'." \
+            "set test-prefix param-3 -- Set the current value of 'test-prefix param-3'." \
+            "" \
+            "Type \"help set test-prefix\" followed by subcommand name for full documentation\\." \
+            "Type \"apropos word\" to search for commands related to \"word\"\\." \
+            "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \
+            "Command name abbreviations are allowed if unambiguous\\."]
+
+    gdb_test "show test-prefix" \
+       [multi_line \
+            "test-prefix param-1:  The current value of 'test-prefix param-1' is \"off\"\\." \
+            "test-prefix param-2:  The current value of 'test-prefix param-2' is \"0\"\\." \
+            "test-prefix param-3:  The current value of 'test-prefix param-3' is \"\"\\."]
+
+    # This next set/show prefix has an invoke method, which will be
+    # called instead of the default behaviour tested above.
+    gdb_test_multiline "Setup set/show parameter prefix with invoke" \
+       "python" "" \
+       "class TestParamPrefix(gdb.Command):" "" \
+       "   \"\"\"TestParamPrefix documentation string.\"\"\"" "" \
+       "   def __init__(self, name, mode):" "" \
+       "      self._mode = mode" "" \
+       "      super().__init__(self._mode + ' ' + name, gdb.COMMAND_NONE, prefix = True)" "" \
+       "   def invoke(self, args, from_tty):" "" \
+       "      print('invoke -- ' + self._mode)" "" \
+       "TestParamPrefix('test-prefix-2', 'set')" "" \
+       "TestParamPrefix('test-prefix-2', 'show')" "" \
+       "gdb.Parameter('test-prefix-2 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \
+       "gdb.Parameter('test-prefix-2 param-2', gdb.COMMAND_NONE, gdb.PARAM_INTEGER)" "" \
+       "gdb.Parameter('test-prefix-2 param-3', gdb.COMMAND_NONE, gdb.PARAM_STRING)" "" \
+       "end"
+
+    gdb_test "set test-prefix-2" "^invoke -- set"
+
+    gdb_test "show test-prefix-2" "^invoke -- show"
+}
+
 test_directories
 test_data_directory
 test_boolean_parameter
@@ -810,5 +871,6 @@ test_throwing_parameter
 test_language
 test_ambiguous_parameter
 test_unknown_prefix
+test_set_show_parameters
 
 rename py_param_test_maybe_no_output ""