This is the mail archive of the gdb-cvs@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] "complete" command and completion word break characters


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=6a2c1b8790e58ce0688507b5b1f8369aa621a665

commit 6a2c1b8790e58ce0688507b5b1f8369aa621a665
Author: Pedro Alves <palves@redhat.com>
Date:   Mon Jul 17 15:30:59 2017 +0100

    "complete" command and completion word break characters
    
    The linespec/locations/completer testcase added later in the series
    tests every completion with both TAB completion and the "complete"
    command.  This exposed problems in the "complete" command, around
    determining the completion word point.
    
    First, the complete command has a too-simple approximation of what
    readline's TAB-completion code does to find the completion word point.
    Unfortunately, readline doesn't expose the functionality it uses
    internally, so to fix this this patch copies over the relevant code,
    and adjusts it a bit to better fit the use cases we need it for.
    (Specifically, our version avoids relying on the
    rl_word_break_characters, etc. globals, and instead takes those as
    arguments.)
    
    A following patch will want to use this function for TAB-completion
    too, but the "complete" command was a good excuse to split this to a
    separate patch.
    
    Then, notice how the complete_command does not call into the completer
    for the command being completed to determine the right set of word
    break characters.  It always uses the default set.  That is fixed by
    having the "complete" command call into complete_line_internal for a
    full handle_brkchars phase, just TAB-completion.
    
    gdb/ChangeLog:
    2017-07-17  Pedro Alves  <palves@redhat.com>
    
    	* cli/cli-cmds.c (complete_command): Use a completion tracker
    	along with completion_find_completion_word for handle_brkchars
    	phase.
    	* completer.c (RL_QF_SINGLE_QUOTE, RL_QF_DOUBLE_QUOTE)
    	(RL_QF_BACKSLASH, RL_QF_OTHER_QUOTE): New.
    	(struct gdb_rl_completion_word_info): New.
    	(gdb_rl_find_completion_word): New.
    	(completion_find_completion_word): New.
    	* completer.h (completion_find_completion_word): Declare.

Diff:
---
 gdb/ChangeLog      |  12 ++++
 gdb/cli/cli-cmds.c |  34 +++++------
 gdb/completer.c    | 173 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 gdb/completer.h    |  10 ++++
 4 files changed, 210 insertions(+), 19 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 2ab9a4a..554a9e5 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,17 @@
 2017-07-17  Pedro Alves  <palves@redhat.com>
 
+	* cli/cli-cmds.c (complete_command): Use a completion tracker
+	along with completion_find_completion_word for handle_brkchars
+	phase.
+	* completer.c (RL_QF_SINGLE_QUOTE, RL_QF_DOUBLE_QUOTE)
+	(RL_QF_BACKSLASH, RL_QF_OTHER_QUOTE): New.
+	(struct gdb_rl_completion_word_info): New.
+	(gdb_rl_find_completion_word): New.
+	(completion_find_completion_word): New.
+	* completer.h (completion_find_completion_word): Declare.
+
+2017-07-17  Pedro Alves  <palves@redhat.com>
+
 	* ada-lang.c (symbol_completion_match): Adjust comments.
 	(symbol_completion_add): Replace vector parameter with
 	completion_tracker parameter.  Use it.
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index fa5dd4c..fb41e24 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -246,7 +246,6 @@ static void
 complete_command (char *arg_entry, int from_tty)
 {
   const char *arg = arg_entry;
-  int argpoint;
 
   dont_repeat ();
 
@@ -264,36 +263,31 @@ complete_command (char *arg_entry, int from_tty)
 
   if (arg == NULL)
     arg = "";
-  argpoint = strlen (arg);
-
-  /* complete_line assumes that its first argument is somewhere
-     within, and except for filenames at the beginning of, the word to
-     be completed.  The following crude imitation of readline's
-     word-breaking tries to accomodate this.  */
-  const char *point = arg + argpoint;
-  while (point > arg)
-    {
-      if (strchr (rl_completer_word_break_characters, point[-1]) != 0)
-        break;
-      point--;
-    }
 
+  completion_tracker tracker_handle_brkchars;
   completion_tracker tracker_handle_completions;
 
+  int quote_char = '\0';
+  const char *word;
+
   TRY
     {
-      complete_line (tracker_handle_completions, point, arg, strlen (arg));
+      word = completion_find_completion_word (tracker_handle_brkchars,
+					      arg, &quote_char);
+
+      /* Completers must be called twice.  */
+      complete_line (tracker_handle_completions, word, arg, strlen (arg));
     }
   CATCH (ex, RETURN_MASK_ALL)
     {
       return;
     }
 
-  std::string arg_prefix (arg, point - arg);
+  std::string arg_prefix (arg, word - arg);
 
   completion_result result
     = (tracker_handle_completions.build_completion_result
-       (point, point - arg, strlen (arg)));
+       (word, word - arg, strlen (arg)));
 
   if (result.number_matches != 0)
     {
@@ -308,16 +302,18 @@ complete_command (char *arg_entry, int from_tty)
 	      printf_unfiltered ("%s%s",
 				 arg_prefix.c_str (),
 				 result.match_list[i + 1]);
+	      if (quote_char)
+		printf_unfiltered ("%c", quote_char);
 	      printf_unfiltered ("\n");
 	    }
 	}
 
       if (result.number_matches == max_completions)
 	{
-	  /* ARG_PREFIX and POINT are included in the output so that emacs
+	  /* ARG_PREFIX and WORD are included in the output so that emacs
 	     will include the message in the output.  */
 	  printf_unfiltered (_("%s%s %s\n"),
-			     arg_prefix.c_str (), point,
+			     arg_prefix.c_str (), word,
 			     get_max_completions_reached_message ());
 	}
     }
diff --git a/gdb/completer.c b/gdb/completer.c
index 85e6d88..196610d 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -212,6 +212,160 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
     (gdb_completer_file_name_break_characters);
 }
 
+/* Possible values for the found_quote flags word used by the completion
+   functions.  It says what kind of (shell-like) quoting we found anywhere
+   in the line. */
+#define RL_QF_SINGLE_QUOTE      0x01
+#define RL_QF_DOUBLE_QUOTE      0x02
+#define RL_QF_BACKSLASH         0x04
+#define RL_QF_OTHER_QUOTE       0x08
+
+/* Find the bounds of the current word for completion purposes, and
+   return a pointer to the end of the word.  This mimics (and is a
+   modified version of) readline's _rl_find_completion_word internal
+   function.
+
+   This function skips quoted substrings (characters between matched
+   pairs of characters in rl_completer_quote_characters).  We try to
+   find an unclosed quoted substring on which to do matching.  If one
+   is not found, we use the word break characters to find the
+   boundaries of the current word.  QC, if non-null, is set to the
+   opening quote character if we found an unclosed quoted substring,
+   '\0' otherwise.  DP, if non-null, is set to the value of the
+   delimiter character that caused a word break.  */
+
+struct gdb_rl_completion_word_info
+{
+  const char *word_break_characters;
+  const char *quote_characters;
+  const char *basic_quote_characters;
+};
+
+static const char *
+gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
+			     int *qc, int *dp,
+			     const char *line_buffer)
+{
+  int scan, end, found_quote, delimiter, pass_next, isbrk;
+  char quote_char;
+  const char *brkchars;
+  int point = strlen (line_buffer);
+
+  /* The algorithm below does '--point'.  Avoid buffer underflow with
+     the empty string.  */
+  if (point == 0)
+    {
+      if (qc != NULL)
+	*qc = '\0';
+      if (dp != NULL)
+	*dp = '\0';
+      return line_buffer;
+    }
+
+  end = point;
+  found_quote = delimiter = 0;
+  quote_char = '\0';
+
+  brkchars = info->word_break_characters;
+
+  if (info->quote_characters != NULL)
+    {
+      /* We have a list of characters which can be used in pairs to
+	 quote substrings for the completer.  Try to find the start of
+	 an unclosed quoted substring.  */
+      /* FOUND_QUOTE is set so we know what kind of quotes we
+	 found.  */
+      for (scan = pass_next = 0;
+	   scan < end;
+	   scan++)
+	{
+	  if (pass_next)
+	    {
+	      pass_next = 0;
+	      continue;
+	    }
+
+	  /* Shell-like semantics for single quotes -- don't allow
+	     backslash to quote anything in single quotes, especially
+	     not the closing quote.  If you don't like this, take out
+	     the check on the value of quote_char.  */
+	  if (quote_char != '\'' && line_buffer[scan] == '\\')
+	    {
+	      pass_next = 1;
+	      found_quote |= RL_QF_BACKSLASH;
+	      continue;
+	    }
+
+	  if (quote_char != '\0')
+	    {
+	      /* Ignore everything until the matching close quote
+		 char.  */
+	      if (line_buffer[scan] == quote_char)
+		{
+		  /* Found matching close.  Abandon this
+		     substring.  */
+		  quote_char = '\0';
+		  point = end;
+		}
+	    }
+	  else if (strchr (info->quote_characters, line_buffer[scan]))
+	    {
+	      /* Found start of a quoted substring.  */
+	      quote_char = line_buffer[scan];
+	      point = scan + 1;
+	      /* Shell-like quoting conventions.  */
+	      if (quote_char == '\'')
+		found_quote |= RL_QF_SINGLE_QUOTE;
+	      else if (quote_char == '"')
+		found_quote |= RL_QF_DOUBLE_QUOTE;
+	      else
+		found_quote |= RL_QF_OTHER_QUOTE;
+	    }
+	}
+    }
+
+  if (point == end && quote_char == '\0')
+    {
+      /* We didn't find an unclosed quoted substring upon which to do
+	 completion, so use the word break characters to find the
+	 substring on which to complete.  */
+      while (--point)
+	{
+	  scan = line_buffer[point];
+
+	  if (strchr (brkchars, scan) != 0)
+	    break;
+	}
+    }
+
+  /* If we are at an unquoted word break, then advance past it.  */
+  scan = line_buffer[point];
+
+  if (scan)
+    {
+      isbrk = strchr (brkchars, scan) != 0;
+
+      if (isbrk)
+	{
+	  /* If the character that caused the word break was a quoting
+	     character, then remember it as the delimiter.  */
+	  if (info->basic_quote_characters
+	      && strchr (info->basic_quote_characters, scan)
+	      && (end - point) > 1)
+	    delimiter = scan;
+
+	  point++;
+	}
+    }
+
+  if (qc != NULL)
+    *qc = quote_char;
+  if (dp != NULL)
+    *dp = delimiter;
+
+  return line_buffer + point;
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -1306,6 +1460,25 @@ gdb_completion_word_break_characters ()
 
 /* See completer.h.  */
 
+const char *
+completion_find_completion_word (completion_tracker &tracker, const char *text,
+				 int *quote_char)
+{
+  size_t point = strlen (text);
+
+  complete_line_internal (tracker, NULL, text, point, handle_brkchars);
+
+  gdb_rl_completion_word_info info;
+
+  info.word_break_characters = rl_completer_word_break_characters;
+  info.quote_characters = gdb_completer_quote_characters;
+  info.basic_quote_characters = rl_basic_quote_characters;
+
+  return gdb_rl_find_completion_word (&info, quote_char, NULL, text);
+}
+
+/* See completer.h.  */
+
 void
 completion_tracker::recompute_lowest_common_denominator (const char *new_match)
 {
diff --git a/gdb/completer.h b/gdb/completer.h
index 4b3b188..cf93cf0 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -203,6 +203,16 @@ extern void complete_line (completion_tracker &tracker,
 			   const char *line_buffer,
 			   int point);
 
+/* Find the bounds of the word in TEXT for completion purposes, and
+   return a pointer to the end of the word.  Calls the completion
+   machinery for a handle_brkchars phase (using TRACKER) to figure out
+   the right work break characters for the command in TEXT.
+   QUOTE_CHAR, if non-null, is set to the opening quote character if
+   we found an unclosed quoted substring, '\0' otherwise.  */
+extern const char *completion_find_completion_word (completion_tracker &tracker,
+						    const char *text,
+						    int *quote_char);
+
 extern char **gdb_rl_attempted_completion_function (const char *text,
 						    int start, int end);


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]