/* Line completion stuff for GDB, the GNU debugger.
- Copyright (C) 2000-2017 Free Software Foundation, Inc.
+ Copyright (C) 2000-2019 Free Software Foundation, Inc.
This file is part of GDB.
#include "expression.h"
#include "filenames.h" /* For DOSish file names. */
#include "language.h"
-#include "gdb_signals.h"
+#include "gdbsupport/gdb_signals.h"
#include "target.h"
#include "reggroups.h"
#include "user-regs.h"
#include "arch-utils.h"
#include "location.h"
#include <algorithm>
+#include "linespec.h"
#include "cli/cli-decode.h"
/* FIXME: This is needed because of lookup_cmd_1 (). We should be
/* The current completion state. */
static gdb_completer_state current_completion;
-/* An enumeration of the various things a user might
- attempt to complete for a location. */
+/* An enumeration of the various things a user might attempt to
+ complete for a location. If you change this, remember to update
+ the explicit_options array below too. */
enum explicit_location_match_type
{
/* The name of a function or method. */
MATCH_FUNCTION,
+ /* The fully-qualified name of a function or method. */
+ MATCH_QUALIFIED,
+
+ /* A line number. */
+ MATCH_LINE,
+
/* The name of a label. */
MATCH_LABEL
};
const char *text, const char *word)
{
int subsequent_name;
- VEC (char_ptr) *return_val = NULL;
subsequent_name = 0;
while (1)
{
- char *p, *q;
-
- p = rl_filename_completion_function (text, subsequent_name);
- if (p == NULL)
+ gdb::unique_xmalloc_ptr<char> p_rl
+ (rl_filename_completion_function (text, subsequent_name));
+ if (p_rl == NULL)
break;
/* We need to set subsequent_name to a non-zero value before the
continue line below, because otherwise, if the first file
subsequent_name = 1;
/* Like emacs, don't complete on old versions. Especially
useful in the "source" command. */
+ const char *p = p_rl.get ();
if (p[strlen (p) - 1] == '~')
- {
- xfree (p);
- continue;
- }
+ continue;
- if (word == text)
- /* Return exactly p. */
- q = p;
- else if (word > text)
- {
- /* Return some portion of p. */
- q = (char *) xmalloc (strlen (p) + 5);
- strcpy (q, p + (word - text));
- xfree (p);
- }
- else
- {
- /* Return some of TEXT plus p. */
- q = (char *) xmalloc (strlen (p) + (text - word) + 5);
- strncpy (q, word, text - word);
- q[text - word] = '\0';
- strcat (q, p);
- xfree (p);
- }
- tracker.add_completion (gdb::unique_xmalloc_ptr<char> (q));
+ tracker.add_completion
+ (make_completion_match_str (std::move (p_rl), text, word));
}
#if 0
/* There is no way to do this just long enough to affect quote
(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;
+}
+
+/* Find the completion word point for TEXT, emulating the algorithm
+ readline uses to find the word point, using WORD_BREAK_CHARACTERS
+ as word break characters. */
+
+static const char *
+advance_to_completion_word (completion_tracker &tracker,
+ const char *word_break_characters,
+ const char *text)
+{
+ gdb_rl_completion_word_info info;
+
+ info.word_break_characters = word_break_characters;
+ info.quote_characters = gdb_completer_quote_characters;
+ info.basic_quote_characters = rl_basic_quote_characters;
+
+ int delimiter;
+ const char *start
+ = gdb_rl_find_completion_word (&info, NULL, &delimiter, text);
+
+ tracker.advance_custom_word_point_by (start - text);
+
+ if (delimiter)
+ {
+ tracker.set_quote_char (delimiter);
+ tracker.set_suppress_append_ws (true);
+ }
+
+ return start;
+}
+
+/* See completer.h. */
+
+const char *
+advance_to_expression_complete_word_point (completion_tracker &tracker,
+ const char *text)
+{
+ const char *brk_chars = current_language->la_word_break_characters ();
+ return advance_to_completion_word (tracker, brk_chars, text);
+}
+
+/* See completer.h. */
+
+const char *
+advance_to_filename_complete_word_point (completion_tracker &tracker,
+ const char *text)
+{
+ const char *brk_chars = gdb_completer_file_name_break_characters;
+ return advance_to_completion_word (tracker, brk_chars, text);
+}
+
+/* See completer.h. */
+
+bool
+completion_tracker::completes_to_completion_word (const char *word)
+{
+ if (m_lowest_common_denominator_unique)
+ {
+ const char *lcd = m_lowest_common_denominator;
+
+ if (strncmp_iw (word, lcd, strlen (lcd)) == 0)
+ {
+ /* Maybe skip the function and complete on keywords. */
+ size_t wordlen = strlen (word);
+ if (word[wordlen - 1] == ' ')
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* See completer.h. */
+
+void
+complete_nested_command_line (completion_tracker &tracker, const char *text)
+{
+ /* Must be called from a custom-word-point completer. */
+ gdb_assert (tracker.use_custom_word_point ());
+
+ /* Disable the custom word point temporarily, because we want to
+ probe whether the command we're completing itself uses a custom
+ word point. */
+ tracker.set_use_custom_word_point (false);
+ size_t save_custom_word_point = tracker.custom_word_point ();
+
+ int quote_char = '\0';
+ const char *word = completion_find_completion_word (tracker, text,
+ "e_char);
+
+ if (tracker.use_custom_word_point ())
+ {
+ /* The command we're completing uses a custom word point, so the
+ tracker already contains the matches. We're done. */
+ return;
+ }
+
+ /* Restore the custom word point settings. */
+ tracker.set_custom_word_point (save_custom_word_point);
+ tracker.set_use_custom_word_point (true);
+
+ /* Run the handle_completions completer phase. */
+ complete_line (tracker, word, text, strlen (text));
+}
+
/* Complete on linespecs, which might be of two possible forms:
file:line
complete_files_symbols (completion_tracker &tracker,
const char *text, const char *word)
{
- int ix;
completion_list fn_list;
const char *p;
int quote_found = 0;
symbols as well as on files. */
if (colon)
{
- collect_file_symbol_completion_matches (tracker, symbol_start, word,
+ collect_file_symbol_completion_matches (tracker,
+ complete_symbol_mode::EXPRESSION,
+ symbol_name_match_type::EXPRESSION,
+ symbol_start, word,
file_to_match);
xfree (file_to_match);
}
{
size_t text_len = strlen (text);
- collect_symbol_completion_matches (tracker, symbol_start, word);
+ collect_symbol_completion_matches (tracker,
+ complete_symbol_mode::EXPRESSION,
+ symbol_name_match_type::EXPRESSION,
+ symbol_start, word);
/* If text includes characters which cannot appear in a file
name, they cannot be asking for completion on files. */
if (strcspn (text,
if (!fn_list.empty () && !tracker.have_completions ())
{
- char *fn;
-
/* If we only have file names as possible completion, we should
bring them in sync with what rl_complete expects. The
problem is that if the user types "break /foo/b TAB", and the
/* No completions at all. As the final resort, try completing
on the entire text as a symbol. */
collect_symbol_completion_matches (tracker,
+ complete_symbol_mode::EXPRESSION,
+ symbol_name_match_type::EXPRESSION,
orig_text, word);
}
}
+/* See completer.h. */
+
+completion_list
+complete_source_filenames (const char *text)
+{
+ size_t text_len = strlen (text);
+
+ /* If text includes characters which cannot appear in a file name,
+ the user cannot be asking for completion on files. */
+ if (strcspn (text,
+ gdb_completer_file_name_break_characters)
+ == text_len)
+ return make_source_files_completion_list (text, text);
+
+ return {};
+}
+
+/* Complete address and linespec locations. */
+
+static void
+complete_address_and_linespec_locations (completion_tracker &tracker,
+ const char *text,
+ symbol_name_match_type match_type)
+{
+ if (*text == '*')
+ {
+ tracker.advance_custom_word_point_by (1);
+ text++;
+ const char *word
+ = advance_to_expression_complete_word_point (tracker, text);
+ complete_expression (tracker, text, word);
+ }
+ else
+ {
+ linespec_complete (tracker, text, match_type);
+ }
+}
+
+/* The explicit location options. Note that indexes into this array
+ must match the explicit_location_match_type enumerators. */
+
+static const char *const explicit_options[] =
+ {
+ "-source",
+ "-function",
+ "-qualified",
+ "-line",
+ "-label",
+ NULL
+ };
+
+/* The probe modifier options. These can appear before a location in
+ breakpoint commands. */
+static const char *const probe_options[] =
+ {
+ "-probe",
+ "-probe-stap",
+ "-probe-dtrace",
+ NULL
+ };
+
/* Returns STRING if not NULL, the empty string otherwise. */
static const char *
collect_explicit_location_matches (completion_tracker &tracker,
struct event_location *location,
enum explicit_location_match_type what,
- const char *word)
+ const char *word,
+ const struct language_defn *language)
{
const struct explicit_location *explicit_loc
= get_explicit_location (location);
+ /* True if the option expects an argument. */
+ bool needs_arg = true;
+
+ /* Note, in the various MATCH_* below, we complete on
+ explicit_loc->foo instead of WORD, because only the former will
+ have already skipped past any quote char. */
switch (what)
{
case MATCH_SOURCE:
{
const char *source = string_or_empty (explicit_loc->source_filename);
completion_list matches
- = make_source_files_completion_list (source, word);
+ = make_source_files_completion_list (source, source);
tracker.add_completions (std::move (matches));
}
break;
case MATCH_FUNCTION:
{
const char *function = string_or_empty (explicit_loc->function_name);
- if (explicit_loc->source_filename != NULL)
- {
- const char *filename = explicit_loc->source_filename;
-
- collect_file_symbol_completion_matches (tracker,
- function, word, filename);
- }
- else
- collect_symbol_completion_matches (tracker, function, word);
+ linespec_complete_function (tracker, function,
+ explicit_loc->func_name_match_type,
+ explicit_loc->source_filename);
}
break;
+ case MATCH_QUALIFIED:
+ needs_arg = false;
+ break;
+ case MATCH_LINE:
+ /* Nothing to offer. */
+ break;
+
case MATCH_LABEL:
- /* Not supported. */
+ {
+ const char *label = string_or_empty (explicit_loc->label_name);
+ linespec_complete_label (tracker, language,
+ explicit_loc->source_filename,
+ explicit_loc->function_name,
+ explicit_loc->func_name_match_type,
+ label);
+ }
break;
default:
gdb_assert_not_reached ("unhandled explicit_location_match_type");
}
+
+ if (!needs_arg || tracker.completes_to_completion_word (word))
+ {
+ tracker.discard_completions ();
+ tracker.advance_custom_word_point_by (strlen (word));
+ complete_on_enum (tracker, explicit_options, "", "");
+ complete_on_enum (tracker, linespec_keywords, "", "");
+ }
+ else if (!tracker.have_completions ())
+ {
+ /* Maybe we have an unterminated linespec keyword at the tail of
+ the string. Try completing on that. */
+ size_t wordlen = strlen (word);
+ const char *keyword = word + wordlen;
+
+ if (wordlen > 0 && keyword[-1] != ' ')
+ {
+ while (keyword > word && *keyword != ' ')
+ keyword--;
+ /* Don't complete on keywords if we'd be completing on the
+ whole explicit linespec option. E.g., "b -function
+ thr<tab>" should not complete to the "thread"
+ keyword. */
+ if (keyword != word)
+ {
+ keyword = skip_spaces (keyword);
+
+ tracker.advance_custom_word_point_by (keyword - word);
+ complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+ }
+ }
+ else if (wordlen > 0 && keyword[-1] == ' ')
+ {
+ /* Assume that we're maybe past the explicit location
+ argument, and we didn't manage to find any match because
+ the user wants to create a pending breakpoint. Offer the
+ keyword and explicit location options as possible
+ completions. */
+ tracker.advance_custom_word_point_by (keyword - word);
+ complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+ complete_on_enum (tracker, explicit_options, keyword, keyword);
+ }
+ }
}
-/* A convenience macro to (safely) back up P to the previous word. */
+/* If the next word in *TEXT_P is any of the keywords in KEYWORDS,
+ then advance both TEXT_P and the word point in the tracker past the
+ keyword and return the (0-based) index in the KEYWORDS array that
+ matched. Otherwise, return -1. */
-static const char *
-backup_text_ptr (const char *p, const char *text)
+static int
+skip_keyword (completion_tracker &tracker,
+ const char * const *keywords, const char **text_p)
{
- while (p > text && isspace (*p))
- --p;
- for (; p > text && !isspace (p[-1]); --p)
- ;
+ const char *text = *text_p;
+ const char *after = skip_to_space (text);
+ size_t len = after - text;
- return p;
+ if (text[len] != ' ')
+ return -1;
+
+ int found = -1;
+ for (int i = 0; keywords[i] != NULL; i++)
+ {
+ if (strncmp (keywords[i], text, len) == 0)
+ {
+ if (found == -1)
+ found = i;
+ else
+ return -1;
+ }
+ }
+
+ if (found != -1)
+ {
+ tracker.advance_custom_word_point_by (len + 1);
+ text += len + 1;
+ *text_p = text;
+ return found;
+ }
+
+ return -1;
}
/* A completer function for explicit locations. This function
- completes both options ("-source", "-line", etc) and values. */
+ completes both options ("-source", "-line", etc) and values. If
+ completing a quoted string, then QUOTED_ARG_START and
+ QUOTED_ARG_END point to the quote characters. LANGUAGE is the
+ current language. */
static void
complete_explicit_location (completion_tracker &tracker,
struct event_location *location,
- const char *text, const char *word)
+ const char *text,
+ const language_defn *language,
+ const char *quoted_arg_start,
+ const char *quoted_arg_end)
{
- const char *p;
+ if (*text != '-')
+ return;
- /* Find the beginning of the word. This is necessary because
- we need to know if we are completing an option name or value. We
- don't get the leading '-' from the completer. */
- p = backup_text_ptr (word, text);
+ int keyword = skip_keyword (tracker, explicit_options, &text);
- if (*p == '-')
+ if (keyword == -1)
+ complete_on_enum (tracker, explicit_options, text, text);
+ else
{
- /* Completing on option name. */
- static const char *const keywords[] =
+ /* Completing on value. */
+ enum explicit_location_match_type what
+ = (explicit_location_match_type) keyword;
+
+ if (quoted_arg_start != NULL && quoted_arg_end != NULL)
{
- "source",
- "function",
- "line",
- "label",
- NULL
- };
+ if (quoted_arg_end[1] == '\0')
+ {
+ /* If completing a quoted string with the cursor right
+ at the terminating quote char, complete the
+ completion word without interpretation, so that
+ readline advances the cursor one whitespace past the
+ quote, even if there's no match. This makes these
+ cases behave the same:
- /* Skip over the '-'. */
- ++p;
+ before: "b -function function()"
+ after: "b -function function() "
- complete_on_enum (tracker, keywords, p, p);
- return;
- }
- else
- {
- /* Completing on value (or unknown). Get the previous word to see what
- the user is completing on. */
- size_t len, offset;
- const char *new_word, *end;
- enum explicit_location_match_type what;
- struct explicit_location *explicit_loc
- = get_explicit_location (location);
-
- /* Backup P to the previous word, which should be the option
- the user is attempting to complete. */
- offset = word - p;
- end = --p;
- p = backup_text_ptr (p, text);
- len = end - p;
-
- if (strncmp (p, "-source", len) == 0)
- {
- what = MATCH_SOURCE;
- new_word = explicit_loc->source_filename + offset;
- }
- else if (strncmp (p, "-function", len) == 0)
- {
- what = MATCH_FUNCTION;
- new_word = explicit_loc->function_name + offset;
- }
- else if (strncmp (p, "-label", len) == 0)
- {
- what = MATCH_LABEL;
- new_word = explicit_loc->label_name + offset;
- }
- else
- {
- /* The user isn't completing on any valid option name,
- e.g., "break -source foo.c [tab]". */
+ before: "b -function 'function()'"
+ after: "b -function 'function()' "
+
+ and trusts the user in this case:
+
+ before: "b -function 'not_loaded_function_yet()'"
+ after: "b -function 'not_loaded_function_yet()' "
+ */
+ tracker.add_completion (make_unique_xstrdup (text));
+ }
+ else if (quoted_arg_end[1] == ' ')
+ {
+ /* We're maybe past the explicit location argument.
+ Skip the argument without interpretation, assuming the
+ user may want to create pending breakpoint. Offer
+ the keyword and explicit location options as possible
+ completions. */
+ tracker.advance_custom_word_point_by (strlen (text));
+ complete_on_enum (tracker, linespec_keywords, "", "");
+ complete_on_enum (tracker, explicit_options, "", "");
+ }
return;
}
- /* If the user hasn't entered a search expression, e.g.,
- "break -function <TAB><TAB>", new_word will be NULL, but
- search routines require non-NULL search words. */
- if (new_word == NULL)
- new_word = "";
-
/* Now gather matches */
- collect_explicit_location_matches (tracker, location, what, new_word);
+ collect_explicit_location_matches (tracker, location, what, text,
+ language);
}
}
void
location_completer (struct cmd_list_element *ignore,
completion_tracker &tracker,
- const char *text, const char *word)
+ const char *text, const char * /* word */)
{
+ int found_probe_option = -1;
+
+ /* If we have a probe modifier, skip it. This can only appear as
+ first argument. Until we have a specific completer for probes,
+ falling back to the linespec completer for the remainder of the
+ line is better than nothing. */
+ if (text[0] == '-' && text[1] == 'p')
+ found_probe_option = skip_keyword (tracker, probe_options, &text);
+
+ const char *option_text = text;
+ int saved_word_point = tracker.custom_word_point ();
+
const char *copy = text;
- event_location_up location = string_to_explicit_location (©,
- current_language,
- 1);
- if (location != NULL)
- complete_explicit_location (tracker, location.get (),
- text, word);
+ explicit_completion_info completion_info;
+ event_location_up location
+ = string_to_explicit_location (©, current_language,
+ &completion_info);
+ if (completion_info.quoted_arg_start != NULL
+ && completion_info.quoted_arg_end == NULL)
+ {
+ /* Found an unbalanced quote. */
+ tracker.set_quote_char (*completion_info.quoted_arg_start);
+ tracker.advance_custom_word_point_by (1);
+ }
+
+ if (completion_info.saw_explicit_location_option)
+ {
+ if (*copy != '\0')
+ {
+ tracker.advance_custom_word_point_by (copy - text);
+ text = copy;
+
+ /* We found a terminator at the tail end of the string,
+ which means we're past the explicit location options. We
+ may have a keyword to complete on. If we have a whole
+ keyword, then complete whatever comes after as an
+ expression. This is mainly for the "if" keyword. If the
+ "thread" and "task" keywords gain their own completers,
+ they should be used here. */
+ int keyword = skip_keyword (tracker, linespec_keywords, &text);
+
+ if (keyword == -1)
+ {
+ complete_on_enum (tracker, linespec_keywords, text, text);
+ }
+ else
+ {
+ const char *word
+ = advance_to_expression_complete_word_point (tracker, text);
+ complete_expression (tracker, text, word);
+ }
+ }
+ else
+ {
+ tracker.advance_custom_word_point_by (completion_info.last_option
+ - text);
+ text = completion_info.last_option;
+
+ complete_explicit_location (tracker, location.get (), text,
+ current_language,
+ completion_info.quoted_arg_start,
+ completion_info.quoted_arg_end);
+
+ }
+ }
+ /* This is an address or linespec location. */
+ else if (location != NULL)
+ {
+ /* Handle non-explicit location options. */
+
+ int keyword = skip_keyword (tracker, explicit_options, &text);
+ if (keyword == -1)
+ complete_on_enum (tracker, explicit_options, text, text);
+ else
+ {
+ tracker.advance_custom_word_point_by (copy - text);
+ text = copy;
+
+ symbol_name_match_type match_type
+ = get_explicit_location (location.get ())->func_name_match_type;
+ complete_address_and_linespec_locations (tracker, text, match_type);
+ }
+ }
else
{
- /* This is an address or linespec location.
- Right now both of these are handled by the (old) linespec
- completer. */
- complete_files_symbols (tracker, text, word);
+ /* No options. */
+ complete_address_and_linespec_locations (tracker, text,
+ symbol_name_match_type::WILD);
}
+
+ /* Add matches for option names, if either:
+
+ - Some completer above found some matches, but the word point did
+ not advance (e.g., "b <tab>" finds all functions, or "b -<tab>"
+ matches all objc selectors), or;
+
+ - Some completer above advanced the word point, but found no
+ matches.
+ */
+ if ((text[0] == '-' || text[0] == '\0')
+ && (!tracker.have_completions ()
+ || tracker.custom_word_point () == saved_word_point))
+ {
+ tracker.set_custom_word_point (saved_word_point);
+ text = option_text;
+
+ if (found_probe_option == -1)
+ complete_on_enum (tracker, probe_options, text, text);
+ complete_on_enum (tracker, explicit_options, text, text);
+ }
+}
+
+/* The corresponding completer_handle_brkchars
+ implementation. */
+
+static void
+location_completer_handle_brkchars (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text,
+ const char *word_ignored)
+{
+ tracker.set_use_custom_word_point (true);
+
+ location_completer (ignore, tracker, text, NULL);
}
/* Helper for expression_completer which recursively adds field and
static void
add_struct_fields (struct type *type, completion_list &output,
- char *fieldname, int namelen)
+ const char *fieldname, int namelen)
{
int i;
int computed_type_name = 0;
{
if (!computed_type_name)
{
- type_name = type_name_no_tag (type);
+ type_name = TYPE_NAME (type);
computed_type_name = 1;
}
/* Omit constructors from the completion list. */
}
}
-/* Complete on expressions. Often this means completing on symbol
- names, but some language parsers also have support for completing
- field names. */
+/* See completer.h. */
-static void
+void
complete_expression (completion_tracker &tracker,
const char *text, const char *word)
{
struct type *type = NULL;
- char *fieldname;
+ gdb::unique_xmalloc_ptr<char> fieldname;
enum type_code code = TYPE_CODE_UNDEF;
/* Perform a tentative parse of the expression, to see whether a
field completion is required. */
- fieldname = NULL;
- TRY
+ try
{
type = parse_expression_for_completion (text, &fieldname, &code);
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
return;
}
- END_CATCH
- if (fieldname && type)
+ if (fieldname != nullptr && type)
{
for (;;)
{
if (TYPE_CODE (type) == TYPE_CODE_UNION
|| TYPE_CODE (type) == TYPE_CODE_STRUCT)
{
- int flen = strlen (fieldname);
completion_list result;
- add_struct_fields (type, result, fieldname, flen);
- xfree (fieldname);
+ add_struct_fields (type, result, fieldname.get (),
+ strlen (fieldname.get ()));
tracker.add_completions (std::move (result));
return;
}
}
- else if (fieldname && code != TYPE_CODE_UNDEF)
+ else if (fieldname != nullptr && code != TYPE_CODE_UNDEF)
{
- VEC (char_ptr) *result;
- struct cleanup *cleanup = make_cleanup (xfree, fieldname);
-
- collect_symbol_completion_matches_type (tracker, fieldname, fieldname,
- code);
- do_cleanups (cleanup);
+ collect_symbol_completion_matches_type (tracker, fieldname.get (),
+ fieldname.get (), code);
return;
}
- xfree (fieldname);
complete_files_symbols (tracker, text, word);
}
completion_tracker &tracker,
const char *text, const char *word)
{
- collect_symbol_completion_matches (tracker, text, word);
+ collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+ symbol_name_match_type::EXPRESSION,
+ text, word);
}
/* Here are some useful test cases for completion. FIXME: These
enum complete_line_internal_reason
{
/* Preliminary phase, called by gdb_completion_word_break_characters
- function, is used to determine the correct set of chars that are
- word delimiters depending on the current command in line_buffer.
- No completion list should be generated; the return value should
- be NULL. This is checked by an assertion. */
+ function, is used to either:
+
+ #1 - Determine the set of chars that are word delimiters
+ depending on the current command in line_buffer.
+
+ #2 - Manually advance RL_POINT to the "word break" point instead
+ of letting readline do it (based on too-simple character
+ matching).
+
+ Simpler completers that just pass a brkchars array to readline
+ (#1 above) must defer generating the completions to the main
+ phase (below). No completion list should be generated in this
+ phase.
+
+ OTOH, completers that manually advance the word point(#2 above)
+ must set "use_custom_word_point" in the tracker and generate
+ their completion in this phase. Note that this is the convenient
+ thing to do since they'll be parsing the input line anyway. */
handle_brkchars,
/* Main phase, called by complete_line function, is used to get the
word = tmp_command + point - strlen (text);
}
- if (point == 0)
+ /* Move P up to the start of the command. */
+ p = skip_spaces (p);
+
+ if (*p == '\0')
{
- /* An empty line we want to consider ambiguous; that is, it
- could be any command. */
+ /* An empty line is ambiguous; that is, it could be any
+ command. */
c = CMD_LIST_AMBIGUOUS;
result_list = 0;
}
p++;
}
+ tracker.advance_custom_word_point_by (p - tmp_command);
+
if (!c)
{
/* It is an unrecognized command. So there are no
break;
}
+ /* Move the custom word point back too. */
+ tracker.advance_custom_word_point_by (q - p);
+
if (reason != handle_brkchars)
complete_on_cmdlist (result_list, tracker, q, word,
ignore_help_classes);
const char *line_buffer, int point,
complete_line_internal_reason reason)
{
- TRY
+ try
{
complete_line_internal_1 (tracker, text, line_buffer, point, reason);
}
- CATCH (except, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &except)
{
if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
- throw_exception (except);
+ throw;
}
}
completion_tracker::completion_tracker ()
{
m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
- htab_hash_string, (htab_eq) streq,
+ htab_hash_string, streq_hash,
+ NULL, xcalloc, xfree);
+}
+
+/* See completer.h. */
+
+void
+completion_tracker::discard_completions ()
+{
+ xfree (m_lowest_common_denominator);
+ m_lowest_common_denominator = NULL;
+
+ m_lowest_common_denominator_unique = false;
+
+ m_entries_vec.clear ();
+
+ htab_delete (m_entries_hash);
+ m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+ htab_hash_string, streq_hash,
NULL, xcalloc, xfree);
}
/* See completer.h. */
bool
-completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
+completion_tracker::maybe_add_completion
+ (gdb::unique_xmalloc_ptr<char> name,
+ completion_match_for_lcd *match_for_lcd,
+ const char *text, const char *word)
{
void **slot;
slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
if (*slot == HTAB_EMPTY_ENTRY)
{
- const char *match_for_lcd_str = name.get ();
+ const char *match_for_lcd_str = NULL;
+
+ if (match_for_lcd != NULL)
+ match_for_lcd_str = match_for_lcd->finish ();
- recompute_lowest_common_denominator (match_for_lcd_str);
+ if (match_for_lcd_str == NULL)
+ match_for_lcd_str = name.get ();
+
+ gdb::unique_xmalloc_ptr<char> lcd
+ = make_completion_match_str (match_for_lcd_str, text, word);
+
+ recompute_lowest_common_denominator (std::move (lcd));
*slot = name.get ();
m_entries_vec.push_back (std::move (name));
/* See completer.h. */
void
-completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name,
+ completion_match_for_lcd *match_for_lcd,
+ const char *text, const char *word)
{
- if (!maybe_add_completion (std::move (name)))
+ if (!maybe_add_completion (std::move (name), match_for_lcd, text, word))
throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
}
add_completion (std::move (candidate));
}
+/* Helper for the make_completion_match_str overloads. Returns NULL
+ as an indication that we want MATCH_NAME exactly. It is up to the
+ caller to xstrdup that string if desired. */
+
+static char *
+make_completion_match_str_1 (const char *match_name,
+ const char *text, const char *word)
+{
+ char *newobj;
+
+ if (word == text)
+ {
+ /* Return NULL as an indication that we want MATCH_NAME
+ exactly. */
+ return NULL;
+ }
+ else if (word > text)
+ {
+ /* Return some portion of MATCH_NAME. */
+ newobj = xstrdup (match_name + (word - text));
+ }
+ else
+ {
+ /* Return some of WORD plus MATCH_NAME. */
+ size_t len = strlen (match_name);
+ newobj = (char *) xmalloc (text - word + len + 1);
+ memcpy (newobj, word, text - word);
+ memcpy (newobj + (text - word), match_name, len + 1);
+ }
+
+ return newobj;
+}
+
+/* See completer.h. */
+
+gdb::unique_xmalloc_ptr<char>
+make_completion_match_str (const char *match_name,
+ const char *text, const char *word)
+{
+ char *newobj = make_completion_match_str_1 (match_name, text, word);
+ if (newobj == NULL)
+ newobj = xstrdup (match_name);
+ return gdb::unique_xmalloc_ptr<char> (newobj);
+}
+
+/* See completer.h. */
+
+gdb::unique_xmalloc_ptr<char>
+make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
+ const char *text, const char *word)
+{
+ char *newobj = make_completion_match_str_1 (match_name.get (), text, word);
+ if (newobj == NULL)
+ return std::move (match_name);
+ return gdb::unique_xmalloc_ptr<char> (newobj);
+}
+
+/* See complete.h. */
+
+completion_result
+complete (const char *line, char const **word, int *quote_char)
+{
+ completion_tracker tracker_handle_brkchars;
+ completion_tracker tracker_handle_completions;
+ completion_tracker *tracker;
+
+ /* The WORD should be set to the end of word to complete. We initialize
+ to the completion point which is assumed to be at the end of LINE.
+ This leaves WORD to be initialized to a sensible value in cases
+ completion_find_completion_word() fails i.e., throws an exception.
+ See bug 24587. */
+ *word = line + strlen (line);
+
+ try
+ {
+ *word = completion_find_completion_word (tracker_handle_brkchars,
+ line, quote_char);
+
+ /* Completers that provide a custom word point in the
+ handle_brkchars phase also compute their completions then.
+ Completers that leave the completion word handling to readline
+ must be called twice. */
+ if (tracker_handle_brkchars.use_custom_word_point ())
+ tracker = &tracker_handle_brkchars;
+ else
+ {
+ complete_line (tracker_handle_completions, *word, line, strlen (line));
+ tracker = &tracker_handle_completions;
+ }
+ }
+ catch (const gdb_exception &ex)
+ {
+ return {};
+ }
+
+ return tracker->build_completion_result (*word, *word - line, strlen (line));
+}
+
+
/* Generate completions all at once. Does nothing if max_completions
is 0. If max_completions is non-negative, this will collect at
most max_completions strings.
continue;
if (strncasecmp (signame, word, len) == 0)
- {
- gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
- tracker.add_completion (std::move (copy));
- }
+ tracker.add_completion (make_unique_xstrdup (signame));
}
}
i++)
{
if (*name != '\0' && strncmp (word, name, len) == 0)
- {
- gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
- tracker.add_completion (std::move (copy));
- }
+ tracker.add_completion (make_unique_xstrdup (name));
}
}
{
name = reggroup_name (group);
if (strncmp (word, name, len) == 0)
- {
- gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
- tracker.add_completion (std::move (copy));
- }
+ tracker.add_completion (make_unique_xstrdup (name));
}
}
}
if (fn == filename_completer)
return filename_completer_handle_brkchars;
+ if (fn == location_completer)
+ return location_completer_handle_brkchars;
+
if (fn == command_completer)
return command_completer_handle_brkchars;
return default_completer_handle_brkchars;
}
+/* Used as brkchars when we want to tell readline we have a custom
+ word point. We do that by making our rl_completion_word_break_hook
+ set RL_POINT to the desired word point, and return the character at
+ the word break point as the break char. This is two bytes in order
+ to fit one break character plus the terminating null. */
+static char gdb_custom_word_point_brkchars[2];
+
+/* Since rl_basic_quote_characters is not completer-specific, we save
+ its original value here, in order to be able to restore it in
+ gdb_rl_attempted_completion_function. */
+static const char *gdb_org_rl_basic_quote_characters = rl_basic_quote_characters;
+
/* Get the list of chars that are considered as word breaks
for the current command. */
complete_line_internal (tracker, NULL, rl_line_buffer,
rl_point, handle_brkchars);
+ if (tracker.use_custom_word_point ())
+ {
+ gdb_assert (tracker.custom_word_point () > 0);
+ rl_point = tracker.custom_word_point () - 1;
+
+ gdb_assert (rl_point >= 0 && rl_point < strlen (rl_line_buffer));
+
+ gdb_custom_word_point_brkchars[0] = rl_line_buffer[rl_point];
+ rl_completer_word_break_characters = gdb_custom_word_point_brkchars;
+ rl_completer_quote_characters = NULL;
+
+ /* Clear this too, so that if we're completing a quoted string,
+ readline doesn't consider the quote character a delimiter.
+ If we didn't do this, readline would auto-complete {b
+ 'fun<tab>} to {'b 'function()'}, i.e., add the terminating
+ \', but, it wouldn't append the separator space either, which
+ is not desirable. So instead we take care of appending the
+ quote character to the LCD ourselves, in
+ gdb_rl_attempted_completion_function. Since this global is
+ not just completer-specific, we'll restore it back to the
+ default in gdb_rl_attempted_completion_function. */
+ rl_basic_quote_characters = NULL;
+ }
+
return rl_completer_word_break_characters;
}
/* New completion starting. */
current_completion.aborted = false;
- TRY
+ try
{
return gdb_completion_word_break_characters_throw ();
}
- CATCH (ex, RETURN_MASK_ALL)
+ catch (const gdb_exception &ex)
{
/* Set this to that gdb_rl_attempted_completion_function knows
to abort early. */
current_completion.aborted = true;
}
- END_CATCH
return NULL;
}
/* 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);
+
+ if (tracker.use_custom_word_point ())
+ {
+ gdb_assert (tracker.custom_word_point () > 0);
+ *quote_char = tracker.quote_char ();
+ return text + tracker.custom_word_point ();
+ }
+
+ 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)
+completion_tracker::recompute_lowest_common_denominator
+ (gdb::unique_xmalloc_ptr<char> &&new_match_up)
{
if (m_lowest_common_denominator == NULL)
{
/* We don't have a lowest common denominator yet, so simply take
- the whole NEW_MATCH as being it. */
- m_lowest_common_denominator = xstrdup (new_match);
+ the whole NEW_MATCH_UP as being it. */
+ m_lowest_common_denominator = new_match_up.release ();
m_lowest_common_denominator_unique = true;
}
else
{
/* Find the common denominator between the currently-known
- lowest common denominator and NEW_MATCH. That becomes the
+ lowest common denominator and NEW_MATCH_UP. That becomes the
new lowest common denominator. */
size_t i;
+ const char *new_match = new_match_up.get ();
for (i = 0;
(new_match[i] != '\0'
}
}
+/* See completer.h. */
+
+void
+completion_tracker::advance_custom_word_point_by (int len)
+{
+ m_custom_word_point += len;
+}
+
/* Build a new C string that is a copy of LCD with the whitespace of
ORIG/ORIG_LEN preserved.
{
while (p_orig < orig_end && *p_orig == ' ')
res += *p_orig++;
- p_lcd = skip_spaces_const (p_lcd);
+ p_lcd = skip_spaces (p_lcd);
}
else
{
if (m_lowest_common_denominator_unique)
{
+ /* We don't rely on readline appending the quote char as
+ delimiter as then readline wouldn't append the ' ' after the
+ completion. */
+ char buf[2] = { (char) quote_char () };
+
+ match_list[0] = reconcat (match_list[0], match_list[0],
+ buf, (char *) NULL);
match_list[1] = NULL;
- /* If we already have a space at the end of the match, tell
- readline to skip appending another. */
+ /* If the tracker wants to, or we already have a space at the
+ end of the match, tell readline to skip appending
+ another. */
bool completion_suppress_append
- = (match_list[0][strlen (match_list[0]) - 1] == ' ');
+ = (suppress_append_ws ()
+ || match_list[0][strlen (match_list[0]) - 1] == ' ');
return completion_result (match_list, 1, completion_suppress_append);
}
return ret;
}
-/* Compare C strings for std::sort. */
-
-static bool
-compare_cstrings (const char *str1, const char *str2)
-{
- return strcmp (str1, str2) < 0;
-}
-
/* See completer.h */
void
static char **
gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
{
- /* Completers must be called twice. If rl_point (i.e., END) is at
- column 0, then readline skips the the handle_brkchars phase, and
- so we create a tracker now in that case too. */
- delete current_completion.tracker;
- current_completion.tracker = new completion_tracker ();
+ /* Completers that provide a custom word point in the
+ handle_brkchars phase also compute their completions then.
+ Completers that leave the completion word handling to readline
+ must be called twice. If rl_point (i.e., END) is at column 0,
+ then readline skips the handle_brkchars phase, and so we create a
+ tracker now in that case too. */
+ if (end == 0 || !current_completion.tracker->use_custom_word_point ())
+ {
+ delete current_completion.tracker;
+ current_completion.tracker = new completion_tracker ();
- complete_line (*current_completion.tracker, text,
- rl_line_buffer, rl_point);
+ complete_line (*current_completion.tracker, text,
+ rl_line_buffer, rl_point);
+ }
completion_tracker &tracker = *current_completion.tracker;
char **
gdb_rl_attempted_completion_function (const char *text, int start, int end)
{
+ /* Restore globals that might have been tweaked in
+ gdb_completion_word_break_characters. */
+ rl_basic_quote_characters = gdb_org_rl_basic_quote_characters;
+
/* If we end up returning NULL, either on error, or simple because
there are no matches, inhibit readline's default filename
completer. */
if (current_completion.aborted)
return NULL;
- TRY
+ try
{
return gdb_rl_attempted_completion_function_throw (text, start, end);
}
- CATCH (ex, RETURN_MASK_ALL)
+ catch (const gdb_exception &ex)
{
}
- END_CATCH
return NULL;
}
}
}
}
-\f
-extern initialize_file_ftype _initialize_completer; /* -Wmissing-prototypes */
void
_initialize_completer (void)