[PR gdb/16238] Add completer for the show user command

The 'show user' command (which shows the definition of non-python/scheme
user defined commands) is currently missing a completer. This is
mentioned in PR 16238.  Having one can improve the user experience.

In this commit I propose an implementation for such completer as well as
the associated tests.

Tested on x86_64 GNU/Linux.

All feedbacks are welcome.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=16238
This commit is contained in:
Lancelot SIX
2021-10-11 23:42:33 +01:00
parent b431e7a3fe
commit cc81bc2dfb
2 changed files with 143 additions and 1 deletions

View File

@ -1631,6 +1631,75 @@ show_user (const char *args, int from_tty)
} }
} }
/* Return true if COMMAND or any of its sub-commands is a user defined command.
This is a helper function for show_user_completer. */
static bool
has_user_subcmd (struct cmd_list_element *command)
{
if (cli_user_command_p (command))
return true;
/* Alias command can yield false positive. Ignore them as the targeted
command should be reachable anyway. */
if (command->is_alias ())
return false;
if (command->is_prefix ())
for (struct cmd_list_element *subcommand = *command->subcommands;
subcommand != nullptr;
subcommand = subcommand->next)
if (has_user_subcmd (subcommand))
return true;
return false;
}
/* Implement completer for the 'show user' command. */
static void
show_user_completer (cmd_list_element *,
completion_tracker &tracker, const char *text,
const char *word)
{
struct cmd_list_element *cmd_group = cmdlist;
/* TEXT can contain a chain of commands and subcommands. Follow the
commands chain until we reach the point where the user wants a
completion. */
while (word > text)
{
const char *curr_cmd = text;
const char *after = skip_to_space (text);
const size_t curr_cmd_len = after - text;
text = skip_spaces (after);
for (struct cmd_list_element *c = cmd_group; c != nullptr; c = c->next)
{
if (strlen (c->name) == curr_cmd_len
&& strncmp (c->name, curr_cmd, curr_cmd_len) == 0)
{
if (c->subcommands == nullptr)
/* We arrived after a command with no child, so nothing more
to complete. */
return;
cmd_group = *c->subcommands;
break;
}
}
}
const int wordlen = strlen (word);
for (struct cmd_list_element *c = cmd_group; c != nullptr; c = c->next)
if (has_user_subcmd (c))
{
if (strncmp (c->name, word, wordlen) == 0)
tracker.add_completion
(gdb::unique_xmalloc_ptr<char> (xstrdup (c->name)));
}
}
/* Search through names of commands and documentations for a certain /* Search through names of commands and documentations for a certain
regular expression. */ regular expression. */
@ -2593,10 +2662,11 @@ you must type \"disassemble 'foo.c'::bar\" and not \"disassemble foo.c:bar\"."))
c = add_com ("make", class_support, make_command, _("\ c = add_com ("make", class_support, make_command, _("\
Run the ``make'' program using the rest of the line as arguments.")); Run the ``make'' program using the rest of the line as arguments."));
set_cmd_completer (c, filename_completer); set_cmd_completer (c, filename_completer);
add_cmd ("user", no_class, show_user, _("\ c = add_cmd ("user", no_class, show_user, _("\
Show definitions of non-python/scheme user defined commands.\n\ Show definitions of non-python/scheme user defined commands.\n\
Argument is the name of the user defined command.\n\ Argument is the name of the user defined command.\n\
With no argument, show definitions of all user defined commands."), &showlist); With no argument, show definitions of all user defined commands."), &showlist);
set_cmd_completer (c, show_user_completer);
add_com ("apropos", class_support, apropos_command, _("\ add_com ("apropos", class_support, apropos_command, _("\
Search for commands matching a REGEXP.\n\ Search for commands matching a REGEXP.\n\
Usage: apropos [-v] REGEXP\n\ Usage: apropos [-v] REGEXP\n\

View File

@ -0,0 +1,72 @@
# Copyright 2021 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Test tab-completion for the 'show user' command.
load_lib completion-support.exp
# This test does not require any file to be loaded.
clean_restart
# Define the 'foo' and 'bar' commands so we have something to complete.
set re [multi_line "Type commands for definition of \"foo\"." \
"End with a line saying just \"end\"." \
">$" ]
gdb_test_multiple "define foo" "define user command: foo" {
-re $re {
gdb_test "print \"foo\"\nend" "" $gdb_test_name
}
}
set re [multi_line "Type commands for definition of \"bar\"." \
"End with a line saying just \"end\"." \
">$"]
gdb_test_multiple "define bar" "define user command: bar" {
-re $re {
gdb_test "print \"bar\"\nend" "" $gdb_test_name
}
}
# The completer should show both options.
test_gdb_complete_multiple "show user " "" "" "bar foo"
# If we give the beginning of one of the commands, it should complete it.
test_gdb_complete_unique "show user f" "show user foo"
test_gdb_complete_unique "show user b" "show user bar"
# Define a user prefix.
gdb_test "define-prefix mygroup"
# Add a user defined command in the user defined prefix.
set re [multi_line "Type commands for definition of \"mygroup mycommand\"." \
"End with a line saying just \"end\"." \
">$"]
set test_name "define user command: mygroup mycommand"
gdb_test_multiple "define mygroup mycommand" $test_name {
-re $re {
gdb_test "print \"42\"\nend" "" $gdb_test_name
}
}
with_test_prefix "with user-prefix" {
# We now expect the completion to yield only 3 results. As the 'mycommand'
# is within the 'mygroup' prefix, it should not be reachable without
# traversing 'mygroup' first.
test_gdb_complete_multiple "show user " "" "" "bar foo mygroup"
}
# Check that we can complete commands defined under a prefix.
test_gdb_complete_unique "show user m" "show user mygroup"
test_gdb_complete_unique "show user mygroup " "show user mygroup mycommand"