Skip to content

Commit bb4a376

Browse files
authored
Merge pull request swiftlang#9644 from swiftlang/cherrypick-swift/release/6.1-dd78d7c7be5b-bf1c3c2deea9
[🍒 swift/release/6.1] [lldb] Update TestSwiftREPLCompletion for completion changes
2 parents cc4af7f + 0ada4fd commit bb4a376

File tree

4 files changed

+150
-9
lines changed

4 files changed

+150
-9
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: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -927,12 +927,86 @@ 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_length) {
931+
constexpr size_t ellipsis_length = 3;
932+
constexpr size_t padding_length = 8;
933+
constexpr size_t separator_length = 4;
934+
935+
const size_t description_col =
936+
std::min(max_completion_length + padding_length, max_length);
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+
if (c.GetCompletion().empty())
940+
continue;
941+
942+
// Print the leading padding.
943+
fprintf(output_file, " ");
944+
945+
// Print the completion with trailing padding to the description column if
946+
// that fits on the screen. Otherwise print whatever fits on the screen
947+
// followed by ellipsis.
948+
const size_t completion_length = c.GetCompletion().size();
949+
if (padding_length + completion_length < max_length) {
950+
fprintf(output_file, "%-*s",
951+
static_cast<int>(description_col - padding_length),
952+
c.GetCompletion().c_str());
953+
} else {
954+
// If the completion doesn't fit on the screen, print ellipsis and don't
955+
// bother with the description.
956+
fprintf(output_file, "%.*s...\n",
957+
static_cast<int>(max_length - padding_length - ellipsis_length),
958+
c.GetCompletion().c_str());
959+
continue;
960+
}
961+
962+
// If we don't have a description, or we don't have enough space left to
963+
// print the separator followed by the ellipsis, we're done.
964+
if (c.GetDescription().empty() ||
965+
description_col + separator_length + ellipsis_length >= max_length) {
966+
fprintf(output_file, "\n");
967+
continue;
968+
}
969+
970+
// Print the separator.
971+
fprintf(output_file, " -- ");
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+
bool first = true;
990+
for (llvm::StringRef line : llvm::split(c.GetDescription(), '\n')) {
991+
if (line.empty())
992+
break;
993+
if (!first)
994+
fprintf(output_file, "%*s",
995+
static_cast<int>(description_col + separator_length), "");
996+
997+
first = false;
998+
const size_t position = description_col + separator_length;
999+
const size_t description_length = line.size();
1000+
if (position + description_length < max_length) {
1001+
fprintf(output_file, "%.*s\n", static_cast<int>(description_length),
1002+
line.data());
1003+
} else {
1004+
fprintf(output_file, "%.*s...\n",
1005+
static_cast<int>(max_length - position - ellipsis_length),
1006+
line.data());
1007+
continue;
1008+
}
1009+
}
9361010
}
9371011
}
9381012

@@ -953,7 +1027,8 @@ void Editline::DisplayCompletions(
9531027
const size_t max_len = longest->GetCompletion().size();
9541028

9551029
if (results.size() < page_size) {
956-
PrintCompletion(editline.m_output_file, results, max_len);
1030+
PrintCompletion(editline.m_output_file, results, max_len,
1031+
editline.GetTerminalWidth());
9571032
return;
9581033
}
9591034

@@ -963,7 +1038,7 @@ void Editline::DisplayCompletions(
9631038
size_t next_size = all ? remaining : std::min(page_size, remaining);
9641039

9651040
PrintCompletion(editline.m_output_file, results.slice(cur_pos, next_size),
966-
max_len);
1041+
max_len, editline.GetTerminalWidth());
9671042

9681043
cur_pos += next_size;
9691044

lldb/test/API/lang/swift/completion/TestSwiftREPLCompletion.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def test_basic_completion(self):
2929
# Try completing something that has multiple completions.
3030
self.child.send("Hash\t")
3131
self.child.expect_exact("Available completions:")
32-
self.child.expect_exact("\tHash --")
33-
self.child.expect_exact("\tHash --")
32+
self.child.expect_exact(" Hash --")
33+
self.child.expect_exact(" Hash --")
3434
self.child.sendline("")
3535

3636
self.quit()
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
from lldbsuite.test.lldbpexpect import PExpectTest
6+
7+
8+
class EditlineCompletionsTest(PExpectTest):
9+
@skipIfAsan
10+
@skipIfEditlineSupportMissing
11+
def test_completion_truncated(self):
12+
"""Test that the completion is correctly truncated."""
13+
self.launch(dimensions=(10, 20))
14+
self.child.send("_regexp-\t")
15+
self.child.expect(" _regexp-a...")
16+
self.child.expect(" _regexp-b...")
17+
18+
@skipIfAsan
19+
@skipIfEditlineSupportMissing
20+
def test_description_truncated(self):
21+
"""Test that the description is correctly truncated."""
22+
self.launch(dimensions=(10, 70))
23+
self.child.send("_regexp-\t")
24+
self.child.expect(
25+
" _regexp-attach -- Attach to process by ID or name."
26+
)
27+
self.child.expect(
28+
" _regexp-break -- Set a breakpoint using one of several..."
29+
)
30+
31+
@skipIfAsan
32+
@skipIfEditlineSupportMissing
33+
def test_separator_omitted(self):
34+
"""Test that the separated is correctly omitted."""
35+
self.launch(dimensions=(10, 32))
36+
self.child.send("_regexp-\t")
37+
self.child.expect(" _regexp-attach \r\n")
38+
self.child.expect(" _regexp-break \r\n")
39+
40+
@skipIfAsan
41+
@skipIfEditlineSupportMissing
42+
def test_separator(self):
43+
"""Test that the separated is correctly printed."""
44+
self.launch(dimensions=(10, 33))
45+
self.child.send("_regexp-\t")
46+
self.child.expect(" _regexp-attach -- A...")
47+
self.child.expect(" _regexp-break -- S...")
48+
49+
@skipIfAsan
50+
@skipIfEditlineSupportMissing
51+
def test_multiline_description(self):
52+
"""Test that multi-line descriptions are correctly padded and truncated."""
53+
self.launch(dimensions=(10, 72))
54+
self.child.send("k\t")
55+
self.child.expect(
56+
" kdp-remote -- Connect to a process via remote KDP server."
57+
)
58+
self.child.expect(
59+
" If no UDP port is specified, port 41139 is assu..."
60+
)
61+
self.child.expect(
62+
" kdp-remote is an abbreviation for 'process conn..."
63+
)
64+
self.child.expect(" kill -- Terminate the current target process.")

0 commit comments

Comments
 (0)