Fix/improve 'help CLASS' output
authorPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Sun, 10 May 2020 16:31:03 +0000 (18:31 +0200)
committerPhilippe Waroquiers <philippe.waroquiers@skynet.be>
Fri, 15 May 2020 20:17:45 +0000 (22:17 +0200)
Currently, help CLASS possibly shows several times the same help,
as it shows it once for the command, and once for each alias.

The final objective of this patch series is to have class_alias used only
for user defined aliases, not anymore for aliases predefined by GDB.
The command 'help aliases' will then only show the user defined aliases.
So, the idea is that GDB predefined aliases will be shown together
with their aliased command.

This commit changes 'help CLASS' so that a command is shown once in the output,
with all its aliases.
This ensures:
  * that the user has only to read once the same help text
  * and sees the command and all its aliases in a glance, a.o. allowing
    the user to choose the preferred way (e.g. the shortest one,
    or the most mnemonic one) to type the command.

For example, the old output:
   (gdb) help stack
   ...
   List of commands:

   backtrace -- Print backtrace of all stack frames, or innermost COUNT frames.
   bt -- Print backtrace of all stack frames, or innermost COUNT frames.
   ...
(note that 'where' is not shown in this output)

becomes
   (gdb) help stack
   ...
   List of commands:

   backtrace, where, bt -- Print backtrace of all stack frames, or innermost COUNT frames.
   ...

The output layout chosen is to have the command first, followed by all its
aliases separated by a comma.  Note that the command and alias names are
title-styled.  For sure, other layouts could be discussed, but this one is IMO
readable and compact.

The function 'help_cmd_list' can be simplified by removing the prefix argument,
as the prefixname of a command can now be retrieved in the GDB command tree
structure.

This also fixes the fact that 'help aliases' wrongly shows a long
list of (non-alias) when defining an alias for a prefix command.
For example, after:
    (gdb) alias montre = show
  then
    (gdb) help aliases
  shows hundreds of sub-commands starting with the non aliased command,
  such as:
    montre -- Generic command for showing things about the debugger.
    show ada -- Generic command for showing Ada-specific settings.
    show ada print-signatures -- Show whether the output of formal ...
    ....

'help_cmd_list' is also made static, as it is only used inside cli-decode.c.

Note that the 'help CLASS' is somewhat broken, in the sense that it
sometimes shows too many commands (commands not belonging to CLASS)
and sometimes shows not enough commands (not showing some commands
belonging to CLASS).
For example, 'help breakpoints' shows the command
'disable pretty-printer' and 'disable unwinder', not related to breakpoints.
On the other end, 'help stack' does not show 'disable unwinder'
while 'disable unwinder' is defined in unwinders.py as belonging to class_stack.
Fixing the missing commands is easy to do,
but fixing the excess commands is not straightforward, as many
subcommands have a class 'no_class' or 'all_class'.
Possibly, some of this might be improved/fixed in another patch series.

With this patch series, the 'abbrev flag' has as only remaining purpose
to avoid having the abbreviation alias appearing in the completion list,
so change 'help alias' accordingly.

gdb/ChangeLog

2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

* cli/cli-decode.h (help_cmd_list): Remove declaration.
* cli/cli-decode.c (help_cmd_list): Declare as static,
remove prefix argument, use bool for recurse arg, rework to show the aliases of
a command together with the command.
(fput_command_name_styled, fput_command_names_styled): New functions.
(print_help_for_command): Remove prefix arg, use bool for recurse arg, use
fput_command_name_styled.
(help_list, help_all): Update callers to remove prefix arg and use bool recurse.
* cli/cli-cmds.c (_initialize_cli_cmds): Update alias_command doc.

gdb/testsuite/ChangeLog

2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>

* gdb.base/alias.exp: Update help output check.

gdb/ChangeLog
gdb/cli/cli-cmds.c
gdb/cli/cli-decode.c
gdb/cli/cli-decode.h
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.base/alias.exp

index 80c027df8ac48e3b7076626f16f5a47d92766986..2298843350b78b20467c5545b366c5dda070675e 100644 (file)
@@ -1,3 +1,15 @@
+2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
+
+       * cli/cli-decode.h (help_cmd_list): Remove declaration.
+       * cli/cli-decode.c (help_cmd_list): Declare as static,
+       remove prefix argument, use bool for recurse arg, rework to show the aliases of
+       a command together with the command.
+       (fput_command_name_styled, fput_command_names_styled): New functions.
+       (print_help_for_command): Remove prefix arg, use bool for recurse arg, use
+       fput_command_name_styled.
+       (help_list, help_all): Update callers to remove prefix arg and use bool recurse.
+       * cli/cli-cmds.c (_initialize_cli_cmds): Update alias_command doc.
+
 2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
 
        * cli/cli-setshow.h (cmd_show_list): Remove prefix argument.
index 8538fadd9c3306778c5b4c2af8b3c543c12276da..eb6e32b04693cb4ec65e5b97af4a4539bfd92e12 100644 (file)
@@ -2454,7 +2454,7 @@ Usage: alias [-a] [--] ALIAS = COMMAND\n\
 ALIAS is the name of the alias command to create.\n\
 COMMAND is the command being aliased to.\n\
 If \"-a\" is specified, the command is an abbreviation,\n\
-and will not appear in help command list output.\n\
+and will not be used in command completion.\n\
 \n\
 Examples:\n\
 Make \"spe\" an alias of \"set print elements\":\n\
index c37dfaffb5c1adcb2f3b4d8b526b2572dfd2df2f..093692e7ac0d27dd95c55053c8637575ad5dd246 100644 (file)
@@ -43,6 +43,11 @@ static struct cmd_list_element *find_cmd (const char *command,
                                          int ignore_help_classes,
                                          int *nfound);
 
+static void help_cmd_list (struct cmd_list_element *list,
+                          enum command_class theclass,
+                          bool recurse,
+                          struct ui_file *stream);
+
 static void help_all (struct ui_file *stream);
 
 /* Look up a command whose 'prefixlist' is KEY.  Return the command if found,
@@ -76,8 +81,8 @@ lookup_cmd_for_prefixlist (struct cmd_list_element **key,
 }
 
 static void
-print_help_for_command (struct cmd_list_element *c, const char *prefix,
-                       int recurse, struct ui_file *stream);
+print_help_for_command (struct cmd_list_element *c,
+                       bool recurse, struct ui_file *stream);
 
 \f
 /* Set the callback function for the specified command.  For each both
@@ -1021,6 +1026,43 @@ add_com_suppress_notification (const char *name, enum command_class theclass,
                                        &cmdlist, suppress_notification);
 }
 
+/* Print the prefix of C followed by name of C in title style.  */
+
+static void
+fput_command_name_styled (struct cmd_list_element *c, struct ui_file *stream)
+{
+  const char *prefixname
+    = c->prefix == nullptr ? "" : c->prefix->prefixname;
+
+  fprintf_styled (stream, title_style.style (), "%s%s", prefixname, c->name);
+}
+
+/* If C has one or more aliases, style print the name of C and
+   the name of its aliases, separated by commas.
+   If ALWAYS_FPUT_C_NAME, print the name of C even if it has no aliases.
+   If one or more names are printed, POSTFIX is printed after the last name.
+*/
+
+static void
+fput_command_names_styled (struct cmd_list_element *c,
+                          bool always_fput_c_name, const char *postfix,
+                          struct ui_file *stream)
+{
+  if (always_fput_c_name ||  c->aliases != nullptr)
+    fput_command_name_styled (c, stream);
+  if (c->aliases != nullptr)
+    {
+      for (cmd_list_element *iter = c->aliases; iter; iter = iter->alias_chain)
+       {
+         fputs_filtered (", ", stream);
+         wrap_here ("   ");
+         fput_command_name_styled (iter, stream);
+       }
+    }
+  if (always_fput_c_name ||  c->aliases != nullptr)
+    fputs_filtered (postfix, stream);
+}
+
 /* If VERBOSE, print the full help for command C and highlight the
    documentation parts matching HIGHLIGHT,
    otherwise print only one-line help for command C.  */
@@ -1208,7 +1250,7 @@ help_list (struct cmd_list_element *list, const char *cmdtype,
   else
     fprintf_filtered (stream, "List of %scommands:\n\n", cmdtype2);
 
-  help_cmd_list (list, theclass, cmdtype, (int) theclass >= 0, stream);
+  help_cmd_list (list, theclass, theclass >= 0, stream);
 
   if (theclass == all_classes)
     {
@@ -1255,7 +1297,7 @@ help_all (struct ui_file *stream)
       if (c->func == NULL)
        {
          fprintf_filtered (stream, "\nCommand class: %s\n\n", c->name);
-         help_cmd_list (cmdlist, c->theclass, "", 1, stream);
+         help_cmd_list (cmdlist, c->theclass, true, stream);
        }
     }
 
@@ -1275,7 +1317,7 @@ help_all (struct ui_file *stream)
              fprintf_filtered (stream, "\nUnclassified commands\n\n");
              seen_unclassified = 1;
            }
-         print_help_for_command (c, "", 1, stream);
+         print_help_for_command (c, 1, stream);
        }
     }
 
@@ -1327,12 +1369,10 @@ print_doc_line (struct ui_file *stream, const char *str,
    If RECURSE is non-zero, also print one-line descriptions
    of all prefixed subcommands.  */
 static void
-print_help_for_command (struct cmd_list_element *c, const char *prefix,
-                       int recurse, struct ui_file *stream)
+print_help_for_command (struct cmd_list_element *c,
+                       bool recurse, struct ui_file *stream)
 {
-  fprintf_styled (stream, title_style.style (),
-                 "%s%s", prefix, c->name);
-  fputs_filtered (" -- ", stream);
+  fput_command_names_styled (c, true, " -- ", stream);
   print_doc_line (stream, c->doc, false);
   fputs_filtered ("\n", stream);
 
@@ -1342,48 +1382,80 @@ print_help_for_command (struct cmd_list_element *c, const char *prefix,
     /* Subcommands of a prefix command typically have 'all_commands'
        as class.  If we pass CLASS to recursive invocation,
        most often we won't see anything.  */
-    help_cmd_list (*c->prefixlist, all_commands, c->prefixname, 1, stream);
+    help_cmd_list (*c->prefixlist, all_commands, true, stream);
 }
 
 /*
  * Implement a help command on command list LIST.
  * RECURSE should be non-zero if this should be done recursively on
  * all sublists of LIST.
- * PREFIX is the prefix to print before each command name.
  * STREAM is the stream upon which the output should be written.
  * THECLASS should be:
  *      A non-negative class number to list only commands in that
- * class.
  *      ALL_COMMANDS to list all commands in list.
  *      ALL_CLASSES  to list all classes in list.
  *
+ *   Note that aliases are only shown when THECLASS is class_alias.
+ *   In the other cases, the aliases will be shown together with their
+ *   aliased command.
+ *
  *   Note that RECURSE will be active on *all* sublists, not just the
  * ones selected by the criteria above (ie. the selection mechanism
  * is at the low level, not the high-level).
  */
-void
+
+static void
 help_cmd_list (struct cmd_list_element *list, enum command_class theclass,
-              const char *prefix, int recurse, struct ui_file *stream)
+              bool recurse, struct ui_file *stream)
 {
   struct cmd_list_element *c;
 
   for (c = list; c; c = c->next)
     {
-      if (c->abbrev_flag == 0
-         && !c->cmd_deprecated
-         && (theclass == all_commands
-             || (theclass == all_classes && c->func == NULL)
-             || (theclass == c->theclass && c->func != NULL)))
+      if (c->abbrev_flag == 1 || c->cmd_deprecated)
        {
-         print_help_for_command (c, prefix, recurse, stream);
+         /* Do not show abbreviations or deprecated commands.  */
+         continue;
        }
-      else if (c->abbrev_flag == 0
-              && recurse
-              && !c->cmd_deprecated
-              && theclass == class_user && c->prefixlist != NULL)
-       /* User-defined commands may be subcommands.  */
-       help_cmd_list (*c->prefixlist, theclass, c->prefixname,
-                      recurse, stream);
+
+      if (c->cmd_pointer != nullptr && theclass != class_alias)
+       {
+         /* Do not show an alias, unless specifically showing the
+            list of aliases:  for all other classes, an alias is
+            shown (if needed) together with its aliased command.  */
+         continue;
+       }
+
+      if (theclass == all_commands
+         || (theclass == all_classes && c->func == NULL)
+         || (theclass == c->theclass && c->func != NULL))
+       {
+         /* show C when
+             - showing all commands
+            - showing all classes and C is a help class
+            - showing commands of THECLASS and C is not the help class  */
+
+         /* If we show the class_alias and C is an alias, do not recurse,
+            as this would show the (possibly very long) not very useful
+            list of sub-commands of the aliased command.  */
+         print_help_for_command
+           (c,
+            recurse && (theclass != class_alias || c->cmd_pointer == nullptr),
+            stream);
+         continue;
+       }
+
+      if (recurse
+         && (theclass == class_user || theclass == class_alias)
+         && c->prefixlist != NULL)
+       {
+         /* User-defined commands or aliases may be subcommands.  */
+         help_cmd_list (*c->prefixlist, theclass, recurse, stream);
+         continue;
+       }
+
+      /* Do not show C or recurse on C, e.g. because C does not belong to
+        THECLASS or because C is a help class.  */
     }
 }
 \f
index 4f7c7701e4011cfa6574ce4ec99feb4ff62badad..f4719bfac47ab855cf1c11c3e1ddc67e8482ada4 100644 (file)
@@ -249,9 +249,6 @@ struct cmd_list_element
     int *suppress_notification = nullptr;
   };
 
-extern void help_cmd_list (struct cmd_list_element *, enum command_class,
-                          const char *, int, struct ui_file *);
-
 /* Functions that implement commands about CLI commands.  */
 
 extern void help_cmd (const char *, struct ui_file *);
index bff0ad0532990a9d5679493cb51ed63c972f7cb3..36afa3dbd3d928c397bb46bdc50a4dd286f45dc5 100644 (file)
@@ -1,5 +1,9 @@
 2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
 
+       * gdb.base/alias.exp: Update help output check.
+
+c2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
+
        * gdb.base/default.exp: Update output following fixes.
 
 2020-05-15  Philippe Waroquiers  <philippe.waroquiers@skynet.be>
index 9a9557668e57282db4e4f452846f9581e8244004..69cfde67c14ad57f19ecd3a9f1daee42d0ec4883 100644 (file)
@@ -65,7 +65,7 @@ gdb_test "show print elements" "Limit .* is 50\[.\]" "verify spe"
 gdb_test_no_output "alias set pr elms = set p elem"
 gdb_test_no_output "set pr elms 51"
 gdb_test "show print elements" "Limit .* is 51\[.\]" "verify set pr elms"
-gdb_test "help set print" "set print elms .*"
+gdb_test "help set print" "set print elements, set print elms, spe .*"
 
 # Verify alias command detects a non existing prefix.
 gdb_test "alias assigne imprime limite-elements = set print elements" \