1ac7ddfbfSEd Maste //===-- CommandObjectHelp.cpp -----------------------------------*- C++ -*-===//
2ac7ddfbfSEd Maste //
3ac7ddfbfSEd Maste // The LLVM Compiler Infrastructure
4ac7ddfbfSEd Maste //
5ac7ddfbfSEd Maste // This file is distributed under the University of Illinois Open Source
6ac7ddfbfSEd Maste // License. See LICENSE.TXT for details.
7ac7ddfbfSEd Maste //
8ac7ddfbfSEd Maste //===----------------------------------------------------------------------===//
9ac7ddfbfSEd Maste
104bb0738eSEd Maste #include "CommandObjectHelp.h"
11ac7ddfbfSEd Maste #include "lldb/Interpreter/CommandInterpreter.h"
12435933ddSDimitry Andric #include "lldb/Interpreter/CommandObjectMultiword.h"
13ac7ddfbfSEd Maste #include "lldb/Interpreter/CommandReturnObject.h"
14435933ddSDimitry Andric #include "lldb/Interpreter/Options.h"
15ac7ddfbfSEd Maste
16ac7ddfbfSEd Maste using namespace lldb;
17ac7ddfbfSEd Maste using namespace lldb_private;
18ac7ddfbfSEd Maste
19ac7ddfbfSEd Maste //-------------------------------------------------------------------------
20ac7ddfbfSEd Maste // CommandObjectHelp
21ac7ddfbfSEd Maste //-------------------------------------------------------------------------
22ac7ddfbfSEd Maste
GenerateAdditionalHelpAvenuesMessage(Stream * s,llvm::StringRef command,llvm::StringRef prefix,llvm::StringRef subcommand,bool include_apropos,bool include_type_lookup)23435933ddSDimitry Andric void CommandObjectHelp::GenerateAdditionalHelpAvenuesMessage(
24435933ddSDimitry Andric Stream *s, llvm::StringRef command, llvm::StringRef prefix, llvm::StringRef subcommand,
25435933ddSDimitry Andric bool include_apropos, bool include_type_lookup) {
26435933ddSDimitry Andric if (!s || command.empty())
27435933ddSDimitry Andric return;
28435933ddSDimitry Andric
29435933ddSDimitry Andric std::string command_str = command.str();
30435933ddSDimitry Andric std::string prefix_str = prefix.str();
31435933ddSDimitry Andric std::string subcommand_str = subcommand.str();
32435933ddSDimitry Andric const std::string &lookup_str = !subcommand_str.empty() ? subcommand_str : command_str;
33435933ddSDimitry Andric s->Printf("'%s' is not a known command.\n", command_str.c_str());
34435933ddSDimitry Andric s->Printf("Try '%shelp' to see a current list of commands.\n",
35435933ddSDimitry Andric prefix.str().c_str());
36435933ddSDimitry Andric if (include_apropos) {
374bb0738eSEd Maste s->Printf("Try '%sapropos %s' for a list of related commands.\n",
38435933ddSDimitry Andric prefix_str.c_str(), lookup_str.c_str());
394bb0738eSEd Maste }
40435933ddSDimitry Andric if (include_type_lookup) {
41435933ddSDimitry Andric s->Printf("Try '%stype lookup %s' for information on types, methods, "
42435933ddSDimitry Andric "functions, modules, etc.",
43435933ddSDimitry Andric prefix_str.c_str(), lookup_str.c_str());
444bb0738eSEd Maste }
454bb0738eSEd Maste }
464bb0738eSEd Maste
CommandObjectHelp(CommandInterpreter & interpreter)474bb0738eSEd Maste CommandObjectHelp::CommandObjectHelp(CommandInterpreter &interpreter)
48435933ddSDimitry Andric : CommandObjectParsed(interpreter, "help", "Show a list of all debugger "
49435933ddSDimitry Andric "commands, or give details "
50435933ddSDimitry Andric "about a specific command.",
514bb0738eSEd Maste "help [<cmd-name>]"),
52435933ddSDimitry Andric m_options() {
53ac7ddfbfSEd Maste CommandArgumentEntry arg;
54ac7ddfbfSEd Maste CommandArgumentData command_arg;
55ac7ddfbfSEd Maste
56ac7ddfbfSEd Maste // Define the first (and only) variant of this arg.
57ac7ddfbfSEd Maste command_arg.arg_type = eArgTypeCommandName;
58ac7ddfbfSEd Maste command_arg.arg_repetition = eArgRepeatStar;
59ac7ddfbfSEd Maste
60435933ddSDimitry Andric // There is only one variant this argument could be; put it into the argument
61435933ddSDimitry Andric // entry.
62ac7ddfbfSEd Maste arg.push_back(command_arg);
63ac7ddfbfSEd Maste
64ac7ddfbfSEd Maste // Push the data for the first argument into the m_arguments vector.
65ac7ddfbfSEd Maste m_arguments.push_back(arg);
66ac7ddfbfSEd Maste }
67ac7ddfbfSEd Maste
684bb0738eSEd Maste CommandObjectHelp::~CommandObjectHelp() = default;
69ac7ddfbfSEd Maste
70*b5893f02SDimitry Andric static constexpr OptionDefinition g_help_options[] = {
71435933ddSDimitry Andric // clang-format off
72*b5893f02SDimitry Andric {LLDB_OPT_SET_ALL, false, "hide-aliases", 'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide aliases in the command list."},
73*b5893f02SDimitry Andric {LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide user-defined commands from the list."},
74*b5893f02SDimitry Andric {LLDB_OPT_SET_ALL, false, "show-hidden-commands", 'h', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Include commands prefixed with an underscore."},
75435933ddSDimitry Andric // clang-format on
76ac7ddfbfSEd Maste };
77ac7ddfbfSEd Maste
78435933ddSDimitry Andric llvm::ArrayRef<OptionDefinition>
GetDefinitions()79435933ddSDimitry Andric CommandObjectHelp::CommandOptions::GetDefinitions() {
80435933ddSDimitry Andric return llvm::makeArrayRef(g_help_options);
81435933ddSDimitry Andric }
82435933ddSDimitry Andric
DoExecute(Args & command,CommandReturnObject & result)83435933ddSDimitry Andric bool CommandObjectHelp::DoExecute(Args &command, CommandReturnObject &result) {
84ac7ddfbfSEd Maste CommandObject::CommandMap::iterator pos;
85ac7ddfbfSEd Maste CommandObject *cmd_obj;
86ac7ddfbfSEd Maste const size_t argc = command.GetArgumentCount();
87ac7ddfbfSEd Maste
884ba319b5SDimitry Andric // 'help' doesn't take any arguments, other than command names. If argc is
894ba319b5SDimitry Andric // 0, we show the user all commands (aliases and user commands if asked for).
904ba319b5SDimitry Andric // Otherwise every argument must be the name of a command or a sub-command.
91435933ddSDimitry Andric if (argc == 0) {
92ac7ddfbfSEd Maste uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin;
93ac7ddfbfSEd Maste if (m_options.m_show_aliases)
94ac7ddfbfSEd Maste cmd_types |= CommandInterpreter::eCommandTypesAliases;
95ac7ddfbfSEd Maste if (m_options.m_show_user_defined)
96ac7ddfbfSEd Maste cmd_types |= CommandInterpreter::eCommandTypesUserDef;
971c3bbb01SEd Maste if (m_options.m_show_hidden)
981c3bbb01SEd Maste cmd_types |= CommandInterpreter::eCommandTypesHidden;
99ac7ddfbfSEd Maste
100ac7ddfbfSEd Maste result.SetStatus(eReturnStatusSuccessFinishNoResult);
101ac7ddfbfSEd Maste m_interpreter.GetHelp(result, cmd_types); // General help
102435933ddSDimitry Andric } else {
103435933ddSDimitry Andric // Get command object for the first command argument. Only search built-in
104435933ddSDimitry Andric // command dictionary.
105ac7ddfbfSEd Maste StringList matches;
106435933ddSDimitry Andric auto command_name = command[0].ref;
107435933ddSDimitry Andric cmd_obj = m_interpreter.GetCommandObject(command_name, &matches);
108ac7ddfbfSEd Maste
109435933ddSDimitry Andric if (cmd_obj != nullptr) {
110ac7ddfbfSEd Maste StringList matches;
111ac7ddfbfSEd Maste bool all_okay = true;
112ac7ddfbfSEd Maste CommandObject *sub_cmd_obj = cmd_obj;
113435933ddSDimitry Andric // Loop down through sub_command dictionaries until we find the command
114435933ddSDimitry Andric // object that corresponds to the help command entered.
1154bb0738eSEd Maste std::string sub_command;
116435933ddSDimitry Andric for (auto &entry : command.entries().drop_front()) {
117435933ddSDimitry Andric sub_command = entry.ref;
118ac7ddfbfSEd Maste matches.Clear();
1194bb0738eSEd Maste if (sub_cmd_obj->IsAlias())
120435933ddSDimitry Andric sub_cmd_obj =
121435933ddSDimitry Andric ((CommandAlias *)sub_cmd_obj)->GetUnderlyingCommand().get();
122435933ddSDimitry Andric if (!sub_cmd_obj->IsMultiwordObject()) {
123ac7ddfbfSEd Maste all_okay = false;
124435933ddSDimitry Andric break;
125435933ddSDimitry Andric } else {
126ac7ddfbfSEd Maste CommandObject *found_cmd;
127435933ddSDimitry Andric found_cmd =
128435933ddSDimitry Andric sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches);
129435933ddSDimitry Andric if (found_cmd == nullptr || matches.GetSize() > 1) {
130ac7ddfbfSEd Maste all_okay = false;
131435933ddSDimitry Andric break;
132435933ddSDimitry Andric } else
133ac7ddfbfSEd Maste sub_cmd_obj = found_cmd;
134ac7ddfbfSEd Maste }
135ac7ddfbfSEd Maste }
136ac7ddfbfSEd Maste
137435933ddSDimitry Andric if (!all_okay || (sub_cmd_obj == nullptr)) {
138ac7ddfbfSEd Maste std::string cmd_string;
139ac7ddfbfSEd Maste command.GetCommandString(cmd_string);
140435933ddSDimitry Andric if (matches.GetSize() >= 2) {
141ac7ddfbfSEd Maste StreamString s;
142ac7ddfbfSEd Maste s.Printf("ambiguous command %s", cmd_string.c_str());
143ac7ddfbfSEd Maste size_t num_matches = matches.GetSize();
144435933ddSDimitry Andric for (size_t match_idx = 0; match_idx < num_matches; match_idx++) {
145ac7ddfbfSEd Maste s.Printf("\n\t%s", matches.GetStringAtIndex(match_idx));
146ac7ddfbfSEd Maste }
147ac7ddfbfSEd Maste s.Printf("\n");
148435933ddSDimitry Andric result.AppendError(s.GetString());
149ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
150ac7ddfbfSEd Maste return false;
151435933ddSDimitry Andric } else if (!sub_cmd_obj) {
1524bb0738eSEd Maste StreamString error_msg_stream;
153435933ddSDimitry Andric GenerateAdditionalHelpAvenuesMessage(
154435933ddSDimitry Andric &error_msg_stream, cmd_string.c_str(),
155435933ddSDimitry Andric m_interpreter.GetCommandPrefix(), sub_command.c_str());
156435933ddSDimitry Andric result.AppendError(error_msg_stream.GetString());
157ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
158ac7ddfbfSEd Maste return false;
159435933ddSDimitry Andric } else {
160435933ddSDimitry Andric GenerateAdditionalHelpAvenuesMessage(
161435933ddSDimitry Andric &result.GetOutputStream(), cmd_string.c_str(),
162435933ddSDimitry Andric m_interpreter.GetCommandPrefix(), sub_command.c_str());
163435933ddSDimitry Andric result.GetOutputStream().Printf(
164435933ddSDimitry Andric "\nThe closest match is '%s'. Help on it follows.\n\n",
165435933ddSDimitry Andric sub_cmd_obj->GetCommandName().str().c_str());
166ac7ddfbfSEd Maste }
167ac7ddfbfSEd Maste }
168ac7ddfbfSEd Maste
169ac7ddfbfSEd Maste sub_cmd_obj->GenerateHelpText(result);
170*b5893f02SDimitry Andric std::string alias_full_name;
171*b5893f02SDimitry Andric // Don't use AliasExists here, that only checks exact name matches. If
172*b5893f02SDimitry Andric // the user typed a shorter unique alias name, we should still tell them
173*b5893f02SDimitry Andric // it was an alias.
174*b5893f02SDimitry Andric if (m_interpreter.GetAliasFullName(command_name, alias_full_name)) {
175ac7ddfbfSEd Maste StreamString sstr;
176*b5893f02SDimitry Andric m_interpreter.GetAlias(alias_full_name)->GetAliasExpansion(sstr);
177435933ddSDimitry Andric result.GetOutputStream().Printf("\n'%s' is an abbreviation for %s\n",
178435933ddSDimitry Andric command[0].c_str(), sstr.GetData());
179ac7ddfbfSEd Maste }
180435933ddSDimitry Andric } else if (matches.GetSize() > 0) {
181ac7ddfbfSEd Maste Stream &output_strm = result.GetOutputStream();
182435933ddSDimitry Andric output_strm.Printf("Help requested with ambiguous command name, possible "
183435933ddSDimitry Andric "completions:\n");
184ac7ddfbfSEd Maste const size_t match_count = matches.GetSize();
185435933ddSDimitry Andric for (size_t i = 0; i < match_count; i++) {
186ac7ddfbfSEd Maste output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i));
187ac7ddfbfSEd Maste }
188435933ddSDimitry Andric } else {
189435933ddSDimitry Andric // Maybe the user is asking for help about a command argument rather than
190435933ddSDimitry Andric // a command.
191435933ddSDimitry Andric const CommandArgumentType arg_type =
192435933ddSDimitry Andric CommandObject::LookupArgumentName(command_name);
193435933ddSDimitry Andric if (arg_type != eArgTypeLastArg) {
194ac7ddfbfSEd Maste Stream &output_strm = result.GetOutputStream();
195ac7ddfbfSEd Maste CommandObject::GetArgumentHelp(output_strm, arg_type, m_interpreter);
196ac7ddfbfSEd Maste result.SetStatus(eReturnStatusSuccessFinishNoResult);
197435933ddSDimitry Andric } else {
1984bb0738eSEd Maste StreamString error_msg_stream;
199435933ddSDimitry Andric GenerateAdditionalHelpAvenuesMessage(&error_msg_stream, command_name,
200435933ddSDimitry Andric m_interpreter.GetCommandPrefix(),
201435933ddSDimitry Andric "");
202435933ddSDimitry Andric result.AppendError(error_msg_stream.GetString());
203ac7ddfbfSEd Maste result.SetStatus(eReturnStatusFailed);
204ac7ddfbfSEd Maste }
205ac7ddfbfSEd Maste }
206ac7ddfbfSEd Maste }
207ac7ddfbfSEd Maste
208ac7ddfbfSEd Maste return result.Succeeded();
209ac7ddfbfSEd Maste }
210ac7ddfbfSEd Maste
HandleCompletion(CompletionRequest & request)2114ba319b5SDimitry Andric int CommandObjectHelp::HandleCompletion(CompletionRequest &request) {
212ac7ddfbfSEd Maste // Return the completions of the commands in the help system:
2134ba319b5SDimitry Andric if (request.GetCursorIndex() == 0) {
2144ba319b5SDimitry Andric return m_interpreter.HandleCompletionMatches(request);
215435933ddSDimitry Andric } else {
2164ba319b5SDimitry Andric CommandObject *cmd_obj =
2174ba319b5SDimitry Andric m_interpreter.GetCommandObject(request.GetParsedLine()[0].ref);
218ac7ddfbfSEd Maste
219435933ddSDimitry Andric // The command that they are getting help on might be ambiguous, in which
2204ba319b5SDimitry Andric // case we should complete that, otherwise complete with the command the
2214ba319b5SDimitry Andric // user is getting help on...
222ac7ddfbfSEd Maste
223435933ddSDimitry Andric if (cmd_obj) {
2244ba319b5SDimitry Andric request.GetParsedLine().Shift();
2254ba319b5SDimitry Andric request.SetCursorIndex(request.GetCursorIndex() - 1);
2264ba319b5SDimitry Andric return cmd_obj->HandleCompletion(request);
227435933ddSDimitry Andric } else {
2284ba319b5SDimitry Andric return m_interpreter.HandleCompletionMatches(request);
229ac7ddfbfSEd Maste }
230ac7ddfbfSEd Maste }
231ac7ddfbfSEd Maste }
232