Skip to content

Commit 3dfc1d9

Browse files
authored
[lldb] Use the terminal height for paging editline completions (#119914)
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. This builds on the improvements of #116456.
1 parent 696d120 commit 3dfc1d9

File tree

11 files changed

+138
-25
lines changed

11 files changed

+138
-25
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: 33 additions & 16 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

@@ -990,6 +1000,8 @@ PrintCompletion(FILE *output_file,
9901000
for (llvm::StringRef line : llvm::split(c.GetDescription(), '\n')) {
9911001
if (line.empty())
9921002
break;
1003+
if (max_height && lines_printed >= *max_height)
1004+
break;
9931005
if (!first)
9941006
fprintf(output_file, "%*s",
9951007
static_cast<int>(description_col + separator_length), "");
@@ -1000,14 +1012,17 @@ PrintCompletion(FILE *output_file,
10001012
if (position + description_length < max_length) {
10011013
fprintf(output_file, "%.*s\n", static_cast<int>(description_length),
10021014
line.data());
1015+
lines_printed++;
10031016
} else {
10041017
fprintf(output_file, "%.*s...\n",
10051018
static_cast<int>(max_length - position - ellipsis_length),
10061019
line.data());
1020+
lines_printed++;
10071021
continue;
10081022
}
10091023
}
10101024
}
1025+
return results_printed;
10111026
}
10121027

10131028
void Editline::DisplayCompletions(
@@ -1016,7 +1031,11 @@ void Editline::DisplayCompletions(
10161031

10171032
fprintf(editline.m_output_file,
10181033
"\n" ANSI_CLEAR_BELOW "Available completions:\n");
1019-
const size_t page_size = 40;
1034+
1035+
/// Account for the current line, the line showing "Available completions"
1036+
/// before and the line saying "More" after.
1037+
const size_t page_size = editline.GetTerminalHeight() - 3;
1038+
10201039
bool all = false;
10211040

10221041
auto longest =
@@ -1026,21 +1045,12 @@ void Editline::DisplayCompletions(
10261045

10271046
const size_t max_len = longest->GetCompletion().size();
10281047

1029-
if (results.size() < page_size) {
1030-
PrintCompletion(editline.m_output_file, results, max_len,
1031-
editline.GetTerminalWidth());
1032-
return;
1033-
}
1034-
10351048
size_t cur_pos = 0;
10361049
while (cur_pos < results.size()) {
1037-
size_t remaining = results.size() - cur_pos;
1038-
size_t next_size = all ? remaining : std::min(page_size, remaining);
1039-
1040-
PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
1041-
max_len, editline.GetTerminalWidth());
1042-
1043-
cur_pos += next_size;
1050+
cur_pos +=
1051+
PrintCompletion(editline.m_output_file, results.slice(cur_pos), max_len,
1052+
editline.GetTerminalWidth(),
1053+
all ? std::nullopt : std::optional<size_t>(page_size));
10441054

10451055
if (cur_pos >= results.size())
10461056
break;
@@ -1525,6 +1535,13 @@ void Editline::ApplyTerminalSizeChange() {
15251535
m_terminal_width = INT_MAX;
15261536
m_current_line_rows = 1;
15271537
}
1538+
1539+
int rows;
1540+
if (el_get(m_editline, EL_GETTC, "li", &rows, nullptr) == 0) {
1541+
m_terminal_height = rows;
1542+
} else {
1543+
m_terminal_height = INT_MAX;
1544+
}
15281545
}
15291546

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

lldb/test/API/functionalities/completion/TestCompletion.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -335,13 +335,22 @@ def test_settings_replace_target_ru(self):
335335
)
336336

337337
def test_settings_show_term(self):
338-
self.complete_from_to("settings show term-", "settings show term-width")
338+
self.complete_from_to("settings show term-w", "settings show term-width")
339339

340340
def test_settings_list_term(self):
341-
self.complete_from_to("settings list term-", "settings list term-width")
341+
self.complete_from_to("settings list term-w", "settings list term-width")
342+
343+
def test_settings_show_term(self):
344+
self.complete_from_to("settings show term-h", "settings show term-height")
345+
346+
def test_settings_list_term(self):
347+
self.complete_from_to("settings list term-h", "settings list term-height")
348+
349+
def test_settings_remove_term(self):
350+
self.complete_from_to("settings remove term-w", "settings remove term-width")
342351

343352
def test_settings_remove_term(self):
344-
self.complete_from_to("settings remove term-", "settings remove term-width")
353+
self.complete_from_to("settings remove term-h", "settings remove term-height")
345354

346355
def test_settings_s(self):
347356
"""Test that 'settings s' completes to ['set', 'show']."""

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)