Unify CLI/TUI interface to readline tab completion.
authorDoug Evans <xdje42@gmail.com>
Sat, 31 Jan 2015 22:11:54 +0000 (14:11 -0800)
committerDoug Evans <xdje42@gmail.com>
Sat, 31 Jan 2015 22:11:54 +0000 (14:11 -0800)
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.

gdb/ChangeLog
gdb/cli-out.c
gdb/cli-out.h
gdb/completer.c
gdb/completer.h
gdb/top.c
gdb/tui/tui-io.c

index b82d4d08663bd10346fe78b29eb889ea15c9c803..33e0dffddc8594b252a80de45ef5a2411033191e 100644 (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.
index 76222c6ded9b515df0a6b8312214859d6f072512..48f2a04f72e8cc4ccba94aac5559082df48995c6 100644 (file)
 #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;
 }
+\f
+/* 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 ();
+}
index bf0706977dfcbf35fca65b8d17af58f644af1ea1..401429ab56047af039c88ac020c314f223f96e6f 100644 (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
index 2b6aa87e5b263ba38e326b857a13e382d2191698..88c8e16a9ae2e788a013f73fbfa95d83d5909208 100644 (file)
@@ -1020,3 +1020,557 @@ skip_quoted (const char *str)
 {
   return skip_quoted_chars (str, NULL, NULL);
 }
+\f
+/* 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);
+}
index 8f925fe86233b2e25528f77c9760772a685a1306..dbb1cfb441cd5f55ba2443c1a3409a2ac2994ef7 100644 (file)
 #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);
index a1462a0b22d201c974143a527c599bf6b8ea6373..000b14ed4ee2e01e7760773a69ae63b52aac25fe 100644 (file)
--- 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");
 
index 19e9485ea3884e108e45ee57f9a3712dd91d16c5..831705cfa7f0f46035e52cf6c3c43e43363e2f59 100644 (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.  */
-
-#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;
 }