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