Path: blob/main/contrib/llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp
39587 views
//===-- CommandObjectMultiword.cpp ----------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Interpreter/CommandObjectMultiword.h"9#include "lldb/Interpreter/CommandInterpreter.h"10#include "lldb/Interpreter/CommandReturnObject.h"11#include "lldb/Interpreter/Options.h"12#include <optional>1314using namespace lldb;15using namespace lldb_private;1617// CommandObjectMultiword1819CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,20const char *name,21const char *help,22const char *syntax,23uint32_t flags)24: CommandObject(interpreter, name, help, syntax, flags),25m_can_be_removed(false) {}2627CommandObjectMultiword::~CommandObjectMultiword() = default;2829CommandObjectSP30CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {31if (m_subcommand_dict.empty())32return {};3334auto pos = m_subcommand_dict.find(std::string(sub_cmd));35if (pos == m_subcommand_dict.end())36return {};3738return pos->second;39}4041CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,42StringList *matches) {43if (m_subcommand_dict.empty())44return {};4546CommandObjectSP return_cmd_sp = GetSubcommandSPExact(sub_cmd);47if (return_cmd_sp) {48if (matches)49matches->AppendString(sub_cmd);50return return_cmd_sp;51}5253CommandObject::CommandMap::iterator pos;5455StringList local_matches;56if (matches == nullptr)57matches = &local_matches;58int num_matches =59AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);6061if (num_matches == 1) {62// Cleaner, but slightly less efficient would be to call back into this63// function, since I now know I have an exact match...6465sub_cmd = matches->GetStringAtIndex(0);66pos = m_subcommand_dict.find(std::string(sub_cmd));67if (pos != m_subcommand_dict.end())68return_cmd_sp = pos->second;69}7071return return_cmd_sp;72}7374CommandObject *75CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,76StringList *matches) {77return GetSubcommandSP(sub_cmd, matches).get();78}7980bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,81const CommandObjectSP &cmd_obj_sp) {82if (cmd_obj_sp)83lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&84"tried to add a CommandObject from a different interpreter");8586CommandMap::iterator pos;87bool success = true;8889pos = m_subcommand_dict.find(std::string(name));90if (pos == m_subcommand_dict.end()) {91m_subcommand_dict[std::string(name)] = cmd_obj_sp;92} else93success = false;9495return success;96}9798llvm::Error CommandObjectMultiword::LoadUserSubcommand(99llvm::StringRef name, const CommandObjectSP &cmd_obj_sp, bool can_replace) {100Status result;101if (cmd_obj_sp)102lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&103"tried to add a CommandObject from a different interpreter");104if (!IsUserCommand()) {105return llvm::createStringError(llvm::inconvertibleErrorCode(),106"can't add a user subcommand to a builtin container command.");107}108// Make sure this a user command if it isn't already:109cmd_obj_sp->SetIsUserCommand(true);110111std::string str_name(name);112113auto pos = m_subcommand_dict.find(str_name);114if (pos == m_subcommand_dict.end()) {115m_subcommand_dict[str_name] = cmd_obj_sp;116return llvm::Error::success();117}118119const char *error_str = nullptr;120if (!can_replace)121error_str = "sub-command already exists";122if (!(*pos).second->IsUserCommand())123error_str = "can't replace a builtin subcommand";124125if (error_str) {126return llvm::createStringError(llvm::inconvertibleErrorCode(), error_str);127}128m_subcommand_dict[str_name] = cmd_obj_sp;129return llvm::Error::success();130}131132llvm::Error CommandObjectMultiword::RemoveUserSubcommand(llvm::StringRef cmd_name,133bool must_be_multiword) {134CommandMap::iterator pos;135std::string str_name(cmd_name);136137pos = m_subcommand_dict.find(str_name);138if (pos == m_subcommand_dict.end()) {139return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not found.",140str_name.c_str());141}142if (!(*pos).second->IsUserCommand()) {143return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not a user command.",144str_name.c_str());145}146147if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {148return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a container command",149str_name.c_str());150}151if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {152return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a user command",153str_name.c_str());154}155156m_subcommand_dict.erase(pos);157158return llvm::Error::success();159}160161void CommandObjectMultiword::Execute(const char *args_string,162CommandReturnObject &result) {163Args args(args_string);164const size_t argc = args.GetArgumentCount();165if (argc == 0) {166this->CommandObject::GenerateHelpText(result);167return;168}169170auto sub_command = args[0].ref();171if (sub_command.empty()) {172result.AppendError("Need to specify a non-empty subcommand.");173return;174}175176if (m_subcommand_dict.empty()) {177result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",178GetCommandName().str().c_str());179return;180}181182StringList matches;183CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);184if (sub_cmd_obj != nullptr) {185// Now call CommandObject::Execute to process options in `rest_of_line`.186// From there the command-specific version of Execute will be called, with187// the processed arguments.188189args.Shift();190sub_cmd_obj->Execute(args_string, result);191return;192}193194std::string error_msg;195const size_t num_subcmd_matches = matches.GetSize();196if (num_subcmd_matches > 0)197error_msg.assign("ambiguous command ");198else199error_msg.assign("invalid command ");200201error_msg.append("'");202error_msg.append(std::string(GetCommandName()));203error_msg.append(" ");204error_msg.append(std::string(sub_command));205error_msg.append("'.");206207if (num_subcmd_matches > 0) {208error_msg.append(" Possible completions:");209for (const std::string &match : matches) {210error_msg.append("\n\t");211error_msg.append(match);212}213}214error_msg.append("\n");215result.AppendRawError(error_msg.c_str());216}217218void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {219// First time through here, generate the help text for the object and push it220// to the return result object as well221222CommandObject::GenerateHelpText(output_stream);223output_stream.PutCString("\nThe following subcommands are supported:\n\n");224225CommandMap::iterator pos;226uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);227228if (max_len)229max_len += 4; // Indent the output by 4 spaces.230231for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {232std::string indented_command(" ");233indented_command.append(pos->first);234if (pos->second->WantsRawCommandString()) {235std::string help_text(std::string(pos->second->GetHelp()));236help_text.append(" Expects 'raw' input (see 'help raw-input'.)");237m_interpreter.OutputFormattedHelpText(output_stream, indented_command,238"--", help_text, max_len);239} else240m_interpreter.OutputFormattedHelpText(output_stream, indented_command,241"--", pos->second->GetHelp(),242max_len);243}244245output_stream.PutCString("\nFor more help on any particular subcommand, type "246"'help <command> <subcommand>'.\n");247}248249void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {250auto arg0 = request.GetParsedLine()[0].ref();251if (request.GetCursorIndex() == 0) {252StringList new_matches, descriptions;253AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,254&descriptions);255request.AddCompletions(new_matches, descriptions);256257if (new_matches.GetSize() == 1 &&258new_matches.GetStringAtIndex(0) != nullptr &&259(arg0 == new_matches.GetStringAtIndex(0))) {260StringList temp_matches;261CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);262if (cmd_obj != nullptr) {263if (request.GetParsedLine().GetArgumentCount() != 1) {264request.GetParsedLine().Shift();265request.AppendEmptyArgument();266cmd_obj->HandleCompletion(request);267}268}269}270return;271}272273StringList new_matches;274CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);275276// The subcommand is ambiguous. The completion isn't meaningful.277if (!sub_command_object)278return;279280// Remove the one match that we got from calling GetSubcommandObject.281new_matches.DeleteStringAtIndex(0);282request.AddCompletions(new_matches);283request.ShiftArguments();284sub_command_object->HandleCompletion(request);285}286287std::optional<std::string>288CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args,289uint32_t index) {290index++;291if (current_command_args.GetArgumentCount() <= index)292return std::nullopt;293CommandObject *sub_command_object =294GetSubcommandObject(current_command_args[index].ref());295if (sub_command_object == nullptr)296return std::nullopt;297return sub_command_object->GetRepeatCommand(current_command_args, index);298}299300CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,301const char *name, const char *help,302const char *syntax, uint32_t flags)303: CommandObject(interpreter, name, help, syntax, flags) {}304305CommandObjectProxy::~CommandObjectProxy() = default;306307Options *CommandObjectProxy::GetOptions() {308CommandObject *proxy_command = GetProxyCommandObject();309if (proxy_command)310return proxy_command->GetOptions();311return CommandObject::GetOptions();312}313314llvm::StringRef CommandObjectProxy::GetHelp() {315CommandObject *proxy_command = GetProxyCommandObject();316if (proxy_command)317return proxy_command->GetHelp();318return CommandObject::GetHelp();319}320321llvm::StringRef CommandObjectProxy::GetSyntax() {322CommandObject *proxy_command = GetProxyCommandObject();323if (proxy_command)324return proxy_command->GetSyntax();325return CommandObject::GetSyntax();326}327328llvm::StringRef CommandObjectProxy::GetHelpLong() {329CommandObject *proxy_command = GetProxyCommandObject();330if (proxy_command)331return proxy_command->GetHelpLong();332return CommandObject::GetHelpLong();333}334335bool CommandObjectProxy::IsRemovable() const {336const CommandObject *proxy_command =337const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();338if (proxy_command)339return proxy_command->IsRemovable();340return false;341}342343bool CommandObjectProxy::IsMultiwordObject() {344CommandObject *proxy_command = GetProxyCommandObject();345if (proxy_command)346return proxy_command->IsMultiwordObject();347return false;348}349350CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {351CommandObject *proxy_command = GetProxyCommandObject();352if (proxy_command)353return proxy_command->GetAsMultiwordCommand();354return nullptr;355}356357void CommandObjectProxy::GenerateHelpText(Stream &result) {358CommandObject *proxy_command = GetProxyCommandObject();359if (proxy_command)360proxy_command->GenerateHelpText(result);361else362CommandObject::GenerateHelpText(result);363}364365lldb::CommandObjectSP366CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,367StringList *matches) {368CommandObject *proxy_command = GetProxyCommandObject();369if (proxy_command)370return proxy_command->GetSubcommandSP(sub_cmd, matches);371return lldb::CommandObjectSP();372}373374CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,375StringList *matches) {376CommandObject *proxy_command = GetProxyCommandObject();377if (proxy_command)378return proxy_command->GetSubcommandObject(sub_cmd, matches);379return nullptr;380}381382bool CommandObjectProxy::LoadSubCommand(383llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {384CommandObject *proxy_command = GetProxyCommandObject();385if (proxy_command)386return proxy_command->LoadSubCommand(cmd_name, command_sp);387return false;388}389390bool CommandObjectProxy::WantsRawCommandString() {391CommandObject *proxy_command = GetProxyCommandObject();392if (proxy_command)393return proxy_command->WantsRawCommandString();394return false;395}396397bool CommandObjectProxy::WantsCompletion() {398CommandObject *proxy_command = GetProxyCommandObject();399if (proxy_command)400return proxy_command->WantsCompletion();401return false;402}403404void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {405CommandObject *proxy_command = GetProxyCommandObject();406if (proxy_command)407proxy_command->HandleCompletion(request);408}409410void CommandObjectProxy::HandleArgumentCompletion(411CompletionRequest &request, OptionElementVector &opt_element_vector) {412CommandObject *proxy_command = GetProxyCommandObject();413if (proxy_command)414proxy_command->HandleArgumentCompletion(request, opt_element_vector);415}416417std::optional<std::string>418CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args,419uint32_t index) {420CommandObject *proxy_command = GetProxyCommandObject();421if (proxy_command)422return proxy_command->GetRepeatCommand(current_command_args, index);423return std::nullopt;424}425426llvm::StringRef CommandObjectProxy::GetUnsupportedError() {427return "command is not implemented";428}429430void CommandObjectProxy::Execute(const char *args_string,431CommandReturnObject &result) {432if (CommandObject *proxy_command = GetProxyCommandObject())433proxy_command->Execute(args_string, result);434else435result.AppendError(GetUnsupportedError());436}437438439