Skip to content

Commit dcd4589

Browse files
committed
[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
1 parent 74f5778 commit dcd4589

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

lldb/source/Core/IOHandlerCursesGUI.cpp

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -480,23 +480,40 @@ class Window {
480480

481481
// Curses doesn't allow direct output of color escape sequences, but that's
482482
// how we get source lines from the Highligher class. Read the line and
483-
// convert color escape sequences to curses color attributes.
484-
void OutputColoredStringTruncated(int right_pad, StringRef string,
483+
// convert color escape sequences to curses color attributes. Use
484+
// first_skip_count to skip leading visible characters. Returns false if all
485+
// visible characters were skipped due to first_skip_count.
486+
bool OutputColoredStringTruncated(int right_pad, StringRef string,
487+
size_t skip_first_count,
485488
bool use_blue_background) {
486489
attr_t saved_attr;
487490
short saved_pair;
491+
bool result = false;
488492
wattr_get(m_window, &saved_attr, &saved_pair, nullptr);
489493
if (use_blue_background)
490494
::wattron(m_window, COLOR_PAIR(WhiteOnBlue));
491495
while (!string.empty()) {
492496
size_t esc_pos = string.find('\x1b');
493497
if (esc_pos == StringRef::npos) {
494-
PutCStringTruncated(right_pad, string.data(), string.size());
498+
string = string.substr(skip_first_count);
499+
if (!string.empty()) {
500+
PutCStringTruncated(right_pad, string.data(), string.size());
501+
result = true;
502+
}
495503
break;
496504
}
497505
if (esc_pos > 0) {
498-
PutCStringTruncated(right_pad, string.data(), esc_pos);
499-
string = string.drop_front(esc_pos);
506+
if (skip_first_count > 0) {
507+
int skip = std::min(esc_pos, skip_first_count);
508+
string = string.substr(skip);
509+
skip_first_count -= skip;
510+
esc_pos -= skip;
511+
}
512+
if (esc_pos > 0) {
513+
PutCStringTruncated(right_pad, string.data(), esc_pos);
514+
result = true;
515+
string = string.drop_front(esc_pos);
516+
}
500517
}
501518
bool consumed = string.consume_front("\x1b");
502519
assert(consumed);
@@ -531,6 +548,7 @@ class Window {
531548
}
532549
}
533550
wattr_set(m_window, saved_attr, saved_pair, nullptr);
551+
return result;
534552
}
535553

536554
void Touch() {
@@ -3379,7 +3397,8 @@ class SourceFileWindowDelegate : public WindowDelegate {
33793397
m_disassembly_scope(nullptr), m_disassembly_sp(), m_disassembly_range(),
33803398
m_title(), m_line_width(4), m_selected_line(0), m_pc_line(0),
33813399
m_stop_id(0), m_frame_idx(UINT32_MAX), m_first_visible_line(0),
3382-
m_min_x(0), m_min_y(0), m_max_x(0), m_max_y(0) {}
3400+
m_first_visible_column(0), m_min_x(0), m_min_y(0), m_max_x(0),
3401+
m_max_y(0) {}
33833402

33843403
~SourceFileWindowDelegate() override = default;
33853404

@@ -3396,6 +3415,8 @@ class SourceFileWindowDelegate : public WindowDelegate {
33963415
{KEY_RETURN, "Run to selected line with one shot breakpoint"},
33973416
{KEY_UP, "Select previous source line"},
33983417
{KEY_DOWN, "Select next source line"},
3418+
{KEY_LEFT, "Scroll to the left"},
3419+
{KEY_RIGHT, "Scroll to the right"},
33993420
{KEY_PPAGE, "Page up"},
34003421
{KEY_NPAGE, "Page down"},
34013422
{'b', "Set breakpoint on selected source/disassembly line"},
@@ -3650,7 +3671,15 @@ class SourceFileWindowDelegate : public WindowDelegate {
36503671
StringRef line = lineStream.GetString();
36513672
if (line.endswith("\n"))
36523673
line = line.drop_back();
3653-
window.OutputColoredStringTruncated(1, line, is_pc_line);
3674+
bool wasWritten = window.OutputColoredStringTruncated(
3675+
1, line, m_first_visible_column, line_is_selected);
3676+
if (line_is_selected && !wasWritten) {
3677+
// Draw an empty space to show the selected line if empty,
3678+
// or draw '<' if nothing is visible because of scrolling too much
3679+
// to the right.
3680+
window.PutCStringTruncated(
3681+
1, line.empty() && m_first_visible_column == 0 ? " " : "<");
3682+
}
36543683

36553684
if (is_pc_line && frame_sp &&
36563685
frame_sp->GetConcreteFrameIndex() == 0) {
@@ -3801,7 +3830,9 @@ class SourceFileWindowDelegate : public WindowDelegate {
38013830
strm.Printf("%s", mnemonic);
38023831

38033832
int right_pad = 1;
3804-
window.PutCStringTruncated(right_pad, strm.GetData());
3833+
window.PutCStringTruncated(
3834+
right_pad,
3835+
strm.GetString().substr(m_first_visible_column).data());
38053836

38063837
if (is_pc_line && frame_sp &&
38073838
frame_sp->GetConcreteFrameIndex() == 0) {
@@ -3896,6 +3927,15 @@ class SourceFileWindowDelegate : public WindowDelegate {
38963927
}
38973928
return eKeyHandled;
38983929

3930+
case KEY_LEFT:
3931+
if (m_first_visible_column > 0)
3932+
--m_first_visible_column;
3933+
return eKeyHandled;
3934+
3935+
case KEY_RIGHT:
3936+
++m_first_visible_column;
3937+
return eKeyHandled;
3938+
38993939
case '\r':
39003940
case '\n':
39013941
case KEY_ENTER:
@@ -4127,6 +4167,7 @@ class SourceFileWindowDelegate : public WindowDelegate {
41274167
uint32_t m_stop_id;
41284168
uint32_t m_frame_idx;
41294169
int m_first_visible_line;
4170+
int m_first_visible_column;
41304171
int m_min_x;
41314172
int m_min_y;
41324173
int m_max_x;

lldb/test/API/commands/gui/viewlarge/TestGuiViewLarge.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ def test_gui(self):
2525
self.expect("run", substrs=["stop reason ="])
2626

2727
escape_key = chr(27).encode()
28+
left_key = chr(27)+'OD' # for vt100 terminal (lldbexpect sets TERM=vt100)
29+
right_key = chr(27)+'OC'
30+
ctrl_l = chr(12)
2831

2932
# Start the GUI and close the welcome window.
3033
self.child.sendline("gui")
@@ -45,6 +48,20 @@ def test_gui(self):
4548
self.child.expect_exact("(int) a_variable_with_a_very_looooooooooooooooooooooooooooooo"+chr(27))
4649
self.child.expect_exact("(int) shortvar = 1"+chr(27))
4750

51+
# Scroll the sources view twice to the right.
52+
self.child.send(right_key)
53+
self.child.send(right_key)
54+
# Force a redraw, otherwise curses will optimize the drawing to not draw all 'o'.
55+
self.child.send(ctrl_l)
56+
# The source code is indented by two spaces, so there'll be just two extra 'o' on the right.
57+
self.child.expect_exact("int a_variable_with_a_very_looooooooooooooooooooooooooooo"+chr(27))
58+
59+
# And scroll back to the left.
60+
self.child.send(left_key)
61+
self.child.send(left_key)
62+
self.child.send(ctrl_l)
63+
self.child.expect_exact("int a_variable_with_a_very_looooooooooooooooooooooooooo"+chr(27))
64+
4865
# Press escape to quit the gui
4966
self.child.send(escape_key)
5067

0 commit comments

Comments
 (0)