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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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/11] 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 >From c5f761db3021accae2c52fe009190e40c4b0f15f Mon Sep 17 00:00:00 2001 From: David Spickett <[email protected]> Date: Tue, 5 May 2026 12:52:05 +0000 Subject: [PATCH 11/11] formatting --- lldb/source/Commands/CommandObjectApropos.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/source/Commands/CommandObjectApropos.cpp b/lldb/source/Commands/CommandObjectApropos.cpp index 70cd828655af8..5f8719f6b5b8f 100644 --- a/lldb/source/Commands/CommandObjectApropos.cpp +++ b/lldb/source/Commands/CommandObjectApropos.cpp @@ -121,7 +121,8 @@ void CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) { const bool dump_qualified_name = true; for (auto property : properties) property->DumpDescription(m_interpreter, result.GetOutputStream(), - properties_max_len, dump_qualified_name, highlight); + properties_max_len, dump_qualified_name, + highlight); } } result.SetStatus(return_status); _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
