[Lldb-commits] [lldb] [lldb] Highlight matching keywords in apropos output (PR #194997)
via lldb-commits
lldb-commits at lists.llvm.org
Wed Apr 29 20:21:41 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Jonas Devlieghere (JDevlieghere)
<details>
<summary>Changes</summary>
When color is enabled, `apropos` now highlights occurrences of the search term in both command names/help text and settings descriptions using the configurable regex match ANSI settings.
Implements #<!-- -->194877
---
Full diff: https://github.com/llvm/llvm-project/pull/194997.diff
9 Files Affected:
- (modified) lldb/include/lldb/Interpreter/CommandInterpreter.h (+8-6)
- (modified) lldb/include/lldb/Interpreter/Property.h (+6-3)
- (modified) lldb/include/lldb/Utility/Stream.h (+3-2)
- (modified) lldb/source/Commands/CommandObjectApropos.cpp (+14-2)
- (modified) lldb/source/Interpreter/CommandInterpreter.cpp (+11-11)
- (modified) lldb/source/Interpreter/Property.cpp (+6-5)
- (modified) lldb/source/Utility/Stream.cpp (+3-1)
- (modified) lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py (+21)
- (modified) lldb/unittests/Utility/StreamTest.cpp (+18)
``````````diff
diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h
index 26e0767951e7f..9f9a165e856a0 100644
--- a/lldb/include/lldb/Interpreter/CommandInterpreter.h
+++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h
@@ -449,12 +449,14 @@ class CommandInterpreter : public Broadcaster,
void GetAliasHelp(const char *alias_name, StreamString &help_string);
- void OutputFormattedHelpText(Stream &strm, llvm::StringRef prefix,
- llvm::StringRef help_text);
-
- void OutputFormattedHelpText(Stream &stream, llvm::StringRef command_word,
- llvm::StringRef separator,
- llvm::StringRef help_text, size_t max_word_len);
+ void OutputFormattedHelpText(
+ Stream &strm, llvm::StringRef prefix, llvm::StringRef help_text,
+ std::optional<Stream::HighlightSettings> highlight = std::nullopt);
+
+ void OutputFormattedHelpText(
+ Stream &stream, llvm::StringRef command_word, llvm::StringRef separator,
+ llvm::StringRef help_text, size_t max_word_len,
+ std::optional<Stream::HighlightSettings> highlight = std::nullopt);
// this mimics OutputFormattedHelpText but it does perform a much simpler
// formatting, basically ensuring line alignment. This is only good if you
diff --git a/lldb/include/lldb/Interpreter/Property.h b/lldb/include/lldb/Interpreter/Property.h
index 74da58275dba8..6e43c990fb366 100644
--- a/lldb/include/lldb/Interpreter/Property.h
+++ b/lldb/include/lldb/Interpreter/Property.h
@@ -11,9 +11,11 @@
#include "lldb/Interpreter/OptionValue.h"
#include "lldb/Utility/Flags.h"
+#include "lldb/Utility/Stream.h"
#include "lldb/lldb-defines.h"
#include "lldb/lldb-private-types.h"
+#include <optional>
#include <string>
namespace lldb_private {
@@ -60,9 +62,10 @@ class Property {
bool DumpQualifiedName(Stream &strm) const;
- void DumpDescription(CommandInterpreter &interpreter, Stream &strm,
- uint32_t output_width,
- bool display_qualified_name) const;
+ void DumpDescription(
+ CommandInterpreter &interpreter, Stream &strm, uint32_t output_width,
+ bool display_qualified_name,
+ std::optional<Stream::HighlightSettings> highlight = std::nullopt) const;
void SetValueChangedCallback(std::function<void()> callback);
diff --git a/lldb/include/lldb/Utility/Stream.h b/lldb/include/lldb/Utility/Stream.h
index f54d1785502a8..9f2290c227ab7 100644
--- a/lldb/include/lldb/Utility/Stream.h
+++ b/lldb/include/lldb/Utility/Stream.h
@@ -38,10 +38,11 @@ class Stream {
llvm::StringRef pattern; ///< Regex pattern for highlighting.
llvm::StringRef prefix; ///< ANSI color code to start colorization.
llvm::StringRef suffix; ///< ANSI color code to end colorization.
+ bool ignore_case = false; ///< Whether to match case-insensitively.
HighlightSettings(llvm::StringRef p, llvm::StringRef pre,
- llvm::StringRef suf)
- : pattern(p), prefix(pre), suffix(suf) {}
+ llvm::StringRef suf, bool ic = false)
+ : pattern(p), prefix(pre), suffix(suf), ignore_case(ic) {}
};
/// Utility class for counting the bytes that were written to a stream in a
diff --git a/lldb/source/Commands/CommandObjectApropos.cpp b/lldb/source/Commands/CommandObjectApropos.cpp
index 7c2d3068f68d7..dc31e60f7fc2a 100644
--- a/lldb/source/Commands/CommandObjectApropos.cpp
+++ b/lldb/source/Commands/CommandObjectApropos.cpp
@@ -7,11 +7,13 @@
//===----------------------------------------------------------------------===//
#include "CommandObjectApropos.h"
+#include "lldb/Core/Debugger.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/Property.h"
#include "lldb/Utility/Args.h"
#include "lldb/Utility/StreamString.h"
+#include "llvm/Support/Regex.h"
using namespace lldb;
using namespace lldb_private;
@@ -35,6 +37,15 @@ void CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
if (!search_word.empty()) {
ReturnStatus return_status = eReturnStatusSuccessFinishNoResult;
+ std::string escaped_search_word;
+ std::optional<Stream::HighlightSettings> highlight;
+ Debugger &dbg = GetDebugger();
+ if (dbg.GetUseColor()) {
+ escaped_search_word = llvm::Regex::escape(search_word);
+ highlight.emplace(escaped_search_word, dbg.GetRegexMatchAnsiPrefix(),
+ dbg.GetRegexMatchAnsiSuffix(), true);
+ }
+
// Find all commands matching the search word.
StringList commands_found;
StringList commands_help;
@@ -54,7 +65,8 @@ void CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
for (size_t i = 0; i < commands_found.GetSize(); ++i)
m_interpreter.OutputFormattedHelpText(
result.GetOutputStream(), commands_found.GetStringAtIndex(i),
- "--", commands_help.GetStringAtIndex(i), commands_max_len);
+ "--", commands_help.GetStringAtIndex(i), commands_max_len,
+ highlight);
return_status = eReturnStatusSuccessFinishResult;
}
@@ -86,7 +98,7 @@ void CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
for (size_t i = 0; i < num_properties; ++i)
properties[i]->DumpDescription(
m_interpreter, result.GetOutputStream(), properties_max_len,
- dump_qualified_name);
+ dump_qualified_name, highlight);
return_status = eReturnStatusSuccessFinishResult;
}
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index fb3c1beb1bfea..17a71d4b41b2f 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -3100,9 +3100,9 @@ void CommandInterpreter::SetSynchronous(bool value) {
m_synchronous_execution = value;
}
-void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
- llvm::StringRef prefix,
- llvm::StringRef help_text) {
+void CommandInterpreter::OutputFormattedHelpText(
+ Stream &strm, llvm::StringRef prefix, llvm::StringRef help_text,
+ std::optional<Stream::HighlightSettings> highlight) {
const uint32_t max_columns = m_debugger.GetTerminalWidth();
size_t line_width_max = max_columns - prefix.size();
@@ -3117,7 +3117,7 @@ void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
while (!help_text.empty()) {
// Prefix the first line, indent subsequent lines to line up
if (!prefixed_yet) {
- strm << prefix;
+ strm.PutCStringColorHighlighted(prefix, highlight);
prefixed_yet = true;
} else
strm.Indent();
@@ -3135,7 +3135,7 @@ void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
// Break at whichever condition triggered first.
this_line = this_line.substr(0, std::min(first_newline, last_space));
- strm.PutCString(this_line);
+ strm.PutCStringColorHighlighted(this_line, highlight);
strm.EOL();
// Remove whitespace / newlines after breaking.
@@ -3144,15 +3144,15 @@ void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
strm.IndentLess(prefix.size());
}
-void CommandInterpreter::OutputFormattedHelpText(Stream &strm,
- llvm::StringRef word_text,
- llvm::StringRef separator,
- llvm::StringRef help_text,
- size_t max_word_len) {
+void CommandInterpreter::OutputFormattedHelpText(
+ Stream &strm, llvm::StringRef word_text, llvm::StringRef separator,
+ llvm::StringRef help_text, size_t max_word_len,
+ std::optional<Stream::HighlightSettings> highlight) {
StreamString prefix_stream;
prefix_stream.Printf(" %-*s %*s ", (int)max_word_len, word_text.data(),
(int)separator.size(), separator.data());
- OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text);
+ OutputFormattedHelpText(strm, prefix_stream.GetString(), help_text,
+ highlight);
}
void CommandInterpreter::OutputHelpText(Stream &strm, llvm::StringRef word_text,
diff --git a/lldb/source/Interpreter/Property.cpp b/lldb/source/Interpreter/Property.cpp
index 56e45363be89a..b049b765e520e 100644
--- a/lldb/source/Interpreter/Property.cpp
+++ b/lldb/source/Interpreter/Property.cpp
@@ -272,9 +272,10 @@ void Property::Dump(const ExecutionContext *exe_ctx, Stream &strm,
}
}
-void Property::DumpDescription(CommandInterpreter &interpreter, Stream &strm,
- uint32_t output_width,
- bool display_qualified_name) const {
+void Property::DumpDescription(
+ CommandInterpreter &interpreter, Stream &strm, uint32_t output_width,
+ bool display_qualified_name,
+ std::optional<Stream::HighlightSettings> highlight) const {
if (!m_value_sp)
return;
llvm::StringRef desc = GetDescription();
@@ -295,10 +296,10 @@ void Property::DumpDescription(CommandInterpreter &interpreter, Stream &strm,
StreamString qualified_name;
DumpQualifiedName(qualified_name);
interpreter.OutputFormattedHelpText(strm, qualified_name.GetString(),
- "--", desc, output_width);
+ "--", desc, output_width, highlight);
} else {
interpreter.OutputFormattedHelpText(strm, m_name, "--", desc,
- output_width);
+ output_width, highlight);
}
}
}
diff --git a/lldb/source/Utility/Stream.cpp b/lldb/source/Utility/Stream.cpp
index c37fa1e6317a1..ad3eb416f9888 100644
--- a/lldb/source/Utility/Stream.cpp
+++ b/lldb/source/Utility/Stream.cpp
@@ -79,7 +79,9 @@ void Stream::PutCStringColorHighlighted(
return;
}
- llvm::Regex reg_pattern(pattern_info->pattern);
+ llvm::Regex reg_pattern(pattern_info->pattern, pattern_info->ignore_case
+ ? llvm::Regex::IgnoreCase
+ : llvm::Regex::NoFlags);
llvm::SmallVector<llvm::StringRef, 1> matches;
llvm::StringRef remaining = text;
std::string format_str = lldb_private::ansi::FormatAnsiTerminalCodes(
diff --git a/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py b/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
index b9073348f7583..2d6a16db2f5f7 100644
--- a/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
+++ b/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
@@ -33,3 +33,24 @@ def test_apropos_with_settings_alignment(self):
self.expect_prompt()
self.quit()
+
+ @skipIfAsan
+ @skipIfEditlineSupportMissing
+ def test_apropos_highlights_matches(self):
+ """Test that apropos highlights matching keywords in output."""
+ self.launch(use_colors=True, dimensions=(100, 200))
+
+ ansi_green = "\x1b[32m"
+ ansi_reset = "\x1b[0m"
+ self.child.sendline(
+ "settings set show-regex-match-ansi-prefix ${ansi.fg.green}"
+ )
+ self.expect_prompt()
+
+ self.child.sendline("apropos disass")
+ # Check command name highlighting.
+ self.child.expect_exact(ansi_green + "disass" + ansi_reset + "emble")
+ # Check settings name highlighting.
+ self.child.expect_exact(ansi_green + "disass" + ansi_reset + "embly-format")
+ self.expect_prompt()
+ self.quit()
diff --git a/lldb/unittests/Utility/StreamTest.cpp b/lldb/unittests/Utility/StreamTest.cpp
index 7fb8bfd0ef3dd..b870a8e8a26f3 100644
--- a/lldb/unittests/Utility/StreamTest.cpp
+++ b/lldb/unittests/Utility/StreamTest.cpp
@@ -727,3 +727,21 @@ TEST_F(StreamTest, PutSLEB128) {
EXPECT_EQ("0x6533", TakeValue());
EXPECT_EQ(6U, bytes);
}
+
+TEST_F(StreamTest, PutCStringColorHighlightedCaseInsensitive) {
+ Stream::HighlightSettings settings("hello", "[", "]", true);
+ s.PutCStringColorHighlighted("Say Hello World", settings);
+ EXPECT_EQ("Say [Hello] World", TakeValue());
+}
+
+TEST_F(StreamTest, PutCStringColorHighlightedCaseSensitive) {
+ Stream::HighlightSettings settings("hello", "[", "]", false);
+ s.PutCStringColorHighlighted("Say Hello World", settings);
+ EXPECT_EQ("Say Hello World", TakeValue());
+}
+
+TEST_F(StreamTest, PutCStringColorHighlightedMultipleMatches) {
+ Stream::HighlightSettings settings("o", "[", "]", false);
+ s.PutCStringColorHighlighted("foo bar boo", settings);
+ EXPECT_EQ("f[o][o] bar b[o][o]", TakeValue());
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/194997
More information about the lldb-commits
mailing list