Skip to content

Commit 3434c7d

Browse files
committed
[lldb] Improve editline completion formatting
This patch improves the formatting of editline completions. The current implementation is naive and doesn't account for the terminal width. Concretely, the old implementation suffered from the following issues: - We would unconditionally pad to the longest completion. If that completion exceeds the width of the terminal, that would result in a lot of superfluous white space and line wrapping. - When printing the description, we wouldn't account for the presence of newlines, and they would continue without leading padding. The new code accounts for both. If the completion exceeds the available terminal width, we show what fits on the current lined followed by ellipsis. We also no longer pad beyond the length of the current line. Finally, we print the description line by line, with the proper leading padding. If a line of the description exceeds the available terminal width, we print ellipsis and won't print the next line. Before: ``` Available completions: _regexp-attach -- Attach to process by ID or name. _regexp-break -- Set a breakpoint using one of several shorthand formats. _regexp-bt -- Show backtrace of the current thread's call sta ck. Any numeric argument displays at most that many frames. The argument 'al l' displays all threads. Use 'settings set frame-format' to customize the pr inting of individual frames and 'settings set thread-format' to customize th e thread header. Frame recognizers may filter thelist. Use 'thread backtrace -u (--unfiltered)' to see them all. _regexp-display -- Evaluate an expression at every stop (see 'help target stop-hook'.) ``` After: ``` Available completions: _regexp-attach -- Attach to process by ID or name. _regexp-break -- Set a breakpoint using one of several shorth... _regexp-bt -- Show backtrace of the current thread's call ... _regexp-display -- Evaluate an expression at every stop (see 'h... ``` rdar://135818198
1 parent cdda76a commit 3434c7d

File tree

2 files changed

+90
-7
lines changed

2 files changed

+90
-7
lines changed

lldb/include/lldb/Host/Editline.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,8 @@ class Editline {
238238
/// Convert the current input lines into a UTF8 StringList
239239
StringList GetInputAsStringList(int line_count = UINT32_MAX);
240240

241+
size_t GetTerminalWidth() { return m_terminal_width; }
242+
241243
private:
242244
/// Sets the lowest line number for multi-line editing sessions. A value of
243245
/// zero suppresses line number printing in the prompt.

lldb/source/Host/common/Editline.cpp

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -927,12 +927,92 @@ unsigned char Editline::BufferEndCommand(int ch) {
927927
static void
928928
PrintCompletion(FILE *output_file,
929929
llvm::ArrayRef<CompletionResult::Completion> results,
930-
size_t max_len) {
930+
size_t max_completion_length, size_t max_lenght) {
931+
932+
constexpr size_t ellipsis_length = 3;
933+
constexpr size_t tab_legnth = 8;
934+
constexpr size_t separator_length = 4;
935+
const size_t description_col =
936+
std::min(max_completion_length + tab_legnth, max_lenght);
937+
931938
for (const CompletionResult::Completion &c : results) {
932-
fprintf(output_file, "\t%-*s", (int)max_len, c.GetCompletion().c_str());
933-
if (!c.GetDescription().empty())
934-
fprintf(output_file, " -- %s", c.GetDescription().c_str());
935-
fprintf(output_file, "\n");
939+
// Print the leading tab-sized padding.
940+
fprintf(output_file, " ");
941+
size_t cursor = tab_legnth;
942+
943+
if (!c.GetCompletion().empty()) {
944+
const size_t completion_length = c.GetCompletion().size();
945+
if (cursor + completion_length < max_lenght) {
946+
fprintf(output_file, "%s", c.GetCompletion().c_str());
947+
cursor = cursor + completion_length;
948+
} else {
949+
// If the completion doesn't fit on the screen, print ellipsis and don't
950+
// bother with the description.
951+
fprintf(output_file, "%s...\n",
952+
c.GetCompletion()
953+
.substr(0, max_lenght - cursor - ellipsis_length)
954+
.c_str());
955+
continue;
956+
}
957+
}
958+
959+
if (!c.GetDescription().empty()) {
960+
// If we have a description, we need at least 4 columns for the separator.
961+
if (cursor + separator_length < max_lenght) {
962+
// Add padding before the separator.
963+
if (cursor < description_col) {
964+
std::string padding(description_col - cursor, ' ');
965+
fprintf(output_file, "%s", padding.c_str());
966+
cursor = description_col;
967+
}
968+
969+
// Print the separator.
970+
fprintf(output_file, " -- ");
971+
cursor = cursor + separator_length;
972+
973+
// Descriptions can contain newlines. We want to print them below each
974+
// other, aligned after the separator. For example, foo has a
975+
// two-line description:
976+
//
977+
// foo -- Something that fits on the line.
978+
// More information below.
979+
//
980+
// However, as soon as a line exceed the available screen width and
981+
// print ellipsis, we don't print the next line. For example, foo has a
982+
// three-line description:
983+
//
984+
// foo -- Something that fits on the line.
985+
// Something much longer that doesn't fit...
986+
//
987+
// Because we had to print ellipsis on line two, we don't print the
988+
// third line.
989+
llvm::StringRef tail = c.GetDescription();
990+
while (!tail.empty()) {
991+
llvm::StringRef head;
992+
std::tie(head, tail) = tail.split('\n');
993+
994+
const size_t description_lenth = head.size();
995+
if (cursor + description_lenth < max_lenght) {
996+
fprintf(output_file, "%s\n", head.str().c_str());
997+
cursor = cursor + description_lenth;
998+
} else {
999+
fprintf(output_file, "%s...\n",
1000+
head.substr(0, max_lenght - cursor - ellipsis_length)
1001+
.str()
1002+
.c_str());
1003+
continue;
1004+
}
1005+
1006+
if (!tail.empty()) {
1007+
std::string padding(description_col + 4, ' ');
1008+
fprintf(output_file, "%s", padding.c_str());
1009+
cursor = description_col + 4;
1010+
}
1011+
}
1012+
}
1013+
} else {
1014+
fprintf(output_file, "\n");
1015+
}
9361016
}
9371017
}
9381018

@@ -953,7 +1033,8 @@ void Editline::DisplayCompletions(
9531033
const size_t max_len = longest->GetCompletion().size();
9541034

9551035
if (results.size() < page_size) {
956-
PrintCompletion(editline.m_output_file, results, max_len);
1036+
PrintCompletion(editline.m_output_file, results, max_len,
1037+
editline.GetTerminalWidth());
9571038
return;
9581039
}
9591040

@@ -963,7 +1044,7 @@ void Editline::DisplayCompletions(
9631044
size_t next_size = all ? remaining : std::min(page_size, remaining);
9641045

9651046
PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
966-
max_len);
1047+
max_len, editline.GetTerminalWidth());
9671048

9681049
cur_pos += next_size;
9691050

0 commit comments

Comments
 (0)