aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog29
-rw-r--r--gdb/cli-out.c84
-rw-r--r--gdb/cli-out.h2
-rw-r--r--gdb/completer.c554
-rw-r--r--gdb/completer.h46
-rw-r--r--gdb/top.c1
-rw-r--r--gdb/tui/tui-io.c248
7 files changed, 803 insertions, 161 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index b82d4d0..33e0dff 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,34 @@
2015-01-31 Doug Evans <xdje42@gmail.com>
+ * cli-out.c: #include completer.h, readline/readline.h.
+ (cli_mld_crlf, cli_mld_putch, cli_mld_puts): New functions.
+ (cli_mld_flush, cld_mld_erase_entire_line): Ditto.
+ (cli_mld_beep, cli_mld_read_key, cli_display_match_list): Ditto.
+ * cli-out.h (cli_display_match_list): Declare.
+ * completer.c (MB_INVALIDCH, MB_NULLWCH): New macros.
+ (ELLIPSIS_LEN): Ditto.
+ (gdb_get_y_or_n, gdb_display_match_list_pager): New functions.
+ (gdb_path_isdir, gdb_printable_part, gdb_fnwidth): Ditto.
+ (gdb_fnprint, gdb_print_filename): Ditto.
+ (gdb_complete_get_screenwidth, gdb_display_match_list_1): Ditto.
+ (gdb_display_match_list): Ditto.
+ * completer.h (mld_crlf_ftype, mld_putch_ftype): New typedefs.
+ (mld_puts_ftype, mld_flush_ftype, mld_erase_entire_line_ftype): Ditto.
+ (mld_beep_ftype, mld_read_key_ftype): Ditto.
+ (match_list_displayer): New struct.
+ (gdb_display_match_list): Declare.
+ * top.c (init_main): Set rl_completion_display_matches_hook.
+ * tui/tui-io.c: #include completer.h.
+ (printable_part, PUTX, print_filename, get_y_or_n): Delete.
+ (tui_mld_crlf, tui_mld_putch, tui_mld_puts): New functions.
+ (tui_mld_flush, tui_mld_erase_entire_line, tui_mld_beep): Ditto.
+ (tui_mld_getc, tui_mld_read_key): Ditto.
+ (tui_rl_display_match_list): Rewrite.
+ (tui_handle_resize_during_io): New arg for_completion. All callers
+ updated.
+
+2015-01-31 Doug Evans <xdje42@gmail.com>
+
Add symbol lookup cache.
* NEWS: Document new options and commands.
* symtab.c (symbol_cache_key): New static global.
diff --git a/gdb/cli-out.c b/gdb/cli-out.c
index 76222c6..48f2a04 100644
--- a/gdb/cli-out.c
+++ b/gdb/cli-out.c
@@ -23,11 +23,12 @@
#include "defs.h"
#include "ui-out.h"
#include "cli-out.h"
+#include "completer.h"
#include "vec.h"
+#include "readline/readline.h"
typedef struct cli_ui_out_data cli_out_data;
-
/* Prototypes for local functions */
static void cli_text (struct ui_out *uiout, const char *string);
@@ -416,3 +417,84 @@ cli_out_set_stream (struct ui_out *uiout, struct ui_file *stream)
return old;
}
+
+/* CLI interface to display tab-completion matches. */
+
+/* CLI version of displayer.crlf. */
+
+static void
+cli_mld_crlf (const struct match_list_displayer *displayer)
+{
+ rl_crlf ();
+}
+
+/* CLI version of displayer.putch. */
+
+static void
+cli_mld_putch (const struct match_list_displayer *displayer, int ch)
+{
+ putc (ch, rl_outstream);
+}
+
+/* CLI version of displayer.puts. */
+
+static void
+cli_mld_puts (const struct match_list_displayer *displayer, const char *s)
+{
+ fputs (s, rl_outstream);
+}
+
+/* CLI version of displayer.flush. */
+
+static void
+cli_mld_flush (const struct match_list_displayer *displayer)
+{
+ fflush (rl_outstream);
+}
+
+/* CLI version of displayer.erase_entire_line. */
+
+static void
+cli_mld_erase_entire_line (const struct match_list_displayer *displayer)
+{
+ extern void _rl_erase_entire_line (void);
+
+ _rl_erase_entire_line ();
+}
+
+/* CLI version of displayer.beep. */
+
+static void
+cli_mld_beep (const struct match_list_displayer *displayer)
+{
+ rl_ding ();
+}
+
+/* CLI version of displayer.read_key. */
+
+static int
+cli_mld_read_key (const struct match_list_displayer *displayer)
+{
+ return rl_read_key ();
+}
+
+/* CLI version of rl_completion_display_matches_hook.
+ See gdb_display_match_list for a description of the arguments. */
+
+void
+cli_display_match_list (char **matches, int len, int max)
+{
+ struct match_list_displayer displayer;
+
+ rl_get_screen_size (&displayer.height, &displayer.width);
+ displayer.crlf = cli_mld_crlf;
+ displayer.putch = cli_mld_putch;
+ displayer.puts = cli_mld_puts;
+ displayer.flush = cli_mld_flush;
+ displayer.erase_entire_line = cli_mld_erase_entire_line;
+ displayer.beep = cli_mld_beep;
+ displayer.read_key = cli_mld_read_key;
+
+ gdb_display_match_list (matches, len, max, &displayer);
+ rl_forced_update_display ();
+}
diff --git a/gdb/cli-out.h b/gdb/cli-out.h
index bf07069..401429a 100644
--- a/gdb/cli-out.h
+++ b/gdb/cli-out.h
@@ -48,4 +48,6 @@ extern void cli_out_data_ctor (struct cli_ui_out_data *data,
extern struct ui_file *cli_out_set_stream (struct ui_out *uiout,
struct ui_file *stream);
+extern void cli_display_match_list (char **matches, int len, int max);
+
#endif
diff --git a/gdb/completer.c b/gdb/completer.c
index 2b6aa87..88c8e16 100644
--- a/gdb/completer.c
+++ b/gdb/completer.c
@@ -1020,3 +1020,557 @@ skip_quoted (const char *str)
{
return skip_quoted_chars (str, NULL, NULL);
}
+
+/* GDB replacement for rl_display_match_list.
+ Readline doesn't provide a clean interface for TUI(curses).
+ A hack previously used was to send readline's rl_outstream through a pipe
+ and read it from the event loop. Bleah. IWBN if readline abstracted
+ away all the necessary bits, and this is what this code does. It
+ replicates the parts of readline we need and then adds an abstraction
+ layer, currently implemented as struct match_list_displayer, so that both
+ CLI and TUI can use it. We copy all this readline code to minimize
+ GDB-specific mods to readline. Once this code performs as desired then
+ we can submit it to the readline maintainers.
+
+ N.B. A lot of the code is the way it is in order to minimize differences
+ from readline's copy. */
+
+/* Not supported here. */
+#undef VISIBLE_STATS
+
+#if defined (HANDLE_MULTIBYTE)
+#define MB_INVALIDCH(x) ((x) == (size_t)-1 || (x) == (size_t)-2)
+#define MB_NULLWCH(x) ((x) == 0)
+#endif
+
+#define ELLIPSIS_LEN 3
+
+/* gdb version of readline/complete.c:get_y_or_n.
+ 'y' -> returns 1, and 'n' -> returns 0.
+ Also supported: space == 'y', RUBOUT == 'n', ctrl-g == start over.
+ If FOR_PAGER is non-zero, then also supported are:
+ NEWLINE or RETURN -> returns 2, and 'q' -> returns 0. */
+
+static int
+gdb_get_y_or_n (int for_pager, const struct match_list_displayer *displayer)
+{
+ int c;
+
+ for (;;)
+ {
+ RL_SETSTATE (RL_STATE_MOREINPUT);
+ c = displayer->read_key (displayer);
+ RL_UNSETSTATE (RL_STATE_MOREINPUT);
+
+ if (c == 'y' || c == 'Y' || c == ' ')
+ return 1;
+ if (c == 'n' || c == 'N' || c == RUBOUT)
+ return 0;
+ if (c == ABORT_CHAR || c < 0)
+ {
+ /* Readline doesn't erase_entire_line here, but without it the
+ --More-- prompt isn't erased and neither is the text entered
+ thus far redisplayed. */
+ displayer->erase_entire_line (displayer);
+ /* Note: The arguments to rl_abort are ignored. */
+ rl_abort (0, 0);
+ }
+ if (for_pager && (c == NEWLINE || c == RETURN))
+ return 2;
+ if (for_pager && (c == 'q' || c == 'Q'))
+ return 0;
+ displayer->beep (displayer);
+ }
+}
+
+/* Pager function for tab-completion.
+ This is based on readline/complete.c:_rl_internal_pager.
+ LINES is the number of lines of output displayed thus far.
+ Returns:
+ -1 -> user pressed 'n' or equivalent,
+ 0 -> user pressed 'y' or equivalent,
+ N -> user pressed NEWLINE or equivalent and N is LINES - 1. */
+
+static int
+gdb_display_match_list_pager (int lines,
+ const struct match_list_displayer *displayer)
+{
+ int i;
+
+ displayer->puts (displayer, "--More--");
+ displayer->flush (displayer);
+ i = gdb_get_y_or_n (1, displayer);
+ displayer->erase_entire_line (displayer);
+ if (i == 0)
+ return -1;
+ else if (i == 2)
+ return (lines - 1);
+ else
+ return 0;
+}
+
+/* Return non-zero if FILENAME is a directory.
+ Based on readline/complete.c:path_isdir. */
+
+static int
+gdb_path_isdir (const char *filename)
+{
+ struct stat finfo;
+
+ return (stat (filename, &finfo) == 0 && S_ISDIR (finfo.st_mode));
+}
+
+/* Return the portion of PATHNAME that should be output when listing
+ possible completions. If we are hacking filename completion, we
+ are only interested in the basename, the portion following the
+ final slash. Otherwise, we return what we were passed. Since
+ printing empty strings is not very informative, if we're doing
+ filename completion, and the basename is the empty string, we look
+ for the previous slash and return the portion following that. If
+ there's no previous slash, we just return what we were passed.
+
+ Based on readline/complete.c:printable_part. */
+
+static char *
+gdb_printable_part (char *pathname)
+{
+ char *temp, *x;
+
+ if (rl_filename_completion_desired == 0) /* don't need to do anything */
+ return (pathname);
+
+ temp = strrchr (pathname, '/');
+#if defined (__MSDOS__)
+ if (temp == 0 && ISALPHA ((unsigned char)pathname[0]) && pathname[1] == ':')
+ temp = pathname + 1;
+#endif
+
+ if (temp == 0 || *temp == '\0')
+ return (pathname);
+ /* If the basename is NULL, we might have a pathname like '/usr/src/'.
+ Look for a previous slash and, if one is found, return the portion
+ following that slash. If there's no previous slash, just return the
+ pathname we were passed. */
+ else if (temp[1] == '\0')
+ {
+ for (x = temp - 1; x > pathname; x--)
+ if (*x == '/')
+ break;
+ return ((*x == '/') ? x + 1 : pathname);
+ }
+ else
+ return ++temp;
+}
+
+/* Compute width of STRING when displayed on screen by print_filename.
+ Based on readline/complete.c:fnwidth. */
+
+static int
+gdb_fnwidth (const char *string)
+{
+ int width, pos;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ int left, w;
+ size_t clen;
+ wchar_t wc;
+
+ left = strlen (string) + 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ width = pos = 0;
+ while (string[pos])
+ {
+ if (CTRL_CHAR (string[pos]) || string[pos] == RUBOUT)
+ {
+ width += 2;
+ pos++;
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ clen = mbrtowc (&wc, string + pos, left - pos, &ps);
+ if (MB_INVALIDCH (clen))
+ {
+ width++;
+ pos++;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (clen))
+ break;
+ else
+ {
+ pos += clen;
+ w = wcwidth (wc);
+ width += (w >= 0) ? w : 1;
+ }
+#else
+ width++;
+ pos++;
+#endif
+ }
+ }
+
+ return width;
+}
+
+/* Print TO_PRINT, one matching completion.
+ PREFIX_BYTES is number of common prefix bytes.
+ Based on readline/complete.c:fnprint. */
+
+static int
+gdb_fnprint (const char *to_print, int prefix_bytes,
+ const struct match_list_displayer *displayer)
+{
+ int printed_len, w;
+ const char *s;
+#if defined (HANDLE_MULTIBYTE)
+ mbstate_t ps;
+ const char *end;
+ size_t tlen;
+ int width;
+ wchar_t wc;
+
+ end = to_print + strlen (to_print) + 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+
+ printed_len = 0;
+
+ /* Don't print only the ellipsis if the common prefix is one of the
+ possible completions */
+ if (to_print[prefix_bytes] == '\0')
+ prefix_bytes = 0;
+
+ if (prefix_bytes)
+ {
+ char ellipsis;
+
+ ellipsis = (to_print[prefix_bytes] == '.') ? '_' : '.';
+ for (w = 0; w < ELLIPSIS_LEN; w++)
+ displayer->putch (displayer, ellipsis);
+ printed_len = ELLIPSIS_LEN;
+ }
+
+ s = to_print + prefix_bytes;
+ while (*s)
+ {
+ if (CTRL_CHAR (*s))
+ {
+ displayer->putch (displayer, '^');
+ displayer->putch (displayer, UNCTRL (*s));
+ printed_len += 2;
+ s++;
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+ }
+ else if (*s == RUBOUT)
+ {
+ displayer->putch (displayer, '^');
+ displayer->putch (displayer, '?');
+ printed_len += 2;
+ s++;
+#if defined (HANDLE_MULTIBYTE)
+ memset (&ps, 0, sizeof (mbstate_t));
+#endif
+ }
+ else
+ {
+#if defined (HANDLE_MULTIBYTE)
+ tlen = mbrtowc (&wc, s, end - s, &ps);
+ if (MB_INVALIDCH (tlen))
+ {
+ tlen = 1;
+ width = 1;
+ memset (&ps, 0, sizeof (mbstate_t));
+ }
+ else if (MB_NULLWCH (tlen))
+ break;
+ else
+ {
+ w = wcwidth (wc);
+ width = (w >= 0) ? w : 1;
+ }
+ for (w = 0; w < tlen; ++w)
+ displayer->putch (displayer, s[w]);
+ s += tlen;
+ printed_len += width;
+#else
+ displayer->putch (displayer, *s);
+ s++;
+ printed_len++;
+#endif
+ }
+ }
+
+ return printed_len;
+}
+
+/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we
+ are using it, check for and output a single character for `special'
+ filenames. Return the number of characters we output.
+ Based on readline/complete.c:print_filename. */
+
+static int
+gdb_print_filename (char *to_print, char *full_pathname, int prefix_bytes,
+ const struct match_list_displayer *displayer)
+{
+ int printed_len, extension_char, slen, tlen;
+ char *s, c, *new_full_pathname, *dn;
+ extern int _rl_complete_mark_directories;
+
+ extension_char = 0;
+ printed_len = gdb_fnprint (to_print, prefix_bytes, displayer);
+
+#if defined (VISIBLE_STATS)
+ if (rl_filename_completion_desired && (rl_visible_stats || _rl_complete_mark_directories))
+#else
+ if (rl_filename_completion_desired && _rl_complete_mark_directories)
+#endif
+ {
+ /* If to_print != full_pathname, to_print is the basename of the
+ path passed. In this case, we try to expand the directory
+ name before checking for the stat character. */
+ if (to_print != full_pathname)
+ {
+ /* Terminate the directory name. */
+ c = to_print[-1];
+ to_print[-1] = '\0';
+
+ /* If setting the last slash in full_pathname to a NUL results in
+ full_pathname being the empty string, we are trying to complete
+ files in the root directory. If we pass a null string to the
+ bash directory completion hook, for example, it will expand it
+ to the current directory. We just want the `/'. */
+ if (full_pathname == 0 || *full_pathname == 0)
+ dn = "/";
+ else if (full_pathname[0] != '/')
+ dn = full_pathname;
+ else if (full_pathname[1] == 0)
+ dn = "//"; /* restore trailing slash to `//' */
+ else if (full_pathname[1] == '/' && full_pathname[2] == 0)
+ dn = "/"; /* don't turn /// into // */
+ else
+ dn = full_pathname;
+ s = tilde_expand (dn);
+ if (rl_directory_completion_hook)
+ (*rl_directory_completion_hook) (&s);
+
+ slen = strlen (s);
+ tlen = strlen (to_print);
+ new_full_pathname = (char *)xmalloc (slen + tlen + 2);
+ strcpy (new_full_pathname, s);
+ if (s[slen - 1] == '/')
+ slen--;
+ else
+ new_full_pathname[slen] = '/';
+ new_full_pathname[slen] = '/';
+ strcpy (new_full_pathname + slen + 1, to_print);
+
+#if defined (VISIBLE_STATS)
+ if (rl_visible_stats)
+ extension_char = stat_char (new_full_pathname);
+ else
+#endif
+ if (gdb_path_isdir (new_full_pathname))
+ extension_char = '/';
+
+ xfree (new_full_pathname);
+ to_print[-1] = c;
+ }
+ else
+ {
+ s = tilde_expand (full_pathname);
+#if defined (VISIBLE_STATS)
+ if (rl_visible_stats)
+ extension_char = stat_char (s);
+ else
+#endif
+ if (gdb_path_isdir (s))
+ extension_char = '/';
+ }
+
+ xfree (s);
+ if (extension_char)
+ {
+ displayer->putch (displayer, extension_char);
+ printed_len++;
+ }
+ }
+
+ return printed_len;
+}
+
+/* GDB version of readline/complete.c:complete_get_screenwidth. */
+
+static int
+gdb_complete_get_screenwidth (const struct match_list_displayer *displayer)
+{
+ /* Readline has other stuff here which it's not clear we need. */
+ return displayer->width;
+}
+
+/* GDB version of readline/complete.c:rl_display_match_list.
+ See gdb_display_match_list for a description of MATCHES, LEN, MAX. */
+
+static void
+gdb_display_match_list_1 (char **matches, int len, int max,
+ const struct match_list_displayer *displayer)
+{
+ int count, limit, printed_len, lines, cols;
+ int i, j, k, l, common_length, sind;
+ char *temp, *t;
+ int page_completions = displayer->height != INT_MAX && pagination_enabled;
+ extern int _rl_completion_prefix_display_length;
+ extern int _rl_qsort_string_compare (const void *, const void *);
+ extern int _rl_print_completions_horizontally;
+ typedef int QSFUNC (const void *, const void *);
+
+ /* Find the length of the prefix common to all items: length as displayed
+ characters (common_length) and as a byte index into the matches (sind) */
+ common_length = sind = 0;
+ if (_rl_completion_prefix_display_length > 0)
+ {
+ t = gdb_printable_part (matches[0]);
+ temp = strrchr (t, '/');
+ common_length = temp ? gdb_fnwidth (temp) : gdb_fnwidth (t);
+ sind = temp ? strlen (temp) : strlen (t);
+
+ if (common_length > _rl_completion_prefix_display_length && common_length > ELLIPSIS_LEN)
+ max -= common_length - ELLIPSIS_LEN;
+ else
+ common_length = sind = 0;
+ }
+
+ /* How many items of MAX length can we fit in the screen window? */
+ cols = gdb_complete_get_screenwidth (displayer);
+ max += 2;
+ limit = cols / max;
+ if (limit != 1 && (limit * max == cols))
+ limit--;
+
+ /* If cols == 0, limit will end up -1 */
+ if (cols < displayer->width && limit < 0)
+ limit = 1;
+
+ /* Avoid a possible floating exception. If max > cols,
+ limit will be 0 and a divide-by-zero fault will result. */
+ if (limit == 0)
+ limit = 1;
+
+ /* How many iterations of the printing loop? */
+ count = (len + (limit - 1)) / limit;
+
+ /* Watch out for special case. If LEN is less than LIMIT, then
+ just do the inner printing loop.
+ 0 < len <= limit implies count = 1. */
+
+ /* Sort the items if they are not already sorted. */
+ if (rl_ignore_completion_duplicates == 0 && rl_sort_completion_matches)
+ qsort (matches + 1, len, sizeof (char *), (QSFUNC *)_rl_qsort_string_compare);
+
+ displayer->crlf (displayer);
+
+ lines = 0;
+ if (_rl_print_completions_horizontally == 0)
+ {
+ /* Print the sorted items, up-and-down alphabetically, like ls. */
+ for (i = 1; i <= count; i++)
+ {
+ for (j = 0, l = i; j < limit; j++)
+ {
+ if (l > len || matches[l] == 0)
+ break;
+ else
+ {
+ temp = gdb_printable_part (matches[l]);
+ printed_len = gdb_print_filename (temp, matches[l], sind,
+ displayer);
+
+ if (j + 1 < limit)
+ for (k = 0; k < max - printed_len; k++)
+ displayer->putch (displayer, ' ');
+ }
+ l += count;
+ }
+ displayer->crlf (displayer);
+ lines++;
+ if (page_completions && lines >= (displayer->height - 1) && i < count)
+ {
+ lines = gdb_display_match_list_pager (lines, displayer);
+ if (lines < 0)
+ return;
+ }
+ }
+ }
+ else
+ {
+ /* Print the sorted items, across alphabetically, like ls -x. */
+ for (i = 1; matches[i]; i++)
+ {
+ temp = gdb_printable_part (matches[i]);
+ printed_len = gdb_print_filename (temp, matches[i], sind, displayer);
+ /* Have we reached the end of this line? */
+ if (matches[i+1])
+ {
+ if (i && (limit > 1) && (i % limit) == 0)
+ {
+ displayer->crlf (displayer);
+ lines++;
+ if (page_completions && lines >= displayer->height - 1)
+ {
+ lines = gdb_display_match_list_pager (lines, displayer);
+ if (lines < 0)
+ return;
+ }
+ }
+ else
+ for (k = 0; k < max - printed_len; k++)
+ displayer->putch (displayer, ' ');
+ }
+ }
+ displayer->crlf (displayer);
+ }
+}
+
+/* Utility for displaying completion list matches, used by both CLI and TUI.
+
+ MATCHES is the list of strings, in argv format, LEN is the number of
+ strings in MATCHES, and MAX is the length of the longest string in MATCHES.
+
+ This function handles the LIST_MAYBE_TRUNCATED marker that we add to the
+ completion list.
+
+ Note: While LIST_MAYBE_TRUNCATED contributes to MAX, it's not long enough
+ that we worry about it. */
+
+void
+gdb_display_match_list (char **matches, int len, int max,
+ const struct match_list_displayer *displayer)
+{
+ if (rl_completion_query_items > 0 && len >= rl_completion_query_items)
+ {
+ char msg[100];
+
+ /* We can't use *query here because they wait for <RET> which is
+ wrong here. This follows the readline version as closely as possible
+ for compatibility's sake. See readline/complete.c. */
+
+ displayer->crlf (displayer);
+
+ xsnprintf (msg, sizeof (msg),
+ "Display all %d possibilities? (y or n)", len);
+ displayer->puts (displayer, msg);
+ displayer->flush (displayer);
+
+ if (gdb_get_y_or_n (0, displayer) == 0)
+ {
+ displayer->crlf (displayer);
+ return;
+ }
+ }
+
+ gdb_display_match_list_1 (matches, len, max, displayer);
+}
diff --git a/gdb/completer.h b/gdb/completer.h
index 8f925fe..dbb1cfb 100644
--- a/gdb/completer.h
+++ b/gdb/completer.h
@@ -20,6 +20,52 @@
#include "gdb_vecs.h"
#include "command.h"
+/* Types of functions in struct match_list_displayer. */
+
+struct match_list_displayer;
+
+typedef void mld_crlf_ftype (const struct match_list_displayer *);
+typedef void mld_putch_ftype (const struct match_list_displayer *, int);
+typedef void mld_puts_ftype (const struct match_list_displayer *,
+ const char *);
+typedef void mld_flush_ftype (const struct match_list_displayer *);
+typedef void mld_erase_entire_line_ftype (const struct match_list_displayer *);
+typedef void mld_beep_ftype (const struct match_list_displayer *);
+typedef int mld_read_key_ftype (const struct match_list_displayer *);
+
+/* Interface between CLI/TUI and gdb_match_list_displayer. */
+
+struct match_list_displayer
+{
+ /* The screen dimensions to work with when displaying matches. */
+ int height, width;
+
+ /* Print cr,lf. */
+ mld_crlf_ftype *crlf;
+
+ /* Not "putc" to avoid issues where it is a stdio macro. Sigh. */
+ mld_putch_ftype *putch;
+
+ /* Print a string. */
+ mld_puts_ftype *puts;
+
+ /* Flush all accumulated output. */
+ mld_flush_ftype *flush;
+
+ /* Erase the currently line on the terminal (but don't discard any text the
+ user has entered, readline may shortly re-print it). */
+ mld_erase_entire_line_ftype *erase_entire_line;
+
+ /* Ring the bell. */
+ mld_beep_ftype *beep;
+
+ /* Read one key. */
+ mld_read_key_ftype *read_key;
+};
+
+extern void gdb_display_match_list (char **matches, int len, int max,
+ const struct match_list_displayer *);
+
extern VEC (char_ptr) *complete_line (const char *text,
const char *line_buffer,
int point);
diff --git a/gdb/top.c b/gdb/top.c
index a1462a0..000b14e 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1828,6 +1828,7 @@ init_main (void)
rl_completion_entry_function = readline_line_completion_function;
rl_completer_word_break_characters = default_word_break_characters ();
rl_completer_quote_characters = get_gdb_completer_quote_characters ();
+ rl_completion_display_matches_hook = cli_display_match_list;
rl_readline_name = "gdb";
rl_terminal_name = getenv ("TERM");
diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c
index 19e9485..831705c 100644
--- a/gdb/tui/tui-io.c
+++ b/gdb/tui/tui-io.c
@@ -37,7 +37,7 @@
#include <fcntl.h>
#include <signal.h>
#include "filestuff.h"
-
+#include "completer.h"
#include "gdb_curses.h"
/* This redefines CTRL if it is not already defined, so it must come
@@ -146,7 +146,7 @@ static int tui_readline_pipe[2];
This may be the main gdb prompt or a secondary prompt. */
static char *tui_rl_saved_prompt;
-static unsigned int tui_handle_resize_during_io (unsigned int);
+static int tui_handle_resize_during_io (int, int);
static void
tui_putc (char c)
@@ -346,182 +346,106 @@ tui_readline_output (int error, gdb_client_data data)
}
#endif
-/* Return the portion of PATHNAME that should be output when listing
- possible completions. If we are hacking filename completion, we
- are only interested in the basename, the portion following the
- final slash. Otherwise, we return what we were passed.
+/* TUI version of displayer.crlf. */
- Comes from readline/complete.c. */
-static const char *
-printable_part (const char *pathname)
+static void
+tui_mld_crlf (const struct match_list_displayer *displayer)
{
- return rl_filename_completion_desired ? lbasename (pathname) : pathname;
+ tui_putc ('\n');
}
-/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and
- we are using it, check for and output a single character for
- `special' filenames. Return the number of characters we
- output. */
-
-#define PUTX(c) \
- do { \
- if (CTRL_CHAR (c)) \
- { \
- tui_puts ("^"); \
- tui_putc (UNCTRL (c)); \
- printed_len += 2; \
- } \
- else if (c == RUBOUT) \
- { \
- tui_puts ("^?"); \
- printed_len += 2; \
- } \
- else \
- { \
- tui_putc (c); \
- printed_len++; \
- } \
- } while (0)
+/* TUI version of displayer.putch. */
-static int
-print_filename (const char *to_print, const char *full_pathname)
+static void
+tui_mld_putch (const struct match_list_displayer *displayer, int ch)
{
- int printed_len = 0;
- const char *s;
-
- for (s = to_print; *s; s++)
- {
- PUTX (*s);
- }
- return printed_len;
+ tui_putc (ch);
}
-/* The user must press "y" or "n". Non-zero return means "y" pressed.
- Comes from readline/complete.c. */
-static int
-get_y_or_n (void)
+/* TUI version of displayer.puts. */
+
+static void
+tui_mld_puts (const struct match_list_displayer *displayer, const char *s)
{
- extern int _rl_abort_internal ();
- int c;
+ tui_puts (s);
+}
- for (;;)
- {
- c = rl_read_key ();
- if (c == 'y' || c == 'Y' || c == ' ')
- return (1);
- if (c == 'n' || c == 'N' || c == RUBOUT)
- return (0);
- if (c == ABORT_CHAR)
- _rl_abort_internal ();
- beep ();
- }
+/* TUI version of displayer.flush. */
+
+static void
+tui_mld_flush (const struct match_list_displayer *displayer)
+{
+ wrefresh (TUI_CMD_WIN->generic.handle);
}
-/* A convenience function for displaying a list of strings in
- columnar format on readline's output stream. MATCHES is the list
- of strings, in argv format, LEN is the number of strings in MATCHES,
- and MAX is the length of the longest string in MATCHES.
+/* TUI version of displayer.erase_entire_line. */
- Comes from readline/complete.c and modified to write in
- the TUI command window using tui_putc/tui_puts. */
static void
-tui_rl_display_match_list (char **matches, int len, int max)
+tui_mld_erase_entire_line (const struct match_list_displayer *displayer)
{
- typedef int QSFUNC (const void *, const void *);
- extern int _rl_qsort_string_compare (const void *,
- const void *);
- extern int _rl_print_completions_horizontally;
-
- int count, limit, printed_len;
- int i, j, k, l;
- const char *temp;
+ WINDOW *w = TUI_CMD_WIN->generic.handle;
- /* Screen dimension correspond to the TUI command window. */
- int screenwidth = TUI_CMD_WIN->generic.width;
+ wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, 0);
+ wclrtoeol (w);
+ wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, 0);
+}
- /* If there are many items, then ask the user if she really wants to
- see them all. */
- if (len >= rl_completion_query_items)
- {
- char msg[256];
+/* TUI version of displayer.beep. */
- xsnprintf (msg, sizeof (msg),
- "\nDisplay all %d possibilities? (y or n)", len);
- tui_puts (msg);
- if (get_y_or_n () == 0)
- {
- tui_puts ("\n");
- return;
- }
- }
+static void
+tui_mld_beep (const struct match_list_displayer *displayer)
+{
+ beep ();
+}
+
+/* Helper function for tui_mld_read_key.
+ This temporarily replaces tui_getc for use during tab-completion
+ match list display. */
+
+static int
+tui_mld_getc (FILE *fp)
+{
+ WINDOW *w = TUI_CMD_WIN->generic.handle;
+ int c = wgetch (w);
- /* How many items of MAX length can we fit in the screen window? */
- max += 2;
- limit = screenwidth / max;
- if (limit != 1 && (limit * max == screenwidth))
- limit--;
+ c = tui_handle_resize_during_io (c, 1);
- /* Avoid a possible floating exception. If max > screenwidth, limit
- will be 0 and a divide-by-zero fault will result. */
- if (limit == 0)
- limit = 1;
+ return c;
+}
- /* How many iterations of the printing loop? */
- count = (len + (limit - 1)) / limit;
+/* TUI version of displayer.read_key. */
- /* Watch out for special case. If LEN is less than LIMIT, then
- just do the inner printing loop.
- 0 < len <= limit implies count = 1. */
+static int
+tui_mld_read_key (const struct match_list_displayer *displayer)
+{
+ rl_getc_func_t *prev = rl_getc_function;
+ int c;
- /* Sort the items if they are not already sorted. */
- if (rl_ignore_completion_duplicates == 0)
- qsort (matches + 1, len, sizeof (char *),
- (QSFUNC *)_rl_qsort_string_compare);
+ /* We can't use tui_getc as we need NEWLINE to not get emitted. */
+ rl_getc_function = tui_mld_getc;
+ c = rl_read_key ();
+ rl_getc_function = prev;
+ return c;
+}
- tui_putc ('\n');
+/* TUI version of rl_completion_display_matches_hook.
+ See gdb_display_match_list for a description of the arguments. */
- if (_rl_print_completions_horizontally == 0)
- {
- /* Print the sorted items, up-and-down alphabetically, like ls. */
- for (i = 1; i <= count; i++)
- {
- for (j = 0, l = i; j < limit; j++)
- {
- if (l > len || matches[l] == 0)
- break;
- else
- {
- temp = printable_part (matches[l]);
- printed_len = print_filename (temp, matches[l]);
-
- if (j + 1 < limit)
- for (k = 0; k < max - printed_len; k++)
- tui_putc (' ');
- }
- l += count;
- }
- tui_putc ('\n');
- }
- }
- else
- {
- /* Print the sorted items, across alphabetically, like ls -x. */
- for (i = 1; matches[i]; i++)
- {
- temp = printable_part (matches[i]);
- printed_len = print_filename (temp, matches[i]);
- /* Have we reached the end of this line? */
- if (matches[i+1])
- {
- if (i && (limit > 1) && (i % limit) == 0)
- tui_putc ('\n');
- else
- for (k = 0; k < max - printed_len; k++)
- tui_putc (' ');
- }
- }
- tui_putc ('\n');
- }
+static void
+tui_rl_display_match_list (char **matches, int len, int max)
+{
+ struct match_list_displayer displayer;
+
+ rl_get_screen_size (&displayer.height, &displayer.width);
+ displayer.crlf = tui_mld_crlf;
+ displayer.putch = tui_mld_putch;
+ displayer.puts = tui_mld_puts;
+ displayer.flush = tui_mld_flush;
+ displayer.erase_entire_line = tui_mld_erase_entire_line;
+ displayer.beep = tui_mld_beep;
+ displayer.read_key = tui_mld_read_key;
+
+ gdb_display_match_list (matches, len, max, &displayer);
}
/* Setup the IO for curses or non-curses mode.
@@ -679,7 +603,7 @@ tui_getc (FILE *fp)
#endif
ch = wgetch (w);
- ch = tui_handle_resize_during_io (ch);
+ ch = tui_handle_resize_during_io (ch, 0);
/* The \n must be echoed because it will not be printed by
readline. */
@@ -803,17 +727,21 @@ tui_expand_tabs (const char *string, int col)
/* Cleanup when a resize has occured.
Returns the character that must be processed. */
-static unsigned int
-tui_handle_resize_during_io (unsigned int original_ch)
+
+static int
+tui_handle_resize_during_io (int original_ch, int for_completion)
{
if (tui_win_resized ())
{
tui_resize_all ();
tui_refresh_all_win ();
- dont_repeat ();
tui_set_win_resized_to (FALSE);
- return '\n';
+ if (!for_completion)
+ {
+ dont_repeat ();
+ return '\n';
+ }
}
- else
- return original_ch;
+
+ return original_ch;
}