Files
David Spickett e15ce19f34 [LLDB] Include components of settings paths in apropos search (#194873)
Fixes #188479.

`apropos` was previously only looking at the "name" of the setting,
which is the final part of what I as a user think of as the "name" as in
the "platform.plugin.something.something-else" form. That that form is
in fact the "qualified name".

In this change, I've extended apropos to look in the paths of settings,
that is the part of the settings name isn't the final name. For example,
"qemu-user" will now show a "path" as a result, which you can then look
up with "settings list".

(the alternative was to apply the search to the qualified name of all
settings, but this would result in a lot of results for top level things
like "platform")

This is the new behaviour:
```
(lldb) apropos qemu-user
No commands found pertaining to 'qemu-user'. Try 'help' to see a complete list of debugger commands.

The following settings paths may relate to 'qemu-user':
  platform.plugin.qemu-user

(use 'settings list <path>' to show settings with a given path)
```
Then users can do:
```
(lldb) settings list platform.plugin.qemu-user

'platform.plugin.qemu-user' variables:
<...>
```
And this is what it looks like for a more common term:
```
(lldb) apropos platform
The following commands may relate to 'platform':
  platform                -- Commands to manage and create platforms.
<...>
The following settings paths may relate to 'platform':
  platform.plugin.remote-android
<...>
(use 'settings list <path>' to show settings with a given path)

The following settings variables may relate to 'platform':
  target.auto-install-main-executable        -- Always install the main executable when connected to a
                                                remote platform.
<...>
```
It does not list any `platform.<...>` in the "The following settings"
section. The results for settings names are the same as before.

Along the way, I fixed a bug in DumpQualifiedName where properties that
were groups of other properties would end up putting their name on the
end of the qualified name twice.
2026-05-18 09:54:14 +01:00

136 lines
5.1 KiB
C++

//===-- CommandObjectApropos.cpp ------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#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;
// CommandObjectApropos
CommandObjectApropos::CommandObjectApropos(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "apropos",
"List debugger commands and settings related to a word or subject.",
nullptr) {
AddSimpleArgumentList(eArgTypeSearchWord);
}
CommandObjectApropos::~CommandObjectApropos() = default;
void CommandObjectApropos::DoExecute(Args &args, CommandReturnObject &result) {
const size_t argc = args.GetArgumentCount();
if (argc == 1) {
auto search_word = args[0].ref();
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;
m_interpreter.FindCommandsForApropos(
search_word, commands_found, commands_help, true, true, true, true);
if (commands_found.GetSize() == 0) {
result.AppendMessageWithFormatv(
"No commands found pertaining to '{0}'. "
"Try 'help' to see a complete list of "
"debugger commands.",
args[0].c_str());
} else {
result.AppendMessageWithFormatv(
"The following commands may relate to '{0}':", args[0].c_str());
const size_t commands_max_len = commands_found.GetMaxStringLength();
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,
highlight);
return_status = eReturnStatusSuccessFinishResult;
}
// Find all the properties matching the search word.
size_t properties_max_len = 0;
std::vector<const Property *> properties;
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);
properties_max_len =
std::max(properties_max_len, qualified_name.GetString().size());
}
if (properties.empty() && property_paths.empty()) {
result.AppendMessageWithFormatv(
"No settings found pertaining to '{0}'. "
"Try 'settings show' to see a complete list of "
"debugger settings.",
args[0].c_str());
} else {
return_status = eReturnStatusSuccessFinishResult;
if (!property_paths.empty()) {
result.AppendMessageWithFormatv(
"\nThe following settings paths may relate to '{0}': \n\n",
search_word);
auto &out_strm = result.GetOutputStream();
out_strm.IndentMore();
for (auto path : property_paths) {
StreamString 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 <path>' to "
"show settings with a given path)");
}
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");
}
} else {
result.AppendError("'apropos' must be called with exactly one argument.\n");
}
}