https://github.com/DavidSpickett updated 
https://github.com/llvm/llvm-project/pull/194873

>From 4c41d422b4545a48c621a481e182babcee683e85 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Wed, 29 Apr 2026 13:11:32 +0000
Subject: [PATCH 01/10] refactor

---
 .../Interpreter/OptionValueProperties.cpp     | 39 +++++++++----------
 1 file changed, 19 insertions(+), 20 deletions(-)

diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index def6cc462f76a..1908aed85fd3d 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -466,28 +466,27 @@ void OptionValueProperties::Apropos(
     llvm::StringRef keyword,
     std::vector<const Property *> &matching_properties) const {
   const size_t num_properties = m_properties.size();
-  StreamString strm;
   for (size_t i = 0; i < num_properties; ++i) {
     const Property *property = ProtectedGetPropertyAtIndex(i);
-    if (property) {
-      const OptionValueProperties *properties =
-          property->GetValue()->GetAsProperties();
-      if (properties) {
-        properties->Apropos(keyword, matching_properties);
-      } else {
-        bool match = false;
-        llvm::StringRef name = property->GetName();
-        if (name.contains_insensitive(keyword))
-          match = true;
-        else {
-          llvm::StringRef desc = property->GetDescription();
-          if (desc.contains_insensitive(keyword))
-            match = true;
-        }
-        if (match) {
-          matching_properties.push_back(property);
-        }
-      }
+    if (!property)
+      continue;
+
+    if (const OptionValueProperties *properties =
+            property->GetValue()->GetAsProperties()) {
+      properties->Apropos(keyword, matching_properties);
+      continue;
+    }
+
+    if (llvm::StringRef name = property->GetName();
+        name.contains_insensitive(keyword)) {
+      matching_properties.push_back(property);
+      continue;
+    }
+
+    if (llvm::StringRef desc = property->GetDescription();
+        desc.contains_insensitive(keyword)) {
+      matching_properties.push_back(property);
+      continue;
     }
   }
 }

>From f3cc98341ccb4aab5ed262a02fbd4c4b07ebdbbc Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Wed, 29 Apr 2026 12:58:14 +0000
Subject: [PATCH 02/10] wip

---
 .../Interpreter/OptionValueProperties.cpp     |  7 ++++++
 .../API/commands/settings/TestSettings.py     | 24 +++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index 1908aed85fd3d..65d913137930f 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -483,6 +483,13 @@ void OptionValueProperties::Apropos(
       continue;
     }
 
+    if (StreamString qualified_name;
+        property->DumpQualifiedName(qualified_name) &&
+        qualified_name.GetString().contains_insensitive(keyword)) {
+      matching_properties.push_back(property);
+      continue;
+    }
+
     if (llvm::StringRef desc = property->GetDescription();
         desc.contains_insensitive(keyword)) {
       matching_properties.push_back(property);
diff --git a/lldb/test/API/commands/settings/TestSettings.py 
b/lldb/test/API/commands/settings/TestSettings.py
index 8410befe399a3..e3302cb9b9aa5 100644
--- a/lldb/test/API/commands/settings/TestSettings.py
+++ b/lldb/test/API/commands/settings/TestSettings.py
@@ -26,6 +26,30 @@ def 
test_apropos_should_also_search_settings_description(self):
             ],
         )
 
+    def test_apropos_should_also_search_settings_qualified_name(self):
+        """Test that 'apropos' command searches the qualified name ("a.b.c.d") 
of settings not just
+        the name ("d")."""
+
+        # 'qemu-user' is one component of the qualified name.
+        self.expect(
+            "apropos 'qemu-user'",
+            substrs=[
+                "platform.plugin.qemu-user.architecture",
+                "platform.plugin.qemu-user.emulator-args",
+            ],
+        )
+
+        # Should be able to search for strings that overlap > 1 component of 
the
+        # qualified name.
+        self.expect(
+            "apropos 'qemu-user.emulator-'",
+            substrs=[
+                "platform.plugin.qemu-user.emulator-args",
+                "platform.plugin.qemu-user.emulator-env-vars",
+                "platform.plugin.qemu-user.emulator-path",
+            ],
+        )
+
     def test_set_interpreter_repeat_prev_command(self):
         """Test the `interpreter.repeat-previous-command` setting."""
         self.build()

>From 4b8ba32c41955d84ea8aecba3b6e08305fcb9bc3 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Wed, 29 Apr 2026 14:09:50 +0000
Subject: [PATCH 03/10]  the faster way

---
 .../lldb/Interpreter/OptionValueProperties.h  |  2 +
 .../Interpreter/OptionValueProperties.cpp     | 51 +++++++++++++++----
 2 files changed, 44 insertions(+), 9 deletions(-)

diff --git a/lldb/include/lldb/Interpreter/OptionValueProperties.h 
b/lldb/include/lldb/Interpreter/OptionValueProperties.h
index 21da8e584a7b4..84118a06a61ff 100644
--- a/lldb/include/lldb/Interpreter/OptionValueProperties.h
+++ b/lldb/include/lldb/Interpreter/OptionValueProperties.h
@@ -84,6 +84,8 @@ class OptionValueProperties
     return ProtectedGetPropertyAtIndex(idx);
   }
 
+  size_t GetNumProperties() const { return m_properties.size(); }
+
   // Property can be a property path like
   // "target.process.extra-startup-command"
   virtual const Property *
diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index 65d913137930f..2248c7984ee55 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -462,6 +462,23 @@ void 
OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
   }
 }
 
+// This function flattens a nested set of properties. This is what we want for
+// search results. If we didn't do this, search results would be presented
+// split up by type of setting.
+static void
+FlattenProperties(const OptionValueProperties *properties,
+                  std::vector<const Property *> &matching_properties) {
+  size_t num_child_properties = properties->GetNumProperties();
+  for (size_t i = 0; i < num_child_properties; ++i)
+    if (auto property = properties->GetPropertyAtIndex(i)) {
+      if (auto children = property->GetValue()->GetAsProperties()) {
+        FlattenProperties(children, matching_properties);
+      } else {
+        matching_properties.push_back(property);
+      }
+    }
+}
+
 void OptionValueProperties::Apropos(
     llvm::StringRef keyword,
     std::vector<const Property *> &matching_properties) const {
@@ -471,21 +488,37 @@ void OptionValueProperties::Apropos(
     if (!property)
       continue;
 
+    // The qualified name includes the category parts. For example
+    // "platform.plugin.qemu-user.qemu-user".
+    StreamString qualified_name_strm;
+    std::optional<llvm::StringRef> qualified_name_str;
+    if (property->DumpQualifiedName(qualified_name_strm))
+      qualified_name_str = qualified_name_strm.GetString();
+
+    // Some properties are a group of other priorities.
     if (const OptionValueProperties *properties =
             property->GetValue()->GetAsProperties()) {
-      properties->Apropos(keyword, matching_properties);
-      continue;
-    }
+      // If the keyword is already in the qualified name, any nested
+      // settings would match too and we can just add them, skipping
+      // getting their qualified names too.
+      if (qualified_name_str &&
+          qualified_name_str->contains_insensitive(keyword)) {
+        FlattenProperties(properties, matching_properties);
+      } else {
+        // Search in all the nested settings.
+        properties->Apropos(keyword, matching_properties);
+      }
 
-    if (llvm::StringRef name = property->GetName();
-        name.contains_insensitive(keyword)) {
-      matching_properties.push_back(property);
       continue;
     }
 
-    if (StreamString qualified_name;
-        property->DumpQualifiedName(qualified_name) &&
-        qualified_name.GetString().contains_insensitive(keyword)) {
+    if (qualified_name_str) {
+      if (qualified_name_str->contains_insensitive(keyword)) {
+        matching_properties.push_back(property);
+        continue;
+      }
+    } else if (llvm::StringRef name = property->GetName();
+               name.contains_insensitive(keyword)) {
       matching_properties.push_back(property);
       continue;
     }

>From f9f8e8ab5c2cb960465d8d5b91fa588d7362a6a4 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Thu, 30 Apr 2026 12:06:35 +0000
Subject: [PATCH 04/10] Revert " the faster way"

This reverts commit 81a21838ec9d4512e4a3518919414df62fba080b.
---
 .../lldb/Interpreter/OptionValueProperties.h  |  2 -
 .../Interpreter/OptionValueProperties.cpp     | 51 ++++---------------
 2 files changed, 9 insertions(+), 44 deletions(-)

diff --git a/lldb/include/lldb/Interpreter/OptionValueProperties.h 
b/lldb/include/lldb/Interpreter/OptionValueProperties.h
index 84118a06a61ff..21da8e584a7b4 100644
--- a/lldb/include/lldb/Interpreter/OptionValueProperties.h
+++ b/lldb/include/lldb/Interpreter/OptionValueProperties.h
@@ -84,8 +84,6 @@ class OptionValueProperties
     return ProtectedGetPropertyAtIndex(idx);
   }
 
-  size_t GetNumProperties() const { return m_properties.size(); }
-
   // Property can be a property path like
   // "target.process.extra-startup-command"
   virtual const Property *
diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index 2248c7984ee55..65d913137930f 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -462,23 +462,6 @@ void 
OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
   }
 }
 
-// This function flattens a nested set of properties. This is what we want for
-// search results. If we didn't do this, search results would be presented
-// split up by type of setting.
-static void
-FlattenProperties(const OptionValueProperties *properties,
-                  std::vector<const Property *> &matching_properties) {
-  size_t num_child_properties = properties->GetNumProperties();
-  for (size_t i = 0; i < num_child_properties; ++i)
-    if (auto property = properties->GetPropertyAtIndex(i)) {
-      if (auto children = property->GetValue()->GetAsProperties()) {
-        FlattenProperties(children, matching_properties);
-      } else {
-        matching_properties.push_back(property);
-      }
-    }
-}
-
 void OptionValueProperties::Apropos(
     llvm::StringRef keyword,
     std::vector<const Property *> &matching_properties) const {
@@ -488,37 +471,21 @@ void OptionValueProperties::Apropos(
     if (!property)
       continue;
 
-    // The qualified name includes the category parts. For example
-    // "platform.plugin.qemu-user.qemu-user".
-    StreamString qualified_name_strm;
-    std::optional<llvm::StringRef> qualified_name_str;
-    if (property->DumpQualifiedName(qualified_name_strm))
-      qualified_name_str = qualified_name_strm.GetString();
-
-    // Some properties are a group of other priorities.
     if (const OptionValueProperties *properties =
             property->GetValue()->GetAsProperties()) {
-      // If the keyword is already in the qualified name, any nested
-      // settings would match too and we can just add them, skipping
-      // getting their qualified names too.
-      if (qualified_name_str &&
-          qualified_name_str->contains_insensitive(keyword)) {
-        FlattenProperties(properties, matching_properties);
-      } else {
-        // Search in all the nested settings.
-        properties->Apropos(keyword, matching_properties);
-      }
+      properties->Apropos(keyword, matching_properties);
+      continue;
+    }
 
+    if (llvm::StringRef name = property->GetName();
+        name.contains_insensitive(keyword)) {
+      matching_properties.push_back(property);
       continue;
     }
 
-    if (qualified_name_str) {
-      if (qualified_name_str->contains_insensitive(keyword)) {
-        matching_properties.push_back(property);
-        continue;
-      }
-    } else if (llvm::StringRef name = property->GetName();
-               name.contains_insensitive(keyword)) {
+    if (StreamString qualified_name;
+        property->DumpQualifiedName(qualified_name) &&
+        qualified_name.GetString().contains_insensitive(keyword)) {
       matching_properties.push_back(property);
       continue;
     }

>From 65118916073ae9a431e87702545b636fa31315ea Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Thu, 30 Apr 2026 12:06:44 +0000
Subject: [PATCH 05/10] get property and property nodes into 2 lists

---
 .../lldb/Core/UserSettingsController.h        |  5 ++-
 .../lldb/Interpreter/OptionValueProperties.h  |  3 +-
 lldb/source/Commands/CommandObjectApropos.cpp |  8 ++--
 lldb/source/Core/UserSettingsController.cpp   | 10 ++---
 .../Interpreter/OptionValueProperties.cpp     | 42 +++++++++++--------
 5 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/lldb/include/lldb/Core/UserSettingsController.h 
b/lldb/include/lldb/Core/UserSettingsController.h
index 29e892fdba45b..a1ba2d32915c8 100644
--- a/lldb/include/lldb/Core/UserSettingsController.h
+++ b/lldb/include/lldb/Core/UserSettingsController.h
@@ -62,8 +62,9 @@ class Properties {
   virtual void DumpAllDescriptions(CommandInterpreter &interpreter,
                                    Stream &strm) const;
 
-  size_t Apropos(llvm::StringRef keyword,
-                 std::vector<const Property *> &matching_properties) const;
+  void Apropos(llvm::StringRef keyword,
+               std::vector<const Property *> &matching_properties,
+               std::vector<const Property *> &matching_property_prefixes) 
const;
 
   // We sometimes need to introduce a setting to enable experimental features,
   // but then we don't want the setting for these to cause errors when the
diff --git a/lldb/include/lldb/Interpreter/OptionValueProperties.h 
b/lldb/include/lldb/Interpreter/OptionValueProperties.h
index 21da8e584a7b4..d9f74c802c433 100644
--- a/lldb/include/lldb/Interpreter/OptionValueProperties.h
+++ b/lldb/include/lldb/Interpreter/OptionValueProperties.h
@@ -58,7 +58,8 @@ class OptionValueProperties
                                    Stream &strm) const;
 
   void Apropos(llvm::StringRef keyword,
-               std::vector<const Property *> &matching_properties) const;
+               std::vector<const Property *> &matching_properties,
+               std::vector<const Property *> &matching_property_prefixes) 
const;
 
   void Initialize(const PropertyCollectionDefinition &setting_definitions);
 
diff --git a/lldb/source/Commands/CommandObjectApropos.cpp 
b/lldb/source/Commands/CommandObjectApropos.cpp
index dc31e60f7fc2a..0159c502b2c14 100644
--- a/lldb/source/Commands/CommandObjectApropos.cpp
+++ b/lldb/source/Commands/CommandObjectApropos.cpp
@@ -73,8 +73,8 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
       // Find all the properties matching the search word.
       size_t properties_max_len = 0;
       std::vector<const Property *> properties;
-      const size_t num_properties =
-          GetDebugger().Apropos(search_word, properties);
+      std::vector<const Property *> property_prefixes;
+      GetDebugger().Apropos(search_word, properties, property_prefixes);
       for (const Property *prop : properties) {
         StreamString qualified_name;
         prop->DumpQualifiedName(qualified_name);
@@ -82,7 +82,7 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
             std::max(properties_max_len, qualified_name.GetString().size());
       }
 
-      if (num_properties == 0) {
+      if (properties.size() == 0) {
         result.AppendMessageWithFormatv(
             "No settings found pertaining to '{0}'. "
             "Try 'settings show' to see a complete list of "
@@ -95,7 +95,7 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
             search_word);
 
         const bool dump_qualified_name = true;
-        for (size_t i = 0; i < num_properties; ++i)
+        for (size_t i = 0; i < properties.size(); ++i)
           properties[i]->DumpDescription(
               m_interpreter, result.GetOutputStream(), properties_max_len,
               dump_qualified_name, highlight);
diff --git a/lldb/source/Core/UserSettingsController.cpp 
b/lldb/source/Core/UserSettingsController.cpp
index 206b2072ddaf2..c7c33b947d1ab 100644
--- a/lldb/source/Core/UserSettingsController.cpp
+++ b/lldb/source/Core/UserSettingsController.cpp
@@ -75,11 +75,11 @@ Status Properties::DumpPropertyValue(const ExecutionContext 
*exe_ctx,
                                             dump_mask, is_json);
 }
 
-size_t
-Properties::Apropos(llvm::StringRef keyword,
-                    std::vector<const Property *> &matching_properties) const {
-  m_collection_sp->Apropos(keyword, matching_properties);
-  return matching_properties.size();
+void Properties::Apropos(
+    llvm::StringRef keyword, std::vector<const Property *> 
&matching_properties,
+    std::vector<const Property *> &matching_property_prefixes) const {
+  m_collection_sp->Apropos(keyword, matching_properties,
+                           matching_property_prefixes);
 }
 
 llvm::StringRef Properties::GetExperimentalSettingsName() {
diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index 65d913137930f..df3f2218284f2 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -463,37 +463,43 @@ void 
OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
 }
 
 void OptionValueProperties::Apropos(
-    llvm::StringRef keyword,
-    std::vector<const Property *> &matching_properties) const {
+    llvm::StringRef keyword, std::vector<const Property *> 
&matching_properties,
+    std::vector<const Property *> &matching_property_prefixes) const {
   const size_t num_properties = m_properties.size();
   for (size_t i = 0; i < num_properties; ++i) {
     const Property *property = ProtectedGetPropertyAtIndex(i);
     if (!property)
       continue;
 
-    if (const OptionValueProperties *properties =
-            property->GetValue()->GetAsProperties()) {
-      properties->Apropos(keyword, matching_properties);
-      continue;
-    }
+    const OptionValueProperties *properties =
+        property->GetValue()->GetAsProperties();
+    if (properties)
+      properties->Apropos(keyword, matching_properties,
+                          matching_property_prefixes);
 
-    if (llvm::StringRef name = property->GetName();
-        name.contains_insensitive(keyword)) {
-      matching_properties.push_back(property);
-      continue;
-    }
+    bool matched = false;
 
+    // TODO: do we need this at all? It allows you to look for a.b?
     if (StreamString qualified_name;
         property->DumpQualifiedName(qualified_name) &&
-        qualified_name.GetString().contains_insensitive(keyword)) {
-      matching_properties.push_back(property);
-      continue;
-    }
+        qualified_name.GetString().contains_insensitive(keyword))
+      matched = true;
+
+    if (llvm::StringRef name = property->GetName();
+        !matched && name.contains_insensitive(keyword))
+      matched = true;
 
     if (llvm::StringRef desc = property->GetDescription();
-        desc.contains_insensitive(keyword)) {
-      matching_properties.push_back(property);
+        !matched && desc.contains_insensitive(keyword))
+      matched = true;
+
+    if (!matched)
       continue;
+
+    if (properties) {
+      matching_property_prefixes.push_back(property);
+    } else {
+      matching_properties.push_back(property);
     }
   }
 }

>From 7bf846b8cfdcb345f3b9ff2f03dd8f45583f77cb Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Thu, 30 Apr 2026 13:09:32 +0000
Subject: [PATCH 06/10] print property prefixes

---
 lldb/source/Commands/CommandObjectApropos.cpp | 44 ++++++++++++++-----
 .../Interpreter/OptionValueProperties.cpp     |  6 ---
 lldb/source/Interpreter/Property.cpp          | 10 +++--
 .../API/commands/settings/TestSettings.py     | 22 +++-------
 4 files changed, 45 insertions(+), 37 deletions(-)

diff --git a/lldb/source/Commands/CommandObjectApropos.cpp 
b/lldb/source/Commands/CommandObjectApropos.cpp
index 0159c502b2c14..8c594893c06e7 100644
--- a/lldb/source/Commands/CommandObjectApropos.cpp
+++ b/lldb/source/Commands/CommandObjectApropos.cpp
@@ -82,7 +82,7 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
             std::max(properties_max_len, qualified_name.GetString().size());
       }
 
-      if (properties.size() == 0) {
+      if (properties.empty() && property_prefixes.empty()) {
         result.AppendMessageWithFormatv(
             "No settings found pertaining to '{0}'. "
             "Try 'settings show' to see a complete list of "
@@ -90,18 +90,40 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
             args[0].c_str());
 
       } else {
-        result.AppendMessageWithFormatv(
-            "\nThe following settings variables may relate to '{0}': \n\n",
-            search_word);
-
-        const bool dump_qualified_name = true;
-        for (size_t i = 0; i < properties.size(); ++i)
-          properties[i]->DumpDescription(
-              m_interpreter, result.GetOutputStream(), properties_max_len,
-              dump_qualified_name, highlight);
         return_status = eReturnStatusSuccessFinishResult;
-      }
 
+        if (!property_prefixes.empty()) {
+          result.AppendMessageWithFormatv(
+              "\nThe following settings prefixes may relate to '{0}': \n\n",
+              search_word);
+
+          auto &out_strm = result.GetOutputStream();
+          out_strm.IndentMore();
+          for (auto prefix : property_prefixes) {
+            StreamString qual_name_strm;
+            // TODO: highlight!!!!
+            if (prefix->DumpQualifiedName(qual_name_strm)) {
+              result.GetOutputStream().Indent();
+              result.GetOutputStream() << qual_name_strm.GetString() << '\n';
+            }
+          }
+          out_strm.IndentLess();
+
+          result.AppendMessageWithFormatv(
+              "\n(use 'settings list' to show settings with a given prefix)");
+        }
+
+        if (!properties.empty()) {
+          result.AppendMessageWithFormatv(
+              "\nThe following settings variables may relate to '{0}': \n\n",
+              search_word);
+
+          const bool dump_qualified_name = true;
+          for (auto property : properties)
+            property->DumpDescription(m_interpreter, result.GetOutputStream(),
+                                      properties_max_len, dump_qualified_name, 
highlight);
+        }
+      }
       result.SetStatus(return_status);
     } else {
       result.AppendError("'' is not a valid search word.\n");
diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index df3f2218284f2..3b0cdd453b0bc 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -479,12 +479,6 @@ void OptionValueProperties::Apropos(
 
     bool matched = false;
 
-    // TODO: do we need this at all? It allows you to look for a.b?
-    if (StreamString qualified_name;
-        property->DumpQualifiedName(qualified_name) &&
-        qualified_name.GetString().contains_insensitive(keyword))
-      matched = true;
-
     if (llvm::StringRef name = property->GetName();
         !matched && name.contains_insensitive(keyword))
       matched = true;
diff --git a/lldb/source/Interpreter/Property.cpp 
b/lldb/source/Interpreter/Property.cpp
index b049b765e520e..7684e193106da 100644
--- a/lldb/source/Interpreter/Property.cpp
+++ b/lldb/source/Interpreter/Property.cpp
@@ -236,9 +236,13 @@ Property::Property(llvm::StringRef name, llvm::StringRef 
desc, bool is_global,
 
 bool Property::DumpQualifiedName(Stream &strm) const {
   if (!m_name.empty()) {
-    if (m_value_sp->DumpQualifiedName(strm))
-      strm.PutChar('.');
-    strm << m_name;
+    bool has_sub_properties = static_cast<bool>(m_value_sp->GetAsProperties());
+    bool dumped_something = m_value_sp->DumpQualifiedName(strm);
+    if (!has_sub_properties) {
+      if (dumped_something)
+        strm.PutChar('.');
+      strm << m_name;
+    }
     return true;
   }
   return false;
diff --git a/lldb/test/API/commands/settings/TestSettings.py 
b/lldb/test/API/commands/settings/TestSettings.py
index e3302cb9b9aa5..5035a3f23632d 100644
--- a/lldb/test/API/commands/settings/TestSettings.py
+++ b/lldb/test/API/commands/settings/TestSettings.py
@@ -26,27 +26,15 @@ def 
test_apropos_should_also_search_settings_description(self):
             ],
         )
 
-    def test_apropos_should_also_search_settings_qualified_name(self):
-        """Test that 'apropos' command searches the qualified name ("a.b.c.d") 
of settings not just
-        the name ("d")."""
+    def test_apropos_searches_settings_prefixes(self):
+        """Test that 'apropos' command searches the prefixes of the qualified 
names."""
 
-        # 'qemu-user' is one component of the qualified name.
         self.expect(
             "apropos 'qemu-user'",
             substrs=[
-                "platform.plugin.qemu-user.architecture",
-                "platform.plugin.qemu-user.emulator-args",
-            ],
-        )
-
-        # Should be able to search for strings that overlap > 1 component of 
the
-        # qualified name.
-        self.expect(
-            "apropos 'qemu-user.emulator-'",
-            substrs=[
-                "platform.plugin.qemu-user.emulator-args",
-                "platform.plugin.qemu-user.emulator-env-vars",
-                "platform.plugin.qemu-user.emulator-path",
+                "The following settings prefixes may relate to 'qemu-user':\n"
+                "  platform.plugin.qemu-user\n"
+                "\n"
             ],
         )
 

>From ca730e299c1904dc323c5392d5d39e76aba9f284 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Tue, 5 May 2026 10:04:27 +0000
Subject: [PATCH 07/10] updates

---
 .../lldb/Core/UserSettingsController.h        |  2 +-
 lldb/include/lldb/Interpreter/OptionValue.h   |  4 ++-
 .../lldb/Interpreter/OptionValueProperties.h  |  2 +-
 lldb/include/lldb/Interpreter/Property.h      |  4 ++-
 lldb/source/Commands/CommandObjectApropos.cpp | 22 ++++++-------
 lldb/source/Core/UserSettingsController.cpp   |  4 +--
 lldb/source/Interpreter/OptionValue.cpp       |  7 +++--
 .../Interpreter/OptionValueProperties.cpp     |  6 ++--
 lldb/source/Interpreter/Property.cpp          |  7 +++--
 .../formatting/TestAproposFormatting.py       | 31 ++++++++++++++++---
 .../API/commands/settings/TestSettings.py     |  7 +++--
 11 files changed, 62 insertions(+), 34 deletions(-)

diff --git a/lldb/include/lldb/Core/UserSettingsController.h 
b/lldb/include/lldb/Core/UserSettingsController.h
index a1ba2d32915c8..aa1040dd2eacf 100644
--- a/lldb/include/lldb/Core/UserSettingsController.h
+++ b/lldb/include/lldb/Core/UserSettingsController.h
@@ -64,7 +64,7 @@ class Properties {
 
   void Apropos(llvm::StringRef keyword,
                std::vector<const Property *> &matching_properties,
-               std::vector<const Property *> &matching_property_prefixes) 
const;
+               std::vector<const Property *> &matching_property_paths) const;
 
   // We sometimes need to introduce a setting to enable experimental features,
   // but then we don't want the setting for these to cause errors when the
diff --git a/lldb/include/lldb/Interpreter/OptionValue.h 
b/lldb/include/lldb/Interpreter/OptionValue.h
index 9c992821251cb..b2b5d6bf6aeea 100644
--- a/lldb/include/lldb/Interpreter/OptionValue.h
+++ b/lldb/include/lldb/Interpreter/OptionValue.h
@@ -126,7 +126,9 @@ class OptionValue {
 
   virtual llvm::StringRef GetName() const { return llvm::StringRef(); }
 
-  virtual bool DumpQualifiedName(Stream &strm) const;
+  virtual bool DumpQualifiedName(
+      Stream &strm,
+      std::optional<Stream::HighlightSettings> highlight = std::nullopt) const;
 
   // Subclasses should NOT override these functions as they use the above
   // functions to implement functionality
diff --git a/lldb/include/lldb/Interpreter/OptionValueProperties.h 
b/lldb/include/lldb/Interpreter/OptionValueProperties.h
index d9f74c802c433..9ef4e670f95cd 100644
--- a/lldb/include/lldb/Interpreter/OptionValueProperties.h
+++ b/lldb/include/lldb/Interpreter/OptionValueProperties.h
@@ -59,7 +59,7 @@ class OptionValueProperties
 
   void Apropos(llvm::StringRef keyword,
                std::vector<const Property *> &matching_properties,
-               std::vector<const Property *> &matching_property_prefixes) 
const;
+               std::vector<const Property *> &matching_property_paths) const;
 
   void Initialize(const PropertyCollectionDefinition &setting_definitions);
 
diff --git a/lldb/include/lldb/Interpreter/Property.h 
b/lldb/include/lldb/Interpreter/Property.h
index 6e43c990fb366..e46db18a69f2b 100644
--- a/lldb/include/lldb/Interpreter/Property.h
+++ b/lldb/include/lldb/Interpreter/Property.h
@@ -60,7 +60,9 @@ class Property {
   void Dump(const ExecutionContext *exe_ctx, Stream &strm,
             uint32_t dump_mask) const;
 
-  bool DumpQualifiedName(Stream &strm) const;
+  bool DumpQualifiedName(
+      Stream &strm,
+      std::optional<Stream::HighlightSettings> highlight = std::nullopt) const;
 
   void DumpDescription(
       CommandInterpreter &interpreter, Stream &strm, uint32_t output_width,
diff --git a/lldb/source/Commands/CommandObjectApropos.cpp 
b/lldb/source/Commands/CommandObjectApropos.cpp
index 8c594893c06e7..70cd828655af8 100644
--- a/lldb/source/Commands/CommandObjectApropos.cpp
+++ b/lldb/source/Commands/CommandObjectApropos.cpp
@@ -23,7 +23,8 @@ using namespace lldb_private;
 CommandObjectApropos::CommandObjectApropos(CommandInterpreter &interpreter)
     : CommandObjectParsed(
           interpreter, "apropos",
-          "List debugger commands related to a word or subject.", nullptr) {
+          "List debugger commands and settings related to a word or subject.",
+          nullptr) {
   AddSimpleArgumentList(eArgTypeSearchWord);
 }
 
@@ -73,8 +74,8 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
       // Find all the properties matching the search word.
       size_t properties_max_len = 0;
       std::vector<const Property *> properties;
-      std::vector<const Property *> property_prefixes;
-      GetDebugger().Apropos(search_word, properties, property_prefixes);
+      std::vector<const Property *> property_paths;
+      GetDebugger().Apropos(search_word, properties, property_paths);
       for (const Property *prop : properties) {
         StreamString qualified_name;
         prop->DumpQualifiedName(qualified_name);
@@ -82,7 +83,7 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
             std::max(properties_max_len, qualified_name.GetString().size());
       }
 
-      if (properties.empty() && property_prefixes.empty()) {
+      if (properties.empty() && property_paths.empty()) {
         result.AppendMessageWithFormatv(
             "No settings found pertaining to '{0}'. "
             "Try 'settings show' to see a complete list of "
@@ -92,25 +93,24 @@ void CommandObjectApropos::DoExecute(Args &args, 
CommandReturnObject &result) {
       } else {
         return_status = eReturnStatusSuccessFinishResult;
 
-        if (!property_prefixes.empty()) {
+        if (!property_paths.empty()) {
           result.AppendMessageWithFormatv(
-              "\nThe following settings prefixes may relate to '{0}': \n\n",
+              "\nThe following settings paths may relate to '{0}': \n\n",
               search_word);
 
           auto &out_strm = result.GetOutputStream();
           out_strm.IndentMore();
-          for (auto prefix : property_prefixes) {
+          for (auto path : property_paths) {
             StreamString qual_name_strm;
-            // TODO: highlight!!!!
-            if (prefix->DumpQualifiedName(qual_name_strm)) {
+            if (path->DumpQualifiedName(qual_name_strm, highlight)) {
               result.GetOutputStream().Indent();
               result.GetOutputStream() << qual_name_strm.GetString() << '\n';
             }
           }
           out_strm.IndentLess();
 
-          result.AppendMessageWithFormatv(
-              "\n(use 'settings list' to show settings with a given prefix)");
+          result.AppendMessageWithFormatv("\n(use 'settings list <path>' to "
+                                          "show settings with a given path)");
         }
 
         if (!properties.empty()) {
diff --git a/lldb/source/Core/UserSettingsController.cpp 
b/lldb/source/Core/UserSettingsController.cpp
index c7c33b947d1ab..3806cbbc9d84e 100644
--- a/lldb/source/Core/UserSettingsController.cpp
+++ b/lldb/source/Core/UserSettingsController.cpp
@@ -77,9 +77,9 @@ Status Properties::DumpPropertyValue(const ExecutionContext 
*exe_ctx,
 
 void Properties::Apropos(
     llvm::StringRef keyword, std::vector<const Property *> 
&matching_properties,
-    std::vector<const Property *> &matching_property_prefixes) const {
+    std::vector<const Property *> &matching_property_paths) const {
   m_collection_sp->Apropos(keyword, matching_properties,
-                           matching_property_prefixes);
+                           matching_property_paths);
 }
 
 llvm::StringRef Properties::GetExperimentalSettingsName() {
diff --git a/lldb/source/Interpreter/OptionValue.cpp 
b/lldb/source/Interpreter/OptionValue.cpp
index aa118107f27c5..3ae4d7781e192 100644
--- a/lldb/source/Interpreter/OptionValue.cpp
+++ b/lldb/source/Interpreter/OptionValue.cpp
@@ -579,11 +579,12 @@ lldb::OptionValueSP 
OptionValue::CreateValueFromCStringForTypeMask(
   return value_sp;
 }
 
-bool OptionValue::DumpQualifiedName(Stream &strm) const {
+bool OptionValue::DumpQualifiedName(
+    Stream &strm, std::optional<Stream::HighlightSettings> highlight) const {
   bool dumped_something = false;
   lldb::OptionValueSP m_parent_sp(m_parent_wp.lock());
   if (m_parent_sp) {
-    if (m_parent_sp->DumpQualifiedName(strm))
+    if (m_parent_sp->DumpQualifiedName(strm, highlight))
       dumped_something = true;
   }
   llvm::StringRef name(GetName());
@@ -592,7 +593,7 @@ bool OptionValue::DumpQualifiedName(Stream &strm) const {
       strm.PutChar('.');
     else
       dumped_something = true;
-    strm << name;
+    strm.PutCStringColorHighlighted(name, highlight);
   }
   return dumped_something;
 }
diff --git a/lldb/source/Interpreter/OptionValueProperties.cpp 
b/lldb/source/Interpreter/OptionValueProperties.cpp
index 3b0cdd453b0bc..bdd96548a0e14 100644
--- a/lldb/source/Interpreter/OptionValueProperties.cpp
+++ b/lldb/source/Interpreter/OptionValueProperties.cpp
@@ -464,7 +464,7 @@ void 
OptionValueProperties::DumpAllDescriptions(CommandInterpreter &interpreter,
 
 void OptionValueProperties::Apropos(
     llvm::StringRef keyword, std::vector<const Property *> 
&matching_properties,
-    std::vector<const Property *> &matching_property_prefixes) const {
+    std::vector<const Property *> &matching_property_paths) const {
   const size_t num_properties = m_properties.size();
   for (size_t i = 0; i < num_properties; ++i) {
     const Property *property = ProtectedGetPropertyAtIndex(i);
@@ -475,7 +475,7 @@ void OptionValueProperties::Apropos(
         property->GetValue()->GetAsProperties();
     if (properties)
       properties->Apropos(keyword, matching_properties,
-                          matching_property_prefixes);
+                          matching_property_paths);
 
     bool matched = false;
 
@@ -491,7 +491,7 @@ void OptionValueProperties::Apropos(
       continue;
 
     if (properties) {
-      matching_property_prefixes.push_back(property);
+      matching_property_paths.push_back(property);
     } else {
       matching_properties.push_back(property);
     }
diff --git a/lldb/source/Interpreter/Property.cpp 
b/lldb/source/Interpreter/Property.cpp
index 7684e193106da..3fb64335dd9ff 100644
--- a/lldb/source/Interpreter/Property.cpp
+++ b/lldb/source/Interpreter/Property.cpp
@@ -234,14 +234,15 @@ Property::Property(llvm::StringRef name, llvm::StringRef 
desc, bool is_global,
     : m_name(name), m_description(desc), m_value_sp(value_sp),
       m_is_global(is_global) {}
 
-bool Property::DumpQualifiedName(Stream &strm) const {
+bool Property::DumpQualifiedName(
+    Stream &strm, std::optional<Stream::HighlightSettings> highlight) const {
   if (!m_name.empty()) {
     bool has_sub_properties = static_cast<bool>(m_value_sp->GetAsProperties());
-    bool dumped_something = m_value_sp->DumpQualifiedName(strm);
+    bool dumped_something = m_value_sp->DumpQualifiedName(strm, highlight);
     if (!has_sub_properties) {
       if (dumped_something)
         strm.PutChar('.');
-      strm << m_name;
+      strm.PutCStringColorHighlighted(m_name, highlight);
     }
     return true;
   }
diff --git a/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py 
b/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
index 73dad81c958a9..cfeb75a941bfe 100644
--- a/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
+++ b/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
@@ -47,12 +47,33 @@ def test_apropos_highlights_matches(self):
         )
         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.child.sendline("apropos plug")
+        # Apropos should highlight in:
+        # Command names.
+        self.child.expect_exact(ansi_green + "plug" + ansi_reset + "in list")
+        # Command descriptions.
+        self.child.expect_exact(
+            "Report info about registered LLDB "
+            + ansi_green
+            + "plug"
+            + ansi_reset
+            + "ins."
+        )
+        # Settings paths.
+        self.child.expect_exact(
+            "platform." + ansi_green + "plug" + ansi_reset + 
"in.remote-android"
+        )
+        # Settings names.
+        self.child.expect_exact(
+            ansi_green
+            + "plug"
+            + ansi_reset
+            + "in.structured-data.darwin-log.auto-enable-options"
+        )
+        # Settings descriptions.
+        self.child.expect_exact("'" + ansi_green + "plug" + ansi_reset + "in")
         self.expect_prompt()
+
         self.quit()
 
     @skipIfAsan
diff --git a/lldb/test/API/commands/settings/TestSettings.py 
b/lldb/test/API/commands/settings/TestSettings.py
index 5035a3f23632d..02da8646349ba 100644
--- a/lldb/test/API/commands/settings/TestSettings.py
+++ b/lldb/test/API/commands/settings/TestSettings.py
@@ -26,13 +26,14 @@ def 
test_apropos_should_also_search_settings_description(self):
             ],
         )
 
-    def test_apropos_searches_settings_prefixes(self):
-        """Test that 'apropos' command searches the prefixes of the qualified 
names."""
+    def test_apropos_searches_settings_paths(self):
+        """Test that 'apropos' command searches the components of the paths of
+        settings."""
 
         self.expect(
             "apropos 'qemu-user'",
             substrs=[
-                "The following settings prefixes may relate to 'qemu-user':\n"
+                "The following settings paths may relate to 'qemu-user':\n"
                 "  platform.plugin.qemu-user\n"
                 "\n"
             ],

>From 65a480b278b035865c7fe66249fbfe695eaeeb44 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Tue, 5 May 2026 12:43:34 +0000
Subject: [PATCH 08/10] fix test

---
 lldb/test/API/python_api/file_handle/TestFileHandle.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lldb/test/API/python_api/file_handle/TestFileHandle.py 
b/lldb/test/API/python_api/file_handle/TestFileHandle.py
index eba758f5a5d28..a0e66199d3f54 100644
--- a/lldb/test/API/python_api/file_handle/TestFileHandle.py
+++ b/lldb/test/API/python_api/file_handle/TestFileHandle.py
@@ -869,4 +869,6 @@ def test_set_sbstream(self):
         with open(self.out_filename, "r") as f:
             output = f.read()
             self.assertIn("Show a list of all debugger commands", output)
-            self.assertIn("List debugger commands related to a word", output)
+            self.assertIn(
+                "List debugger commands and settings related to a word", output
+            )

>From 7c425a7f03c7ee6360bcd45223fbbf84db98735e Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Tue, 5 May 2026 12:45:58 +0000
Subject: [PATCH 09/10] stray newline

---
 .../API/commands/apropos/formatting/TestAproposFormatting.py     | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py 
b/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
index cfeb75a941bfe..b9a8f52dbdc16 100644
--- a/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
+++ b/lldb/test/API/commands/apropos/formatting/TestAproposFormatting.py
@@ -73,7 +73,6 @@ def test_apropos_highlights_matches(self):
         # Settings descriptions.
         self.child.expect_exact("'" + ansi_green + "plug" + ansi_reset + "in")
         self.expect_prompt()
-
         self.quit()
 
     @skipIfAsan

>From ff740e629c26c43464029d748a0b546d5c0e5362 Mon Sep 17 00:00:00 2001
From: David Spickett <[email protected]>
Date: Tue, 5 May 2026 12:49:37 +0000
Subject: [PATCH 10/10] release notes

---
 llvm/docs/ReleaseNotes.md | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md
index f315cb4dbaa0f..0fce0350f1d90 100644
--- a/llvm/docs/ReleaseNotes.md
+++ b/llvm/docs/ReleaseNotes.md
@@ -243,7 +243,10 @@ Makes programs 10x faster by doing Special New Thing.
 * Breakpoint commands now accept `.` to refer to the location(s) at which the 
current thread is stopped. For
   example, `breakpoint disable .` disables the just-hit breakpoint location. 
Another usage is to automate a
   command to run at the current location: `breakpoint command add -o 'p 
my_var' .`.
-* The `apropos` command now highlights matching keywords in its output when 
color is enabled.
+* The `apropos` command now:
+  * Highlights matching keywords in its output when color is enabled.
+  * Searches the components of settings paths. For example `apropos qemu-user` 
will now
+    show `platform.plugin.qemu-user` as one of the results.
 
 #### Deprecated APIs
 

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to