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(x: 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(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 | |
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(k: std::string(name), args: 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(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 | |
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(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 | |
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(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 | |
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(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 | |
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(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 | |
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(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 | |
303 | std::optional<std::string> |
304 | CommandObjectMultiword::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(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 | |
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_obj: 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 ¤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 | |
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(in_string: GetUnsupportedError()); |
452 | } |
453 |
Definitions
- CommandObjectMultiword
- ~CommandObjectMultiword
- GetSubcommandSPExact
- GetSubcommandSP
- GetSubcommandObject
- LoadSubCommand
- LoadUserSubcommand
- RemoveUserSubcommand
- Execute
- GetSubcommandsHintText
- GenerateHelpText
- HandleCompletion
- GetRepeatCommand
- CommandObjectProxy
- ~CommandObjectProxy
- GetOptions
- GetHelp
- GetSyntax
- GetHelpLong
- IsRemovable
- IsMultiwordObject
- GetAsMultiwordCommand
- GenerateHelpText
- GetSubcommandSP
- GetSubcommandObject
- LoadSubCommand
- WantsRawCommandString
- WantsCompletion
- HandleCompletion
- HandleArgumentCompletion
- GetRepeatCommand
- GetUnsupportedError
Improve your Profiling and Debugging skills
Find out more