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