aboutsummaryrefslogtreecommitdiff
path: root/gdb/completer.c
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2017-07-17 14:45:59 +0100
committerPedro Alves <palves@redhat.com>2017-07-17 14:45:59 +0100
commiteb3ff9a55175dcdac8328b558d54951a14d719b1 (patch)
tree002edb35666103e05c3604870777236c6de628b3 /gdb/completer.c
parent6e1dbf8cda1c66256179d0b15c96bc97ea5cf7b3 (diff)
Introduce class completion_tracker & rewrite completion<->readline interaction
This patch reworks the whole completion machinery, and prepares it for later enhancements. Adds a new "completion_tracker" class that is meant to hold everything about the state of the current completion operation. This class now has the responsibility of tracking the list of completion matches, and checking whether the max completions limit has been reached. You can look at this as this patch starting out by C++fying the existing "completion_tracker" in symtab.c (it's just an htab_t typedef currently), moving it to completer.h/c, and then making it a class/generalizing/enhancing it. Unlike with the current tracking, completion_tracker now checks whether the limit has been reached on each completion match list insertion. This both simplifies the max-completions handling code (maybe_add_completion_enum is gone, for example), and is a prerequisite for follow up patches. The current completion_tracker is only used for symbol completions, and the symbol code gets at the current instance via globals. This patch cleans that up by adding a completion_tracker reference to the signature of the completion functions, and passing the tracker around everywhere necessary. Then, the patch changes how the completion match list is handed over to readline. Currently, we're using the rl_completion_entry_function readline entry point, and the patch switches to rl_attempted_completion_function. A following patch will want to let GDB itself decide the common completion prefix between all matches (what readline calls the "lowest common denominator"), instead of having readline compute it, and that's not possible with the rl_completion_entry_function entry point. Also, rl_attempted_completion_function lets GDB hand over the match list to readline as an array in one go instead of passing down matches one by one, so from that angle it's a nicer entry point anyway. Lastly, the patch catches exceptions around the readline entry points, because we can't let C++ exceptions cross readline. We handle that in the readline input entry point, but the completion entry point isn't guarded, so GDB can abort if completion throws. E.g., in current master: (gdb) b -function "fun<tab> terminate called after throwing an instance of 'gdb_exception_RETURN_MASK_ERROR' Aborted (core dumped) This patch fixes that. This will be exercised in the new tests added later on in the series. gdb/ChangeLog: 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. (ada_make_symbol_completion_list): Rename to... (ada_collect_symbol_completion_matches): ... this. Add completion_tracker parameter and use it. (ada_language_defn): Adjust. * break-catch-syscall.c (catch_syscall_completer): Adjust prototype and work with completion_tracker instead of VEC. * breakpoint.c (condition_completer): Adjust prototype and work with completion_tracker instead of VEC. * c-lang.c (c_language_defn, cplus_language_defn) (asm_language_defn, minimal_language_defn): Adjust to renames. * cli/cli-cmds.c (complete_command): Rework using completion_tracker. Catch exceptions when completing. * cli/cli-decode.c (integer_unlimited_completer) (complete_on_cmdlist, complete_on_enum): Adjust prototype and work with completion_tracker instead of VEC. * command.h (struct completion_tracker): Forward declare. (completer_ftype, completer_handle_brkchars_ftype): Change types. (complete_on_cmdlist, complete_on_enum): Adjust. * completer.c: Include <algorithm>. (struct gdb_completer_state): New. (current_completion): New global. (readline_line_completion_function): Delete. (noop_completer, filename_completer) (filename_completer_handle_brkchars, complete_files_symbols) (linespec_location_completer): Adjust to work with a completion_tracker instead of a VEC. (string_or_empty): New. (collect_explicit_location_matches): Adjust to work with a completion_tracker instead of a VEC. (explicit_location_completer): Rename to ... (complete_explicit_location): ... this and adjust to work with a completion_tracker instead of a VEC. (location_completer): Adjust to work with a completion_tracker instead of a VEC. (add_struct_fields): Adjust to work with a completion_list instead of VEC. (expression_completer): Rename to ... (complete_expression): ... this and adjust to work with a completion_tracker instead of a VEC. Use complete_files_symbols. (expression_completer): Reimplement on top of complete_expression. (symbol_completer): Adjust to work with a completion_tracker instead of a VEC. (enum complete_line_internal_reason): Add describing comments. (complete_line_internal_normal_command): Adjust to work with a completion_tracker instead of a VEC. (complete_line_internal): Rename to ... (complete_line_internal_1): ... this and adjust to work with a completion_tracker instead of a VEC. Assert TEXT is NULL in the handle_brkchars phase. (new_completion_tracker): Delete. (complete_line_internal): Reimplement as TRY/CATCH wrapper around complete_line_internal_1. (free_completion_tracker): Delete. (INITIAL_COMPLETION_HTAB_SIZE): New. (completion_tracker::completion_tracker) (completion_tracker::~completion_tracker): New. (maybe_add_completion): Delete. (completion_tracker::maybe_add_completion) (completion_tracker::add_completion) (completion_tracker::add_completions): New. (throw_max_completions_reached_error): Delete. (complete_line): Adjust to work with a completion_tracker instead of a VEC. Don't create a completion_tracker_t or check for max completions here. (command_completer, command_completer_handle_brkchars) (signal_completer, reg_or_group_completer_1) (reg_or_group_completer, default_completer_handle_brkchars): Adjust to work with a completion_tracker. (gdb_completion_word_break_characters_throw): New. (gdb_completion_word_break_characters): Reimplement. (line_completion_function): Delete. (completion_tracker::recompute_lowest_common_denominator) (expand_preserving_ws) (completion_tracker::build_completion_result) (completion_result::completion_result) (completion_result::completion_result) (completion_result::~completion_result) (completion_result::completion_result) (completion_result::release_match_list, compare_cstrings) (completion_result::sort_match_list) (completion_result::reset_match_list) (gdb_rl_attempted_completion_function_throw) (gdb_rl_attempted_completion_function): New. * completer.h (completion_list, struct completion_result) (class completion_tracker): New. (complete_line): Add completion_tracker parameter. (readline_line_completion_function): Delete. (gdb_rl_attempted_completion_function): New. (noop_completer, filename_completer, expression_completer) (location_completer, symbol_completer, command_completer) (signal_completer, reg_or_group_completer): Update prototypes. (completion_tracker_t, new_completion_tracker) (make_cleanup_free_completion_tracker): Delete. (enum maybe_add_completion_enum): Delete. (maybe_add_completion): Delete. (throw_max_completions_reached_error): Delete. * corefile.c (complete_set_gnutarget): Adjust to work with a completion_tracker instead of a VEC. * cp-abi.c (cp_abi_completer): Adjust to work with a completion_tracker instead of a VEC. * d-lang.c (d_language_defn): Adjust. * disasm.c (disassembler_options_completer): Adjust to work with a completion_tracker instead of a VEC. * f-lang.c (f_make_symbol_completion_list): Rename to ... (f_collect_symbol_completion_matches): ... this. Adjust to work with a completion_tracker instead of a VEC. (f_language_defn): Adjust. * go-lang.c (go_language_defn): Adjust. * guile/scm-cmd.c (cmdscm_add_completion, cmdscm_completer): Adjust to work with a completion_tracker instead of a VEC. * infrun.c (handle_completer): Likewise. * interps.c (interpreter_completer): Likewise. * interps.h (interpreter_completer): Likewise. * language.c (unknown_language_defn, auto_language_defn) (local_language_defn): Adjust. * language.h (language_defn::la_make_symbol_completion_list): Rename to ... (language_defn::la_collect_symbol_completion_matches): ... this and adjust to work with a completion_tracker instead of a VEC. * m2-lang.c (m2_language_defn): Adjust. * objc-lang.c (objc_language_defn): Adjust. * opencl-lang.c (opencl_language_defn): Adjust. * p-lang.c (pascal_language_defn): Adjust. * python/py-cmd.c (cmdpy_completer_helper): Handle NULL word. (cmdpy_completer_handle_brkchars, cmdpy_completer): Adjust to work with a completion_tracker. * rust-lang.c (rust_language_defn): Adjust. * symtab.c (free_completion_list, do_free_completion_list) (return_val, completion_tracker): Delete. (completion_list_add_name, completion_list_add_symbol) (completion_list_add_msymbol, completion_list_objc_symbol) (completion_list_add_fields, add_symtab_completions): Add completion_tracker parameter and use it. (default_make_symbol_completion_list_break_on_1): Rename to... (default_collect_symbol_completion_matches_break_on): ... this. Add completion_tracker parameter and use it instead of allocating a completion tracker here. (default_make_symbol_completion_list_break_on): Delete old implementation. (default_make_symbol_completion_list): Delete. (default_collect_symbol_completion_matches): New. (make_symbol_completion_list): Delete. (collect_symbol_completion_matches): New. (make_symbol_completion_type): Rename to ... (collect_symbol_completion_matches_type): ... this. Add completion_tracker parameter and use it instead of VEC. (make_file_symbol_completion_list_1): Rename to... (collect_file_symbol_completion_matches): ... this. Add completion_tracker parameter and use it instead of VEC. (make_file_symbol_completion_list): Delete. (add_filename_to_list): Use completion_list instead of a VEC. (add_partial_filename_data::list): Now a completion_list. (make_source_files_completion_list): Work with a completion_list instead of a VEC. * symtab.h: Include "completer.h". (default_make_symbol_completion_list_break_on) (default_make_symbol_completion_list, make_symbol_completion_list) (make_symbol_completion_type, make_file_symbol_completion_list) (make_source_files_completion_list): Delete. (default_collect_symbol_completion_matches_break_on) (default_collect_symbol_completion_matches) (collect_symbol_completion_matches) (collect_symbol_completion_matches_type) (collect_file_symbol_completion_matches) (make_source_files_completion_list): New. * top.c (init_main): Don't install a rl_completion_entry_function hook. Install a rl_attempted_completion_function hook instead. * tui/tui-layout.c (layout_completer): Adjust to work with a completion_tracker. * tui/tui-regs.c (tui_reggroup_completer): * tui/tui-win.c (window_name_completer, focus_completer) (winheight_completer): Adjust to work with a completion_tracker. * value.c: Include "completer.h". (complete_internalvar): Adjust to work with a completion_tracker. * value.h (complete_internalvar): Likewise.
Diffstat (limited to 'gdb/completer.c')
-rw-r--r--gdb/completer.c931
1 files changed, 578 insertions, 353 deletions
diff --git a/gdb/completer.c b/gdb/completer.c
index 625ff4f561..85e6d88b74 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -28,7 +28,7 @@
#include "user-regs.h"
#include "arch-utils.h"
#include "location.h"
-
+#include <algorithm>
#include "cli/cli-decode.h"
/* FIXME: This is needed because of lookup_cmd_1 (). We should be
@@ -44,6 +44,25 @@
#include "completer.h"
+/* Misc state that needs to be tracked across several different
+ readline completer entry point calls, all related to a single
+ completion invocation. */
+
+struct gdb_completer_state
+{
+ /* The current completion's completion tracker. This is a global
+ because a tracker can be shared between the handle_brkchars and
+ handle_completion phases, which involves different readline
+ callbacks. */
+ completion_tracker *tracker = NULL;
+
+ /* Whether the current completion was aborted. */
+ bool aborted = false;
+};
+
+/* 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. */
@@ -60,10 +79,6 @@ enum explicit_location_match_type
};
/* Prototypes for local functions. */
-static
-char *line_completion_function (const char *text, int matches,
- char *line_buffer,
- int point);
/* readline uses the word breaks for two things:
(1) In figuring out where to point the TEXT parameter to the
@@ -113,28 +128,21 @@ get_gdb_completer_quote_characters (void)
return gdb_completer_quote_characters;
}
-/* Line completion interface function for readline. */
-
-char *
-readline_line_completion_function (const char *text, int matches)
-{
- return line_completion_function (text, matches,
- rl_line_buffer, rl_point);
-}
-
/* This can be used for functions which don't want to complete on
symbols but don't want to complete on anything else either. */
-VEC (char_ptr) *
+
+void
noop_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *prefix)
{
- return NULL;
}
/* Complete on filenames. */
-VEC (char_ptr) *
-filename_completer (struct cmd_list_element *ignore,
+void
+filename_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
int subsequent_name;
@@ -180,7 +188,7 @@ filename_completer (struct cmd_list_element *ignore,
strcat (q, p);
xfree (p);
}
- VEC_safe_push (char_ptr, return_val, q);
+ tracker.add_completion (gdb::unique_xmalloc_ptr<char> (q));
}
#if 0
/* There is no way to do this just long enough to affect quote
@@ -190,7 +198,6 @@ filename_completer (struct cmd_list_element *ignore,
with respect to inserting quotes. */
rl_completer_word_break_characters = "";
#endif
- return return_val;
}
/* The corresponding completer_handle_brkchars
@@ -198,6 +205,7 @@ filename_completer (struct cmd_list_element *ignore,
static void
filename_completer_handle_brkchars (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
set_rl_completer_word_break_characters
@@ -213,13 +221,12 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
This is intended to be used in commands that set breakpoints
etc. */
-static VEC (char_ptr) *
-linespec_location_completer (struct cmd_list_element *ignore,
- const char *text, const char *word)
+static void
+complete_files_symbols (completion_tracker &tracker,
+ const char *text, const char *word)
{
- int n_syms, n_files, ix;
- VEC (char_ptr) *fn_list = NULL;
- VEC (char_ptr) *list = NULL;
+ int ix;
+ completion_list fn_list;
const char *p;
int quote_found = 0;
int quoted = *text == '\'' || *text == '"';
@@ -228,7 +235,6 @@ linespec_location_completer (struct cmd_list_element *ignore,
char *file_to_match = NULL;
const char *symbol_start = text;
const char *orig_text = text;
- size_t text_len;
/* Do we have an unquoted colon, as in "break foo.c:bar"? */
for (p = text; *p != '\0'; ++p)
@@ -269,7 +275,6 @@ linespec_location_completer (struct cmd_list_element *ignore,
if (quoted)
text++;
- text_len = strlen (text);
/* Where is the file name? */
if (colon)
@@ -291,44 +296,23 @@ linespec_location_completer (struct cmd_list_element *ignore,
symbols as well as on files. */
if (colon)
{
- list = make_file_symbol_completion_list (symbol_start, word,
- file_to_match);
+ collect_file_symbol_completion_matches (tracker, symbol_start, word,
+ file_to_match);
xfree (file_to_match);
}
else
{
- list = make_symbol_completion_list (symbol_start, word);
+ size_t text_len = strlen (text);
+
+ collect_symbol_completion_matches (tracker, 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 (strcspn (text,
gdb_completer_file_name_break_characters) == text_len)
fn_list = make_source_files_completion_list (text, text);
}
- n_syms = VEC_length (char_ptr, list);
- n_files = VEC_length (char_ptr, fn_list);
-
- /* Catenate fn_list[] onto the end of list[]. */
- if (!n_syms)
- {
- VEC_free (char_ptr, list); /* Paranoia. */
- list = fn_list;
- fn_list = NULL;
- }
- else
- {
- char *fn;
-
- for (ix = 0; VEC_iterate (char_ptr, fn_list, ix, fn); ++ix)
- VEC_safe_push (char_ptr, list, fn);
- VEC_free (char_ptr, fn_list);
- }
-
- if (n_syms && n_files)
- {
- /* Nothing. */
- }
- else if (n_files)
+ if (!fn_list.empty () && !tracker.have_completions ())
{
char *fn;
@@ -347,31 +331,41 @@ linespec_location_completer (struct cmd_list_element *ignore,
completion, because rl_complete will prepend "/foo/" to each
candidate completion. The loop below removes that leading
part. */
- for (ix = 0; VEC_iterate (char_ptr, list, ix, fn); ++ix)
+ for (const auto &fn_up: fn_list)
{
- memmove (fn, fn + (word - text),
- strlen (fn) + 1 - (word - text));
+ char *fn = fn_up.get ();
+ memmove (fn, fn + (word - text), strlen (fn) + 1 - (word - text));
}
}
- else if (!n_syms)
+
+ tracker.add_completions (std::move (fn_list));
+
+ if (!tracker.have_completions ())
{
/* No completions at all. As the final resort, try completing
on the entire text as a symbol. */
- list = make_symbol_completion_list (orig_text, word);
+ collect_symbol_completion_matches (tracker,
+ orig_text, word);
}
+}
- return list;
+/* Returns STRING if not NULL, the empty string otherwise. */
+
+static const char *
+string_or_empty (const char *string)
+{
+ return string != NULL ? string : "";
}
/* A helper function to collect explicit location matches for the given
LOCATION, which is attempting to match on WORD. */
-static VEC (char_ptr) *
-collect_explicit_location_matches (struct event_location *location,
+static void
+collect_explicit_location_matches (completion_tracker &tracker,
+ struct event_location *location,
enum explicit_location_match_type what,
const char *word)
{
- VEC (char_ptr) *matches = NULL;
const struct explicit_location *explicit_loc
= get_explicit_location (location);
@@ -379,26 +373,25 @@ collect_explicit_location_matches (struct event_location *location,
{
case MATCH_SOURCE:
{
- const char *text = (explicit_loc->source_filename == NULL
- ? "" : explicit_loc->source_filename);
-
- matches = make_source_files_completion_list (text, word);
+ const char *source = string_or_empty (explicit_loc->source_filename);
+ completion_list matches
+ = make_source_files_completion_list (source, word);
+ tracker.add_completions (std::move (matches));
}
break;
case MATCH_FUNCTION:
{
- const char *text = (explicit_loc->function_name == NULL
- ? "" : explicit_loc->function_name);
-
+ const char *function = string_or_empty (explicit_loc->function_name);
if (explicit_loc->source_filename != NULL)
{
const char *filename = explicit_loc->source_filename;
- matches = make_file_symbol_completion_list (text, word, filename);
+ collect_file_symbol_completion_matches (tracker,
+ function, word, filename);
}
- else
- matches = make_symbol_completion_list (text, word);
+ else
+ collect_symbol_completion_matches (tracker, function, word);
}
break;
@@ -409,8 +402,6 @@ collect_explicit_location_matches (struct event_location *location,
default:
gdb_assert_not_reached ("unhandled explicit_location_match_type");
}
-
- return matches;
}
/* A convenience macro to (safely) back up P to the previous word. */
@@ -429,13 +420,12 @@ backup_text_ptr (const char *p, const char *text)
/* A completer function for explicit locations. This function
completes both options ("-source", "-line", etc) and values. */
-static VEC (char_ptr) *
-explicit_location_completer (struct cmd_list_element *ignore,
- struct event_location *location,
- const char *text, const char *word)
+static void
+complete_explicit_location (completion_tracker &tracker,
+ struct event_location *location,
+ const char *text, const char *word)
{
const char *p;
- VEC (char_ptr) *matches = NULL;
/* Find the beginning of the word. This is necessary because
we need to know if we are completing an option name or value. We
@@ -457,7 +447,8 @@ explicit_location_completer (struct cmd_list_element *ignore,
/* Skip over the '-'. */
++p;
- return complete_on_enum (keywords, p, p);
+ complete_on_enum (tracker, keywords, p, p);
+ return;
}
else
{
@@ -495,7 +486,7 @@ explicit_location_completer (struct cmd_list_element *ignore,
{
/* The user isn't completing on any valid option name,
e.g., "break -source foo.c [tab]". */
- return NULL;
+ return;
}
/* If the user hasn't entered a search expression, e.g.,
@@ -505,43 +496,40 @@ explicit_location_completer (struct cmd_list_element *ignore,
new_word = "";
/* Now gather matches */
- matches = collect_explicit_location_matches (location, what, new_word);
+ collect_explicit_location_matches (tracker, location, what, new_word);
}
-
- return matches;
}
/* A completer for locations. */
-VEC (char_ptr) *
+void
location_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
- VEC (char_ptr) *matches = NULL;
const char *copy = text;
event_location_up location = string_to_explicit_location (&copy,
current_language,
1);
if (location != NULL)
- matches = explicit_location_completer (ignore, location.get (),
- text, word);
+ complete_explicit_location (tracker, location.get (),
+ text, word);
else
{
/* This is an address or linespec location.
Right now both of these are handled by the (old) linespec
completer. */
- matches = linespec_location_completer (ignore, text, word);
+ complete_files_symbols (tracker, text, word);
}
-
- return matches;
}
/* Helper for expression_completer which recursively adds field and
- method names from TYPE, a struct or union type, to the array
- OUTPUT. */
+ method names from TYPE, a struct or union type, to the OUTPUT
+ list. */
+
static void
-add_struct_fields (struct type *type, VEC (char_ptr) **output,
+add_struct_fields (struct type *type, completion_list &output,
char *fieldname, int namelen)
{
int i;
@@ -560,8 +548,7 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
{
if (! strncmp (TYPE_FIELD_NAME (type, i),
fieldname, namelen))
- VEC_safe_push (char_ptr, *output,
- xstrdup (TYPE_FIELD_NAME (type, i)));
+ output.emplace_back (xstrdup (TYPE_FIELD_NAME (type, i)));
}
else if (TYPE_CODE (TYPE_FIELD_TYPE (type, i)) == TYPE_CODE_UNION)
{
@@ -585,7 +572,7 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
}
/* Omit constructors from the completion list. */
if (!type_name || strcmp (type_name, name))
- VEC_safe_push (char_ptr, *output, xstrdup (name));
+ output.emplace_back (xstrdup (name));
}
}
}
@@ -593,9 +580,10 @@ add_struct_fields (struct type *type, VEC (char_ptr) **output,
/* Complete on expressions. Often this means completing on symbol
names, but some language parsers also have support for completing
field names. */
-VEC (char_ptr) *
-expression_completer (struct cmd_list_element *ignore,
- const char *text, const char *word)
+
+static void
+complete_expression (completion_tracker &tracker,
+ const char *text, const char *word)
{
struct type *type = NULL;
char *fieldname;
@@ -610,7 +598,7 @@ expression_completer (struct cmd_list_element *ignore,
}
CATCH (except, RETURN_MASK_ERROR)
{
- return NULL;
+ return;
}
END_CATCH
@@ -628,11 +616,12 @@ expression_completer (struct cmd_list_element *ignore,
|| TYPE_CODE (type) == TYPE_CODE_STRUCT)
{
int flen = strlen (fieldname);
- VEC (char_ptr) *result = NULL;
+ completion_list result;
- add_struct_fields (type, &result, fieldname, flen);
+ add_struct_fields (type, result, fieldname, flen);
xfree (fieldname);
- return result;
+ tracker.add_completions (std::move (result));
+ return;
}
}
else if (fieldname && code != TYPE_CODE_UNDEF)
@@ -640,14 +629,26 @@ expression_completer (struct cmd_list_element *ignore,
VEC (char_ptr) *result;
struct cleanup *cleanup = make_cleanup (xfree, fieldname);
- result = make_symbol_completion_type (fieldname, fieldname, code);
+ collect_symbol_completion_matches_type (tracker, fieldname, fieldname,
+ code);
do_cleanups (cleanup);
- return result;
+ return;
}
xfree (fieldname);
- /* Not ideal but it is what we used to do before... */
- return linespec_location_completer (ignore, text, word);
+ complete_files_symbols (tracker, text, word);
+}
+
+/* Complete on expressions. Often this means completing on symbol
+ names, but some language parsers also have support for completing
+ field names. */
+
+void
+expression_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
+ const char *text, const char *word)
+{
+ complete_expression (tracker, text, word);
}
/* See definition in completer.h. */
@@ -677,11 +678,12 @@ set_gdb_completion_word_break_characters (completer_ftype *fn)
/* Complete on symbols. */
-VEC (char_ptr) *
+void
symbol_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
- return make_symbol_completion_list (text, word);
+ collect_symbol_completion_matches (tracker, text, word);
}
/* Here are some useful test cases for completion. FIXME: These
@@ -707,18 +709,30 @@ symbol_completer (struct cmd_list_element *ignore,
"file ../gdb.stabs/we" "ird" (needs to not break word at slash)
*/
-typedef enum
+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. */
handle_brkchars,
+
+ /* Main phase, called by complete_line function, is used to get the
+ list of possible completions. */
handle_completions,
- handle_help
-}
-complete_line_internal_reason;
+
+ /* Special case when completing a 'help' command. In this case,
+ once sub-command completions are exhausted, we simply return
+ NULL. */
+ handle_help,
+};
/* Helper for complete_line_internal to simplify it. */
-static VEC (char_ptr) *
-complete_line_internal_normal_command (const char *command, const char *word,
+static void
+complete_line_internal_normal_command (completion_tracker &tracker,
+ const char *command, const char *word,
const char *cmd_args,
complete_line_internal_reason reason,
struct cmd_list_element *c)
@@ -753,12 +767,11 @@ complete_line_internal_normal_command (const char *command, const char *word,
(c->completer));
}
- brkchars_fn (c, p, word);
+ brkchars_fn (c, tracker, p, word);
}
if (reason != handle_brkchars && c->completer != NULL)
- return (*c->completer) (c, p, word);
- return NULL;
+ (*c->completer) (c, tracker, p, word);
}
/* Internal function used to handle completions.
@@ -770,35 +783,19 @@ complete_line_internal_normal_command (const char *command, const char *word,
text of the line. POINT is the offset in that line of the cursor.
You should pretend that the line ends at POINT.
- REASON is of type complete_line_internal_reason.
-
- If REASON is handle_brkchars:
- 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 in that function.
-
- If REASON is handle_completions:
- Main phase, called by complete_line function, is used to get the list
- of posible completions.
+ See complete_line_internal_reason for description of REASON. */
- If REASON is handle_help:
- Special case when completing a 'help' command. In this case,
- once sub-command completions are exhausted, we simply return NULL.
- */
-
-static VEC (char_ptr) *
-complete_line_internal (const char *text,
- const char *line_buffer, int point,
- complete_line_internal_reason reason)
+static void
+complete_line_internal_1 (completion_tracker &tracker,
+ const char *text,
+ const char *line_buffer, int point,
+ complete_line_internal_reason reason)
{
- VEC (char_ptr) *list = NULL;
char *tmp_command;
const char *p;
int ignore_help_classes;
/* Pointer within tmp_command which corresponds to text. */
- char *word;
+ const char *word;
struct cmd_list_element *c, *result_list;
/* Choose the default set of word break characters to break
@@ -821,10 +818,18 @@ complete_line_internal (const char *text,
strncpy (tmp_command, line_buffer, point);
tmp_command[point] = '\0';
- /* Since text always contains some number of characters leading up
- to point, we can find the equivalent position in tmp_command
- by subtracting that many characters from the end of tmp_command. */
- word = tmp_command + point - strlen (text);
+ if (reason == handle_brkchars)
+ {
+ gdb_assert (text == NULL);
+ word = NULL;
+ }
+ else
+ {
+ /* Since text always contains some number of characters leading up
+ to point, we can find the equivalent position in tmp_command
+ by subtracting that many characters from the end of tmp_command. */
+ word = tmp_command + point - strlen (text);
+ }
if (point == 0)
{
@@ -848,7 +853,6 @@ complete_line_internal (const char *text,
{
/* It is an unrecognized command. So there are no
possible completions. */
- list = NULL;
}
else if (c == CMD_LIST_AMBIGUOUS)
{
@@ -866,7 +870,6 @@ complete_line_internal (const char *text,
example, "info t " or "info t foo" does not complete
to anything, because "info t" can be "info target" or
"info terminal". */
- list = NULL;
}
else
{
@@ -875,14 +878,14 @@ complete_line_internal (const char *text,
if (result_list)
{
if (reason != handle_brkchars)
- list = complete_on_cmdlist (*result_list->prefixlist, p,
- word, ignore_help_classes);
+ complete_on_cmdlist (*result_list->prefixlist, tracker, p,
+ word, ignore_help_classes);
}
else
{
if (reason != handle_brkchars)
- list = complete_on_cmdlist (cmdlist, p, word,
- ignore_help_classes);
+ complete_on_cmdlist (cmdlist, tracker, p, word,
+ ignore_help_classes);
}
/* Ensure that readline does the right thing with respect to
inserting quotes. */
@@ -908,8 +911,8 @@ complete_line_internal (const char *text,
/* It is a prefix command; what comes after it is
a subcommand (e.g. "info "). */
if (reason != handle_brkchars)
- list = complete_on_cmdlist (*c->prefixlist, p, word,
- ignore_help_classes);
+ complete_on_cmdlist (*c->prefixlist, tracker, p, word,
+ ignore_help_classes);
/* Ensure that readline does the right thing
with respect to inserting quotes. */
@@ -917,11 +920,11 @@ complete_line_internal (const char *text,
(gdb_completer_command_word_break_characters);
}
else if (reason == handle_help)
- list = NULL;
+ ;
else if (c->enums)
{
if (reason != handle_brkchars)
- list = complete_on_enum (c->enums, p, word);
+ complete_on_enum (tracker, c->enums, p, word);
set_rl_completer_word_break_characters
(gdb_completer_command_word_break_characters);
}
@@ -929,9 +932,9 @@ complete_line_internal (const char *text,
{
/* It is a normal command; what comes after it is
completed by the command's completer function. */
- list = complete_line_internal_normal_command (tmp_command,
- word, p,
- reason, c);
+ complete_line_internal_normal_command (tracker,
+ tmp_command, word, p,
+ reason, c);
}
}
else
@@ -953,8 +956,8 @@ complete_line_internal (const char *text,
}
if (reason != handle_brkchars)
- list = complete_on_cmdlist (result_list, q, word,
- ignore_help_classes);
+ complete_on_cmdlist (result_list, tracker, q, word,
+ ignore_help_classes);
/* Ensure that readline does the right thing
with respect to inserting quotes. */
@@ -963,7 +966,7 @@ complete_line_internal (const char *text,
}
}
else if (reason == handle_help)
- list = NULL;
+ ;
else
{
/* There is non-whitespace beyond the command. */
@@ -972,105 +975,115 @@ complete_line_internal (const char *text,
{
/* It is an unrecognized subcommand of a prefix command,
e.g. "info adsfkdj". */
- list = NULL;
}
else if (c->enums)
{
if (reason != handle_brkchars)
- list = complete_on_enum (c->enums, p, word);
+ complete_on_enum (tracker, c->enums, p, word);
}
else
{
/* It is a normal command. */
- list = complete_line_internal_normal_command (tmp_command,
- word, p,
- reason, c);
+ complete_line_internal_normal_command (tracker,
+ tmp_command, word, p,
+ reason, c);
}
}
}
-
- return list;
}
-/* See completer.h. */
+/* Wrapper around complete_line_internal_1 to handle
+ MAX_COMPLETIONS_REACHED_ERROR. */
-int max_completions = 200;
+static void
+complete_line_internal (completion_tracker &tracker,
+ const char *text,
+ const char *line_buffer, int point,
+ complete_line_internal_reason reason)
+{
+ TRY
+ {
+ complete_line_internal_1 (tracker, text, line_buffer, point, reason);
+ }
+ CATCH (except, RETURN_MASK_ERROR)
+ {
+ if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
+ throw_exception (except);
+ }
+}
/* See completer.h. */
-completion_tracker_t
-new_completion_tracker (void)
-{
- if (max_completions <= 0)
- return NULL;
+int max_completions = 200;
- return htab_create_alloc (max_completions,
- htab_hash_string, (htab_eq) streq,
- NULL, xcalloc, xfree);
-}
+/* Initial size of the table. It automagically grows from here. */
+#define INITIAL_COMPLETION_HTAB_SIZE 200
-/* Cleanup routine to free a completion tracker and reset the pointer
- to NULL. */
+/* See completer.h. */
-static void
-free_completion_tracker (void *p)
+completion_tracker::completion_tracker ()
{
- completion_tracker_t *tracker_ptr = (completion_tracker_t *) p;
-
- htab_delete (*tracker_ptr);
- *tracker_ptr = NULL;
+ m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+ htab_hash_string, (htab_eq) streq,
+ NULL, xcalloc, xfree);
}
/* See completer.h. */
-struct cleanup *
-make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
+completion_tracker::~completion_tracker ()
{
- if (*tracker_ptr == NULL)
- return make_cleanup (null_cleanup, NULL);
-
- return make_cleanup (free_completion_tracker, tracker_ptr);
+ xfree (m_lowest_common_denominator);
+ htab_delete (m_entries_hash);
}
/* See completer.h. */
-enum maybe_add_completion_enum
-maybe_add_completion (completion_tracker_t tracker, char *name)
+bool
+completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
{
void **slot;
- if (max_completions < 0)
- return MAYBE_ADD_COMPLETION_OK;
if (max_completions == 0)
- return MAYBE_ADD_COMPLETION_MAX_REACHED;
+ return false;
- gdb_assert (tracker != NULL);
+ if (htab_elements (m_entries_hash) >= max_completions)
+ return false;
- if (htab_elements (tracker) >= max_completions)
- return MAYBE_ADD_COMPLETION_MAX_REACHED;
+ slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
+ if (*slot == HTAB_EMPTY_ENTRY)
+ {
+ const char *match_for_lcd_str = name.get ();
- slot = htab_find_slot (tracker, name, INSERT);
+ recompute_lowest_common_denominator (match_for_lcd_str);
- if (*slot != HTAB_EMPTY_ENTRY)
- return MAYBE_ADD_COMPLETION_DUPLICATE;
+ *slot = name.get ();
+ m_entries_vec.push_back (std::move (name));
+ }
- *slot = name;
+ return true;
+}
- return (htab_elements (tracker) < max_completions
- ? MAYBE_ADD_COMPLETION_OK
- : MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
+/* See completer.h. */
+
+void
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
+{
+ if (!maybe_add_completion (std::move (name)))
+ throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
}
+/* See completer.h. */
+
void
-throw_max_completions_reached_error (void)
+completion_tracker::add_completions (completion_list &&list)
{
- throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
+ for (auto &candidate : list)
+ add_completion (std::move (candidate));
}
-/* Generate completions all at once. Returns a vector of unique strings
- allocated with xmalloc. Returns NULL if there are no completions
- or if max_completions is 0. If max_completions is non-negative, this will
- return at most max_completions strings.
+/* 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.
TEXT is the caller's idea of the "word" we are looking at.
@@ -1080,77 +1093,32 @@ throw_max_completions_reached_error (void)
POINT is the offset in that line of the cursor. You
should pretend that the line ends at POINT. */
-VEC (char_ptr) *
-complete_line (const char *text, const char *line_buffer, int point)
+void
+complete_line (completion_tracker &tracker,
+ const char *text, const char *line_buffer, int point)
{
- VEC (char_ptr) *list;
- VEC (char_ptr) *result = NULL;
- struct cleanup *cleanups;
- completion_tracker_t tracker;
- char *candidate;
- int ix, max_reached;
-
if (max_completions == 0)
- return NULL;
- list = complete_line_internal (text, line_buffer, point,
- handle_completions);
- if (max_completions < 0)
- return list;
-
- tracker = new_completion_tracker ();
- cleanups = make_cleanup_free_completion_tracker (&tracker);
- make_cleanup_free_char_ptr_vec (list);
-
- /* Do a final test for too many completions. Individual completers may
- do some of this, but are not required to. Duplicates are also removed
- here. Otherwise the user is left scratching his/her head: readline and
- complete_command will remove duplicates, and if removal of duplicates
- there brings the total under max_completions the user may think gdb quit
- searching too early. */
-
- for (ix = 0, max_reached = 0;
- !max_reached && VEC_iterate (char_ptr, list, ix, candidate);
- ++ix)
- {
- enum maybe_add_completion_enum add_status;
-
- add_status = maybe_add_completion (tracker, candidate);
-
- switch (add_status)
- {
- case MAYBE_ADD_COMPLETION_OK:
- VEC_safe_push (char_ptr, result, xstrdup (candidate));
- break;
- case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
- VEC_safe_push (char_ptr, result, xstrdup (candidate));
- max_reached = 1;
- break;
- case MAYBE_ADD_COMPLETION_MAX_REACHED:
- gdb_assert_not_reached ("more than max completions reached");
- case MAYBE_ADD_COMPLETION_DUPLICATE:
- break;
- }
- }
-
- do_cleanups (cleanups);
-
- return result;
+ return;
+ complete_line_internal (tracker, text, line_buffer, point,
+ handle_completions);
}
/* Complete on command names. Used by "help". */
-VEC (char_ptr) *
+void
command_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
- return complete_line_internal (word, text,
- strlen (text), handle_help);
+ complete_line_internal (tracker, word, text,
+ strlen (text), handle_help);
}
/* The corresponding completer_handle_brkchars implementation. */
static void
command_completer_handle_brkchars (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
set_rl_completer_word_break_characters
@@ -1159,11 +1127,11 @@ command_completer_handle_brkchars (struct cmd_list_element *ignore,
/* Complete on signals. */
-VEC (char_ptr) *
+void
signal_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
- VEC (char_ptr) *return_val = NULL;
size_t len = strlen (word);
int signum;
const char *signame;
@@ -1181,10 +1149,11 @@ signal_completer (struct cmd_list_element *ignore,
continue;
if (strncasecmp (signame, word, len) == 0)
- VEC_safe_push (char_ptr, return_val, xstrdup (signame));
+ {
+ gdb::unique_xmalloc_ptr<char> copy (xstrdup (signame));
+ tracker.add_completion (std::move (copy));
+ }
}
-
- return return_val;
}
/* Bit-flags for selecting what the register and/or register-group
@@ -1200,12 +1169,11 @@ DEF_ENUM_FLAGS_TYPE (enum reg_completer_target, reg_completer_targets);
/* Complete register names and/or reggroup names based on the value passed
in TARGETS. At least one bit in TARGETS must be set. */
-static VEC (char_ptr) *
-reg_or_group_completer_1 (struct cmd_list_element *ignore,
+static void
+reg_or_group_completer_1 (completion_tracker &tracker,
const char *text, const char *word,
reg_completer_targets targets)
{
- VEC (char_ptr) *result = NULL;
size_t len = strlen (word);
struct gdbarch *gdbarch;
const char *name;
@@ -1223,7 +1191,10 @@ reg_or_group_completer_1 (struct cmd_list_element *ignore,
i++)
{
if (*name != '\0' && strncmp (word, name, len) == 0)
- VEC_safe_push (char_ptr, result, xstrdup (name));
+ {
+ gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+ tracker.add_completion (std::move (copy));
+ }
}
}
@@ -1237,38 +1208,42 @@ reg_or_group_completer_1 (struct cmd_list_element *ignore,
{
name = reggroup_name (group);
if (strncmp (word, name, len) == 0)
- VEC_safe_push (char_ptr, result, xstrdup (name));
+ {
+ gdb::unique_xmalloc_ptr<char> copy (xstrdup (name));
+ tracker.add_completion (std::move (copy));
+ }
}
}
-
- return result;
}
/* Perform completion on register and reggroup names. */
-VEC (char_ptr) *
+void
reg_or_group_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
- return reg_or_group_completer_1 (ignore, text, word,
- (complete_register_names
- | complete_reggroup_names));
+ reg_or_group_completer_1 (tracker, text, word,
+ (complete_register_names
+ | complete_reggroup_names));
}
/* Perform completion on reggroup names. */
-VEC (char_ptr) *
+void
reggroup_completer (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
- return reg_or_group_completer_1 (ignore, text, word,
- complete_reggroup_names);
+ reg_or_group_completer_1 (tracker, text, word,
+ complete_reggroup_names);
}
/* The default completer_handle_brkchars implementation. */
static void
default_completer_handle_brkchars (struct cmd_list_element *ignore,
+ completion_tracker &tracker,
const char *text, const char *word)
{
set_rl_completer_word_break_characters
@@ -1292,89 +1267,339 @@ completer_handle_brkchars_func_for_completer (completer_ftype *fn)
/* Get the list of chars that are considered as word breaks
for the current command. */
-char *
-gdb_completion_word_break_characters (void)
+static char *
+gdb_completion_word_break_characters_throw ()
{
- VEC (char_ptr) *list;
+ /* New completion starting. Get rid of the previous tracker and
+ start afresh. */
+ delete current_completion.tracker;
+ current_completion.tracker = new completion_tracker ();
+
+ completion_tracker &tracker = *current_completion.tracker;
+
+ complete_line_internal (tracker, NULL, rl_line_buffer,
+ rl_point, handle_brkchars);
- list = complete_line_internal (rl_line_buffer, rl_line_buffer, rl_point,
- handle_brkchars);
- gdb_assert (list == NULL);
return rl_completer_word_break_characters;
}
-/* Generate completions one by one for the completer. Each time we
- are called return another potential completion to the caller.
- line_completion just completes on commands or passes the buck to
- the command's completer function, the stuff specific to symbol
- completion is in make_symbol_completion_list.
-
- TEXT is the caller's idea of the "word" we are looking at.
+char *
+gdb_completion_word_break_characters ()
+{
+ /* New completion starting. */
+ current_completion.aborted = false;
- MATCHES is the number of matches that have currently been collected
- from calling this completion function. When zero, then we need to
- initialize, otherwise the initialization has already taken place
- and we can just return the next potential completion string.
+ TRY
+ {
+ return gdb_completion_word_break_characters_throw ();
+ }
+ CATCH (ex, RETURN_MASK_ALL)
+ {
+ /* Set this to that gdb_rl_attempted_completion_function knows
+ to abort early. */
+ current_completion.aborted = true;
+ }
+ END_CATCH
- LINE_BUFFER is available to be looked at; it contains the entire
- text of the line. POINT is the offset in that line of the cursor.
- You should pretend that the line ends at POINT.
+ return NULL;
+}
- Returns NULL if there are no more completions, else a pointer to a
- string which is a possible completion, it is the caller's
- responsibility to free the string. */
+/* See completer.h. */
-static char *
-line_completion_function (const char *text, int matches,
- char *line_buffer, int point)
+void
+completion_tracker::recompute_lowest_common_denominator (const char *new_match)
{
- static VEC (char_ptr) *list = NULL; /* Cache of completions. */
- static int index; /* Next cached completion. */
- char *output = NULL;
-
- if (matches == 0)
+ 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);
+ m_lowest_common_denominator_unique = true;
+ }
+ else
{
- /* The caller is beginning to accumulate a new set of
- completions, so we need to find all of them now, and cache
- them for returning one at a time on future calls. */
+ /* Find the common denominator between the currently-known
+ lowest common denominator and NEW_MATCH. That becomes the
+ new lowest common denominator. */
+ size_t i;
- if (list)
+ for (i = 0;
+ (new_match[i] != '\0'
+ && new_match[i] == m_lowest_common_denominator[i]);
+ i++)
+ ;
+ if (m_lowest_common_denominator[i] != new_match[i])
{
- /* Free the storage used by LIST, but not by the strings
- inside. This is because rl_complete_internal () frees
- the strings. As complete_line may abort by calling
- `error' clear LIST now. */
- VEC_free (char_ptr, list);
+ m_lowest_common_denominator[i] = '\0';
+ m_lowest_common_denominator_unique = false;
}
- index = 0;
- list = complete_line (text, line_buffer, point);
}
+}
+
+/* Build a new C string that is a copy of LCD with the whitespace of
+ ORIG/ORIG_LEN preserved.
+
+ Say the user is completing a symbol name, with spaces, like:
+
+ "foo ( i"
+
+ and the resulting completion match is:
+
+ "foo(int)"
+
+ we want to end up with an input line like:
+
+ "foo ( int)"
+ ^^^^^^^ => text from LCD [1], whitespace from ORIG preserved.
+ ^^ => new text from LCD
+
+ [1] - We must take characters from the LCD instead of the original
+ text, since some completions want to change upper/lowercase. E.g.:
- /* If we found a list of potential completions during initialization
- then dole them out one at a time. After returning the last one,
- return NULL (and continue to do so) each time we are called after
- that, until a new list is available. */
+ "handle sig<>"
- if (list)
+ completes to:
+
+ "handle SIG[QUIT|etc.]"
+*/
+
+static char *
+expand_preserving_ws (const char *orig, size_t orig_len,
+ const char *lcd)
+{
+ const char *p_orig = orig;
+ const char *orig_end = orig + orig_len;
+ const char *p_lcd = lcd;
+ std::string res;
+
+ while (p_orig < orig_end)
{
- if (index < VEC_length (char_ptr, list))
+ if (*p_orig == ' ')
+ {
+ while (p_orig < orig_end && *p_orig == ' ')
+ res += *p_orig++;
+ p_lcd = skip_spaces_const (p_lcd);
+ }
+ else
{
- output = VEC_index (char_ptr, list, index);
- index++;
+ /* Take characters from the LCD instead of the original
+ text, since some completions change upper/lowercase.
+ E.g.:
+ "handle sig<>"
+ completes to:
+ "handle SIG[QUIT|etc.]"
+ */
+ res += *p_lcd;
+ p_orig++;
+ p_lcd++;
}
}
-#if 0
- /* Can't do this because readline hasn't yet checked the word breaks
- for figuring out whether to insert a quote. */
- if (output == NULL)
- /* Make sure the word break characters are set back to normal for
- the next time that readline tries to complete something. */
- rl_completer_word_break_characters =
- current_language->la_word_break_characters();
-#endif
+ while (*p_lcd != '\0')
+ res += *p_lcd++;
+
+ return xstrdup (res.c_str ());
+}
+
+/* See completer.h. */
+
+completion_result
+completion_tracker::build_completion_result (const char *text,
+ int start, int end)
+{
+ completion_list &list = m_entries_vec; /* The completions. */
- return (output);
+ if (list.empty ())
+ return {};
+
+ /* +1 for the LCD, and +1 for NULL termination. */
+ char **match_list = XNEWVEC (char *, 1 + list.size () + 1);
+
+ /* Build replacement word, based on the LCD. */
+
+ match_list[0]
+ = expand_preserving_ws (text, end - start,
+ m_lowest_common_denominator);
+
+ if (m_lowest_common_denominator_unique)
+ {
+ match_list[1] = NULL;
+
+ /* If 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] == ' ');
+
+ return completion_result (match_list, 1, completion_suppress_append);
+ }
+ else
+ {
+ int ix;
+
+ for (ix = 0; ix < list.size (); ++ix)
+ match_list[ix + 1] = list[ix].release ();
+ match_list[ix + 1] = NULL;
+
+ return completion_result (match_list, list.size (), false);
+ }
+}
+
+/* See completer.h */
+
+completion_result::completion_result ()
+ : match_list (NULL), number_matches (0),
+ completion_suppress_append (false)
+{}
+
+/* See completer.h */
+
+completion_result::completion_result (char **match_list_,
+ size_t number_matches_,
+ bool completion_suppress_append_)
+ : match_list (match_list_),
+ number_matches (number_matches_),
+ completion_suppress_append (completion_suppress_append_)
+{}
+
+/* See completer.h */
+
+completion_result::~completion_result ()
+{
+ reset_match_list ();
+}
+
+/* See completer.h */
+
+completion_result::completion_result (completion_result &&rhs)
+{
+ if (this == &rhs)
+ return;
+
+ reset_match_list ();
+ match_list = rhs.match_list;
+ rhs.match_list = NULL;
+ number_matches = rhs.number_matches;
+ rhs.number_matches = 0;
+}
+
+/* See completer.h */
+
+char **
+completion_result::release_match_list ()
+{
+ char **ret = match_list;
+ match_list = NULL;
+ 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
+completion_result::sort_match_list ()
+{
+ if (number_matches > 1)
+ {
+ /* Element 0 is special (it's the common prefix), leave it
+ be. */
+ std::sort (&match_list[1],
+ &match_list[number_matches + 1],
+ compare_cstrings);
+ }
+}
+
+/* See completer.h */
+
+void
+completion_result::reset_match_list ()
+{
+ if (match_list != NULL)
+ {
+ for (char **p = match_list; *p != NULL; p++)
+ xfree (*p);
+ xfree (match_list);
+ match_list = NULL;
+ }
+}
+
+/* Helper for gdb_rl_attempted_completion_function, which does most of
+ the work. This is called by readline to build the match list array
+ and to determine the lowest common denominator. The real matches
+ list starts at match[1], while match[0] is the slot holding
+ readline's idea of the lowest common denominator of all matches,
+ which is what readline replaces the completion "word" with.
+
+ TEXT is the caller's idea of the "word" we are looking at, as
+ computed in the handle_brkchars phase.
+
+ START is the offset from RL_LINE_BUFFER where TEXT starts. END is
+ the offset from RL_LINE_BUFFER where TEXT ends (i.e., where
+ rl_point is).
+
+ You should thus pretend that the line ends at END (relative to
+ RL_LINE_BUFFER).
+
+ RL_LINE_BUFFER contains the entire text of the line. RL_POINT is
+ the offset in that line of the cursor. You should pretend that the
+ line ends at POINT.
+
+ Returns NULL if there are no completions. */
+
+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 ();
+
+ complete_line (*current_completion.tracker, text,
+ rl_line_buffer, rl_point);
+
+ completion_tracker &tracker = *current_completion.tracker;
+
+ completion_result result
+ = tracker.build_completion_result (text, start, end);
+
+ rl_completion_suppress_append = result.completion_suppress_append;
+ return result.release_match_list ();
+}
+
+/* Function installed as "rl_attempted_completion_function" readline
+ hook. Wrapper around gdb_rl_attempted_completion_function_throw
+ that catches C++ exceptions, which can't cross readline. */
+
+char **
+gdb_rl_attempted_completion_function (const char *text, int start, int end)
+{
+ /* If we end up returning NULL, either on error, or simple because
+ there are no matches, inhibit readline's default filename
+ completer. */
+ rl_attempted_completion_over = 1;
+
+ /* If the handle_brkchars phase was aborted, don't try
+ completing. */
+ if (current_completion.aborted)
+ return NULL;
+
+ TRY
+ {
+ return gdb_rl_attempted_completion_function_throw (text, start, end);
+ }
+ CATCH (ex, RETURN_MASK_ALL)
+ {
+ }
+ END_CATCH
+
+ return NULL;
}
/* Skip over the possibly quoted word STR (as defined by the quote