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
15using namespace lldb;
16using namespace lldb_private;
17
18// CommandObjectMultiword
19
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
30CommandObjectSP
31CommandObjectMultiword::GetSubcommandSPExact(llvm::StringRef sub_cmd) {
32 if (m_subcommand_dict.empty())
33 return {};
34
35 auto pos = m_subcommand_dict.find(x: 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(str: 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(in_map: m_subcommand_dict, cmd_str: sub_cmd, matches&: *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(idx: 0);
67 pos = m_subcommand_dict.find(x: 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(k: std::string(name), args: 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(EC: llvm::inconvertibleErrorCode(),
98 S: "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(k: str_name, args: 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(EC: llvm::inconvertibleErrorCode(), S: 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(x: str_name);
128 if (pos == m_subcommand_dict.end()) {
129 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' not found.",
130 Vals: str_name.c_str());
131 }
132 if (!(*pos).second->IsUserCommand()) {
133 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' not a user command.",
134 Vals: str_name.c_str());
135 }
136
137 if (must_be_multiword && !(*pos).second->IsMultiwordObject()) {
138 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' is not a container command",
139 Vals: str_name.c_str());
140 }
141 if (!must_be_multiword && (*pos).second->IsMultiwordObject()) {
142 return llvm::createStringError(EC: llvm::inconvertibleErrorCode(),Fmt: "subcommand '%s' is not a user command",
143 Vals: str_name.c_str());
144 }
145
146 m_subcommand_dict.erase(position: 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(in_string: "Need to specify a non-empty subcommand.");
163 return;
164 }
165
166 if (m_subcommand_dict.empty()) {
167 result.AppendErrorWithFormat(format: "'%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_cmd: sub_command, matches: &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(s: "ambiguous command ");
188 error_msg.append(s: "'");
189 error_msg.append(str: std::string(GetCommandName()));
190 error_msg.append(s: " ");
191 error_msg.append(str: std::string(sub_command));
192 error_msg.append(s: "'.");
193
194 error_msg.append(s: " Possible completions:");
195 for (const std::string &match : matches) {
196 error_msg.append(s: "\n\t");
197 error_msg.append(str: match);
198 }
199 } else {
200 // Try to offer some alternatives to help correct the command.
201 error_msg.assign(
202 str: 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(s: "\n");
208 result.AppendRawError(in_string: 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(s: 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(s: " ");
222 buffer.append(str: pos->first);
223 buffer.append(s: ",");
224 }
225 if (i < m_subcommand_dict.size())
226 buffer.append(s: " and others");
227 else
228 buffer.pop_back();
229
230 buffer.append(s: ".");
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 it
236 // to the return result object as well
237
238 CommandObject::GenerateHelpText(result&: output_stream);
239 output_stream.PutCString(cstr: "\nThe following subcommands are supported:\n\n");
240
241 CommandMap::iterator pos;
242 uint32_t max_len = FindLongestCommandWord(dict&: 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(str: pos->first);
250 if (pos->second->WantsRawCommandString()) {
251 std::string help_text(std::string(pos->second->GetHelp()));
252 help_text.append(s: " Expects 'raw' input (see 'help raw-input'.)");
253 m_interpreter.OutputFormattedHelpText(stream&: output_stream, command_word: indented_command,
254 separator: "--", help_text, max_word_len: max_len);
255 } else
256 m_interpreter.OutputFormattedHelpText(stream&: output_stream, command_word: indented_command,
257 separator: "--", help_text: pos->second->GetHelp(),
258 max_word_len: max_len);
259 }
260
261 output_stream.PutCString(cstr: "\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(in_map: m_subcommand_dict, cmd_str: arg0, matches&: new_matches,
270 descriptions: &descriptions);
271 request.AddCompletions(completions: new_matches, descriptions);
272
273 if (new_matches.GetSize() == 1 &&
274 new_matches.GetStringAtIndex(idx: 0) != nullptr &&
275 (arg0 == new_matches.GetStringAtIndex(idx: 0))) {
276 StringList temp_matches;
277 CommandObject *cmd_obj = GetSubcommandObject(sub_cmd: arg0, matches: &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(sub_cmd: arg0, matches: &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(id: 0);
298 request.AddCompletions(completions: new_matches);
299 request.ShiftArguments();
300 sub_command_object->HandleCompletion(request);
301}
302
303std::optional<std::string>
304CommandObjectMultiword::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(sub_cmd: 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 else
378 CommandObject::GenerateHelpText(result);
379}
380
381lldb::CommandObjectSP
382CommandObjectProxy::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_obj: 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 &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
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 else
451 result.AppendError(in_string: GetUnsupportedError());
452}
453

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Commands/CommandObjectMultiword.cpp