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 | // A list of command names forming a path to the command we want help on. |
52 | // No names is allowed - in which case we dump the top-level help. |
53 | AddSimpleArgumentList(arg_type: eArgTypeCommand, repetition_type: eArgRepeatStar); |
54 | } |
55 | |
56 | CommandObjectHelp::~CommandObjectHelp() = default; |
57 | |
58 | #define LLDB_OPTIONS_help |
59 | #include "CommandOptions.inc" |
60 | |
61 | llvm::ArrayRef<OptionDefinition> |
62 | CommandObjectHelp::CommandOptions::GetDefinitions() { |
63 | return llvm::ArrayRef(g_help_options); |
64 | } |
65 | |
66 | void CommandObjectHelp::DoExecute(Args &command, CommandReturnObject &result) { |
67 | CommandObject::CommandMap::iterator pos; |
68 | CommandObject *cmd_obj; |
69 | const size_t argc = command.GetArgumentCount(); |
70 | |
71 | // 'help' doesn't take any arguments, other than command names. If argc is |
72 | // 0, we show the user all commands (aliases and user commands if asked for). |
73 | // Otherwise every argument must be the name of a command or a sub-command. |
74 | if (argc == 0) { |
75 | uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin; |
76 | if (m_options.m_show_aliases) |
77 | cmd_types |= CommandInterpreter::eCommandTypesAliases; |
78 | if (m_options.m_show_user_defined) { |
79 | cmd_types |= CommandInterpreter::eCommandTypesUserDef; |
80 | cmd_types |= CommandInterpreter::eCommandTypesUserMW; |
81 | } |
82 | if (m_options.m_show_hidden) |
83 | cmd_types |= CommandInterpreter::eCommandTypesHidden; |
84 | |
85 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
86 | m_interpreter.GetHelp(result, types: cmd_types); // General help |
87 | } else { |
88 | // Get command object for the first command argument. Only search built-in |
89 | // command dictionary. |
90 | StringList matches; |
91 | auto command_name = command[0].ref(); |
92 | cmd_obj = m_interpreter.GetCommandObject(cmd: command_name, matches: &matches); |
93 | |
94 | if (cmd_obj != nullptr) { |
95 | StringList matches; |
96 | bool all_okay = true; |
97 | CommandObject *sub_cmd_obj = cmd_obj; |
98 | // Loop down through sub_command dictionaries until we find the command |
99 | // object that corresponds to the help command entered. |
100 | std::string sub_command; |
101 | for (auto &entry : command.entries().drop_front()) { |
102 | sub_command = std::string(entry.ref()); |
103 | matches.Clear(); |
104 | if (sub_cmd_obj->IsAlias()) |
105 | sub_cmd_obj = |
106 | ((CommandAlias *)sub_cmd_obj)->GetUnderlyingCommand().get(); |
107 | if (!sub_cmd_obj->IsMultiwordObject()) { |
108 | all_okay = false; |
109 | break; |
110 | } else { |
111 | CommandObject *found_cmd; |
112 | found_cmd = |
113 | sub_cmd_obj->GetSubcommandObject(sub_cmd: sub_command.c_str(), matches: &matches); |
114 | if (found_cmd == nullptr || matches.GetSize() > 1) { |
115 | all_okay = false; |
116 | break; |
117 | } else |
118 | sub_cmd_obj = found_cmd; |
119 | } |
120 | } |
121 | |
122 | if (!all_okay || (sub_cmd_obj == nullptr)) { |
123 | std::string cmd_string; |
124 | command.GetCommandString(command&: cmd_string); |
125 | if (matches.GetSize() >= 2) { |
126 | StreamString s; |
127 | s.Printf(format: "ambiguous command %s" , cmd_string.c_str()); |
128 | size_t num_matches = matches.GetSize(); |
129 | for (size_t match_idx = 0; match_idx < num_matches; match_idx++) { |
130 | s.Printf(format: "\n\t%s" , matches.GetStringAtIndex(idx: match_idx)); |
131 | } |
132 | s.Printf(format: "\n" ); |
133 | result.AppendError(in_string: s.GetString()); |
134 | return; |
135 | } else if (!sub_cmd_obj) { |
136 | StreamString error_msg_stream; |
137 | GenerateAdditionalHelpAvenuesMessage( |
138 | s: &error_msg_stream, command: cmd_string.c_str(), |
139 | prefix: m_interpreter.GetCommandPrefix(), subcommand: sub_command.c_str()); |
140 | result.AppendError(in_string: error_msg_stream.GetString()); |
141 | return; |
142 | } else { |
143 | GenerateAdditionalHelpAvenuesMessage( |
144 | s: &result.GetOutputStream(), command: cmd_string.c_str(), |
145 | prefix: m_interpreter.GetCommandPrefix(), subcommand: sub_command.c_str()); |
146 | result.GetOutputStream().Printf( |
147 | format: "\nThe closest match is '%s'. Help on it follows.\n\n" , |
148 | sub_cmd_obj->GetCommandName().str().c_str()); |
149 | } |
150 | } |
151 | |
152 | sub_cmd_obj->GenerateHelpText(result); |
153 | std::string alias_full_name; |
154 | // Don't use AliasExists here, that only checks exact name matches. If |
155 | // the user typed a shorter unique alias name, we should still tell them |
156 | // it was an alias. |
157 | if (m_interpreter.GetAliasFullName(cmd: command_name, full_name&: alias_full_name)) { |
158 | StreamString sstr; |
159 | m_interpreter.GetAlias(alias_name: alias_full_name)->GetAliasExpansion(help_string&: sstr); |
160 | result.GetOutputStream().Printf(format: "\n'%s' is an abbreviation for %s\n" , |
161 | command[0].c_str(), sstr.GetData()); |
162 | } |
163 | } else if (matches.GetSize() > 0) { |
164 | Stream &output_strm = result.GetOutputStream(); |
165 | output_strm.Printf(format: "Help requested with ambiguous command name, possible " |
166 | "completions:\n" ); |
167 | const size_t match_count = matches.GetSize(); |
168 | for (size_t i = 0; i < match_count; i++) { |
169 | output_strm.Printf(format: "\t%s\n" , matches.GetStringAtIndex(idx: i)); |
170 | } |
171 | } else { |
172 | // Maybe the user is asking for help about a command argument rather than |
173 | // a command. |
174 | const CommandArgumentType arg_type = |
175 | CommandObject::LookupArgumentName(arg_name: command_name); |
176 | if (arg_type != eArgTypeLastArg) { |
177 | Stream &output_strm = result.GetOutputStream(); |
178 | CommandObject::GetArgumentHelp(str&: output_strm, arg_type, interpreter&: m_interpreter); |
179 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
180 | } else { |
181 | StreamString error_msg_stream; |
182 | GenerateAdditionalHelpAvenuesMessage(s: &error_msg_stream, command: command_name, |
183 | prefix: m_interpreter.GetCommandPrefix(), |
184 | subcommand: "" ); |
185 | result.AppendError(in_string: error_msg_stream.GetString()); |
186 | } |
187 | } |
188 | } |
189 | } |
190 | |
191 | void CommandObjectHelp::HandleCompletion(CompletionRequest &request) { |
192 | // Return the completions of the commands in the help system: |
193 | if (request.GetCursorIndex() == 0) { |
194 | m_interpreter.HandleCompletionMatches(request); |
195 | return; |
196 | } |
197 | CommandObject *cmd_obj = |
198 | m_interpreter.GetCommandObject(cmd: request.GetParsedLine()[0].ref()); |
199 | |
200 | // The command that they are getting help on might be ambiguous, in which |
201 | // case we should complete that, otherwise complete with the command the |
202 | // user is getting help on... |
203 | |
204 | if (cmd_obj) { |
205 | request.ShiftArguments(); |
206 | cmd_obj->HandleCompletion(request); |
207 | return; |
208 | } |
209 | m_interpreter.HandleCompletionMatches(request); |
210 | } |
211 | |