Skip to content

Commit 470ffa2

Browse files
committed
[lldb] Support format string in the prompt
Implement ansi::StripAnsiTerminalCodes and fix a long standing bug where using format strings in lldb's prompt resulted in an incorrect prompt column width.
1 parent f358847 commit 470ffa2

File tree

4 files changed

+72
-1
lines changed

4 files changed

+72
-1
lines changed

lldb/include/lldb/Utility/AnsiTerminal.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,45 @@ inline std::string FormatAnsiTerminalCodes(llvm::StringRef format,
171171
}
172172
return fmt;
173173
}
174+
175+
inline std::string StripAnsiTerminalCodes(llvm::StringRef str) {
176+
std::string stripped;
177+
while (!str.empty()) {
178+
llvm::StringRef left, right;
179+
180+
std::tie(left, right) = str.split(ANSI_ESC_START);
181+
stripped += left;
182+
183+
// ANSI_ESC_START not found.
184+
if (left == str && right.empty())
185+
break;
186+
187+
auto end = llvm::StringRef::npos;
188+
for (size_t i = 0; i < right.size(); i++) {
189+
char c = right[i];
190+
if (c == 'm' || c == 'G') {
191+
end = i;
192+
break;
193+
}
194+
if (isdigit(c) || c == ';')
195+
continue;
196+
197+
break;
198+
}
199+
200+
// ANSI_ESC_END not found.
201+
if (end != llvm::StringRef::npos) {
202+
str = right.substr(end + 1);
203+
continue;
204+
}
205+
206+
stripped += ANSI_ESC_START;
207+
str = right;
208+
}
209+
return stripped;
174210
}
211+
212+
} // namespace ansi
175213
} // namespace lldb_private
176214

177215
#endif

lldb/source/Host/common/Editline.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "lldb/Host/Editline.h"
1515
#include "lldb/Host/FileSystem.h"
1616
#include "lldb/Host/Host.h"
17+
#include "lldb/Utility/AnsiTerminal.h"
1718
#include "lldb/Utility/CompletionRequest.h"
1819
#include "lldb/Utility/FileSpec.h"
1920
#include "lldb/Utility/LLDBAssert.h"
@@ -85,7 +86,8 @@ bool IsOnlySpaces(const EditLineStringType &content) {
8586
}
8687

8788
static size_t ColumnWidth(llvm::StringRef str) {
88-
return llvm::sys::locale::columnWidth(str);
89+
std::string stripped = ansi::StripAnsiTerminalCodes(str);
90+
return llvm::sys::locale::columnWidth(stripped);
8991
}
9092

9193
static int GetOperation(HistoryOperation op) {

lldb/test/API/terminal/TestEditline.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,22 @@ def test_prompt_color(self):
6969
# Column: 1....6.8
7070
self.child.expect(re.escape("\x1b[31m(lldb) \x1b[0m\x1b[8G"))
7171

72+
@skipIfAsan
73+
@skipIfEditlineSupportMissing
74+
def test_prompt_format_color(self):
75+
"""Test that we can change the prompt color with a format string."""
76+
self.launch(use_colors=True)
77+
# Clear the prefix and suffix setting to simplify the output.
78+
self.child.send('settings set prompt-ansi-prefix ""\n')
79+
self.child.send('settings set prompt-ansi-suffix ""\n')
80+
self.child.send('settings set prompt "${ansi.fg.red}(lldb)${ansi.normal} "\n')
81+
self.child.send("foo")
82+
# Make sure this change is reflected immediately. Check that the color
83+
# is set (31) and the cursor position (8) is correct.
84+
# Prompt: (lldb) _
85+
# Column: 1....6.8
86+
self.child.expect(re.escape("\x1b[31m(lldb)\x1b[0m foo"))
87+
7288
@skipIfAsan
7389
@skipIfEditlineSupportMissing
7490
def test_prompt_no_color(self):

lldb/unittests/Utility/AnsiTerminalTest.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,21 @@ TEST(AnsiTerminal, Empty) { EXPECT_EQ("", ansi::FormatAnsiTerminalCodes("")); }
1616

1717
TEST(AnsiTerminal, WhiteSpace) {
1818
EXPECT_EQ(" ", ansi::FormatAnsiTerminalCodes(" "));
19+
EXPECT_EQ(" ", ansi::StripAnsiTerminalCodes(" "));
1920
}
2021

2122
TEST(AnsiTerminal, AtEnd) {
2223
EXPECT_EQ("abc\x1B[30m",
2324
ansi::FormatAnsiTerminalCodes("abc${ansi.fg.black}"));
25+
26+
EXPECT_EQ("abc", ansi::StripAnsiTerminalCodes("abc\x1B[30m"));
2427
}
2528

2629
TEST(AnsiTerminal, AtStart) {
2730
EXPECT_EQ("\x1B[30mabc",
2831
ansi::FormatAnsiTerminalCodes("${ansi.fg.black}abc"));
32+
33+
EXPECT_EQ("abc", ansi::StripAnsiTerminalCodes("\x1B[30mabc"));
2934
}
3035

3136
TEST(AnsiTerminal, KnownPrefix) {
@@ -45,10 +50,20 @@ TEST(AnsiTerminal, Incomplete) {
4550
TEST(AnsiTerminal, Twice) {
4651
EXPECT_EQ("\x1B[30m\x1B[31mabc",
4752
ansi::FormatAnsiTerminalCodes("${ansi.fg.black}${ansi.fg.red}abc"));
53+
54+
EXPECT_EQ("abc", ansi::StripAnsiTerminalCodes("\x1B[30m\x1B[31mabc"));
4855
}
4956

5057
TEST(AnsiTerminal, Basic) {
5158
EXPECT_EQ(
5259
"abc\x1B[31mabc\x1B[0mabc",
5360
ansi::FormatAnsiTerminalCodes("abc${ansi.fg.red}abc${ansi.normal}abc"));
61+
62+
EXPECT_EQ("abcabcabc",
63+
ansi::StripAnsiTerminalCodes("abc\x1B[31mabc\x1B[0mabc"));
64+
}
65+
66+
TEST(AnsiTerminal, InvalidEscapeCode) {
67+
EXPECT_EQ("abc\x1B[31kabcabc",
68+
ansi::StripAnsiTerminalCodes("abc\x1B[31kabc\x1B[0mabc"));
5469
}

0 commit comments

Comments
 (0)