diff options
author | Doug Evans <xdje42@gmail.com> | 2015-01-31 14:11:54 -0800 |
---|---|---|
committer | Doug Evans <xdje42@gmail.com> | 2015-01-31 14:11:54 -0800 |
commit | 82083d6dbbc0b2f6a76095582c6e7ffb3e06432a (patch) | |
tree | be2581d31b188398863a27e60e2ac2ad69c0d910 /gdb | |
parent | f57d2163da62044205c3f53e0ecf186923296b63 (diff) | |
download | gdb-82083d6dbbc0b2f6a76095582c6e7ffb3e06432a.zip gdb-82083d6dbbc0b2f6a76095582c6e7ffb3e06432a.tar.gz gdb-82083d6dbbc0b2f6a76095582c6e7ffb3e06432a.tar.bz2 |
Unify CLI/TUI interface to readline tab completion.
This copies a lot of code from readline, but this is temporary.
Readline currently doesn't export what we need.
The plan is to have something that has been working for awhile,
and then we'll have a complete story to present to the readline
maintainers.
gdb/ChangeLog:
* 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.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 29 | ||||
-rw-r--r-- | gdb/cli-out.c | 84 | ||||
-rw-r--r-- | gdb/cli-out.h | 2 | ||||
-rw-r--r-- | gdb/completer.c | 554 | ||||
-rw-r--r-- | gdb/completer.h | 46 | ||||
-rw-r--r-- | gdb/top.c | 1 | ||||
-rw-r--r-- | gdb/tui/tui-io.c | 248 |
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); @@ -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; } |