From dcd4589a0d68032076f6951190ab170fef9c3cf5 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lubo=C5=A1=20Lu=C5=88=C3=A1k?= Date: Wed, 5 Aug 2020 13:00:37 +0200 Subject: [PATCH] [lldb][gui] use left/right in the source view to scroll I intentionally decided not to reset the column automatically anywhere, because I don't know where and if at all that should happen. There should be always an indication of being scrolled (too much) to the right, so I'll leave this to whoever has an opinion. Differential Revision: https://reviews.llvm.org/D85290 --- lldb/source/Core/IOHandlerCursesGUI.cpp | 57 +++++++++++++++++++--- .../API/commands/gui/viewlarge/TestGuiViewLarge.py | 17 +++++++ 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) -- 2.7.4