[Lldb-commits] [lldb] [lldb] Improve editline completion formatting (PR #116456)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Fri Nov 15 16:18:00 PST 2024
https://github.com/JDevlieghere created https://github.com/llvm/llvm-project/pull/116456
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
>From a694405bd2e1b8cc96183c2f406dddbb8555cd51 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 15 Nov 2024 16:06:47 -0800
Subject: [PATCH] [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
---
lldb/include/lldb/Host/Editline.h | 2 +
lldb/source/Host/common/Editline.cpp | 96 ++++++++++++++++++++++++++--
2 files changed, 91 insertions(+), 7 deletions(-)
diff --git a/lldb/include/lldb/Host/Editline.h b/lldb/include/lldb/Host/Editline.h
index 57e2c831e3499d..e8e8a6c0d4f67e 100644
--- a/lldb/include/lldb/Host/Editline.h
+++ b/lldb/include/lldb/Host/Editline.h
@@ -238,6 +238,8 @@ class Editline {
/// Convert the current input lines into a UTF8 StringList
StringList GetInputAsStringList(int line_count = UINT32_MAX);
+ size_t GetTerminalWidth() { return m_terminal_width; }
+
private:
/// Sets the lowest line number for multi-line editing sessions. A value of
/// zero suppresses line number printing in the prompt.
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index f95f854c5f220c..aeeeca74e3f07a 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -25,6 +25,7 @@
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Locale.h"
#include "llvm/Support/Threading.h"
@@ -927,12 +928,92 @@ unsigned char Editline::BufferEndCommand(int ch) {
static void
PrintCompletion(FILE *output_file,
llvm::ArrayRef<CompletionResult::Completion> results,
- size_t max_len) {
+ size_t max_completion_length, size_t max_lenght) {
+
+ constexpr size_t ellipsis_length = 3;
+ constexpr size_t tab_legnth = 8;
+ constexpr size_t separator_length = 4;
+ const size_t description_col =
+ std::min(max_completion_length + tab_legnth, max_lenght);
+
for (const CompletionResult::Completion &c : results) {
- fprintf(output_file, "\t%-*s", (int)max_len, c.GetCompletion().c_str());
- if (!c.GetDescription().empty())
- fprintf(output_file, " -- %s", c.GetDescription().c_str());
- fprintf(output_file, "\n");
+ // Print the leading tab-sized padding.
+ fprintf(output_file, " ");
+ size_t cursor = tab_legnth;
+
+ if (!c.GetCompletion().empty()) {
+ const size_t completion_length = c.GetCompletion().size();
+ if (cursor + completion_length < max_lenght) {
+ fprintf(output_file, "%s", c.GetCompletion().c_str());
+ cursor = cursor + completion_length;
+ } else {
+ // If the completion doesn't fit on the screen, print ellipsis and don't
+ // bother with the description.
+ fprintf(output_file, "%s...\n",
+ c.GetCompletion()
+ .substr(0, max_lenght - cursor - ellipsis_length)
+ .c_str());
+ continue;
+ }
+ }
+
+ if (!c.GetDescription().empty()) {
+ // If we have a description, we need at least 4 columns for the separator.
+ if (cursor + separator_length < max_lenght) {
+ // Add padding before the separator.
+ if (cursor < description_col) {
+ std::string padding(description_col - cursor, ' ');
+ fprintf(output_file, "%s", padding.c_str());
+ cursor = description_col;
+ }
+
+ // Print the separator.
+ fprintf(output_file, " -- ");
+ cursor = cursor + separator_length;
+
+ // Descriptions can contain newlines. We want to print them below each
+ // other, aligned after the separator. For example, foo has a
+ // two-line description:
+ //
+ // foo -- Something that fits on the line.
+ // More information below.
+ //
+ // However, as soon as a line exceed the available screen width and
+ // print ellipsis, we don't print the next line. For example, foo has a
+ // three-line description:
+ //
+ // foo -- Something that fits on the line.
+ // Something much longer that doesn't fit...
+ //
+ // Because we had to print ellipsis on line two, we don't print the
+ // third line.
+ llvm::StringRef tail = c.GetDescription();
+ while (!tail.empty()) {
+ llvm::StringRef head;
+ std::tie(head, tail) = tail.split('\n');
+
+ const size_t description_lenth = head.size();
+ if (cursor + description_lenth < max_lenght) {
+ fprintf(output_file, "%s\n", head.str().c_str());
+ cursor = cursor + description_lenth;
+ } else {
+ fprintf(output_file, "%s...\n",
+ head.substr(0, max_lenght - cursor - ellipsis_length)
+ .str()
+ .c_str());
+ continue;
+ }
+
+ if (!tail.empty()) {
+ std::string padding(description_col + 4, ' ');
+ fprintf(output_file, "%s", padding.c_str());
+ cursor = description_col + 4;
+ }
+ }
+ }
+ } else {
+ fprintf(output_file, "\n");
+ }
}
}
@@ -953,7 +1034,8 @@ void Editline::DisplayCompletions(
const size_t max_len = longest->GetCompletion().size();
if (results.size() < page_size) {
- PrintCompletion(editline.m_output_file, results, max_len);
+ PrintCompletion(editline.m_output_file, results, max_len,
+ editline.GetTerminalWidth());
return;
}
@@ -963,7 +1045,7 @@ void Editline::DisplayCompletions(
size_t next_size = all ? remaining : std::min(page_size, remaining);
PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
- max_len);
+ max_len, editline.GetTerminalWidth());
cur_pos += next_size;
More information about the lldb-commits
mailing list