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