453 lines · cpp
1//===-- CommandObjectMultiword.cpp ----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Interpreter/CommandObjectMultiword.h"10#include "lldb/Interpreter/CommandInterpreter.h"11#include "lldb/Interpreter/CommandReturnObject.h"12#include "lldb/Interpreter/Options.h"13#include <optional>14 15using namespace lldb;16using namespace lldb_private;17 18// CommandObjectMultiword19 20CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter &interpreter,21 const char *name,22 const char *help,23 const char *syntax,24 uint32_t flags)25 : CommandObject(interpreter, name, help, syntax, flags),26 m_can_be_removed(false) {}27 28CommandObjectMultiword::~CommandObjectMultiword() = default;29 30CommandObjectSP31CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {32 if (m_subcommand_dict.empty())33 return {};34 35 auto pos = m_subcommand_dict.find(sub_cmd);36 if (pos == m_subcommand_dict.end())37 return {};38 39 return pos->second;40}41 42CommandObjectSP CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd,43 StringList *matches) {44 if (m_subcommand_dict.empty())45 return {};46 47 CommandObjectSP return_cmd_sp = GetSubcommandSPExact(sub_cmd);48 if (return_cmd_sp) {49 if (matches)50 matches->AppendString(sub_cmd);51 return return_cmd_sp;52 }53 54 CommandObject::CommandMap::iterator pos;55 56 StringList local_matches;57 if (matches == nullptr)58 matches = &local_matches;59 int num_matches =60 AddNamesMatchingPartialString(m_subcommand_dict, sub_cmd, *matches);61 62 if (num_matches == 1) {63 // Cleaner, but slightly less efficient would be to call back into this64 // function, since I now know I have an exact match...65 66 sub_cmd = matches->GetStringAtIndex(0);67 pos = m_subcommand_dict.find(sub_cmd);68 if (pos != m_subcommand_dict.end())69 return_cmd_sp = pos->second;70 }71 72 return return_cmd_sp;73}74 75CommandObject *76CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,77 StringList *matches) {78 return GetSubcommandSP(sub_cmd, matches).get();79}80 81bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name,82 const CommandObjectSP &cmd_obj_sp) {83 if (cmd_obj_sp)84 lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&85 "tried to add a CommandObject from a different interpreter");86 87 return m_subcommand_dict.try_emplace(std::string(name), cmd_obj_sp).second;88}89 90llvm::Error CommandObjectMultiword::LoadUserSubcommand(91 llvm::StringRef name, const CommandObjectSP &cmd_obj_sp, bool can_replace) {92 Status result;93 if (cmd_obj_sp)94 lldbassert((&GetCommandInterpreter() == &cmd_obj_sp->GetCommandInterpreter()) &&95 "tried to add a CommandObject from a different interpreter");96 if (!IsUserCommand()) {97 return llvm::createStringError(llvm::inconvertibleErrorCode(),98 "can't add a user subcommand to a builtin container command.");99 }100 // Make sure this a user command if it isn't already:101 cmd_obj_sp->SetIsUserCommand(true);102 103 std::string str_name(name);104 105 auto [pos, inserted] = m_subcommand_dict.try_emplace(str_name, cmd_obj_sp);106 if (inserted)107 return llvm::Error::success();108 109 const char *error_str = nullptr;110 if (!can_replace)111 error_str = "sub-command already exists";112 if (!(*pos).second->IsUserCommand())113 error_str = "can't replace a builtin subcommand";114 115 if (error_str) {116 return llvm::createStringError(llvm::inconvertibleErrorCode(), error_str);117 }118 pos->second = cmd_obj_sp;119 return llvm::Error::success();120}121 122llvm::Error CommandObjectMultiword::RemoveUserSubcommand(llvm::StringRef cmd_name,123 bool must_be_multiword) {124 CommandMap::iterator pos;125 std::string str_name(cmd_name);126 127 pos = m_subcommand_dict.find(str_name);128 if (pos == m_subcommand_dict.end()) {129 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not found.",130 str_name.c_str());131 }132 if (!(*pos).second->IsUserCommand()) {133 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not a user command.",134 str_name.c_str());135 }136 137 if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {138 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a container command",139 str_name.c_str());140 }141 if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {142 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a user command",143 str_name.c_str());144 }145 146 m_subcommand_dict.erase(pos);147 148 return llvm::Error::success();149}150 151void CommandObjectMultiword::Execute(const char *args_string,152 CommandReturnObject &result) {153 Args args(args_string);154 const size_t argc = args.GetArgumentCount();155 if (argc == 0) {156 this->CommandObject::GenerateHelpText(result);157 return;158 }159 160 auto sub_command = args[0].ref();161 if (sub_command.empty()) {162 result.AppendError("need to specify a non-empty subcommand");163 return;164 }165 166 if (m_subcommand_dict.empty()) {167 result.AppendErrorWithFormat("'%s' does not have any subcommands.\n",168 GetCommandName().str().c_str());169 return;170 }171 172 StringList matches;173 CommandObject *sub_cmd_obj = GetSubcommandObject(sub_command, &matches);174 if (sub_cmd_obj != nullptr) {175 // Now call CommandObject::Execute to process options in `rest_of_line`.176 // From there the command-specific version of Execute will be called, with177 // the processed arguments.178 179 args.Shift();180 sub_cmd_obj->Execute(args_string, result);181 return;182 }183 184 std::string error_msg;185 const size_t num_subcmd_matches = matches.GetSize();186 if (num_subcmd_matches > 0) {187 error_msg.assign("ambiguous command ");188 error_msg.append("'");189 error_msg.append(std::string(GetCommandName()));190 error_msg.append(" ");191 error_msg.append(std::string(sub_command));192 error_msg.append("'.");193 194 error_msg.append(" Possible completions:");195 for (const std::string &match : matches) {196 error_msg.append("\n\t");197 error_msg.append(match);198 }199 } else {200 // Try to offer some alternatives to help correct the command.201 error_msg.assign(202 llvm::Twine("\"" + sub_command + "\" is not a valid subcommand of \"" +203 GetCommandName() + "\"." + GetSubcommandsHintText() +204 " Use \"help " + GetCommandName() + "\" to find out more.")205 .str());206 }207 error_msg.append("\n");208 result.AppendRawError(error_msg.c_str());209}210 211std::string CommandObjectMultiword::GetSubcommandsHintText() {212 if (m_subcommand_dict.empty())213 return "";214 const size_t maxCount = 5;215 size_t i = 0;216 std::string buffer = " Valid subcommand";217 buffer.append(m_subcommand_dict.size() > 1 ? "s are:" : " is");218 CommandMap::iterator pos;219 for (pos = m_subcommand_dict.begin();220 pos != m_subcommand_dict.end() && i < maxCount; ++pos, ++i) {221 buffer.append(" ");222 buffer.append(pos->first);223 buffer.append(",");224 }225 if (i < m_subcommand_dict.size())226 buffer.append(" and others");227 else228 buffer.pop_back();229 230 buffer.append(".");231 return buffer;232}233 234void CommandObjectMultiword::GenerateHelpText(Stream &output_stream) {235 // First time through here, generate the help text for the object and push it236 // to the return result object as well237 238 CommandObject::GenerateHelpText(output_stream);239 output_stream.PutCString("\nThe following subcommands are supported:\n\n");240 241 CommandMap::iterator pos;242 uint32_t max_len = FindLongestCommandWord(m_subcommand_dict);243 244 if (max_len)245 max_len += 4; // Indent the output by 4 spaces.246 247 for (pos = m_subcommand_dict.begin(); pos != m_subcommand_dict.end(); ++pos) {248 std::string indented_command(" ");249 indented_command.append(pos->first);250 if (pos->second->WantsRawCommandString()) {251 std::string help_text(std::string(pos->second->GetHelp()));252 help_text.append(" Expects 'raw' input (see 'help raw-input'.)");253 m_interpreter.OutputFormattedHelpText(output_stream, indented_command,254 "--", help_text, max_len);255 } else256 m_interpreter.OutputFormattedHelpText(output_stream, indented_command,257 "--", pos->second->GetHelp(),258 max_len);259 }260 261 output_stream.PutCString("\nFor more help on any particular subcommand, type "262 "'help <command> <subcommand>'.\n");263}264 265void CommandObjectMultiword::HandleCompletion(CompletionRequest &request) {266 auto arg0 = request.GetParsedLine()[0].ref();267 if (request.GetCursorIndex() == 0) {268 StringList new_matches, descriptions;269 AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches,270 &descriptions);271 request.AddCompletions(new_matches, descriptions);272 273 if (new_matches.GetSize() == 1 &&274 new_matches.GetStringAtIndex(0) != nullptr &&275 (arg0 == new_matches.GetStringAtIndex(0))) {276 StringList temp_matches;277 CommandObject *cmd_obj = GetSubcommandObject(arg0, &temp_matches);278 if (cmd_obj != nullptr) {279 if (request.GetParsedLine().GetArgumentCount() != 1) {280 request.GetParsedLine().Shift();281 request.AppendEmptyArgument();282 cmd_obj->HandleCompletion(request);283 }284 }285 }286 return;287 }288 289 StringList new_matches;290 CommandObject *sub_command_object = GetSubcommandObject(arg0, &new_matches);291 292 // The subcommand is ambiguous. The completion isn't meaningful.293 if (!sub_command_object)294 return;295 296 // Remove the one match that we got from calling GetSubcommandObject.297 new_matches.DeleteStringAtIndex(0);298 request.AddCompletions(new_matches);299 request.ShiftArguments();300 sub_command_object->HandleCompletion(request);301}302 303std::optional<std::string>304CommandObjectMultiword::GetRepeatCommand(Args ¤t_command_args,305 uint32_t index) {306 index++;307 if (current_command_args.GetArgumentCount() <= index)308 return std::nullopt;309 CommandObject *sub_command_object =310 GetSubcommandObject(current_command_args[index].ref());311 if (sub_command_object == nullptr)312 return std::nullopt;313 return sub_command_object->GetRepeatCommand(current_command_args, index);314}315 316CommandObjectProxy::CommandObjectProxy(CommandInterpreter &interpreter,317 const char *name, const char *help,318 const char *syntax, uint32_t flags)319 : CommandObject(interpreter, name, help, syntax, flags) {}320 321CommandObjectProxy::~CommandObjectProxy() = default;322 323Options *CommandObjectProxy::GetOptions() {324 CommandObject *proxy_command = GetProxyCommandObject();325 if (proxy_command)326 return proxy_command->GetOptions();327 return CommandObject::GetOptions();328}329 330llvm::StringRef CommandObjectProxy::GetHelp() {331 CommandObject *proxy_command = GetProxyCommandObject();332 if (proxy_command)333 return proxy_command->GetHelp();334 return CommandObject::GetHelp();335}336 337llvm::StringRef CommandObjectProxy::GetSyntax() {338 CommandObject *proxy_command = GetProxyCommandObject();339 if (proxy_command)340 return proxy_command->GetSyntax();341 return CommandObject::GetSyntax();342}343 344llvm::StringRef CommandObjectProxy::GetHelpLong() {345 CommandObject *proxy_command = GetProxyCommandObject();346 if (proxy_command)347 return proxy_command->GetHelpLong();348 return CommandObject::GetHelpLong();349}350 351bool CommandObjectProxy::IsRemovable() const {352 const CommandObject *proxy_command =353 const_cast<CommandObjectProxy *>(this)->GetProxyCommandObject();354 if (proxy_command)355 return proxy_command->IsRemovable();356 return false;357}358 359bool CommandObjectProxy::IsMultiwordObject() {360 CommandObject *proxy_command = GetProxyCommandObject();361 if (proxy_command)362 return proxy_command->IsMultiwordObject();363 return false;364}365 366CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {367 CommandObject *proxy_command = GetProxyCommandObject();368 if (proxy_command)369 return proxy_command->GetAsMultiwordCommand();370 return nullptr;371}372 373void CommandObjectProxy::GenerateHelpText(Stream &result) {374 CommandObject *proxy_command = GetProxyCommandObject();375 if (proxy_command)376 proxy_command->GenerateHelpText(result);377 else378 CommandObject::GenerateHelpText(result);379}380 381lldb::CommandObjectSP382CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd,383 StringList *matches) {384 CommandObject *proxy_command = GetProxyCommandObject();385 if (proxy_command)386 return proxy_command->GetSubcommandSP(sub_cmd, matches);387 return lldb::CommandObjectSP();388}389 390CommandObject *CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd,391 StringList *matches) {392 CommandObject *proxy_command = GetProxyCommandObject();393 if (proxy_command)394 return proxy_command->GetSubcommandObject(sub_cmd, matches);395 return nullptr;396}397 398bool CommandObjectProxy::LoadSubCommand(399 llvm::StringRef cmd_name, const lldb::CommandObjectSP &command_sp) {400 CommandObject *proxy_command = GetProxyCommandObject();401 if (proxy_command)402 return proxy_command->LoadSubCommand(cmd_name, command_sp);403 return false;404}405 406bool CommandObjectProxy::WantsRawCommandString() {407 CommandObject *proxy_command = GetProxyCommandObject();408 if (proxy_command)409 return proxy_command->WantsRawCommandString();410 return false;411}412 413bool CommandObjectProxy::WantsCompletion() {414 CommandObject *proxy_command = GetProxyCommandObject();415 if (proxy_command)416 return proxy_command->WantsCompletion();417 return false;418}419 420void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {421 CommandObject *proxy_command = GetProxyCommandObject();422 if (proxy_command)423 proxy_command->HandleCompletion(request);424}425 426void CommandObjectProxy::HandleArgumentCompletion(427 CompletionRequest &request, OptionElementVector &opt_element_vector) {428 CommandObject *proxy_command = GetProxyCommandObject();429 if (proxy_command)430 proxy_command->HandleArgumentCompletion(request, opt_element_vector);431}432 433std::optional<std::string>434CommandObjectProxy::GetRepeatCommand(Args ¤t_command_args,435 uint32_t index) {436 CommandObject *proxy_command = GetProxyCommandObject();437 if (proxy_command)438 return proxy_command->GetRepeatCommand(current_command_args, index);439 return std::nullopt;440}441 442llvm::StringRef CommandObjectProxy::GetUnsupportedError() {443 return "command is not implemented";444}445 446void CommandObjectProxy::Execute(const char *args_string,447 CommandReturnObject &result) {448 if (CommandObject *proxy_command = GetProxyCommandObject())449 proxy_command->Execute(args_string, result);450 else451 result.AppendError(GetUnsupportedError());452}453