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