Skip to content

Commit 8dcef20

Browse files
committed
[lldb] Add a setting to customize the prompt color (llvm#66218)
Users often want to change the look of their prompt and currently the only way to do that is by using ANSI escape codes in the prompt itself. This is not only tedious, it also results in extra whitespace because our Editline wrapper, when computing the cursor column, doesn't ignore the invisible escape codes. We already have various *-ansi-{prefix,suffix} settings that allow the users to customize the color of auto-suggestions and progress events, using mnemonics like ${ansi.fg.yellow}. This patch brings the same mechanism to the prompt. rdar://115390406 (cherry picked from commit 645a385)
1 parent d954644 commit 8dcef20

File tree

7 files changed

+87
-19
lines changed

7 files changed

+87
-19
lines changed

lldb/include/lldb/Core/Debugger.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
297297

298298
llvm::StringRef GetPrompt() const;
299299

300+
llvm::StringRef GetPromptAnsiPrefix() const;
301+
302+
llvm::StringRef GetPromptAnsiSuffix() const;
303+
300304
void SetPrompt(llvm::StringRef p);
301305
void SetPrompt(const char *) = delete;
302306

lldb/include/lldb/Host/Editline.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ class Editline {
216216
m_fix_indentation_callback_chars = indent_chars;
217217
}
218218

219+
void SetPromptAnsiPrefix(std::string prefix) {
220+
m_prompt_ansi_prefix = std::move(prefix);
221+
}
222+
223+
void SetPromptAnsiSuffix(std::string suffix) {
224+
m_prompt_ansi_suffix = std::move(suffix);
225+
}
226+
219227
void SetSuggestionAnsiPrefix(std::string prefix) {
220228
m_suggestion_ansi_prefix = std::move(prefix);
221229
}
@@ -403,6 +411,8 @@ class Editline {
403411
CompleteCallbackType m_completion_callback;
404412
SuggestionCallbackType m_suggestion_callback;
405413

414+
std::string m_prompt_ansi_prefix;
415+
std::string m_prompt_ansi_suffix;
406416
std::string m_suggestion_ansi_prefix;
407417
std::string m_suggestion_ansi_suffix;
408418

lldb/source/Core/CoreProperties.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ let Definition = "debugger" in {
9999
DefaultEnumValue<"OptionValueString::eOptionEncodeCharacterEscapeSequences">,
100100
DefaultStringValue<"(lldb) ">,
101101
Desc<"The debugger command line prompt displayed for the user.">;
102+
def PromptAnsiPrefix: Property<"prompt-ansi-prefix", "String">,
103+
Global,
104+
DefaultStringValue<"${ansi.faint}">,
105+
Desc<"When in a color-enabled terminal, use the ANSI terminal code specified in this format immediately before the prompt.">;
106+
def PromptAnsiSuffix: Property<"prompt-ansi-suffix", "String">,
107+
Global,
108+
DefaultStringValue<"${ansi.normal}">,
109+
Desc<"When in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the prompt.">;
102110
def ScriptLanguage: Property<"script-lang", "Enum">,
103111
Global,
104112
DefaultEnumValue<"eScriptLanguagePython">,

lldb/source/Core/Debugger.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,13 @@ Status Debugger::SetPropertyValue(const ExecutionContext *exe_ctx,
235235
// use-color changed. Ping the prompt so it can reset the ansi terminal
236236
// codes.
237237
SetPrompt(GetPrompt());
238+
} else if (property_path ==
239+
g_debugger_properties[ePropertyPromptAnsiPrefix].name ||
240+
property_path ==
241+
g_debugger_properties[ePropertyPromptAnsiSuffix].name) {
242+
// Prompt colors changed. Ping the prompt so it can reset the ansi
243+
// terminal codes.
244+
SetPrompt(GetPrompt());
238245
} else if (property_path ==
239246
g_debugger_properties[ePropertyUseSourceCache].name) {
240247
// use-source-cache changed. Wipe out the cache contents if it was
@@ -301,6 +308,18 @@ llvm::StringRef Debugger::GetPrompt() const {
301308
idx, g_debugger_properties[idx].default_cstr_value);
302309
}
303310

311+
llvm::StringRef Debugger::GetPromptAnsiPrefix() const {
312+
const uint32_t idx = ePropertyPromptAnsiPrefix;
313+
return GetPropertyAtIndexAs<llvm::StringRef>(
314+
idx, g_debugger_properties[idx].default_cstr_value);
315+
}
316+
317+
llvm::StringRef Debugger::GetPromptAnsiSuffix() const {
318+
const uint32_t idx = ePropertyPromptAnsiSuffix;
319+
return GetPropertyAtIndexAs<llvm::StringRef>(
320+
idx, g_debugger_properties[idx].default_cstr_value);
321+
}
322+
304323
void Debugger::SetPrompt(llvm::StringRef p) {
305324
constexpr uint32_t idx = ePropertyPrompt;
306325
SetPropertyAtIndex(idx, p);

lldb/source/Core/IOHandler.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,15 @@ bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) {
474474
m_prompt = std::string(prompt);
475475

476476
#if LLDB_ENABLE_LIBEDIT
477-
if (m_editline_up)
477+
if (m_editline_up) {
478478
m_editline_up->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str());
479+
if (m_debugger.GetUseColor()) {
480+
m_editline_up->SetPromptAnsiPrefix(
481+
ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiPrefix()));
482+
m_editline_up->SetPromptAnsiSuffix(
483+
ansi::FormatAnsiTerminalCodes(m_debugger.GetPromptAnsiSuffix()));
484+
}
485+
}
479486
#endif
480487
return true;
481488
}

lldb/source/Host/common/Editline.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,6 @@ int setupterm(char *term, int fildes, int *errret);
5353

5454
/// https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-048.pdf
5555
#define ESCAPE "\x1b"
56-
/// Faint, decreased intensity or second colour.
57-
#define ANSI_FAINT ESCAPE "[2m"
58-
/// Normal colour or normal intensity (neither bold nor faint).
59-
#define ANSI_UNFAINT ESCAPE "[0m"
6056
#define ANSI_CLEAR_BELOW ESCAPE "[J"
6157
#define ANSI_CLEAR_RIGHT ESCAPE "[K"
6258
#define ANSI_SET_COLUMN_N ESCAPE "[%dG"
@@ -431,15 +427,13 @@ void Editline::MoveCursor(CursorLocation from, CursorLocation to) {
431427
void Editline::DisplayInput(int firstIndex) {
432428
fprintf(m_output_file, ANSI_SET_COLUMN_N ANSI_CLEAR_BELOW, 1);
433429
int line_count = (int)m_input_lines.size();
434-
const char *faint = m_color_prompts ? ANSI_FAINT : "";
435-
const char *unfaint = m_color_prompts ? ANSI_UNFAINT : "";
436-
437430
for (int index = firstIndex; index < line_count; index++) {
438-
fprintf(m_output_file, "%s"
439-
"%s"
440-
"%s" EditLineStringFormatSpec " ",
441-
faint, PromptForIndex(index).c_str(), unfaint,
442-
m_input_lines[index].c_str());
431+
fprintf(m_output_file,
432+
"%s"
433+
"%s"
434+
"%s" EditLineStringFormatSpec " ",
435+
m_prompt_ansi_prefix.c_str(), PromptForIndex(index).c_str(),
436+
m_prompt_ansi_suffix.c_str(), m_input_lines[index].c_str());
443437
if (index < line_count - 1)
444438
fprintf(m_output_file, "\n");
445439
}
@@ -548,14 +542,16 @@ unsigned char Editline::RecallHistory(HistoryOperation op) {
548542
int Editline::GetCharacter(EditLineGetCharType *c) {
549543
const LineInfoW *info = el_wline(m_editline);
550544

551-
// Paint a faint version of the desired prompt over the version libedit draws
552-
// (will only be requested if colors are supported)
545+
// Paint a ANSI formatted version of the desired prompt over the version
546+
// libedit draws. (will only be requested if colors are supported)
553547
if (m_needs_prompt_repaint) {
554548
MoveCursor(CursorLocation::EditingCursor, CursorLocation::EditingPrompt);
555-
fprintf(m_output_file, "%s"
556-
"%s"
557-
"%s",
558-
ANSI_FAINT, Prompt(), ANSI_UNFAINT);
549+
fprintf(m_output_file,
550+
"%s"
551+
"%s"
552+
"%s",
553+
m_prompt_ansi_prefix.c_str(), Prompt(),
554+
m_prompt_ansi_suffix.c_str());
559555
MoveCursor(CursorLocation::EditingPrompt, CursorLocation::EditingCursor);
560556
m_needs_prompt_repaint = false;
561557
}

lldb/test/API/terminal/TestEditline.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,27 @@ def test_prompt_unicode(self):
5555
# Prompt: 🐛 _
5656
# Column: 1..4
5757
self.child.expect(re.escape("🐛 \x1b[0m\x1b[4G"))
58+
59+
@skipIfAsan
60+
@skipIfEditlineSupportMissing
61+
def test_prompt_color(self):
62+
"""Test that we can change the prompt color with prompt-ansi-prefix."""
63+
self.launch(use_colors=True)
64+
self.child.send('settings set prompt-ansi-prefix "${ansi.fg.red}"\n')
65+
# Make sure this change is reflected immediately. Check that the color
66+
# is set (31) and the cursor position (8) is correct.
67+
# Prompt: (lldb) _
68+
# Column: 1....6.8
69+
self.child.expect(re.escape("\x1b[31m(lldb) \x1b[0m\x1b[8G"))
70+
71+
@skipIfAsan
72+
@skipIfEditlineSupportMissing
73+
def test_prompt_no_color(self):
74+
"""Test that prompt-ansi-prefix doesn't color the prompt when colors are off."""
75+
self.launch(use_colors=False)
76+
self.child.send('settings set prompt-ansi-prefix "${ansi.fg.red}"\n')
77+
# Send foo so we can match the newline before the prompt and the foo
78+
# after the prompt.
79+
self.child.send("foo")
80+
# Check that there are no escape codes.
81+
self.child.expect(re.escape("\n(lldb) foo"))

0 commit comments

Comments
 (0)