[Lldb-commits] [lldb] 2841cdb - [lldb] Support format string in the prompt (#123430)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Jan 21 21:01:06 PST 2025
Author: Jonas Devlieghere
Date: 2025-01-21T21:01:02-08:00
New Revision: 2841cdbfda92b3fb5fbec726f3376b198106d496
URL: https://github.com/llvm/llvm-project/commit/2841cdbfda92b3fb5fbec726f3376b198106d496
DIFF: https://github.com/llvm/llvm-project/commit/2841cdbfda92b3fb5fbec726f3376b198106d496.diff
LOG: [lldb] Support format string in the prompt (#123430)
Implement ansi::StripAnsiTerminalCodes and fix a long standing bug where
using format strings in lldb's prompt resulted in an incorrect prompt
column width.
Added:
Modified:
lldb/include/lldb/Host/Editline.h
lldb/include/lldb/Utility/AnsiTerminal.h
lldb/source/Core/IOHandler.cpp
lldb/source/Host/common/Editline.cpp
lldb/test/API/terminal/TestEditline.py
lldb/unittests/Editline/EditlineTest.cpp
lldb/unittests/Utility/AnsiTerminalTest.cpp
Removed:
################################################################################
diff --git a/lldb/include/lldb/Host/Editline.h b/lldb/include/lldb/Host/Editline.h
index 26deba38f8471c..27b863870090cb 100644
--- a/lldb/include/lldb/Host/Editline.h
+++ b/lldb/include/lldb/Host/Editline.h
@@ -152,7 +152,7 @@ using namespace line_editor;
class Editline {
public:
Editline(const char *editor_name, FILE *input_file, FILE *output_file,
- FILE *error_file, std::recursive_mutex &output_mutex);
+ FILE *error_file, bool color, std::recursive_mutex &output_mutex);
~Editline();
@@ -212,19 +212,23 @@ class Editline {
}
void SetPromptAnsiPrefix(std::string prefix) {
- m_prompt_ansi_prefix = std::move(prefix);
+ if (m_color)
+ m_prompt_ansi_prefix = std::move(prefix);
}
void SetPromptAnsiSuffix(std::string suffix) {
- m_prompt_ansi_suffix = std::move(suffix);
+ if (m_color)
+ m_prompt_ansi_suffix = std::move(suffix);
}
void SetSuggestionAnsiPrefix(std::string prefix) {
- m_suggestion_ansi_prefix = std::move(prefix);
+ if (m_color)
+ m_suggestion_ansi_prefix = std::move(prefix);
}
void SetSuggestionAnsiSuffix(std::string suffix) {
- m_suggestion_ansi_suffix = std::move(suffix);
+ if (m_color)
+ m_suggestion_ansi_suffix = std::move(suffix);
}
/// Prompts for and reads a single line of user input.
@@ -400,6 +404,7 @@ class Editline {
CompleteCallbackType m_completion_callback;
SuggestionCallbackType m_suggestion_callback;
+ bool m_color;
std::string m_prompt_ansi_prefix;
std::string m_prompt_ansi_suffix;
std::string m_suggestion_ansi_prefix;
diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h
index 67795971d2ca89..1939c49c7b859c 100644
--- a/lldb/include/lldb/Utility/AnsiTerminal.h
+++ b/lldb/include/lldb/Utility/AnsiTerminal.h
@@ -171,7 +171,32 @@ 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;
+
+ size_t end = right.find_first_not_of("0123456789;");
+ if (end < right.size() && (right[end] == 'm' || right[end] == 'G')) {
+ str = right.substr(end + 1);
+ } else {
+ // ANSI_ESC_END not found.
+ stripped += ANSI_ESC_START;
+ str = right;
+ }
+ }
+ return stripped;
}
+
+} // namespace ansi
} // namespace lldb_private
#endif
diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp
index 695c2481e353db..ca06b52b874db6 100644
--- a/lldb/source/Core/IOHandler.cpp
+++ b/lldb/source/Core/IOHandler.cpp
@@ -264,7 +264,7 @@ IOHandlerEditline::IOHandlerEditline(
if (use_editline) {
m_editline_up = std::make_unique<Editline>(editline_name, GetInputFILE(),
GetOutputFILE(), GetErrorFILE(),
- GetOutputMutex());
+ m_color, GetOutputMutex());
m_editline_up->SetIsInputCompleteCallback(
[this](Editline *editline, StringList &lines) {
return this->IsInputCompleteCallback(editline, lines);
@@ -278,12 +278,10 @@ IOHandlerEditline::IOHandlerEditline(
m_editline_up->SetSuggestionCallback([this](llvm::StringRef line) {
return this->SuggestionCallback(line);
});
- if (m_color) {
- m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
- debugger.GetAutosuggestionAnsiPrefix()));
- m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
- debugger.GetAutosuggestionAnsiSuffix()));
- }
+ m_editline_up->SetSuggestionAnsiPrefix(ansi::FormatAnsiTerminalCodes(
+ debugger.GetAutosuggestionAnsiPrefix()));
+ m_editline_up->SetSuggestionAnsiSuffix(ansi::FormatAnsiTerminalCodes(
+ debugger.GetAutosuggestionAnsiSuffix()));
}
// See if the delegate supports fixing indentation
const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters();
@@ -478,12 +476,10 @@ bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
#if LLDB_ENABLE_LIBEDIT
if (m_editline_up) {
m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
- if (m_color) {
- m_editline_up->SetPromptAnsiPrefix(
- ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
- m_editline_up->SetPromptAnsiSuffix(
- ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
- }
+ m_editline_up->SetPromptAnsiPrefix(
+ ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
+ m_editline_up->SetPromptAnsiSuffix(
+ ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
}
#endif
return true;
diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp
index 6e35b15d69651d..73da1d84816187 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) {
@@ -610,7 +612,7 @@ int Editline::GetCharacter(EditLineGetCharType *c) {
}
const char *Editline::Prompt() {
- if (!m_prompt_ansi_prefix.empty() || !m_prompt_ansi_suffix.empty())
+ if (m_color)
m_needs_prompt_repaint = true;
return m_current_prompt.c_str();
}
@@ -1471,11 +1473,11 @@ Editline *Editline::InstanceFor(EditLine *editline) {
}
Editline::Editline(const char *editline_name, FILE *input_file,
- FILE *output_file, FILE *error_file,
+ FILE *output_file, FILE *error_file, bool color,
std::recursive_mutex &output_mutex)
: m_editor_status(EditorStatus::Complete), m_input_file(input_file),
m_output_file(output_file), m_error_file(error_file),
- m_input_connection(fileno(input_file), false),
+ m_input_connection(fileno(input_file), false), m_color(color),
m_output_mutex(output_mutex) {
// Get a shared history instance
m_editor_name = (editline_name == nullptr) ? "lldb-tmp" : editline_name;
diff --git a/lldb/test/API/terminal/TestEditline.py b/lldb/test/API/terminal/TestEditline.py
index aa7d827e599441..ddaa441d5f7c1d 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 *
@@ -69,6 +68,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.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.
+ # Prompt: (lldb) _
+ # Column: 1....6.8
+ self.child.expect(re.escape("\x1b[31m(lldb) \x1b[0m\x1b[8Gfoo"))
+
@skipIfAsan
@skipIfEditlineSupportMissing
def test_prompt_no_color(self):
diff --git a/lldb/unittests/Editline/EditlineTest.cpp b/lldb/unittests/Editline/EditlineTest.cpp
index 333ad77a0a16fd..1327b587e7c3d6 100644
--- a/lldb/unittests/Editline/EditlineTest.cpp
+++ b/lldb/unittests/Editline/EditlineTest.cpp
@@ -118,7 +118,7 @@ EditlineAdapter::EditlineAdapter()
// Create an Editline instance.
_editline_sp.reset(new lldb_private::Editline(
"gtest editor", *_el_secondary_file, *_el_secondary_file,
- *_el_secondary_file, output_mutex));
+ *_el_secondary_file, /*color=*/false, output_mutex));
_editline_sp->SetPrompt("> ");
// Hookup our input complete callback.
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"));
}
More information about the lldb-commits
mailing list