Use a curses pad for source and disassembly windows

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.
This commit is contained in:
Tom Tromey 2020-09-27 20:30:30 -06:00
parent c15c15c8d9
commit 9e820dec13
8 changed files with 140 additions and 71 deletions

View 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

View 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;

View 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

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

View 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;
};

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

View 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. */

View 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');
}