xref: /llvm-project/lldb/source/Commands/CommandObjectMultiword.cpp (revision 15ce2e183fb801ff418eb1347a9d5893e5665782)
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-exception
6 //
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 
15 using namespace lldb;
16 using namespace lldb_private;
17 
18 // CommandObjectMultiword
19 
20 CommandObjectMultiword::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 
28 CommandObjectMultiword::~CommandObjectMultiword() = default;
29 
30 CommandObjectSP
31 CommandObjectMultiword::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 
42 CommandObjectSP 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 this
64     // 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 
75 CommandObject *
76 CommandObjectMultiword::GetSubcommandObject(llvm::StringRef sub_cmd,
77                                             StringList *matches) {
78   return GetSubcommandSP(sub_cmd, matches).get();
79 }
80 
81 bool 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 
90 llvm::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 
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);
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 
151 void 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, with
177     // 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 
211 std::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   else
228     buffer.pop_back();
229 
230   buffer.append(".");
231   return buffer;
232 }
233 
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
237 
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     } else
256       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 
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,
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 
303 std::optional<std::string>
304 CommandObjectMultiword::GetRepeatCommand(Args &current_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 
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) {}
320 
321 CommandObjectProxy::~CommandObjectProxy() = default;
322 
323 Options *CommandObjectProxy::GetOptions() {
324   CommandObject *proxy_command = GetProxyCommandObject();
325   if (proxy_command)
326     return proxy_command->GetOptions();
327   return CommandObject::GetOptions();
328 }
329 
330 llvm::StringRef CommandObjectProxy::GetHelp() {
331   CommandObject *proxy_command = GetProxyCommandObject();
332   if (proxy_command)
333     return proxy_command->GetHelp();
334   return CommandObject::GetHelp();
335 }
336 
337 llvm::StringRef CommandObjectProxy::GetSyntax() {
338   CommandObject *proxy_command = GetProxyCommandObject();
339   if (proxy_command)
340     return proxy_command->GetSyntax();
341   return CommandObject::GetSyntax();
342 }
343 
344 llvm::StringRef CommandObjectProxy::GetHelpLong() {
345   CommandObject *proxy_command = GetProxyCommandObject();
346   if (proxy_command)
347     return proxy_command->GetHelpLong();
348   return CommandObject::GetHelpLong();
349 }
350 
351 bool 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 
359 bool CommandObjectProxy::IsMultiwordObject() {
360   CommandObject *proxy_command = GetProxyCommandObject();
361   if (proxy_command)
362     return proxy_command->IsMultiwordObject();
363   return false;
364 }
365 
366 CommandObjectMultiword *CommandObjectProxy::GetAsMultiwordCommand() {
367   CommandObject *proxy_command = GetProxyCommandObject();
368   if (proxy_command)
369     return proxy_command->GetAsMultiwordCommand();
370   return nullptr;
371 }
372 
373 void CommandObjectProxy::GenerateHelpText(Stream &result) {
374   CommandObject *proxy_command = GetProxyCommandObject();
375   if (proxy_command)
376     proxy_command->GenerateHelpText(result);
377   else
378     CommandObject::GenerateHelpText(result);
379 }
380 
381 lldb::CommandObjectSP
382 CommandObjectProxy::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 
390 CommandObject *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 
398 bool 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 
406 bool CommandObjectProxy::WantsRawCommandString() {
407   CommandObject *proxy_command = GetProxyCommandObject();
408   if (proxy_command)
409     return proxy_command->WantsRawCommandString();
410   return false;
411 }
412 
413 bool CommandObjectProxy::WantsCompletion() {
414   CommandObject *proxy_command = GetProxyCommandObject();
415   if (proxy_command)
416     return proxy_command->WantsCompletion();
417   return false;
418 }
419 
420 void CommandObjectProxy::HandleCompletion(CompletionRequest &request) {
421   CommandObject *proxy_command = GetProxyCommandObject();
422   if (proxy_command)
423     proxy_command->HandleCompletion(request);
424 }
425 
426 void 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 
433 std::optional<std::string>
434 CommandObjectProxy::GetRepeatCommand(Args &current_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 
442 llvm::StringRef CommandObjectProxy::GetUnsupportedError() {
443   return "command is not implemented";
444 }
445 
446 void CommandObjectProxy::Execute(const char *args_string,
447                                  CommandReturnObject &result) {
448   if (CommandObject *proxy_command = GetProxyCommandObject())
449     proxy_command->Execute(args_string, result);
450   else
451     result.AppendError(GetUnsupportedError());
452 }
453