Skip to content

Commit f6fdfaf

Browse files
committed
[lldb] Use the terminal height for paging editline completions
Currently, we arbitrarily paginate editline completions to 40 elements. On large terminals, that leaves some real-estate unused. On small terminals, it's pretty annoying to not see the first completions. We can address both issues by using the terminal height for pagination.
1 parent 88bcf72 commit f6fdfaf

File tree

10 files changed

+124
-19
lines changed

10 files changed

+124
-19
lines changed

lldb/include/lldb/API/SBDebugger.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,10 @@ class LLDB_API SBDebugger {
382382

383383
void SetTerminalWidth(uint32_t term_width);
384384

385+
uint32_t GetTerminalHeight() const;
386+
387+
void SetTerminalHeight(uint32_t term_height);
388+
385389
lldb::user_id_t GetID();
386390

387391
const char *GetPrompt() const;

lldb/include/lldb/Core/Debugger.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
280280

281281
bool SetTerminalWidth(uint64_t term_width);
282282

283+
uint64_t GetTerminalHeight() const;
284+
285+
bool SetTerminalHeight(uint64_t term_height);
286+
283287
llvm::StringRef GetPrompt() const;
284288

285289
llvm::StringRef GetPromptAnsiPrefix() const;

lldb/include/lldb/Host/Editline.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,8 @@ class Editline {
240240

241241
size_t GetTerminalWidth() { return m_terminal_width; }
242242

243+
size_t GetTerminalHeight() { return m_terminal_height; }
244+
243245
private:
244246
/// Sets the lowest line number for multi-line editing sessions. A value of
245247
/// zero suppresses line number printing in the prompt.
@@ -373,6 +375,7 @@ class Editline {
373375
std::vector<EditLineStringType> m_input_lines;
374376
EditorStatus m_editor_status;
375377
int m_terminal_width = 0;
378+
int m_terminal_height = 0;
376379
int m_base_line_number = 0;
377380
unsigned m_current_line_index = 0;
378381
int m_current_line_rows = -1;

lldb/source/API/SBDebugger.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1405,6 +1405,19 @@ void SBDebugger::SetTerminalWidth(uint32_t term_width) {
14051405
m_opaque_sp->SetTerminalWidth(term_width);
14061406
}
14071407

1408+
uint32_t SBDebugger::GetTerminalHeight() const {
1409+
LLDB_INSTRUMENT_VA(this);
1410+
1411+
return (m_opaque_sp ? m_opaque_sp->GetTerminalWidth() : 0);
1412+
}
1413+
1414+
void SBDebugger::SetTerminalHeight(uint32_t term_height) {
1415+
LLDB_INSTRUMENT_VA(this, term_height);
1416+
1417+
if (m_opaque_sp)
1418+
m_opaque_sp->SetTerminalHeight(term_height);
1419+
}
1420+
14081421
const char *SBDebugger::GetPrompt() const {
14091422
LLDB_INSTRUMENT_VA(this);
14101423

lldb/source/Core/CoreProperties.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ let Definition = "debugger" in {
136136
Global,
137137
DefaultUnsignedValue<80>,
138138
Desc<"The maximum number of columns to use for displaying text.">;
139+
def TerminalHeight: Property<"term-height", "UInt64">,
140+
Global,
141+
DefaultUnsignedValue<24>,
142+
Desc<"The number of rows used for displaying text.">;
139143
def ThreadFormat: Property<"thread-format", "FormatEntity">,
140144
Global,
141145
DefaultStringValue<"thread #${thread.index}: tid = ${thread.id%tid}{, ${frame.pc}}{ ${module.file.basename}{`${function.name-with-args}{${frame.no-debug}${function.pc-offset}}}}{ at ${ansi.fg.cyan}${line.file.basename}${ansi.normal}:${ansi.fg.yellow}${line.number}${ansi.normal}{:${ansi.fg.yellow}${line.column}${ansi.normal}}}{, name = ${ansi.fg.green}'${thread.name}'${ansi.normal}}{, queue = ${ansi.fg.green}'${thread.queue}'${ansi.normal}}{, activity = ${ansi.fg.green}'${thread.info.activity.name}'${ansi.normal}}{, ${thread.info.trace_messages} messages}{, stop reason = ${ansi.fg.red}${thread.stop-reason}${ansi.normal}}{\\\\nReturn value: ${thread.return-value}}{\\\\nCompleted expression: ${thread.completed-expression}}\\\\n">,

lldb/source/Core/Debugger.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,29 @@ uint64_t Debugger::GetTerminalWidth() const {
371371
}
372372

373373
bool Debugger::SetTerminalWidth(uint64_t term_width) {
374+
const uint32_t idx = ePropertyTerminalWidth;
375+
const bool success = SetPropertyAtIndex(idx, term_width);
376+
374377
if (auto handler_sp = m_io_handler_stack.Top())
375378
handler_sp->TerminalSizeChanged();
376379

377-
const uint32_t idx = ePropertyTerminalWidth;
378-
return SetPropertyAtIndex(idx, term_width);
380+
return success;
381+
}
382+
383+
uint64_t Debugger::GetTerminalHeight() const {
384+
const uint32_t idx = ePropertyTerminalHeight;
385+
return GetPropertyAtIndexAs<uint64_t>(
386+
idx, g_debugger_properties[idx].default_uint_value);
387+
}
388+
389+
bool Debugger::SetTerminalHeight(uint64_t term_height) {
390+
const uint32_t idx = ePropertyTerminalHeight;
391+
const bool success = SetPropertyAtIndex(idx, term_height);
392+
393+
if (auto handler_sp = m_io_handler_stack.Top())
394+
handler_sp->TerminalSizeChanged();
395+
396+
return success;
379397
}
380398

381399
bool Debugger::GetUseExternalEditor() const {
@@ -919,7 +937,11 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton)
919937
m_collection_sp->GetPropertyAtIndexAsOptionValueUInt64(
920938
ePropertyTerminalWidth);
921939
term_width->SetMinimumValue(10);
922-
term_width->SetMaximumValue(1024);
940+
941+
OptionValueUInt64 *term_height =
942+
m_collection_sp->GetPropertyAtIndexAsOptionValueUInt64(
943+
ePropertyTerminalHeight);
944+
term_height->SetMinimumValue(10);
923945

924946
// Turn off use-color if this is a dumb terminal.
925947
const char *term = getenv("TERM");

lldb/source/Host/common/Editline.cpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -924,18 +924,26 @@ unsigned char Editline::BufferEndCommand(int ch) {
924924

925925
/// Prints completions and their descriptions to the given file. Only the
926926
/// completions in the interval [start, end) are printed.
927-
static void
927+
static size_t
928928
PrintCompletion(FILE *output_file,
929929
llvm::ArrayRef<CompletionResult::Completion> results,
930-
size_t max_completion_length, size_t max_length) {
930+
size_t max_completion_length, size_t max_length,
931+
std::optional<size_t> max_height = std::nullopt) {
931932
constexpr size_t ellipsis_length = 3;
932933
constexpr size_t padding_length = 8;
933934
constexpr size_t separator_length = 4;
934935

935936
const size_t description_col =
936937
std::min(max_completion_length + padding_length, max_length);
937938

939+
size_t lines_printed = 0;
940+
size_t results_printed = 0;
938941
for (const CompletionResult::Completion &c : results) {
942+
if (max_height && lines_printed == *max_height)
943+
break;
944+
945+
results_printed++;
946+
939947
if (c.GetCompletion().empty())
940948
continue;
941949

@@ -956,6 +964,7 @@ PrintCompletion(FILE *output_file,
956964
fprintf(output_file, "%.*s...\n",
957965
static_cast<int>(max_length - padding_length - ellipsis_length),
958966
c.GetCompletion().c_str());
967+
lines_printed++;
959968
continue;
960969
}
961970

@@ -964,6 +973,7 @@ PrintCompletion(FILE *output_file,
964973
if (c.GetDescription().empty() ||
965974
description_col + separator_length + ellipsis_length >= max_length) {
966975
fprintf(output_file, "\n");
976+
lines_printed++;
967977
continue;
968978
}
969979

@@ -1000,14 +1010,17 @@ PrintCompletion(FILE *output_file,
10001010
if (position + description_length < max_length) {
10011011
fprintf(output_file, "%.*s\n", static_cast<int>(description_length),
10021012
line.data());
1013+
lines_printed++;
10031014
} else {
10041015
fprintf(output_file, "%.*s...\n",
10051016
static_cast<int>(max_length - position - ellipsis_length),
10061017
line.data());
1018+
lines_printed++;
10071019
continue;
10081020
}
10091021
}
10101022
}
1023+
return results_printed;
10111024
}
10121025

10131026
void Editline::DisplayCompletions(
@@ -1016,7 +1029,11 @@ void Editline::DisplayCompletions(
10161029

10171030
fprintf(editline.m_output_file,
10181031
"\n" ANSI_CLEAR_BELOW "Available completions:\n");
1019-
const size_t page_size = 40;
1032+
1033+
/// Account for the current line, the line showing "Available completions"
1034+
/// before and the line saying "More" after.
1035+
const size_t page_size = editline.GetTerminalHeight() - 3;
1036+
10201037
bool all = false;
10211038

10221039
auto longest =
@@ -1026,21 +1043,15 @@ void Editline::DisplayCompletions(
10261043

10271044
const size_t max_len = longest->GetCompletion().size();
10281045

1029-
if (results.size() < page_size) {
1030-
PrintCompletion(editline.m_output_file, results, max_len,
1031-
editline.GetTerminalWidth());
1032-
return;
1033-
}
1034-
10351046
size_t cur_pos = 0;
10361047
while (cur_pos < results.size()) {
10371048
size_t remaining = results.size() - cur_pos;
10381049
size_t next_size = all ? remaining : std::min(page_size, remaining);
10391050

1040-
PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
1041-
max_len, editline.GetTerminalWidth());
1042-
1043-
cur_pos += next_size;
1051+
cur_pos += PrintCompletion(
1052+
editline.m_output_file, results.slice(cur_pos, next_size), max_len,
1053+
editline.GetTerminalWidth(),
1054+
all ? std::nullopt : std::optional<size_t>(page_size));
10441055

10451056
if (cur_pos >= results.size())
10461057
break;
@@ -1525,6 +1536,13 @@ void Editline::ApplyTerminalSizeChange() {
15251536
m_terminal_width = INT_MAX;
15261537
m_current_line_rows = 1;
15271538
}
1539+
1540+
int rows;
1541+
if (el_get(m_editline, EL_GETTC, "li", &rows, nullptr) == 0) {
1542+
m_terminal_height = rows;
1543+
} else {
1544+
m_terminal_height = INT_MAX;
1545+
}
15281546
}
15291547

15301548
const char *Editline::GetPrompt() { return m_set_prompt.c_str(); }

lldb/test/API/terminal/TestEditlineCompletions.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,37 @@ def test_multiline_description(self):
6262
" kdp-remote is an abbreviation for 'process conn..."
6363
)
6464
self.child.expect(" kill -- Terminate the current target process.")
65+
66+
@skipIfAsan
67+
@skipIfEditlineSupportMissing
68+
def test_completion_pagination(self):
69+
"""Test that we use the terminal height for pagination."""
70+
self.launch(dimensions=(10, 30))
71+
self.child.send("_regexp-\t")
72+
self.child.expect("Available completions:")
73+
self.child.expect(" _regexp-attach")
74+
self.child.expect(" _regexp-break")
75+
self.child.expect(" _regexp-bt")
76+
self.child.expect(" _regexp-display")
77+
self.child.expect(" _regexp-down")
78+
self.child.expect(" _regexp-env")
79+
self.child.expect(" _regexp-jump")
80+
self.child.expect("More")
81+
82+
@skipIfAsan
83+
@skipIfEditlineSupportMissing
84+
def test_completion_multiline_pagination(self):
85+
"""Test that we use the terminal height for pagination and account for multi-line descriptions."""
86+
self.launch(dimensions=(6, 72))
87+
self.child.send("k\t")
88+
self.child.expect("Available completions:")
89+
self.child.expect(
90+
" kdp-remote -- Connect to a process via remote KDP server."
91+
)
92+
self.child.expect(
93+
" If no UDP port is specified, port 41139 is assu..."
94+
)
95+
self.child.expect(
96+
" kdp-remote is an abbreviation for 'process conn..."
97+
)
98+
self.child.expect("More")

lldb/tools/driver/Driver.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,8 @@ int Driver::MainLoop() {
451451
::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
452452
if (window_size.ws_col > 0)
453453
m_debugger.SetTerminalWidth(window_size.ws_col);
454+
if (window_size.ws_row > 0)
455+
m_debugger.SetTerminalHeight(window_size.ws_row);
454456
}
455457

456458
SBCommandInterpreter sb_interpreter = m_debugger.GetCommandInterpreter();
@@ -627,16 +629,17 @@ int Driver::MainLoop() {
627629
return sb_interpreter.GetQuitStatus();
628630
}
629631

630-
void Driver::ResizeWindow(unsigned short col) {
632+
void Driver::ResizeWindow(unsigned short col, unsigned short row) {
631633
GetDebugger().SetTerminalWidth(col);
634+
GetDebugger().SetTerminalHeight(row);
632635
}
633636

634637
void sigwinch_handler(int signo) {
635638
struct winsize window_size;
636639
if ((isatty(STDIN_FILENO) != 0) &&
637640
::ioctl(STDIN_FILENO, TIOCGWINSZ, &window_size) == 0) {
638641
if ((window_size.ws_col > 0) && g_driver != nullptr) {
639-
g_driver->ResizeWindow(window_size.ws_col);
642+
g_driver->ResizeWindow(window_size.ws_col, window_size.ws_row);
640643
}
641644
}
642645
}

lldb/tools/driver/Driver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class Driver : public lldb::SBBroadcaster {
9292

9393
lldb::SBDebugger &GetDebugger() { return m_debugger; }
9494

95-
void ResizeWindow(unsigned short col);
95+
void ResizeWindow(unsigned short col, unsigned short row);
9696

9797
private:
9898
lldb::SBDebugger m_debugger;

0 commit comments

Comments
 (0)