[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