1//===-- CommandObjectHelp.cpp -----------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "lldb/lldb-python.h"
11
12#include "CommandObjectHelp.h"
13
14// C Includes
15// C++ Includes
16// Other libraries and framework includes
17// Project includes
18#include "lldb/Interpreter/CommandObjectMultiword.h"
19#include "lldb/Interpreter/CommandInterpreter.h"
20#include "lldb/Interpreter/Options.h"
21#include "lldb/Interpreter/CommandReturnObject.h"
22
23using namespace lldb;
24using namespace lldb_private;
25
26//-------------------------------------------------------------------------
27// CommandObjectHelp
28//-------------------------------------------------------------------------
29
30CommandObjectHelp::CommandObjectHelp (CommandInterpreter &interpreter) :
31    CommandObjectParsed (interpreter,
32                         "help",
33                         "Show a list of all debugger commands, or give details about specific commands.",
34                         "help [<cmd-name>]"), m_options (interpreter)
35{
36    CommandArgumentEntry arg;
37    CommandArgumentData command_arg;
38
39    // Define the first (and only) variant of this arg.
40    command_arg.arg_type = eArgTypeCommandName;
41    command_arg.arg_repetition = eArgRepeatStar;
42
43    // There is only one variant this argument could be; put it into the argument entry.
44    arg.push_back (command_arg);
45
46    // Push the data for the first argument into the m_arguments vector.
47    m_arguments.push_back (arg);
48}
49
50CommandObjectHelp::~CommandObjectHelp()
51{
52}
53
54OptionDefinition
55CommandObjectHelp::CommandOptions::g_option_table[] =
56{
57    { LLDB_OPT_SET_ALL, false, "show-aliases", 'a', no_argument, NULL, 0, eArgTypeNone,         "Show aliases in the command list."},
58    { LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', no_argument, NULL, 0, eArgTypeNone,         "Hide user-defined commands from the list."},
59    { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
60};
61
62bool
63CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
64{
65    CommandObject::CommandMap::iterator pos;
66    CommandObject *cmd_obj;
67    const size_t argc = command.GetArgumentCount ();
68
69    // 'help' doesn't take any arguments, other than command names.  If argc is 0, we show the user
70    // all commands (aliases and user commands if asked for).  Otherwise every argument must be the name of a command or a sub-command.
71    if (argc == 0)
72    {
73        uint32_t cmd_types = CommandInterpreter::eCommandTypesBuiltin;
74        if (m_options.m_show_aliases)
75            cmd_types |= CommandInterpreter::eCommandTypesAliases;
76        if (m_options.m_show_user_defined)
77            cmd_types |= CommandInterpreter::eCommandTypesUserDef;
78
79        result.SetStatus (eReturnStatusSuccessFinishNoResult);
80        m_interpreter.GetHelp (result, cmd_types);  // General help
81    }
82    else
83    {
84        // Get command object for the first command argument. Only search built-in command dictionary.
85        StringList matches;
86        cmd_obj = m_interpreter.GetCommandObject (command.GetArgumentAtIndex (0), &matches);
87        bool is_alias_command = m_interpreter.AliasExists (command.GetArgumentAtIndex (0));
88        std::string alias_name = command.GetArgumentAtIndex(0);
89
90        if (cmd_obj != NULL)
91        {
92            StringList matches;
93            bool all_okay = true;
94            CommandObject *sub_cmd_obj = cmd_obj;
95            // Loop down through sub_command dictionaries until we find the command object that corresponds
96            // to the help command entered.
97            for (size_t i = 1; i < argc && all_okay; ++i)
98            {
99                std::string sub_command = command.GetArgumentAtIndex(i);
100                matches.Clear();
101                if (! sub_cmd_obj->IsMultiwordObject ())
102                {
103                    all_okay = false;
104                }
105                else
106                {
107                    CommandObject *found_cmd;
108                    found_cmd = sub_cmd_obj->GetSubcommandObject(sub_command.c_str(), &matches);
109                    if (found_cmd == NULL)
110                        all_okay = false;
111                    else if (matches.GetSize() > 1)
112                        all_okay = false;
113                    else
114                        sub_cmd_obj = found_cmd;
115                }
116            }
117
118            if (!all_okay || (sub_cmd_obj == NULL))
119            {
120                std::string cmd_string;
121                command.GetCommandString (cmd_string);
122                if (matches.GetSize() >= 2)
123                {
124                    StreamString s;
125                    s.Printf ("ambiguous command %s", cmd_string.c_str());
126                    size_t num_matches = matches.GetSize();
127                    for (size_t match_idx = 0; match_idx < num_matches; match_idx++)
128                    {
129                        s.Printf ("\n\t%s", matches.GetStringAtIndex(match_idx));
130                    }
131                    s.Printf ("\n");
132                    result.AppendError(s.GetData());
133                    result.SetStatus (eReturnStatusFailed);
134                    return false;
135                }
136                else if (!sub_cmd_obj)
137                {
138                    result.AppendErrorWithFormat("'%s' is not a known command.\n"
139                                                 "Try 'help' to see a current list of commands.\n",
140                                                 cmd_string.c_str());
141                    result.SetStatus (eReturnStatusFailed);
142                    return false;
143                }
144                else
145                {
146                    result.GetOutputStream().Printf("'%s' is not a known command.\n"
147                                                   "Try 'help' to see a current list of commands.\n"
148                                                    "The closest match is '%s'. Help on it follows.\n\n",
149                                                   cmd_string.c_str(),
150                                                   sub_cmd_obj->GetCommandName());
151                }
152            }
153
154            sub_cmd_obj->GenerateHelpText(result);
155
156            if (is_alias_command)
157            {
158                StreamString sstr;
159                m_interpreter.GetAliasHelp (alias_name.c_str(), cmd_obj->GetCommandName(), sstr);
160                result.GetOutputStream().Printf ("\n'%s' is an abbreviation for %s\n", alias_name.c_str(), sstr.GetData());
161            }
162        }
163        else if (matches.GetSize() > 0)
164        {
165            Stream &output_strm = result.GetOutputStream();
166            output_strm.Printf("Help requested with ambiguous command name, possible completions:\n");
167            const size_t match_count = matches.GetSize();
168            for (size_t i = 0; i < match_count; i++)
169            {
170                output_strm.Printf("\t%s\n", matches.GetStringAtIndex(i));
171            }
172        }
173        else
174        {
175            // Maybe the user is asking for help about a command argument rather than a command.
176            const CommandArgumentType arg_type = CommandObject::LookupArgumentName (command.GetArgumentAtIndex (0));
177            if (arg_type != eArgTypeLastArg)
178            {
179                Stream &output_strm = result.GetOutputStream ();
180                CommandObject::GetArgumentHelp (output_strm, arg_type, m_interpreter);
181                result.SetStatus (eReturnStatusSuccessFinishNoResult);
182            }
183            else
184            {
185                result.AppendErrorWithFormat
186                    ("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n",
187                     command.GetArgumentAtIndex(0));
188                result.SetStatus (eReturnStatusFailed);
189            }
190        }
191    }
192
193    return result.Succeeded();
194}
195
196int
197CommandObjectHelp::HandleCompletion
198(
199    Args &input,
200    int &cursor_index,
201    int &cursor_char_position,
202    int match_start_point,
203    int max_return_elements,
204    bool &word_complete,
205    StringList &matches
206)
207{
208    // Return the completions of the commands in the help system:
209    if (cursor_index == 0)
210    {
211        return m_interpreter.HandleCompletionMatches (input,
212                                                    cursor_index,
213                                                    cursor_char_position,
214                                                    match_start_point,
215                                                    max_return_elements,
216                                                    word_complete,
217                                                    matches);
218    }
219    else
220    {
221        CommandObject *cmd_obj = m_interpreter.GetCommandObject (input.GetArgumentAtIndex(0));
222
223        // The command that they are getting help on might be ambiguous, in which case we should complete that,
224        // otherwise complete with the command the user is getting help on...
225
226        if (cmd_obj)
227        {
228            input.Shift();
229            cursor_index--;
230            return cmd_obj->HandleCompletion (input,
231                                              cursor_index,
232                                              cursor_char_position,
233                                              match_start_point,
234                                              max_return_elements,
235                                              word_complete,
236                                              matches);
237        }
238        else
239        {
240            return m_interpreter.HandleCompletionMatches (input,
241                                                        cursor_index,
242                                                        cursor_char_position,
243                                                        match_start_point,
244                                                        max_return_elements,
245                                                        word_complete,
246                                                        matches);
247        }
248    }
249}
250