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.
This commit is contained in:
Doug Evans 2015-01-31 14:11:54 -08:00
parent f57d2163da
commit 82083d6dbb
7 changed files with 809 additions and 167 deletions

View File

@ -1,3 +1,32 @@
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.

View File

@ -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 ();
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);

View File

@ -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");

View File

@ -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. */
/* TUI version of displayer.putch. */
#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)
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 ();
tui_puts (s);
}
/* TUI version of displayer.flush. */
static void
tui_mld_flush (const struct match_list_displayer *displayer)
{
wrefresh (TUI_CMD_WIN->generic.handle);
}
/* TUI version of displayer.erase_entire_line. */
static void
tui_mld_erase_entire_line (const struct match_list_displayer *displayer)
{
WINDOW *w = TUI_CMD_WIN->generic.handle;
wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, 0);
wclrtoeol (w);
wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, 0);
}
/* TUI version of displayer.beep. */
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);
c = tui_handle_resize_during_io (c, 1);
return c;
}
/* TUI version of displayer.read_key. */
static int
tui_mld_read_key (const struct match_list_displayer *displayer)
{
rl_getc_func_t *prev = rl_getc_function;
int c;
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 ();
}
/* 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;
}
/* 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 rl_completion_display_matches_hook.
See gdb_display_match_list for a description of the arguments. */
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)
{
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;
struct match_list_displayer displayer;
/* Screen dimension correspond to the TUI command window. */
int screenwidth = TUI_CMD_WIN->generic.width;
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;
/* 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];
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;
}
}
/* 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--;
/* 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;
/* 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)
qsort (matches + 1, len, sizeof (char *),
(QSFUNC *)_rl_qsort_string_compare);
tui_putc ('\n');
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');
}
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;
}