1 | //===-- CommandObjectHelp.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 "CommandObjectHelp.h" |
10 | #include "lldb/Interpreter/CommandInterpreter.h" |
11 | #include "lldb/Interpreter/CommandOptionArgumentTable.h" |
12 | #include "lldb/Interpreter/CommandReturnObject.h" |
13 | |
14 | using namespace lldb; |
15 | using namespace lldb_private; |
16 | |
17 | // CommandObjectHelp |
18 | |
19 | void CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage( |
20 | Stream *s, llvm::StringRef command, llvm::StringRef prefix, |
21 | llvm::StringRef subcommand, bool include_upropos, |
22 | bool include_type_lookup) { |
23 | if (!s || command.empty()) |
24 | return; |
25 | |
26 | std::string command_str = command.str(); |
27 | std::string prefix_str = prefix.str(); |
28 | std::string subcommand_str = subcommand.str(); |
29 | const std::string &lookup_str = |
30 | !subcommand_str.empty() ? subcommand_str : command_str; |
31 | s->Printf(format: "'%s' is not a known command.\n" , command_str.c_str()); |
32 | s->Printf(format: "Try '%shelp' to see a current list of commands.\n" , |
33 | prefix.str().c_str()); |
34 | if (include_upropos) { |
35 | s->Printf(format: "Try '%sapropos %s' for a list of related commands.\n" , |
36 | prefix_str.c_str(), lookup_str.c_str()); |
37 | } |
38 | if (include_type_lookup) { |
39 | s->Printf(format: "Try '%stype lookup %s' for information on types, methods, " |
40 | "functions, modules, etc." , |
41 | prefix_str.c_str(), lookup_str.c_str()); |
42 | } |
43 | } |
44 | |
45 | CommandObjectHelp::CommandObjectHelp(CommandInterpreter &interpreter) |
46 | : CommandObjectParsed(interpreter, "help" , |
47 | "Show a list of all debugger " |
48 | "commands, or give details " |
49 | "about a specific command." , |
50 | "help [<cmd-name>]" ) { |
51 | CommandArgumentEntry arg; |
52 | CommandArgumentData command_arg; |
53 | |
54 | // A list of command names forming a path to the command we want help on. |
55 | // No names is allowed - in which case we dump the top-level help. |
56 | command_arg.arg_type = eArgTypeCommand; |
57 | command_arg.arg_repetition = eArgRepeatStar; |
58 | |
59 | // There is only one variant this argument could be; put it into the argument |
60 | // entry. |
61 | arg.push_back(x: command_arg); |
62 | |
63 | // Push the data for the first argument into the m_arguments vector. |
64 | m_arguments.push_back(x: arg); |
65 | } |
66 | |
67 | CommandObjectHelp::~CommandObjectHelp() = default; |
68 | |
69 | #define LLDB_OPTIONS_help |
70 | #include "CommandOptions.inc" |
71 | |
72 | llvm::ArrayRef<OptionDefinition> |
73 | CommandObjectHelp::CommandOptions::GetDefinitions() { |
74 | return llvm::ArrayRef(g_help_options); |
75 | } |
76 | |
77 | void CommandObjectHelp::DoExecute(Args &command, CommandReturnObject &result) { |
78 | CommandObject::CommandMap::iterator pos; |
79 | CommandObject *cmd_obj; |
80 | const size_t argc = command.GetArgumentCount(); |
81 | |
82 | // 'help' doesn't take any arguments, other than command names. If argc is |
83 | // 0, we show the user all commands (aliases and user commands if asked for). |
84 | // Otherwise every argument must be the name of a command or a sub-command. |
85 | if (argc == 0) { |
86 | uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin; |
87 | if (m_options.m_show_aliases) |
88 | cmd_types |= CommandInterpreter::eCommandTypesAliases; |
89 | if (m_options.m_show_user_defined) { |
90 | cmd_types |= CommandInterpreter::eCommandTypesUserDef; |
91 | cmd_types |= CommandInterpreter::eCommandTypesUserMW; |
92 | } |
93 | if (m_options.m_show_hidden) |
94 | cmd_types |= CommandInterpreter::eCommandTypesHidden; |
95 | |
96 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
97 | m_interpreter.GetHelp(result, types: cmd_types); // General help |
98 | } else { |
99 | // Get command object for the first command argument. Only search built-in |
100 | // command dictionary. |
101 | StringList matches; |
102 | auto command_name = command[0].ref(); |
103 | cmd_obj = m_interpreter.GetCommandObject(cmd: command_name, matches: &matches); |
104 | |
105 | if (cmd_obj != nullptr) { |
106 | StringList matches; |
107 | bool all_okay = true; |
108 | CommandObject *sub_cmd_obj = cmd_obj; |
109 | // Loop down through sub_command dictionaries until we find the command |
110 | // object that corresponds to the help command entered. |
111 | std::string sub_command; |
112 | for (auto &entry : command.entries().drop_front()) { |
113 | sub_command = std::string(entry.ref()); |
114 | matches.Clear(); |
115 | if (sub_cmd_obj->IsAlias()) |
116 | sub_cmd_obj = |
117 | ((CommandAlias *)sub_cmd_obj)->GetUnderlyingCommand().get(); |
118 | if (!sub_cmd_obj->IsMultiwordObject()) { |
119 | all_okay = false; |
120 | break; |
121 | } else { |
122 | CommandObject *found_cmd; |
123 | found_cmd = |
124 | sub_cmd_obj->GetSubcommandObject(sub_cmd: sub_command.c_str(), matches: &matches); |
125 | if (found_cmd == nullptr || matches.GetSize() > 1) { |
126 | all_okay = false; |
127 | break; |
128 | } else |
129 | sub_cmd_obj = found_cmd; |
130 | } |
131 | } |
132 | |
133 | if (!all_okay || (sub_cmd_obj == nullptr)) { |
134 | std::string cmd_string; |
135 | command.GetCommandString(command&: cmd_string); |
136 | if (matches.GetSize() >= 2) { |
137 | StreamString s; |
138 | s.Printf(format: "ambiguous command %s" , cmd_string.c_str()); |
139 | size_t num_matches = matches.GetSize(); |
140 | for (size_t match_idx = 0; match_idx < num_matches; match_idx++) { |
141 | s.Printf(format: "\n\t%s" , matches.GetStringAtIndex(idx: match_idx)); |
142 | } |
143 | s.Printf(format: "\n" ); |
144 | result.AppendError(in_string: s.GetString()); |
145 | return; |
146 | } else if (!sub_cmd_obj) { |
147 | StreamString error_msg_stream; |
148 | GenerateAdditionalHelpAvenuesMessage( |
149 | s: &error_msg_stream, command: cmd_string.c_str(), |
150 | prefix: m_interpreter.GetCommandPrefix(), subcommand: sub_command.c_str()); |
151 | result.AppendError(in_string: error_msg_stream.GetString()); |
152 | return; |
153 | } else { |
154 | GenerateAdditionalHelpAvenuesMessage( |
155 | s: &result.GetOutputStream(), command: cmd_string.c_str(), |
156 | prefix: m_interpreter.GetCommandPrefix(), subcommand: sub_command.c_str()); |
157 | result.GetOutputStream().Printf( |
158 | format: "\nThe closest match is '%s'. Help on it follows.\n\n" , |
159 | sub_cmd_obj->GetCommandName().str().c_str()); |
160 | } |
161 | } |
162 | |
163 | sub_cmd_obj->GenerateHelpText(result); |
164 | std::string alias_full_name; |
165 | // Don't use AliasExists here, that only checks exact name matches. If |
166 | // the user typed a shorter unique alias name, we should still tell them |
167 | // it was an alias. |
168 | if (m_interpreter.GetAliasFullName(cmd: command_name, full_name&: alias_full_name)) { |
169 | StreamString sstr; |
170 | m_interpreter.GetAlias(alias_name: alias_full_name)->GetAliasExpansion(help_string&: sstr); |
171 | result.GetOutputStream().Printf(format: "\n'%s' is an abbreviation for %s\n" , |
172 | command[0].c_str(), sstr.GetData()); |
173 | } |
174 | } else if (matches.GetSize() > 0) { |
175 | Stream &output_strm = result.GetOutputStream(); |
176 | output_strm.Printf(format: "Help requested with ambiguous command name, possible " |
177 | "completions:\n" ); |
178 | const size_t match_count = matches.GetSize(); |
179 | for (size_t i = 0; i < match_count; i++) { |
180 | output_strm.Printf(format: "\t%s\n" , matches.GetStringAtIndex(idx: i)); |
181 | } |
182 | } else { |
183 | // Maybe the user is asking for help about a command argument rather than |
184 | // a command. |
185 | const CommandArgumentType arg_type = |
186 | CommandObject::LookupArgumentName(arg_name: command_name); |
187 | if (arg_type != eArgTypeLastArg) { |
188 | Stream &output_strm = result.GetOutputStream(); |
189 | CommandObject::GetArgumentHelp(str&: output_strm, arg_type, interpreter&: m_interpreter); |
190 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
191 | } else { |
192 | StreamString error_msg_stream; |
193 | GenerateAdditionalHelpAvenuesMessage(s: &error_msg_stream, command: command_name, |
194 | prefix: m_interpreter.GetCommandPrefix(), |
195 | subcommand: "" ); |
196 | result.AppendError(in_string: error_msg_stream.GetString()); |
197 | } |
198 | } |
199 | } |
200 | } |
201 | |
202 | void CommandObjectHelp::HandleCompletion(CompletionRequest &request) { |
203 | // Return the completions of the commands in the help system: |
204 | if (request.GetCursorIndex() == 0) { |
205 | m_interpreter.HandleCompletionMatches(request); |
206 | return; |
207 | } |
208 | CommandObject *cmd_obj = |
209 | m_interpreter.GetCommandObject(cmd: request.GetParsedLine()[0].ref()); |
210 | |
211 | // The command that they are getting help on might be ambiguous, in which |
212 | // case we should complete that, otherwise complete with the command the |
213 | // user is getting help on... |
214 | |
215 | if (cmd_obj) { |
216 | request.ShiftArguments(); |
217 | cmd_obj->HandleCompletion(request); |
218 | return; |
219 | } |
220 | m_interpreter.HandleCompletionMatches(request); |
221 | } |
222 | |