[5/5] pipe command completer

Message ID 20190627191427.20742-6-palves@redhat.com
State New, archived
Headers

Commit Message

Pedro Alves June 27, 2019, 7:14 p.m. UTC
  This commit adds a completer for the "pipe" command.  It can complete
"pipe"'s options, and the specified GDB command.

To make the completer aware of the "-d" option, this converts the
option processing to use gdb::option.

Tests included.

gdb/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* cli/cli-cmds.c (struct pipe_cmd_opts): New.
	(pipe_cmd_option_defs): New.
	(make_pipe_cmd_options_def_group): New.
	(pipe_command): Use gdb::option::process_options.
	(pipe_command_completer): New function.
	(_initialize_cli_cmds): Install completer for "pipe" command.

gdb/testsuite/ChangeLog:
yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

	* gdb.base/shell.exp: Load completion-support.exp.
	Adjust expected error output.  Add completion tests.
---
 gdb/cli/cli-cmds.c               | 92 ++++++++++++++++++++++++++++++++++++----
 gdb/testsuite/gdb.base/shell.exp | 47 +++++++++++++++++++-
 2 files changed, 128 insertions(+), 11 deletions(-)
  

Comments

Tom Tromey June 28, 2019, 3:16 p.m. UTC | #1
>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> This commit adds a completer for the "pipe" command.  It can complete
Pedro> "pipe"'s options, and the specified GDB command.

Pedro> To make the completer aware of the "-d" option, this converts the
Pedro> option processing to use gdb::option.

Thank you for doing this.

Pedro> gdb/ChangeLog:
Pedro> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>

Pedro> 	* cli/cli-cmds.c (struct pipe_cmd_opts): New.
Pedro> 	(pipe_cmd_option_defs): New.

BTW this is PR cli/24732.

Tom
  
Pedro Alves July 3, 2019, 4:28 p.m. UTC | #2
On 6/28/19 4:16 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> This commit adds a completer for the "pipe" command.  It can complete
> Pedro> "pipe"'s options, and the specified GDB command.
> 
> Pedro> To make the completer aware of the "-d" option, this converts the
> Pedro> option processing to use gdb::option.
> 
> Thank you for doing this.
> 
> Pedro> gdb/ChangeLog:
> Pedro> yyyy-mm-dd  Pedro Alves  <palves@redhat.com>
> 
> Pedro> 	* cli/cli-cmds.c (struct pipe_cmd_opts): New.
> Pedro> 	(pipe_cmd_option_defs): New.
> 
> BTW this is PR cli/24732.

Thanks, I added that to the ChangeLogs and pushed this in.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index bc32fbbaf88..054d80b9b96 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -960,32 +960,68 @@  edit_command (const char *arg, int from_tty)
   xfree (p);
 }
 
+/* The options for the "pipe" command.  */
+
+struct pipe_cmd_opts
+{
+  /* For "-d".  */
+  char *delimiter = nullptr;
+
+  ~pipe_cmd_opts ()
+  {
+    xfree (delimiter);
+  }
+};
+
+static const gdb::option::option_def pipe_cmd_option_defs[] = {
+
+  gdb::option::string_option_def<pipe_cmd_opts> {
+    "d",
+    [] (pipe_cmd_opts *opts) { return &opts->delimiter; },
+    nullptr,
+    N_("Indicates to use the specified delimiter string to separate\n\
+COMMAND from SHELL_COMMAND, in alternative to |.  This is useful in\n\
+case COMMAND contains a | character."),
+  },
+
+};
+
+/* Create an option_def_group for the "pipe" command's options, with
+   OPTS as context.  */
+
+static inline gdb::option::option_def_group
+make_pipe_cmd_options_def_group (pipe_cmd_opts *opts)
+{
+  return {{pipe_cmd_option_defs}, opts};
+}
+
 /* Implementation of the "pipe" command.  */
 
 static void
 pipe_command (const char *arg, int from_tty)
 {
-  std::string delim ("|");
+  pipe_cmd_opts opts;
 
-  if (arg != nullptr && check_for_argument (&arg, "-d", 2))
-    {
-      delim = extract_arg (&arg);
-      if (delim.empty ())
-	error (_("Missing delimiter DELIM after -d"));
-    }
+  auto grp = make_pipe_cmd_options_def_group (&opts);
+  gdb::option::process_options
+    (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp);
+
+  const char *delim = "|";
+  if (opts.delimiter != nullptr)
+    delim = opts.delimiter;
 
   const char *command = arg;
   if (command == nullptr)
     error (_("Missing COMMAND"));
 
-  arg = strstr (arg, delim.c_str ());
+  arg = strstr (arg, delim);
 
   if (arg == nullptr)
     error (_("Missing delimiter before SHELL_COMMAND"));
 
   std::string gdb_cmd (command, arg - command);
 
-  arg += delim.length (); /* Skip the delimiter.  */
+  arg += strlen (delim); /* Skip the delimiter.  */
 
   if (gdb_cmd.empty ())
     gdb_cmd = repeat_previous ();
@@ -1019,6 +1055,43 @@  pipe_command (const char *arg, int from_tty)
   exit_status_set_internal_vars (exit_status);
 }
 
+/* Completer for the pipe command.  */
+
+static void
+pipe_command_completer (struct cmd_list_element *ignore,
+			completion_tracker &tracker,
+			const char *text, const char *word_ignored)
+{
+  pipe_cmd_opts opts;
+
+  const char *org_text = text;
+  auto grp = make_pipe_cmd_options_def_group (&opts);
+  if (gdb::option::complete_options
+      (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp))
+    return;
+
+  const char *delimiter = "|";
+  if (opts.delimiter != nullptr)
+    delimiter = opts.delimiter;
+
+  /* Check if we're past option values already.  */
+  if (text > org_text && !isspace (text[-1]))
+    return;
+
+  const char *delim = strstr (text, delimiter);
+
+  /* If we're still not past the delimiter, complete the gdb
+     command.  */
+  if (delim == nullptr || delim == text)
+    {
+      complete_nested_command_line (tracker, text);
+      return;
+    }
+
+  /* We're past the delimiter.  What follows is a shell command, which
+     we don't know how to complete.  */
+}
+
 static void
 list_command (const char *arg, int from_tty)
 {
@@ -2044,6 +2117,7 @@  case COMMAND contains a | character.\n\
 \n\
 With no COMMAND, repeat the last executed command\n\
 and send its output to SHELL_COMMAND."));
+  set_cmd_completer_handle_brkchars (c, pipe_command_completer);
   add_com_alias ("|", "pipe", class_support, 0);
 
   add_com ("list", class_files, list_command, _("\
diff --git a/gdb/testsuite/gdb.base/shell.exp b/gdb/testsuite/gdb.base/shell.exp
index 2136d48919f..719435d4eb5 100644
--- a/gdb/testsuite/gdb.base/shell.exp
+++ b/gdb/testsuite/gdb.base/shell.exp
@@ -15,6 +15,8 @@ 
 
 # Test that the "shell", "!", "|" and "pipe" commands work.
 
+load_lib completion-support.exp
+
 gdb_exit
 gdb_start
 
@@ -92,8 +94,8 @@  gdb_test "p \$_shell_exitsignal" " = 2" "pipe interrupt exitsignal"
 
 # Error handling verifications.
 gdb_test "|" "Missing COMMAND" "all missing"
-gdb_test "|-d" "Missing delimiter DELIM after -d" "-d value missing"
-gdb_test "|-d    " "Missing delimiter DELIM after -d" "-d spaces value missing"
+gdb_test "|-d" "-d requires an argument" "-d value missing"
+gdb_test "|-d    " "-d requires an argument" "-d spaces value missing"
 gdb_test "| echo coucou" \
     "Missing delimiter before SHELL_COMMAND" \
     "| delimiter missing"
@@ -110,3 +112,44 @@  gdb_test "|-d! echo coucou ! wc" \
     "Missing delimiter before SHELL_COMMAND" \
     "delimiter missing due to missing space"
 
+# Completion tests.
+
+test_gdb_complete_unique \
+    "pipe" \
+    "pipe"
+
+# Note that unlike "pipe", "|" doesn't require a space.  This checks
+# that completion behaves that way too.
+foreach cmd {"pipe " "| " "|"} {
+    test_gdb_completion_offers_commands "$cmd"
+
+    # There's only one option.
+    test_gdb_complete_unique \
+	"${cmd}-" \
+	"${cmd}-d"
+
+    # Cannot complete "-d"'s argument.
+    test_gdb_complete_none "${cmd}-d "
+    test_gdb_complete_none "${cmd}-d main"
+
+    # Check completing a GDB command, with and without -d.
+    test_gdb_complete_unique \
+	"${cmd}maint set test-se" \
+	"${cmd}maint set test-settings"
+    test_gdb_complete_unique \
+	"${cmd}-d XXX maint set test-se" \
+	"${cmd}-d XXX maint set test-settings"
+
+    # Check that GDB doesn't try to complete the shell command.
+    test_gdb_complete_none \
+	"${cmd}print 1 | "
+
+    # Same, while making sure that the completer understands "-d".
+    test_gdb_complete_unique \
+	"${cmd}-d XXX maint set" \
+	"${cmd}-d XXX maint set"
+    test_gdb_complete_none \
+	"${cmd}-d set maint set"
+    test_gdb_complete_none \
+	"${cmd}-d set maint set "
+}