Add max-completions parameter, and implement tab-completion limiting.
This commit adds a new exception, MAX_COMPLETIONS_REACHED_ERROR, to be thrown whenever the completer has generated too many candidates to be useful. A new user-settable variable, "max_completions", is added to control this behaviour. A top-level completion limit is added to complete_line_internal, as the final check to ensure the user never sees too many completions. An additional limit is added to default_make_symbol_completion_list_break_on, to halt time-consuming symbol table expansions. gdb/ChangeLog: PR cli/9007 PR cli/11920 PR cli/15548 * cli/cli-cmds.c (complete_command): Notify user if max-completions reached. * common/common-exceptions.h (enum errors) <MAX_COMPLETIONS_REACHED_ERROR>: New value. * completer.h (get_max_completions_reached_message): New declaration. (max_completions): Likewise. (completion_tracker_t): New typedef. (new_completion_tracker): New declaration. (make_cleanup_free_completion_tracker): Likewise. (maybe_add_completion_enum): New enum. (maybe_add_completion): New declaration. (throw_max_completions_reached_error): Likewise. * completer.c (max_completions): New global variable. (new_completion_tracker): New function. (free_completion_tracker): Likewise. (make_cleanup_free_completion_tracker): Likewise. (maybe_add_completions): Likewise. (throw_max_completions_reached_error): Likewise. (complete_line): Remove duplicates and limit result to max_completions entries. (get_max_completions_reached_message): New function. (gdb_display_match_list): Handle max_completions. (_initialize_completer): New declaration and function. * symtab.c: Include completer.h. (completion_tracker): New static variable. (completion_list_add_name): Call maybe_add_completion. (default_make_symbol_completion_list_break_on_1): Renamed from default_make_symbol_completion_list_break_on. Maintain completion_tracker across calls to completion_list_add_name. (default_make_symbol_completion_list_break_on): New function. * top.c (init_main): Set rl_completion_display_matches_hook. * tui/tui-io.c: Include completer.h. (tui_old_rl_display_matches_hook): New static global. (tui_rl_display_match_list): Notify user if max-completions reached. (tui_setup_io): Save/restore rl_completion_display_matches_hook. * NEWS (New Options): Mention set/show max-completions. gdb/doc/ChangeLog: * gdb.texinfo (Command Completion): Document new "set/show max-completions" option. gdb/testsuite/ChangeLog: * gdb.base/completion.exp: Disable completion limiting for existing tests. Add new tests to check completion limiting. * gdb.linespec/ls-errs.exp: Disable completion limiting.
This commit is contained in:
parent
e11c72c7e4
commit
ef0b411a11
10 changed files with 478 additions and 24 deletions
7
gdb/NEWS
7
gdb/NEWS
|
@ -26,6 +26,13 @@ maint flush-symbol-cache
|
||||||
|
|
||||||
* New options
|
* New options
|
||||||
|
|
||||||
|
set max-completions
|
||||||
|
show max-completions
|
||||||
|
Set the maximum number of candidates to be considered during
|
||||||
|
completion. The default value is 200. This limit allows GDB
|
||||||
|
to avoid generating large completion lists, the computation of
|
||||||
|
which can cause the debugger to become temporarily unresponsive.
|
||||||
|
|
||||||
maint set symbol-cache-size
|
maint set symbol-cache-size
|
||||||
maint show symbol-cache-size
|
maint show symbol-cache-size
|
||||||
Control the size of the symbol cache.
|
Control the size of the symbol cache.
|
||||||
|
|
|
@ -236,7 +236,8 @@ help_command (char *command, int from_tty)
|
||||||
help_cmd (command, gdb_stdout);
|
help_cmd (command, gdb_stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The "complete" command is used by Emacs to implement completion. */
|
/* Note: The "complete" command is used by Emacs to implement completion.
|
||||||
|
[Is that why this function writes output with *_unfiltered?] */
|
||||||
|
|
||||||
static void
|
static void
|
||||||
complete_command (char *arg, int from_tty)
|
complete_command (char *arg, int from_tty)
|
||||||
|
@ -247,6 +248,18 @@ complete_command (char *arg, int from_tty)
|
||||||
|
|
||||||
dont_repeat ();
|
dont_repeat ();
|
||||||
|
|
||||||
|
if (max_completions == 0)
|
||||||
|
{
|
||||||
|
/* Only print this for non-mi frontends. An MI frontend may not
|
||||||
|
be able to handle this. */
|
||||||
|
if (!ui_out_is_mi_like_p (current_uiout))
|
||||||
|
{
|
||||||
|
printf_unfiltered (_("max-completions is zero,"
|
||||||
|
" completion is disabled.\n"));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg == NULL)
|
if (arg == NULL)
|
||||||
arg = "";
|
arg = "";
|
||||||
argpoint = strlen (arg);
|
argpoint = strlen (arg);
|
||||||
|
@ -293,6 +306,15 @@ complete_command (char *arg, int from_tty)
|
||||||
|
|
||||||
xfree (prev);
|
xfree (prev);
|
||||||
VEC_free (char_ptr, completions);
|
VEC_free (char_ptr, completions);
|
||||||
|
|
||||||
|
if (size == max_completions)
|
||||||
|
{
|
||||||
|
/* ARG_PREFIX and POINT are included in the output so that emacs
|
||||||
|
will include the message in the output. */
|
||||||
|
printf_unfiltered (_("%s%s %s\n"),
|
||||||
|
arg_prefix, point,
|
||||||
|
get_max_completions_reached_message ());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,12 @@ enum errors {
|
||||||
/* Requested feature, method, mechanism, etc. is not supported. */
|
/* Requested feature, method, mechanism, etc. is not supported. */
|
||||||
NOT_SUPPORTED_ERROR,
|
NOT_SUPPORTED_ERROR,
|
||||||
|
|
||||||
|
/* The number of candidates generated during line completion has
|
||||||
|
reached the user's specified limit. This isn't an error, this exception
|
||||||
|
is used to halt searching for more completions, but for consistency
|
||||||
|
"_ERROR" is appended to the name. */
|
||||||
|
MAX_COMPLETIONS_REACHED_ERROR,
|
||||||
|
|
||||||
/* Add more errors here. */
|
/* Add more errors here. */
|
||||||
NR_ERRORS
|
NR_ERRORS
|
||||||
};
|
};
|
||||||
|
|
201
gdb/completer.c
201
gdb/completer.c
|
@ -781,9 +781,93 @@ complete_line_internal (const char *text,
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
/* Generate completions all at once. Returns a vector of strings.
|
|
||||||
Each element is allocated with xmalloc. It can also return NULL if
|
/* See completer.h. */
|
||||||
there are no completions.
|
|
||||||
|
int max_completions = 200;
|
||||||
|
|
||||||
|
/* See completer.h. */
|
||||||
|
|
||||||
|
completion_tracker_t
|
||||||
|
new_completion_tracker (void)
|
||||||
|
{
|
||||||
|
if (max_completions <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return htab_create_alloc (max_completions,
|
||||||
|
htab_hash_string, (htab_eq) streq,
|
||||||
|
NULL, xcalloc, xfree);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cleanup routine to free a completion tracker and reset the pointer
|
||||||
|
to NULL. */
|
||||||
|
|
||||||
|
static void
|
||||||
|
free_completion_tracker (void *p)
|
||||||
|
{
|
||||||
|
completion_tracker_t *tracker_ptr = p;
|
||||||
|
|
||||||
|
htab_delete (*tracker_ptr);
|
||||||
|
*tracker_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See completer.h. */
|
||||||
|
|
||||||
|
struct cleanup *
|
||||||
|
make_cleanup_free_completion_tracker (completion_tracker_t *tracker_ptr)
|
||||||
|
{
|
||||||
|
if (*tracker_ptr == NULL)
|
||||||
|
return make_cleanup (null_cleanup, NULL);
|
||||||
|
|
||||||
|
return make_cleanup (free_completion_tracker, tracker_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* See completer.h. */
|
||||||
|
|
||||||
|
enum maybe_add_completion_enum
|
||||||
|
maybe_add_completion (completion_tracker_t tracker, char *name)
|
||||||
|
{
|
||||||
|
void **slot;
|
||||||
|
|
||||||
|
if (max_completions < 0)
|
||||||
|
return MAYBE_ADD_COMPLETION_OK;
|
||||||
|
if (max_completions == 0)
|
||||||
|
return MAYBE_ADD_COMPLETION_MAX_REACHED;
|
||||||
|
|
||||||
|
gdb_assert (tracker != NULL);
|
||||||
|
|
||||||
|
if (htab_elements (tracker) >= max_completions)
|
||||||
|
return MAYBE_ADD_COMPLETION_MAX_REACHED;
|
||||||
|
|
||||||
|
slot = htab_find_slot (tracker, name, INSERT);
|
||||||
|
|
||||||
|
if (*slot != HTAB_EMPTY_ENTRY)
|
||||||
|
return MAYBE_ADD_COMPLETION_DUPLICATE;
|
||||||
|
|
||||||
|
*slot = name;
|
||||||
|
|
||||||
|
return (htab_elements (tracker) < max_completions
|
||||||
|
? MAYBE_ADD_COMPLETION_OK
|
||||||
|
: MAYBE_ADD_COMPLETION_OK_MAX_REACHED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
throw_max_completions_reached_error (void)
|
||||||
|
{
|
||||||
|
throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 + 1 strings.
|
||||||
|
|
||||||
|
If max_completions strings are collected, an extra string is added which
|
||||||
|
is a text message to inform the user that the list may be truncated.
|
||||||
|
This extra string serves two purposes:
|
||||||
|
1) Inform the user.
|
||||||
|
2) Prevent readline from being able to find a common prefix to advance
|
||||||
|
point to, since it's working with an incomplete list.
|
||||||
|
|
||||||
TEXT is the caller's idea of the "word" we are looking at.
|
TEXT is the caller's idea of the "word" we are looking at.
|
||||||
|
|
||||||
|
@ -796,8 +880,58 @@ complete_line_internal (const char *text,
|
||||||
VEC (char_ptr) *
|
VEC (char_ptr) *
|
||||||
complete_line (const char *text, const char *line_buffer, int point)
|
complete_line (const char *text, const char *line_buffer, int point)
|
||||||
{
|
{
|
||||||
return complete_line_internal (text, line_buffer,
|
VEC (char_ptr) *list;
|
||||||
point, handle_completions);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Complete on command names. Used by "help". */
|
/* Complete on command names. Used by "help". */
|
||||||
|
@ -1020,6 +1154,15 @@ skip_quoted (const char *str)
|
||||||
{
|
{
|
||||||
return skip_quoted_chars (str, NULL, NULL);
|
return skip_quoted_chars (str, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return a message indicating that the maximum number of completions
|
||||||
|
has been reached and that there may be more. */
|
||||||
|
|
||||||
|
const char *
|
||||||
|
get_max_completions_reached_message (void)
|
||||||
|
{
|
||||||
|
return _("*** List may be truncated, max-completions reached. ***");
|
||||||
|
}
|
||||||
|
|
||||||
/* GDB replacement for rl_display_match_list.
|
/* GDB replacement for rl_display_match_list.
|
||||||
Readline doesn't provide a clean interface for TUI(curses).
|
Readline doesn't provide a clean interface for TUI(curses).
|
||||||
|
@ -1413,9 +1556,10 @@ gdb_complete_get_screenwidth (const struct match_list_displayer *displayer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GDB version of readline/complete.c:rl_display_match_list.
|
/* GDB version of readline/complete.c:rl_display_match_list.
|
||||||
See gdb_display_match_list for a description of MATCHES, LEN, MAX. */
|
See gdb_display_match_list for a description of MATCHES, LEN, MAX.
|
||||||
|
Returns non-zero if all matches are displayed. */
|
||||||
|
|
||||||
static void
|
static int
|
||||||
gdb_display_match_list_1 (char **matches, int len, int max,
|
gdb_display_match_list_1 (char **matches, int len, int max,
|
||||||
const struct match_list_displayer *displayer)
|
const struct match_list_displayer *displayer)
|
||||||
{
|
{
|
||||||
|
@ -1501,7 +1645,7 @@ gdb_display_match_list_1 (char **matches, int len, int max,
|
||||||
{
|
{
|
||||||
lines = gdb_display_match_list_pager (lines, displayer);
|
lines = gdb_display_match_list_pager (lines, displayer);
|
||||||
if (lines < 0)
|
if (lines < 0)
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1523,7 +1667,7 @@ gdb_display_match_list_1 (char **matches, int len, int max,
|
||||||
{
|
{
|
||||||
lines = gdb_display_match_list_pager (lines, displayer);
|
lines = gdb_display_match_list_pager (lines, displayer);
|
||||||
if (lines < 0)
|
if (lines < 0)
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1533,6 +1677,8 @@ gdb_display_match_list_1 (char **matches, int len, int max,
|
||||||
}
|
}
|
||||||
displayer->crlf (displayer);
|
displayer->crlf (displayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Utility for displaying completion list matches, used by both CLI and TUI.
|
/* Utility for displaying completion list matches, used by both CLI and TUI.
|
||||||
|
@ -1545,6 +1691,13 @@ void
|
||||||
gdb_display_match_list (char **matches, int len, int max,
|
gdb_display_match_list (char **matches, int len, int max,
|
||||||
const struct match_list_displayer *displayer)
|
const struct match_list_displayer *displayer)
|
||||||
{
|
{
|
||||||
|
/* Readline will never call this if complete_line returned NULL. */
|
||||||
|
gdb_assert (max_completions != 0);
|
||||||
|
|
||||||
|
/* complete_line will never return more than this. */
|
||||||
|
if (max_completions > 0)
|
||||||
|
gdb_assert (len <= max_completions);
|
||||||
|
|
||||||
if (rl_completion_query_items > 0 && len >= rl_completion_query_items)
|
if (rl_completion_query_items > 0 && len >= rl_completion_query_items)
|
||||||
{
|
{
|
||||||
char msg[100];
|
char msg[100];
|
||||||
|
@ -1567,5 +1720,33 @@ gdb_display_match_list (char **matches, int len, int max,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gdb_display_match_list_1 (matches, len, max, displayer);
|
if (gdb_display_match_list_1 (matches, len, max, displayer))
|
||||||
|
{
|
||||||
|
/* Note: MAX_COMPLETIONS may be -1 or zero, but LEN is always > 0. */
|
||||||
|
if (len == max_completions)
|
||||||
|
{
|
||||||
|
/* The maximum number of completions has been reached. Warn the user
|
||||||
|
that there may be more. */
|
||||||
|
const char *message = get_max_completions_reached_message ();
|
||||||
|
|
||||||
|
displayer->puts (displayer, message);
|
||||||
|
displayer->crlf (displayer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern initialize_file_ftype _initialize_completer; /* -Wmissing-prototypes */
|
||||||
|
|
||||||
|
void
|
||||||
|
_initialize_completer (void)
|
||||||
|
{
|
||||||
|
add_setshow_zuinteger_unlimited_cmd ("max-completions", no_class,
|
||||||
|
&max_completions, _("\
|
||||||
|
Set maximum number of completion candidates."), _("\
|
||||||
|
Show maximum number of completion candidates."), _("\
|
||||||
|
Use this to limit the number of candidates considered\n\
|
||||||
|
during completion. Specifying \"unlimited\" or -1\n\
|
||||||
|
disables limiting. Note that setting either no limit or\n\
|
||||||
|
a very large limit can make completion slow."),
|
||||||
|
NULL, NULL, &setlist, &showlist);
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,8 @@ struct match_list_displayer
|
||||||
extern void gdb_display_match_list (char **matches, int len, int max,
|
extern void gdb_display_match_list (char **matches, int len, int max,
|
||||||
const struct match_list_displayer *);
|
const struct match_list_displayer *);
|
||||||
|
|
||||||
|
extern const char *get_max_completions_reached_message (void);
|
||||||
|
|
||||||
extern VEC (char_ptr) *complete_line (const char *text,
|
extern VEC (char_ptr) *complete_line (const char *text,
|
||||||
const char *line_buffer,
|
const char *line_buffer,
|
||||||
int point);
|
int point);
|
||||||
|
@ -112,4 +114,68 @@ extern const char *skip_quoted_chars (const char *, const char *,
|
||||||
|
|
||||||
extern const char *skip_quoted (const char *);
|
extern const char *skip_quoted (const char *);
|
||||||
|
|
||||||
|
/* Maximum number of candidates to consider before the completer
|
||||||
|
bails by throwing MAX_COMPLETIONS_REACHED_ERROR. Negative values
|
||||||
|
disable limiting. */
|
||||||
|
|
||||||
|
extern int max_completions;
|
||||||
|
|
||||||
|
/* Object to track how many unique completions have been generated.
|
||||||
|
Used to limit the size of generated completion lists. */
|
||||||
|
|
||||||
|
typedef htab_t completion_tracker_t;
|
||||||
|
|
||||||
|
/* Create a new completion tracker.
|
||||||
|
The result is a hash table to track added completions, or NULL
|
||||||
|
if max_completions <= 0. If max_completions < 0, tracking is disabled.
|
||||||
|
If max_completions == 0, the max is indeed zero. */
|
||||||
|
|
||||||
|
extern completion_tracker_t new_completion_tracker (void);
|
||||||
|
|
||||||
|
/* Make a cleanup to free a completion tracker, and reset its pointer
|
||||||
|
to NULL. */
|
||||||
|
|
||||||
|
extern struct cleanup *make_cleanup_free_completion_tracker
|
||||||
|
(completion_tracker_t *tracker_ptr);
|
||||||
|
|
||||||
|
/* Return values for maybe_add_completion. */
|
||||||
|
|
||||||
|
enum maybe_add_completion_enum
|
||||||
|
{
|
||||||
|
/* NAME has been recorded and max_completions has not been reached,
|
||||||
|
or completion tracking is disabled (max_completions < 0). */
|
||||||
|
MAYBE_ADD_COMPLETION_OK,
|
||||||
|
|
||||||
|
/* NAME has been recorded and max_completions has been reached
|
||||||
|
(thus the caller can stop searching). */
|
||||||
|
MAYBE_ADD_COMPLETION_OK_MAX_REACHED,
|
||||||
|
|
||||||
|
/* max-completions entries has been reached.
|
||||||
|
Whether NAME is a duplicate or not is not determined. */
|
||||||
|
MAYBE_ADD_COMPLETION_MAX_REACHED,
|
||||||
|
|
||||||
|
/* NAME has already been recorded.
|
||||||
|
Note that this is never returned if completion tracking is disabled
|
||||||
|
(max_completions < 0). */
|
||||||
|
MAYBE_ADD_COMPLETION_DUPLICATE
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Add the completion NAME to the list of generated completions if
|
||||||
|
it is not there already.
|
||||||
|
If max_completions is negative, nothing is done, not even watching
|
||||||
|
for duplicates, and MAYBE_ADD_COMPLETION_OK is always returned.
|
||||||
|
|
||||||
|
If MAYBE_ADD_COMPLETION_MAX_REACHED is returned, callers are required to
|
||||||
|
record at least one more completion. The final list will be pruned to
|
||||||
|
max_completions, but recording at least one more than max_completions is
|
||||||
|
the signal to the completion machinery that too many completions were
|
||||||
|
found. */
|
||||||
|
|
||||||
|
extern enum maybe_add_completion_enum
|
||||||
|
maybe_add_completion (completion_tracker_t tracker, char *name);
|
||||||
|
|
||||||
|
/* Wrapper to throw MAX_COMPLETIONS_REACHED_ERROR. */
|
||||||
|
|
||||||
|
extern void throw_max_completions_reached_error (void);
|
||||||
|
|
||||||
#endif /* defined (COMPLETER_H) */
|
#endif /* defined (COMPLETER_H) */
|
||||||
|
|
|
@ -1600,6 +1600,38 @@ means @kbd{@key{META} ?}. You can type this either by holding down a
|
||||||
key designated as the @key{META} shift on your keyboard (if there is
|
key designated as the @key{META} shift on your keyboard (if there is
|
||||||
one) while typing @kbd{?}, or as @key{ESC} followed by @kbd{?}.
|
one) while typing @kbd{?}, or as @key{ESC} followed by @kbd{?}.
|
||||||
|
|
||||||
|
If the number of possible completions is large, @value{GDBN} will
|
||||||
|
print as much of the list as it has collected, as well as a message
|
||||||
|
indicating that the list may be truncated.
|
||||||
|
|
||||||
|
@smallexample
|
||||||
|
(@value{GDBP}) b m@key{TAB}@key{TAB}
|
||||||
|
main
|
||||||
|
<... the rest of the possible completions ...>
|
||||||
|
*** List may be truncated, max-completions reached. ***
|
||||||
|
(@value{GDBP}) b m
|
||||||
|
@end smallexample
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
This behavior can be controlled with the following commands:
|
||||||
|
|
||||||
|
@table @code
|
||||||
|
@kindex set max-completions
|
||||||
|
@item set max-completions @var{limit}
|
||||||
|
@itemx set max-completions unlimited
|
||||||
|
Set the maximum number of completion candidates. @value{GDBN} will
|
||||||
|
stop looking for more completions once it collects this many candidates.
|
||||||
|
This is useful when completing on things like function names as collecting
|
||||||
|
all the possible candidates can be time consuming.
|
||||||
|
The default value is 200. A value of zero disables tab-completion.
|
||||||
|
Note that setting either no limit or a very large limit can make
|
||||||
|
completion slow.
|
||||||
|
@kindex show max-completions
|
||||||
|
@item show max-completions
|
||||||
|
Show the maximum number of candidates that @value{GDBN} will collect and show
|
||||||
|
during completion.
|
||||||
|
@end table
|
||||||
|
|
||||||
@cindex quotes in commands
|
@cindex quotes in commands
|
||||||
@cindex completion of quoted strings
|
@cindex completion of quoted strings
|
||||||
Sometimes the string you need, while logically a ``word'', may contain
|
Sometimes the string you need, while logically a ``word'', may contain
|
||||||
|
|
66
gdb/symtab.c
66
gdb/symtab.c
|
@ -60,6 +60,7 @@
|
||||||
#include "macroscope.h"
|
#include "macroscope.h"
|
||||||
|
|
||||||
#include "parser-defs.h"
|
#include "parser-defs.h"
|
||||||
|
#include "completer.h"
|
||||||
|
|
||||||
/* Forward declarations for local functions. */
|
/* Forward declarations for local functions. */
|
||||||
|
|
||||||
|
@ -5001,6 +5002,15 @@ static VEC (char_ptr) *return_val;
|
||||||
completion_list_add_name \
|
completion_list_add_name \
|
||||||
(MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
|
(MSYMBOL_NATURAL_NAME (symbol), (sym_text), (len), (text), (word))
|
||||||
|
|
||||||
|
/* Tracker for how many unique completions have been generated. Used
|
||||||
|
to terminate completion list generation early if the list has grown
|
||||||
|
to a size so large as to be useless. This helps avoid GDB seeming
|
||||||
|
to lock up in the event the user requests to complete on something
|
||||||
|
vague that necessitates the time consuming expansion of many symbol
|
||||||
|
tables. */
|
||||||
|
|
||||||
|
static completion_tracker_t completion_tracker;
|
||||||
|
|
||||||
/* Test to see if the symbol specified by SYMNAME (which is already
|
/* Test to see if the symbol specified by SYMNAME (which is already
|
||||||
demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
|
demangled for C++ symbols) matches SYM_TEXT in the first SYM_TEXT_LEN
|
||||||
characters. If so, add it to the current completion list. */
|
characters. If so, add it to the current completion list. */
|
||||||
|
@ -5019,6 +5029,7 @@ completion_list_add_name (const char *symname,
|
||||||
|
|
||||||
{
|
{
|
||||||
char *new;
|
char *new;
|
||||||
|
enum maybe_add_completion_enum add_status;
|
||||||
|
|
||||||
if (word == sym_text)
|
if (word == sym_text)
|
||||||
{
|
{
|
||||||
|
@ -5040,7 +5051,22 @@ completion_list_add_name (const char *symname,
|
||||||
strcat (new, symname);
|
strcat (new, symname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
add_status = maybe_add_completion (completion_tracker, new);
|
||||||
|
|
||||||
|
switch (add_status)
|
||||||
|
{
|
||||||
|
case MAYBE_ADD_COMPLETION_OK:
|
||||||
VEC_safe_push (char_ptr, return_val, new);
|
VEC_safe_push (char_ptr, return_val, new);
|
||||||
|
break;
|
||||||
|
case MAYBE_ADD_COMPLETION_OK_MAX_REACHED:
|
||||||
|
VEC_safe_push (char_ptr, return_val, new);
|
||||||
|
throw_max_completions_reached_error ();
|
||||||
|
case MAYBE_ADD_COMPLETION_MAX_REACHED:
|
||||||
|
throw_max_completions_reached_error ();
|
||||||
|
case MAYBE_ADD_COMPLETION_DUPLICATE:
|
||||||
|
xfree (new);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5253,8 +5279,8 @@ symtab_expansion_callback (struct compunit_symtab *symtab,
|
||||||
datum->code);
|
datum->code);
|
||||||
}
|
}
|
||||||
|
|
||||||
VEC (char_ptr) *
|
static void
|
||||||
default_make_symbol_completion_list_break_on (const char *text,
|
default_make_symbol_completion_list_break_on_1 (const char *text,
|
||||||
const char *word,
|
const char *word,
|
||||||
const char *break_on,
|
const char *break_on,
|
||||||
enum type_code code)
|
enum type_code code)
|
||||||
|
@ -5275,7 +5301,7 @@ default_make_symbol_completion_list_break_on (const char *text,
|
||||||
/* Length of sym_text. */
|
/* Length of sym_text. */
|
||||||
int sym_text_len;
|
int sym_text_len;
|
||||||
struct add_name_data datum;
|
struct add_name_data datum;
|
||||||
struct cleanup *back_to;
|
struct cleanup *cleanups;
|
||||||
|
|
||||||
/* Now look for the symbol we are supposed to complete on. */
|
/* Now look for the symbol we are supposed to complete on. */
|
||||||
{
|
{
|
||||||
|
@ -5310,7 +5336,7 @@ default_make_symbol_completion_list_break_on (const char *text,
|
||||||
/* A double-quoted string is never a symbol, nor does it make sense
|
/* A double-quoted string is never a symbol, nor does it make sense
|
||||||
to complete it any other way. */
|
to complete it any other way. */
|
||||||
{
|
{
|
||||||
return NULL;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5346,8 +5372,8 @@ default_make_symbol_completion_list_break_on (const char *text,
|
||||||
}
|
}
|
||||||
gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
|
gdb_assert (sym_text[sym_text_len] == '\0' || sym_text[sym_text_len] == '(');
|
||||||
|
|
||||||
return_val = NULL;
|
completion_tracker = new_completion_tracker ();
|
||||||
back_to = make_cleanup (do_free_completion_list, &return_val);
|
cleanups = make_cleanup_free_completion_tracker (&completion_tracker);
|
||||||
|
|
||||||
datum.sym_text = sym_text;
|
datum.sym_text = sym_text;
|
||||||
datum.sym_text_len = sym_text_len;
|
datum.sym_text_len = sym_text_len;
|
||||||
|
@ -5461,8 +5487,34 @@ default_make_symbol_completion_list_break_on (const char *text,
|
||||||
macro_for_each (macro_user_macros, add_macro_name, &datum);
|
macro_for_each (macro_user_macros, add_macro_name, &datum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
do_cleanups (cleanups);
|
||||||
|
}
|
||||||
|
|
||||||
|
VEC (char_ptr) *
|
||||||
|
default_make_symbol_completion_list_break_on (const char *text,
|
||||||
|
const char *word,
|
||||||
|
const char *break_on,
|
||||||
|
enum type_code code)
|
||||||
|
{
|
||||||
|
struct cleanup *back_to;
|
||||||
|
volatile struct gdb_exception except;
|
||||||
|
|
||||||
|
return_val = NULL;
|
||||||
|
back_to = make_cleanup (do_free_completion_list, &return_val);
|
||||||
|
|
||||||
|
TRY_CATCH (except, RETURN_MASK_ERROR)
|
||||||
|
{
|
||||||
|
default_make_symbol_completion_list_break_on_1 (text, word,
|
||||||
|
break_on, code);
|
||||||
|
}
|
||||||
|
if (except.reason < 0)
|
||||||
|
{
|
||||||
|
if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
|
||||||
|
throw_exception (except);
|
||||||
|
}
|
||||||
|
|
||||||
discard_cleanups (back_to);
|
discard_cleanups (back_to);
|
||||||
return (return_val);
|
return return_val;
|
||||||
}
|
}
|
||||||
|
|
||||||
VEC (char_ptr) *
|
VEC (char_ptr) *
|
||||||
|
|
|
@ -67,6 +67,7 @@ if ![runto_main] then {
|
||||||
}
|
}
|
||||||
|
|
||||||
set timeout 30
|
set timeout 30
|
||||||
|
gdb_test_no_output "set max-completions unlimited"
|
||||||
|
|
||||||
gdb_test_no_output "complete print values\[0\].x." \
|
gdb_test_no_output "complete print values\[0\].x." \
|
||||||
"field completion with invalid field"
|
"field completion with invalid field"
|
||||||
|
@ -775,4 +776,86 @@ gdb_test_multiple "" "$test" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
#
|
||||||
|
# Completion limiting.
|
||||||
|
#
|
||||||
|
|
||||||
|
gdb_test_no_output "set max-completions 5"
|
||||||
|
|
||||||
|
set test "command-name completion limiting using tab character"
|
||||||
|
send_gdb "p\t"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "^p\\\x07$" {
|
||||||
|
send_gdb "\t"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "List may be truncated, max-completions reached.*\r\n$gdb_prompt p$" {
|
||||||
|
# Complete the command and ignore the output to resync
|
||||||
|
# gdb for the next test.
|
||||||
|
send_gdb "\n"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt p$" {
|
||||||
|
# Complete the command and ignore the output to resync
|
||||||
|
# gdb for the next test.
|
||||||
|
send_gdb "\n"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
fail "$test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set test "command-name completion limiting using complete command"
|
||||||
|
send_gdb "complete p\n"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "List may be truncated, max-completions reached.*\r\n$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gdb_test_no_output "set max-completions 3"
|
||||||
|
|
||||||
|
set test "symbol-name completion limiting using tab character"
|
||||||
|
send_gdb "p marker\t"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "^p marker\\\x07$" {
|
||||||
|
send_gdb "\t"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "List may be truncated, max-completions reached.*\r\n$gdb_prompt p marker$" {
|
||||||
|
# Complete the command and ignore the output to resync
|
||||||
|
# gdb for the next test.
|
||||||
|
send_gdb "\n"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-re "$gdb_prompt p marker$" {
|
||||||
|
# Complete the command and ignore the output to resync
|
||||||
|
# gdb for the next test.
|
||||||
|
send_gdb "\n"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "$gdb_prompt $" {
|
||||||
|
fail "$test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set test "symbol-name completion limiting using complete command"
|
||||||
|
send_gdb "complete p mark\n"
|
||||||
|
gdb_test_multiple "" "$test" {
|
||||||
|
-re "List may be truncated, max-completions reached.*\r\n$gdb_prompt $" {
|
||||||
|
pass "$test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ if {[prepare_for_testing $testfile $exefile $srcfile \
|
||||||
# Turn off the pending breakpoint queries.
|
# Turn off the pending breakpoint queries.
|
||||||
gdb_test_no_output "set breakpoint pending off"
|
gdb_test_no_output "set breakpoint pending off"
|
||||||
|
|
||||||
|
# Turn off completion limiting
|
||||||
|
gdb_test_no_output "set max-completions unlimited"
|
||||||
|
|
||||||
# We intentionally do not use gdb_breakpoint for these tests.
|
# We intentionally do not use gdb_breakpoint for these tests.
|
||||||
|
|
||||||
# Break at 'linespec' and expect the message in ::error_messages indexed by
|
# Break at 'linespec' and expect the message in ::error_messages indexed by
|
||||||
|
|
|
@ -132,6 +132,7 @@ static rl_getc_func_t *tui_old_rl_getc_function;
|
||||||
static rl_voidfunc_t *tui_old_rl_redisplay_function;
|
static rl_voidfunc_t *tui_old_rl_redisplay_function;
|
||||||
static rl_vintfunc_t *tui_old_rl_prep_terminal;
|
static rl_vintfunc_t *tui_old_rl_prep_terminal;
|
||||||
static rl_voidfunc_t *tui_old_rl_deprep_terminal;
|
static rl_voidfunc_t *tui_old_rl_deprep_terminal;
|
||||||
|
static rl_compdisp_func_t *tui_old_rl_display_matches_hook;
|
||||||
static int tui_old_rl_echoing_p;
|
static int tui_old_rl_echoing_p;
|
||||||
|
|
||||||
/* Readline output stream.
|
/* Readline output stream.
|
||||||
|
@ -468,6 +469,7 @@ tui_setup_io (int mode)
|
||||||
tui_old_rl_deprep_terminal = rl_deprep_term_function;
|
tui_old_rl_deprep_terminal = rl_deprep_term_function;
|
||||||
tui_old_rl_prep_terminal = rl_prep_term_function;
|
tui_old_rl_prep_terminal = rl_prep_term_function;
|
||||||
tui_old_rl_getc_function = rl_getc_function;
|
tui_old_rl_getc_function = rl_getc_function;
|
||||||
|
tui_old_rl_display_matches_hook = rl_completion_display_matches_hook;
|
||||||
tui_old_rl_outstream = rl_outstream;
|
tui_old_rl_outstream = rl_outstream;
|
||||||
tui_old_rl_echoing_p = _rl_echoing_p;
|
tui_old_rl_echoing_p = _rl_echoing_p;
|
||||||
rl_redisplay_function = tui_redisplay_readline;
|
rl_redisplay_function = tui_redisplay_readline;
|
||||||
|
@ -511,8 +513,8 @@ tui_setup_io (int mode)
|
||||||
rl_deprep_term_function = tui_old_rl_deprep_terminal;
|
rl_deprep_term_function = tui_old_rl_deprep_terminal;
|
||||||
rl_prep_term_function = tui_old_rl_prep_terminal;
|
rl_prep_term_function = tui_old_rl_prep_terminal;
|
||||||
rl_getc_function = tui_old_rl_getc_function;
|
rl_getc_function = tui_old_rl_getc_function;
|
||||||
|
rl_completion_display_matches_hook = tui_old_rl_display_matches_hook;
|
||||||
rl_outstream = tui_old_rl_outstream;
|
rl_outstream = tui_old_rl_outstream;
|
||||||
rl_completion_display_matches_hook = 0;
|
|
||||||
_rl_echoing_p = tui_old_rl_echoing_p;
|
_rl_echoing_p = tui_old_rl_echoing_p;
|
||||||
rl_already_prompted = 0;
|
rl_already_prompted = 0;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue