aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lldb/source/Core/IOHandlerCursesGUI.cpp57
-rw-r--r--lldb/test/API/commands/gui/viewlarge/TestGuiViewLarge.py17
2 files changed, 66 insertions, 8 deletions
diff --git a/lldb/source/Core/IOHandlerCursesGUI.cpp b/lldb/source/Core/IOHandlerCursesGUI.cpp
index 6a24625f..10aff7a 100644
--- a/lldb/source/Core/IOHandlerCursesGUI.cpp
+++ b/lldb/source/Core/IOHandlerCursesGUI.cpp
@@ -480,23 +480,40 @@ public:
// Curses doesn't allow direct output of color escape sequences, but that's
// how we get source lines from the Highligher class. Read the line and
- // convert color escape sequences to curses color attributes.
- void OutputColoredStringTruncated(int right_pad, StringRef string,
+ // convert color escape sequences to curses color attributes. Use
+ // first_skip_count to skip leading visible characters. Returns false if all
+ // visible characters were skipped due to first_skip_count.
+ bool OutputColoredStringTruncated(int right_pad, StringRef string,
+ size_t skip_first_count,
bool use_blue_background) {
attr_t saved_attr;
short saved_pair;
+ bool result = false;
wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
if (use_blue_background)
::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
while (!string.empty()) {
size_t esc_pos = string.find('\x1b');
if (esc_pos == StringRef::npos) {
- PutCStringTruncated(right_pad, string.data(), string.size());
+ string = string.substr(skip_first_count);
+ if (!string.empty()) {
+ PutCStringTruncated(right_pad, string.data(), string.size());
+ result = true;
+ }
break;
}
if (esc_pos > 0) {
- PutCStringTruncated(right_pad, string.data(), esc_pos);
- string = string.drop_front(esc_pos);
+ if (skip_first_count > 0) {
+ int skip = std::min(esc_pos, skip_first_count);
+ string = string.substr(skip);
+ skip_first_count -= skip;
+ esc_pos -= skip;
+ }
+ if (esc_pos > 0) {
+ PutCStringTruncated(right_pad, string.data(), esc_pos);
+ result = true;
+ string = string.drop_front(esc_pos);
+ }
}
bool consumed = string.consume_front("\x1b");
assert(consumed);
@@ -531,6 +548,7 @@ public:
}
}
wattr_set(m_window, saved_attr, saved_pair, nullptr);
+ return result;
}
void Touch() {
@@ -3379,7 +3397,8 @@ public:
m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
- m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
+ m_first_visible_column(0), m_min_x(0), m_min_y(0), m_max_x(0),
+ m_max_y(0) {}
~SourceFileWindowDelegate() override = default;
@@ -3396,6 +3415,8 @@ public:
{KEY_RETURN, "Run to selected line with one shot breakpoint"},
{KEY_UP, "Select previous source line"},
{KEY_DOWN, "Select next source line"},
+ {KEY_LEFT, "Scroll to the left"},
+ {KEY_RIGHT, "Scroll to the right"},
{KEY_PPAGE, "Page up"},
{KEY_NPAGE, "Page down"},
{'b', "Set breakpoint on selected source/disassembly line"},
@@ -3650,7 +3671,15 @@ public:
StringRef line = lineStream.GetString();
if (line.endswith("\n"))
line = line.drop_back();
- window.OutputColoredStringTruncated(1, line, is_pc_line);
+ bool wasWritten = window.OutputColoredStringTruncated(
+ 1, line, m_first_visible_column, line_is_selected);
+ if (line_is_selected && !wasWritten) {
+ // Draw an empty space to show the selected line if empty,
+ // or draw '<' if nothing is visible because of scrolling too much
+ // to the right.
+ window.PutCStringTruncated(
+ 1, line.empty() && m_first_visible_column == 0 ? " " : "<");
+ }
if (is_pc_line && frame_sp &&
frame_sp->GetConcreteFrameIndex() == 0) {
@@ -3801,7 +3830,9 @@ public:
strm.Printf("%s", mnemonic);
int right_pad = 1;
- window.PutCStringTruncated(right_pad, strm.GetData());
+ window.PutCStringTruncated(
+ right_pad,
+ strm.GetString().substr(m_first_visible_column).data());
if (is_pc_line && frame_sp &&
frame_sp->GetConcreteFrameIndex() == 0) {
@@ -3896,6 +3927,15 @@ public:
}
return eKeyHandled;
+ case KEY_LEFT:
+ if (m_first_visible_column > 0)
+ --m_first_visible_column;
+ return eKeyHandled;
+
+ case KEY_RIGHT:
+ ++m_first_visible_column;
+ return eKeyHandled;
+
case '\r':
case '\n':
case KEY_ENTER:
@@ -4127,6 +4167,7 @@ protected:
uint32_t m_stop_id;
uint32_t m_frame_idx;
int m_first_visible_line;
+ int m_first_visible_column;
int m_min_x;
int m_min_y;
int m_max_x;
diff --git a/lldb/test/API/commands/gui/viewlarge/TestGuiViewLarge.py b/lldb/test/API/commands/gui/viewlarge/TestGuiViewLarge.py
index ebb99f6..19f8334 100644
--- a/lldb/test/API/commands/gui/viewlarge/TestGuiViewLarge.py
+++ b/lldb/test/API/commands/gui/viewlarge/TestGuiViewLarge.py
@@ -25,6 +25,9 @@ class GuiViewLargeCommandTest(PExpectTest):
self.expect("run", substrs=["stop reason ="])
escape_key = chr(27).encode()
+ left_key = chr(27)+'OD' # for vt100 terminal (lldbexpect sets TERM=vt100)
+ right_key = chr(27)+'OC'
+ ctrl_l = chr(12)
# Start the GUI and close the welcome window.
self.child.sendline("gui")
@@ -45,6 +48,20 @@ class GuiViewLargeCommandTest(PExpectTest):
self.child.expect_exact("(int) a_variable_with_a_very_looooooooooooooooooooooooooooooo"+chr(27))
self.child.expect_exact("(int) shortvar = 1"+chr(27))
+ # Scroll the sources view twice to the right.
+ self.child.send(right_key)
+ self.child.send(right_key)
+ # Force a redraw, otherwise curses will optimize the drawing to not draw all 'o'.
+ self.child.send(ctrl_l)
+ # The source code is indented by two spaces, so there'll be just two extra 'o' on the right.
+ self.child.expect_exact("int a_variable_with_a_very_looooooooooooooooooooooooooooo"+chr(27))
+
+ # And scroll back to the left.
+ self.child.send(left_key)
+ self.child.send(left_key)
+ self.child.send(ctrl_l)
+ self.child.expect_exact("int a_variable_with_a_very_looooooooooooooooooooooooooo"+chr(27))
+
# Press escape to quit the gui
self.child.send(escape_key)