1 //===-- CommandObjectMultiword.cpp ----------------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Interpreter/CommandObjectMultiword.h"
10 #include "lldb/Interpreter/CommandInterpreter.h"
11 #include "lldb/Interpreter/CommandReturnObject.h"
12 #include "lldb/Interpreter/Options.h"
16 using namespace lldb_private
;
18 // CommandObjectMultiword
20 CommandObjectMultiword::CommandObjectMultiword(CommandInterpreter
&interpreter
,
25 : CommandObject(interpreter
, name
, help
, syntax
, flags
),
26 m_can_be_removed(false) {}
28 CommandObjectMultiword::~CommandObjectMultiword() = default;
31 CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd
) {
32 if (m_subcommand_dict
.empty())
35 auto pos
= m_subcommand_dict
.find(sub_cmd
);
36 if (pos
== m_subcommand_dict
.end())
42 CommandObjectSP
CommandObjectMultiword::GetSubcommandSP(llvm::StringRef sub_cmd
,
43 StringList
*matches
) {
44 if (m_subcommand_dict
.empty())
47 CommandObjectSP return_cmd_sp
= GetSubcommandSPExact(sub_cmd
);
50 matches
->AppendString(sub_cmd
);
54 CommandObject::CommandMap::iterator pos
;
56 StringList local_matches
;
57 if (matches
== nullptr)
58 matches
= &local_matches
;
60 AddNamesMatchingPartialString(m_subcommand_dict
, sub_cmd
, *matches
);
62 if (num_matches
== 1) {
63 // Cleaner, but slightly less efficient would be to call back into this
64 // function, since I now know I have an exact match...
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
;
76 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd
,
77 StringList
*matches
) {
78 return GetSubcommandSP(sub_cmd
, matches
).get();
81 bool CommandObjectMultiword::LoadSubCommand(llvm::StringRef name
,
82 const CommandObjectSP
&cmd_obj_sp
) {
84 lldbassert((&GetCommandInterpreter() == &cmd_obj_sp
->GetCommandInterpreter()) &&
85 "tried to add a CommandObject from a different interpreter");
87 return m_subcommand_dict
.try_emplace(std::string(name
), cmd_obj_sp
).second
;
90 llvm::Error
CommandObjectMultiword::LoadUserSubcommand(
91 llvm::StringRef name
, const CommandObjectSP
&cmd_obj_sp
, bool can_replace
) {
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.");
100 // Make sure this a user command if it isn't already:
101 cmd_obj_sp
->SetIsUserCommand(true);
103 std::string
str_name(name
);
105 auto [pos
, inserted
] = m_subcommand_dict
.try_emplace(str_name
, cmd_obj_sp
);
107 return llvm::Error::success();
109 const char *error_str
= nullptr;
111 error_str
= "sub-command already exists";
112 if (!(*pos
).second
->IsUserCommand())
113 error_str
= "can't replace a builtin subcommand";
116 return llvm::createStringError(llvm::inconvertibleErrorCode(), error_str
);
118 pos
->second
= cmd_obj_sp
;
119 return llvm::Error::success();
122 llvm::Error
CommandObjectMultiword::RemoveUserSubcommand(llvm::StringRef cmd_name
,
123 bool must_be_multiword
) {
124 CommandMap::iterator pos
;
125 std::string
str_name(cmd_name
);
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.",
132 if (!(*pos
).second
->IsUserCommand()) {
133 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' not a user command.",
137 if (must_be_multiword
&& !(*pos
).second
->IsMultiwordObject()) {
138 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a container command",
141 if (!must_be_multiword
&& (*pos
).second
->IsMultiwordObject()) {
142 return llvm::createStringError(llvm::inconvertibleErrorCode(),"subcommand '%s' is not a user command",
146 m_subcommand_dict
.erase(pos
);
148 return llvm::Error::success();
151 void CommandObjectMultiword::Execute(const char *args_string
,
152 CommandReturnObject
&result
) {
153 Args
args(args_string
);
154 const size_t argc
= args
.GetArgumentCount();
156 this->CommandObject::GenerateHelpText(result
);
160 auto sub_command
= args
[0].ref();
161 if (sub_command
.empty()) {
162 result
.AppendError("Need to specify a non-empty subcommand.");
166 if (m_subcommand_dict
.empty()) {
167 result
.AppendErrorWithFormat("'%s' does not have any subcommands.\n",
168 GetCommandName().str().c_str());
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, with
177 // the processed arguments.
180 sub_cmd_obj
->Execute(args_string
, result
);
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("'.");
194 error_msg
.append(" Possible completions:");
195 for (const std::string
&match
: matches
) {
196 error_msg
.append("\n\t");
197 error_msg
.append(match
);
200 // Try to offer some alternatives to help correct the command.
202 llvm::Twine("\"" + sub_command
+ "\" is not a valid subcommand of \"" +
203 GetCommandName() + "\"." + GetSubcommandsHintText() +
204 " Use \"help " + GetCommandName() + "\" to find out more.")
207 error_msg
.append("\n");
208 result
.AppendRawError(error_msg
.c_str());
211 std::string
CommandObjectMultiword::GetSubcommandsHintText() {
212 if (m_subcommand_dict
.empty())
214 const size_t maxCount
= 5;
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
) {
222 buffer
.append(pos
->first
);
225 if (i
< m_subcommand_dict
.size())
226 buffer
.append(" and others");
234 void CommandObjectMultiword::GenerateHelpText(Stream
&output_stream
) {
235 // First time through here, generate the help text for the object and push it
236 // to the return result object as well
238 CommandObject::GenerateHelpText(output_stream
);
239 output_stream
.PutCString("\nThe following subcommands are supported:\n\n");
241 CommandMap::iterator pos
;
242 uint32_t max_len
= FindLongestCommandWord(m_subcommand_dict
);
245 max_len
+= 4; // Indent the output by 4 spaces.
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
);
256 m_interpreter
.OutputFormattedHelpText(output_stream
, indented_command
,
257 "--", pos
->second
->GetHelp(),
261 output_stream
.PutCString("\nFor more help on any particular subcommand, type "
262 "'help <command> <subcommand>'.\n");
265 void 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
,
271 request
.AddCompletions(new_matches
, descriptions
);
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
);
289 StringList new_matches
;
290 CommandObject
*sub_command_object
= GetSubcommandObject(arg0
, &new_matches
);
292 // The subcommand is ambiguous. The completion isn't meaningful.
293 if (!sub_command_object
)
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
);
303 std::optional
<std::string
>
304 CommandObjectMultiword::GetRepeatCommand(Args
¤t_command_args
,
307 if (current_command_args
.GetArgumentCount() <= index
)
309 CommandObject
*sub_command_object
=
310 GetSubcommandObject(current_command_args
[index
].ref());
311 if (sub_command_object
== nullptr)
313 return sub_command_object
->GetRepeatCommand(current_command_args
, index
);
316 CommandObjectProxy::CommandObjectProxy(CommandInterpreter
&interpreter
,
317 const char *name
, const char *help
,
318 const char *syntax
, uint32_t flags
)
319 : CommandObject(interpreter
, name
, help
, syntax
, flags
) {}
321 CommandObjectProxy::~CommandObjectProxy() = default;
323 Options
*CommandObjectProxy::GetOptions() {
324 CommandObject
*proxy_command
= GetProxyCommandObject();
326 return proxy_command
->GetOptions();
327 return CommandObject::GetOptions();
330 llvm::StringRef
CommandObjectProxy::GetHelp() {
331 CommandObject
*proxy_command
= GetProxyCommandObject();
333 return proxy_command
->GetHelp();
334 return CommandObject::GetHelp();
337 llvm::StringRef
CommandObjectProxy::GetSyntax() {
338 CommandObject
*proxy_command
= GetProxyCommandObject();
340 return proxy_command
->GetSyntax();
341 return CommandObject::GetSyntax();
344 llvm::StringRef
CommandObjectProxy::GetHelpLong() {
345 CommandObject
*proxy_command
= GetProxyCommandObject();
347 return proxy_command
->GetHelpLong();
348 return CommandObject::GetHelpLong();
351 bool CommandObjectProxy::IsRemovable() const {
352 const CommandObject
*proxy_command
=
353 const_cast<CommandObjectProxy
*>(this)->GetProxyCommandObject();
355 return proxy_command
->IsRemovable();
359 bool CommandObjectProxy::IsMultiwordObject() {
360 CommandObject
*proxy_command
= GetProxyCommandObject();
362 return proxy_command
->IsMultiwordObject();
366 CommandObjectMultiword
*CommandObjectProxy::GetAsMultiwordCommand() {
367 CommandObject
*proxy_command
= GetProxyCommandObject();
369 return proxy_command
->GetAsMultiwordCommand();
373 void CommandObjectProxy::GenerateHelpText(Stream
&result
) {
374 CommandObject
*proxy_command
= GetProxyCommandObject();
376 proxy_command
->GenerateHelpText(result
);
378 CommandObject::GenerateHelpText(result
);
381 lldb::CommandObjectSP
382 CommandObjectProxy::GetSubcommandSP(llvm::StringRef sub_cmd
,
383 StringList
*matches
) {
384 CommandObject
*proxy_command
= GetProxyCommandObject();
386 return proxy_command
->GetSubcommandSP(sub_cmd
, matches
);
387 return lldb::CommandObjectSP();
390 CommandObject
*CommandObjectProxy::GetSubcommandObject(llvm::StringRef sub_cmd
,
391 StringList
*matches
) {
392 CommandObject
*proxy_command
= GetProxyCommandObject();
394 return proxy_command
->GetSubcommandObject(sub_cmd
, matches
);
398 bool CommandObjectProxy::LoadSubCommand(
399 llvm::StringRef cmd_name
, const lldb::CommandObjectSP
&command_sp
) {
400 CommandObject
*proxy_command
= GetProxyCommandObject();
402 return proxy_command
->LoadSubCommand(cmd_name
, command_sp
);
406 bool CommandObjectProxy::WantsRawCommandString() {
407 CommandObject
*proxy_command
= GetProxyCommandObject();
409 return proxy_command
->WantsRawCommandString();
413 bool CommandObjectProxy::WantsCompletion() {
414 CommandObject
*proxy_command
= GetProxyCommandObject();
416 return proxy_command
->WantsCompletion();
420 void CommandObjectProxy::HandleCompletion(CompletionRequest
&request
) {
421 CommandObject
*proxy_command
= GetProxyCommandObject();
423 proxy_command
->HandleCompletion(request
);
426 void CommandObjectProxy::HandleArgumentCompletion(
427 CompletionRequest
&request
, OptionElementVector
&opt_element_vector
) {
428 CommandObject
*proxy_command
= GetProxyCommandObject();
430 proxy_command
->HandleArgumentCompletion(request
, opt_element_vector
);
433 std::optional
<std::string
>
434 CommandObjectProxy::GetRepeatCommand(Args
¤t_command_args
,
436 CommandObject
*proxy_command
= GetProxyCommandObject();
438 return proxy_command
->GetRepeatCommand(current_command_args
, index
);
442 llvm::StringRef
CommandObjectProxy::GetUnsupportedError() {
443 return "command is not implemented";
446 void CommandObjectProxy::Execute(const char *args_string
,
447 CommandReturnObject
&result
) {
448 if (CommandObject
*proxy_command
= GetProxyCommandObject())
449 proxy_command
->Execute(args_string
, result
);
451 result
.AppendError(GetUnsupportedError());