[Lldb-commits] [lldb] [lldb] Support format string in the prompt (PR #123430)
Jonas Devlieghere via lldb-commits
lldb-commits at lists.llvm.org
Mon Jan 20 11:58:40 PST 2025
https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/123430
>From 10e89226a485d73acfb07ad6d72f3004d270a2f5 Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Fri, 17 Jan 2025 16:51:21 -0800
Subject: [PATCH 1/2] [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.
---
lldb/include/lldb/Utility/AnsiTerminal.h | 38 +++++++++++++++++++++
lldb/source/Host/common/Editline.cpp | 4 ++-
lldb/test/API/terminal/TestEditline.py | 16 +++++++++
lldb/unittests/Utility/AnsiTerminalTest.cpp | 15 ++++++++
4 files changed, 72 insertions(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h
index 67795971d2ca89..22601e873dfe9e 100644
--- a/lldb/include/lldb/Utility/AnsiTerminal.h
+++ b/lldb/include/lldb/Utility/AnsiTerminal.h
@@ -171,7 +171,45 @@ inline std::string FormatAnsiTerminalCodes(llvm::StringRef format,
}
return fmt;
}
+
+inline std::string StripAnsiTerminalCodes(llvm::StringRef str) {
+ std::string stripped;
+ while (!str.empty()) {
+ llvm::StringRef left, right;
+
+ std::tie(left, right) = str.split(ANSI_ESC_START);
+ stripped += left;
+
+ // ANSI_ESC_START not found.
+ if (left == str && right.empty())
+ break;
+
+ auto end = llvm::StringRef::npos;
+ for (size_t i = 0; i < right.size(); i++) {
+ char c = right[i];
+ if (c == 'm' || c == 'G') {
+ end = i;
+ break;
+ }
+ if (isdigit(c) || c == ';')
+ continue;
+
+ break;
+ }
+
+ // ANSI_ESC_END not found.
+ if (end != llvm::StringRef::npos) {
+ str = right.substr(end + 1);
+ continue;
+ }
+
+ stripped += ANSI_ESC_START;
+ str = right;
+ }
+ return stripped;
}
+
+} // namespace ansi
} // namespace lldb_private
#endif
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 6e35b15d69651d..d31fe3af946b37 100644
--- a/lldb/source/Host/common/Editline.cpp
+++ b/lldb/source/Host/common/Editline.cpp
@@ -14,6 +14,7 @@
#include "lldb/Host/Editline.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/Host.h"
+#include "lldb/Utility/AnsiTerminal.h"
#include "lldb/Utility/CompletionRequest.h"
#include "lldb/Utility/FileSpec.h"
#include "lldb/Utility/LLDBAssert.h"
@@ -85,7 +86,8 @@ bool IsOnlySpaces(const EditLineStringType &content) {
}
static size_t ColumnWidth(llvm::StringRef str) {
- return llvm::sys::locale::columnWidth(str);
+ std::string stripped = ansi::StripAnsiTerminalCodes(str);
+ return llvm::sys::locale::columnWidth(stripped);
}
static int GetOperation(HistoryOperation op) {
diff --git a/lldb/test/API/terminal/TestEditline.py b/lldb/test/API/terminal/TestEditline.py
index aa7d827e599441..40f8a50e06bf6d 100644
--- a/lldb/test/API/terminal/TestEditline.py
+++ b/lldb/test/API/terminal/TestEditline.py
@@ -69,6 +69,22 @@ def test_prompt_color(self):
# Column: 1....6.8
self.child.expect(re.escape("\x1b[31m(lldb) \x1b[0m\x1b[8G"))
+ @skipIfAsan
+ @skipIfEditlineSupportMissing
+ def test_prompt_format_color(self):
+ """Test that we can change the prompt color with a format string."""
+ self.launch(use_colors=True)
+ # Clear the prefix and suffix setting to simplify the output.
+ self.child.send('settings set prompt-ansi-prefix ""\n')
+ self.child.send('settings set prompt-ansi-suffix ""\n')
+ self.child.send('settings set prompt "${ansi.fg.red}(lldb)${ansi.normal} "\n')
+ self.child.send("foo")
+ # Make sure this change is reflected immediately. Check that the color
+ # is set (31) and the cursor position (8) is correct.
+ # Prompt: (lldb) _
+ # Column: 1....6.8
+ self.child.expect(re.escape("\x1b[31m(lldb)\x1b[0m foo"))
+
@skipIfAsan
@skipIfEditlineSupportMissing
def test_prompt_no_color(self):
diff --git a/lldb/unittests/Utility/AnsiTerminalTest.cpp b/lldb/unittests/Utility/AnsiTerminalTest.cpp
index a6dbfd61061420..1ba9565c3f6af3 100644
--- a/lldb/unittests/Utility/AnsiTerminalTest.cpp
+++ b/lldb/unittests/Utility/AnsiTerminalTest.cpp
@@ -16,16 +16,21 @@ TEST(AnsiTerminal, Empty) { EXPECT_EQ("", ansi::FormatAnsiTerminalCodes("")); }
TEST(AnsiTerminal, WhiteSpace) {
EXPECT_EQ(" ", ansi::FormatAnsiTerminalCodes(" "));
+ EXPECT_EQ(" ", ansi::StripAnsiTerminalCodes(" "));
}
TEST(AnsiTerminal, AtEnd) {
EXPECT_EQ("abc\x1B[30m",
ansi::FormatAnsiTerminalCodes("abc${ansi.fg.black}"));
+
+ EXPECT_EQ("abc", ansi::StripAnsiTerminalCodes("abc\x1B[30m"));
}
TEST(AnsiTerminal, AtStart) {
EXPECT_EQ("\x1B[30mabc",
ansi::FormatAnsiTerminalCodes("${ansi.fg.black}abc"));
+
+ EXPECT_EQ("abc", ansi::StripAnsiTerminalCodes("\x1B[30mabc"));
}
TEST(AnsiTerminal, KnownPrefix) {
@@ -45,10 +50,20 @@ TEST(AnsiTerminal, Incomplete) {
TEST(AnsiTerminal, Twice) {
EXPECT_EQ("\x1B[30m\x1B[31mabc",
ansi::FormatAnsiTerminalCodes("${ansi.fg.black}${ansi.fg.red}abc"));
+
+ EXPECT_EQ("abc", ansi::StripAnsiTerminalCodes("\x1B[30m\x1B[31mabc"));
}
TEST(AnsiTerminal, Basic) {
EXPECT_EQ(
"abc\x1B[31mabc\x1B[0mabc",
ansi::FormatAnsiTerminalCodes("abc${ansi.fg.red}abc${ansi.normal}abc"));
+
+ EXPECT_EQ("abcabcabc",
+ ansi::StripAnsiTerminalCodes("abc\x1B[31mabc\x1B[0mabc"));
+}
+
+TEST(AnsiTerminal, InvalidEscapeCode) {
+ EXPECT_EQ("abc\x1B[31kabcabc",
+ ansi::StripAnsiTerminalCodes("abc\x1B[31kabc\x1B[0mabc"));
}
>From bb799e95f19140deeae6a9750e8188018aeffc9b Mon Sep 17 00:00:00 2001
From: Jonas Devlieghere <jonas at devlieghere.com>
Date: Mon, 20 Jan 2025 11:58:26 -0800
Subject: [PATCH 2/2] Address code review feedback
---
lldb/include/lldb/Utility/AnsiTerminal.h | 25 ++++++------------------
lldb/test/API/terminal/TestEditline.py | 7 +++----
2 files changed, 9 insertions(+), 23 deletions(-)
diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h
index 22601e873dfe9e..1939c49c7b859c 100644
--- a/lldb/include/lldb/Utility/AnsiTerminal.h
+++ b/lldb/include/lldb/Utility/AnsiTerminal.h
@@ -184,27 +184,14 @@ inline std::string StripAnsiTerminalCodes(llvm::StringRef str) {
if (left == str && right.empty())
break;
- auto end = llvm::StringRef::npos;
- for (size_t i = 0; i < right.size(); i++) {
- char c = right[i];
- if (c == 'm' || c == 'G') {
- end = i;
- break;
- }
- if (isdigit(c) || c == ';')
- continue;
-
- break;
- }
-
- // ANSI_ESC_END not found.
- if (end != llvm::StringRef::npos) {
+ size_t end = right.find_first_not_of("0123456789;");
+ if (end < right.size() && (right[end] == 'm' || right[end] == 'G')) {
str = right.substr(end + 1);
- continue;
+ } else {
+ // ANSI_ESC_END not found.
+ stripped += ANSI_ESC_START;
+ str = right;
}
-
- stripped += ANSI_ESC_START;
- str = right;
}
return stripped;
}
diff --git a/lldb/test/API/terminal/TestEditline.py b/lldb/test/API/terminal/TestEditline.py
index 40f8a50e06bf6d..60baccf7304d62 100644
--- a/lldb/test/API/terminal/TestEditline.py
+++ b/lldb/test/API/terminal/TestEditline.py
@@ -2,7 +2,6 @@
Test that the lldb editline handling is configured correctly.
"""
-
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
@@ -75,9 +74,9 @@ def test_prompt_format_color(self):
"""Test that we can change the prompt color with a format string."""
self.launch(use_colors=True)
# Clear the prefix and suffix setting to simplify the output.
- self.child.send('settings set prompt-ansi-prefix ""\n')
- self.child.send('settings set prompt-ansi-suffix ""\n')
- self.child.send('settings set prompt "${ansi.fg.red}(lldb)${ansi.normal} "\n')
+ self.expect('settings set prompt-ansi-prefix ""')
+ self.expect('settings set prompt-ansi-suffix ""')
+ self.expect('settings set prompt "${ansi.fg.red}(lldb)${ansi.normal} "')
self.child.send("foo")
# Make sure this change is reflected immediately. Check that the color
# is set (31) and the cursor position (8) is correct.
More information about the lldb-commits
mailing list