Use a curses pad for source and disassembly windows
authorTom Tromey <tom@tromey.com>
Mon, 28 Sep 2020 02:30:30 +0000 (20:30 -0600)
committerTom Tromey <tom@tromey.com>
Mon, 28 Sep 2020 02:30:32 +0000 (20:30 -0600)
This changes the TUI source and disassembly windows to use a curses
pad for their text.  This is an important step toward properly
handling non-ASCII characters, because it makes it easy to scroll
horizontally without needing gdb to also understand multi-byte
character boundaries -- this can be wholly delegated to curses.
Horizontal scrolling is probably also faster now, because no
re-rendering is required.

gdb/ChangeLog
2020-09-27  Tom Tromey  <tom@tromey.com>

* unittests/tui-selftests.c: Update.
* tui/tui-winsource.h (struct tui_source_window_base)
<extra_margin, show_line_number, refresh_pad>: New methods.
<m_max_length, m_pad>: New members.
(tui_copy_source_line): Update.
* tui/tui-winsource.c (tui_copy_source_line): Remove line_no,
first_col, line_width, ndigits parameters.  Add length.
(tui_source_window_base::show_source_line): Write to pad.  Line
number now 0-based.
(tui_source_window_base::refresh_pad): New method.
(tui_source_window_base::show_source_content): Write to pad.  Call
refresh_pad.
(tui_source_window_base::do_scroll_horizontal): Call refresh_pad,
not refill.
(tui_source_window_base::update_exec_info): Call
show_line_number.
* tui/tui-source.h (struct tui_source_window) <extra_margin>: New
method.
<m_digits>: New member.
* tui/tui-source.c (tui_source_window::set_contents): Set m_digits
and m_max_length.
(tui_source_window::show_line_number): New method.
* tui/tui-io.h (tui_puts): Fix comment.
* tui/tui-disasm.c (tui_disasm_window::set_contents): Set
m_max_length.

gdb/ChangeLog
gdb/tui/tui-disasm.c
gdb/tui/tui-io.h
gdb/tui/tui-source.c
gdb/tui/tui-source.h
gdb/tui/tui-winsource.c
gdb/tui/tui-winsource.h
gdb/unittests/tui-selftests.c

index c835fdb14ad952be1bff1cf82e64c8b8da10e2ee..38b9fa6b353e5be3776cfdf122b5545eb0f496d0 100644 (file)
@@ -1,3 +1,31 @@
+2020-09-27  Tom Tromey  <tom@tromey.com>
+
+       * unittests/tui-selftests.c: Update.
+       * tui/tui-winsource.h (struct tui_source_window_base)
+       <extra_margin, show_line_number, refresh_pad>: New methods.
+       <m_max_length, m_pad>: New members.
+       (tui_copy_source_line): Update.
+       * tui/tui-winsource.c (tui_copy_source_line): Remove line_no,
+       first_col, line_width, ndigits parameters.  Add length.
+       (tui_source_window_base::show_source_line): Write to pad.  Line
+       number now 0-based.
+       (tui_source_window_base::refresh_pad): New method.
+       (tui_source_window_base::show_source_content): Write to pad.  Call
+       refresh_pad.
+       (tui_source_window_base::do_scroll_horizontal): Call refresh_pad,
+       not refill.
+       (tui_source_window_base::update_exec_info): Call
+       show_line_number.
+       * tui/tui-source.h (struct tui_source_window) <extra_margin>: New
+       method.
+       <m_digits>: New member.
+       * tui/tui-source.c (tui_source_window::set_contents): Set m_digits
+       and m_max_length.
+       (tui_source_window::show_line_number): New method.
+       * tui/tui-io.h (tui_puts): Fix comment.
+       * tui/tui-disasm.c (tui_disasm_window::set_contents): Set
+       m_max_length.
+
 2020-09-27  Tom Tromey  <tom@tromey.com>
 
        * tui/tui-winsource.c
index d684b02fd12efa7c331ea0d1c987b462c0c261c3..0b7980ee80b3a5d7113648e21c5db30b9fa2b4d2 100644 (file)
@@ -318,8 +318,7 @@ tui_disasm_window::set_contents (struct gdbarch *arch,
                                 const struct symtab_and_line &sal)
 {
   int i;
-  int offset = m_horizontal_offset;
-  int max_lines, line_width;
+  int max_lines;
   CORE_ADDR cur_pc;
   struct tui_locator_window *locator = tui_locator_win_info_ptr ();
   int tab_len = tui_tab_width;
@@ -336,7 +335,6 @@ tui_disasm_window::set_contents (struct gdbarch *arch,
 
   /* Window size, excluding highlight box.  */
   max_lines = height - 2;
-  line_width = width - TUI_EXECINFO_SIZE - 2;
 
   /* Get temporary table that will hold all strings (addr & insn).  */
   std::vector<tui_asm_line> asm_lines;
@@ -348,6 +346,7 @@ tui_disasm_window::set_contents (struct gdbarch *arch,
 
   /* Now construct each line.  */
   m_content.resize (max_lines);
+  m_max_length = -1;
   for (i = 0; i < max_lines; i++)
     {
       tui_source_element *src = &m_content[i];
@@ -370,7 +369,9 @@ tui_disasm_window::set_contents (struct gdbarch *arch,
        }
 
       const char *ptr = line.c_str ();
-      src->line = tui_copy_source_line (&ptr, -1, offset, line_width, 0);
+      int line_len;
+      src->line = tui_copy_source_line (&ptr, &line_len);
+      m_max_length = std::max (m_max_length, line_len);
 
       src->line_or_addr.loa = LOA_ADDRESS;
       src->line_or_addr.u.addr = addr;
index 2cc47ba4beb7f44e81229921584d5b5c414eb97c..5e21bb7811dda8e08139a59c22d827607dc9e278 100644 (file)
@@ -27,7 +27,8 @@
 struct ui_out;
 class cli_ui_out;
 
-/* Print the string in the curses command window.  */
+/* Print the string in the given curses window.  If no window is
+   provided, the command window is used.  */
 extern void tui_puts (const char *, WINDOW * = nullptr);
 
 /* Print LENGTH characters from the buffer pointed to by BUF to the
index fd5bd7dd9608617889f55218948f7896913e248c..cc6680406d9bee6656c3b1b78feeadb138af97df 100644 (file)
@@ -50,12 +50,9 @@ tui_source_window::set_contents (struct gdbarch *arch,
   if (s == NULL)
     return false;
 
-  int line_width, nlines;
-
-  line_width = width - TUI_EXECINFO_SIZE - 1;
   /* Take hilite (window border) into account, when
      calculating the number of lines.  */
-  nlines = height - 2;
+  int nlines = height - 2;
 
   std::string srclines;
   const std::vector<off_t> *offsets;
@@ -78,15 +75,16 @@ tui_source_window::set_contents (struct gdbarch *arch,
   m_start_line_or_addr.loa = LOA_LINE;
   cur_line_no = m_start_line_or_addr.u.line_no = line_no;
 
-  int digits = 0;
+  m_digits = 7;
   if (compact_source)
     {
       /* Solaris 11+gcc 5.5 has ambiguous overloads of log10, so we
         cast to double to get the right one.  */
       double l = log10 ((double) offsets->size ());
-      digits = 1 + (int) l;
+      m_digits = 1 + (int) l;
     }
 
+  m_max_length = -1;
   const char *iter = srclines.c_str ();
   m_content.resize (nlines);
   while (cur_line < nlines)
@@ -95,9 +93,11 @@ tui_source_window::set_contents (struct gdbarch *arch,
 
       std::string text;
       if (*iter != '\0')
-       text = tui_copy_source_line (&iter, cur_line_no,
-                                    m_horizontal_offset,
-                                    line_width, digits);
+       {
+         int line_len;
+         text = tui_copy_source_line (&iter, &line_len);
+         m_max_length = std::max (m_max_length, line_len);
+       }
 
       /* Set whether element is the execution point
         and whether there is a break point on it.  */
@@ -225,3 +225,14 @@ tui_source_window::display_start_addr (struct gdbarch **gdbarch_p,
   *gdbarch_p = m_gdbarch;
   find_line_pc (cursal.symtab, m_start_line_or_addr.u.line_no, addr_p);
 }
+
+/* See tui-winsource.h.  */
+
+void
+tui_source_window::show_line_number (int offset) const
+{
+  int lineno = m_content[0].line_or_addr.u.line_no + offset;
+  char text[20];
+  xsnprintf (text, sizeof (text), "%*d ", m_digits - 1, lineno);
+  waddstr (handle.get (), text);
+}
index 1df84cf304c2d258b826b03442e56f2e8c37483d..020f710c2374056433fe27ddc7ea3ed021d1cf1f 100644 (file)
@@ -63,12 +63,23 @@ protected:
   bool set_contents (struct gdbarch *gdbarch,
                     const struct symtab_and_line &sal) override;
 
+  int extra_margin () const override
+  {
+    return m_digits;
+  }
+
+  void show_line_number (int lineno) const override;
+
 private:
 
   /* Answer whether a particular line number or address is displayed
      in the current source window.  */
   bool line_is_displayed (int line) const;
 
+  /* How many digits to use when formatting the line number.  This
+     includes the trailing space.  */
+  int m_digits;
+
   /* It is the resolved form as returned by symtab_to_fullname.  */
   gdb::unique_xmalloc_ptr<char> m_fullname;
 };
index 6723bad82ccc6745c91c45da0cfe805a77207c08..2300b9ab1e923a0edbe0816b1dc75c2687e85898 100644 (file)
@@ -65,27 +65,13 @@ tui_display_main ()
 /* See tui-winsource.h.  */
 
 std::string
-tui_copy_source_line (const char **ptr, int line_no, int first_col,
-                     int line_width, int ndigits)
+tui_copy_source_line (const char **ptr, int *length)
 {
   const char *lineptr = *ptr;
 
   /* Init the line with the line number.  */
   std::string result;
 
-  if (line_no > 0)
-    {
-      if (ndigits > 0)
-       result = string_printf ("%*d ", ndigits, line_no);
-      else
-       {
-         result = string_printf ("%-6d", line_no);
-         int len = result.size ();
-         len = len - ((len / tui_tab_width) * tui_tab_width);
-         result.append (len, ' ');
-       }
-    }
-
   int column = 0;
   char c;
   do
@@ -112,21 +98,11 @@ tui_copy_source_line (const char **ptr, int line_no, int first_col,
 
          --column;
          for (int j = column % max_tab_len;
-              j < max_tab_len && column < first_col + line_width;
+              j < max_tab_len;
               column++, j++)
-           if (column >= first_col)
-             result.push_back (' ');
+           result.push_back (' ');
        };
 
-      /* We have to process all the text in order to pick up all the
-        escapes.  */
-      if (column <= first_col || column > first_col + line_width)
-       {
-         if (c == '\t')
-           process_tab ();
-         continue;
-       }
-
       if (c == '\n' || c == '\r' || c == '\0')
        {
          /* Nothing.  */
@@ -135,11 +111,13 @@ tui_copy_source_line (const char **ptr, int line_no, int first_col,
        {
          result.push_back ('^');
          result.push_back (c + 0100);
+         ++column;
        }
       else if (c == 0177)
        {
          result.push_back ('^');
          result.push_back ('?');
+         ++column;
        }
       else if (c == '\t')
        process_tab ();
@@ -152,6 +130,9 @@ tui_copy_source_line (const char **ptr, int line_no, int first_col,
     ++lineptr;
   *ptr = lineptr;
 
+  if (length != nullptr)
+    *length = column;
+
   return result;
 }
 
@@ -254,24 +235,31 @@ void
 tui_source_window_base::show_source_line (int lineno)
 {
   struct tui_source_element *line;
-  int x;
 
-  line = &m_content[lineno - 1];
+  line = &m_content[lineno];
   if (line->is_exec_point)
-    tui_set_reverse_mode (handle.get (), true);
+    tui_set_reverse_mode (m_pad.get (), true);
 
-  wmove (handle.get (), lineno, TUI_EXECINFO_SIZE);
-  tui_puts (line->line.c_str (), handle.get ());
+  wmove (m_pad.get (), lineno, 0);
+  tui_puts (line->line.c_str (), m_pad.get ());
   if (line->is_exec_point)
-    tui_set_reverse_mode (handle.get (), false);
+    tui_set_reverse_mode (m_pad.get (), false);
+}
 
-  /* Clear to end of line but stop before the border.  */
-  x = getcurx (handle.get ());
-  while (x + 1 < width)
-    {
-      waddch (handle.get (), ' ');
-      x = getcurx (handle.get ());
-    }
+/* See tui-winsource.h.  */
+
+void
+tui_source_window_base::refresh_pad ()
+{
+  int pad_width = std::max (m_max_length, width);
+  int left_margin = 1 + TUI_EXECINFO_SIZE + extra_margin ();
+  int view_width = width - left_margin - 1;
+  int pad_x = std::min (pad_width - view_width, m_horizontal_offset);
+  /* Ensure that an equal number of scrolls will work if the user
+     scrolled beyond where we clip.  */
+  m_horizontal_offset = pad_x;
+  prefresh (m_pad.get (), 0, pad_x, y + 1, x + left_margin,
+           y + 1 + m_content.size (), x + left_margin + view_width - 1);
 }
 
 void
@@ -279,10 +267,18 @@ tui_source_window_base::show_source_content ()
 {
   gdb_assert (!m_content.empty ());
 
-  for (int lineno = 1; lineno <= m_content.size (); lineno++)
+  check_and_display_highlight_if_needed ();
+
+  int pad_width = std::max (m_max_length, width);
+  if (m_pad == nullptr || pad_width > getmaxx (m_pad.get ()))
+    m_pad.reset (newpad (m_content.size (), pad_width));
+
+  werase (m_pad.get ());
+  for (int lineno = 0; lineno < m_content.size (); lineno++)
     show_source_line (lineno);
 
-  check_and_display_highlight_if_needed ();
+  refresh_pad ();
+
   refresh_window ();
 }
 
@@ -380,7 +376,7 @@ tui_source_window_base::do_scroll_horizontal (int num_to_scroll)
       if (offset < 0)
        offset = 0;
       m_horizontal_offset = offset;
-      refill ();
+      refresh_pad ();
     }
 }
 
@@ -519,6 +515,8 @@ tui_source_window_base::update_exec_info ()
        element[TUI_EXEC_POS] = '>';
 
       mvwaddstr (handle.get (), i + 1, 1, element);
+
+      show_line_number (i);
     }
   refresh_window ();
 }
index ba9c0fd505e09a82b5953313e730c82d29bde8d0..5fc6a6d812662ece0f09491d2dae560394df6997 100644 (file)
@@ -107,6 +107,20 @@ protected:
   virtual bool set_contents (struct gdbarch *gdbarch,
                             const struct symtab_and_line &sal) = 0;
 
+  /* Return the number of extra margin characters needed by this
+     instance.  */
+  virtual int extra_margin () const
+  {
+    return 0;
+  }
+
+  /* Display the line number in the window margin.  OFFSET indicates
+     which line to display; it is 0-based, with 0 meaning the line at
+     the top of the window.  */
+  virtual void show_line_number (int offset) const
+  {
+  }
+
   /* Redraw the complete line of a source or disassembly window.  */
   void show_source_line (int lineno);
 
@@ -119,6 +133,9 @@ protected:
 
   std::vector<tui_source_element> m_content;
 
+  /* Length of longest line to be displayed.  */
+  int m_max_length;
+
 public:
 
   /* Refill the source window's source cache and update it.  If this
@@ -162,11 +179,17 @@ private:
 
   void show_source_content ();
 
+  /* Re-display the pad in the window.  */
+  void refresh_pad ();
+
   /* Called when the user "set style enabled" setting is changed.  */
   void style_changed ();
 
   /* A token used to register and unregister an observer.  */
   gdb::observers::token m_observable;
+
+  /* Pad used to display fixme mumble  */
+  std::unique_ptr<WINDOW, curses_deleter> m_pad;
 };
 
 
@@ -264,19 +287,15 @@ extern void tui_display_main (void);
 extern void tui_update_source_windows_with_addr (struct gdbarch *, CORE_ADDR);
 extern void tui_update_source_windows_with_line (struct symtab_and_line sal);
 
-/* Extract some source text from PTR.  LINE_NO is the line number.  If
-   it is positive, it is printed at the start of the line.  FIRST_COL
-   is the first column to extract, and LINE_WIDTH is the number of
-   characters to display.  NDIGITS is used to format the line number
-   (if it is positive).  If NDIGITS is greater than 0, then that many
-   digits are used; otherwise the line number is formatted with 6
-   digits and the text is aligned to the next tab stop.  Returns a
-   string holding the desired text.  PTR is updated to point to the
-   start of the next line.  */
+/* Extract some source text from PTR.  Returns a string holding the
+   desired text.  PTR is updated to point to the start of the next
+   line.  If LENGTH is non-NULL, then the length of the line is stored
+   there.  Escape sequences are not counted against the length.
+   Actually an approximation is used -- each byte of a multi-byte
+   sequence counts as a character here.  */
 
 extern std::string tui_copy_source_line (const char **ptr,
-                                        int line_no, int first_col,
-                                        int line_width, int ndigits);
+                                        int *length = nullptr);
 
 /* Constant definitions. */
 #define SCROLL_THRESHOLD 2     /* Threshold for lazy scroll.  */
index 6144e23f3949c1ae3342a1f91021bdb47f24761d..7ab2a634b6129229350a071258afc70825d44706 100644 (file)
@@ -31,13 +31,13 @@ static void
 run_tests ()
 {
   const char *text = "hello";
-  std::string result = tui_copy_source_line (&text, 0, 0, 50, 0);
+  std::string result = tui_copy_source_line (&text);
   SELF_CHECK (result == "hello");
   SELF_CHECK (*text == '\0');
 
   text = "hello\n";
-  result = tui_copy_source_line (&text, 0, 0, 3, 0);
-  SELF_CHECK (result == "hel");
+  result = tui_copy_source_line (&text);
+  SELF_CHECK (result == "hello");
   SELF_CHECK (*text == '\0');
 }