forked from OSchip/llvm-project
Three related changes to help:
The default help display now shows the alias collection by default, and hides commands whose named begin with an underscore. Help is primarily useful to those unfamiliar with LLDB and should aim to answer typical questions while still being able to provide more esoteric answers when required. To that latter end an argument to include the hidden commands in help has been added, and instead of having a help flag to show aliases there is now one to hide them. This final change might be controversial as it repurposes the -a shorthand as the opposite of its original meaning. The previous implementation of OutputFormattedHelpText was easily confused by embedded newlines. The new algorithm correctly breaks on the FIRST newline or LAST space/tab before the target column count rather than treating all whitespace interchangeably. Command interpreters now have the ability to specify help prologue text and a command prefix string. Neither are used in the current LLDB sources but are required to support REPL-like extensions where LLDB commands must be prefixed and additional help text is required to explain how to access traditional debugging commands. <rdar://problem/17751929> <rdar://problem/16953815> <rdar://problem/16953841> <rdar://problem/16930173> <rdar://problem/16879028> llvm-svn: 226068
This commit is contained in:
parent
64e033f9c4
commit
a487aa4cdb
|
@ -158,8 +158,6 @@ public:
|
|||
// To get the target's source manager, call GetSourceManager on the target instead.
|
||||
SourceManager &
|
||||
GetSourceManager ();
|
||||
|
||||
public:
|
||||
|
||||
lldb::TargetSP
|
||||
GetSelectedTarget ()
|
||||
|
@ -224,6 +222,12 @@ public:
|
|||
ConstString
|
||||
GetTopIOHandlerControlSequence(char ch);
|
||||
|
||||
const char *
|
||||
GetIOHandlerCommandPrefix();
|
||||
|
||||
const char *
|
||||
GetIOHandlerHelpPrologue();
|
||||
|
||||
bool
|
||||
HideTopIOHandler();
|
||||
|
||||
|
@ -352,7 +356,6 @@ public:
|
|||
|
||||
bool
|
||||
GetNotifyVoid () const;
|
||||
|
||||
|
||||
const ConstString &
|
||||
GetInstanceName()
|
||||
|
|
|
@ -224,6 +224,7 @@ public:
|
|||
eCommandTypesBuiltin = 0x0001, // native commands such as "frame"
|
||||
eCommandTypesUserDef = 0x0002, // scripted commands
|
||||
eCommandTypesAliases = 0x0004, // aliases such as "po"
|
||||
eCommandTypesHidden = 0x0008, // commands prefixed with an underscore
|
||||
eCommandTypesAllThem = 0xFFFF // all commands
|
||||
};
|
||||
|
||||
|
@ -430,6 +431,11 @@ public:
|
|||
const char *command_name,
|
||||
StreamString &help_string);
|
||||
|
||||
void
|
||||
OutputFormattedHelpText (Stream &strm,
|
||||
const char *prefix,
|
||||
const char *help_text);
|
||||
|
||||
void
|
||||
OutputFormattedHelpText (Stream &stream,
|
||||
const char *command_word,
|
||||
|
@ -607,6 +613,9 @@ public:
|
|||
bool asynchronously,
|
||||
void *baton);
|
||||
|
||||
const char *
|
||||
GetCommandPrefix ();
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Properties
|
||||
//------------------------------------------------------------------
|
||||
|
|
|
@ -54,9 +54,10 @@ CommandObjectHelp::~CommandObjectHelp()
|
|||
OptionDefinition
|
||||
CommandObjectHelp::CommandOptions::g_option_table[] =
|
||||
{
|
||||
{ LLDB_OPT_SET_ALL, false, "show-aliases", 'a', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Show aliases in the command list."},
|
||||
{ LLDB_OPT_SET_ALL, false, "hide-aliases", 'a', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Hide aliases in the command list."},
|
||||
{ LLDB_OPT_SET_ALL, false, "hide-user-commands", 'u', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Hide user-defined commands from the list."},
|
||||
{ 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL }
|
||||
{ LLDB_OPT_SET_ALL, false, "show-hidden-commands", 'h', OptionParser::eNoArgument, NULL, NULL, 0, eArgTypeNone, "Include commands prefixed with an underscore."},
|
||||
{ 0, false, NULL, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
|
||||
};
|
||||
|
||||
bool
|
||||
|
@ -75,6 +76,8 @@ CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
|
|||
cmd_types |= CommandInterpreter::eCommandTypesAliases;
|
||||
if (m_options.m_show_user_defined)
|
||||
cmd_types |= CommandInterpreter::eCommandTypesUserDef;
|
||||
if (m_options.m_show_hidden)
|
||||
cmd_types |= CommandInterpreter::eCommandTypesHidden;
|
||||
|
||||
result.SetStatus (eReturnStatusSuccessFinishNoResult);
|
||||
m_interpreter.GetHelp (result, cmd_types); // General help
|
||||
|
@ -136,17 +139,19 @@ CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
|
|||
else if (!sub_cmd_obj)
|
||||
{
|
||||
result.AppendErrorWithFormat("'%s' is not a known command.\n"
|
||||
"Try 'help' to see a current list of commands.\n",
|
||||
cmd_string.c_str());
|
||||
"Try '%shelp' to see a current list of commands.\n",
|
||||
cmd_string.c_str(),
|
||||
m_interpreter.GetCommandPrefix());
|
||||
result.SetStatus (eReturnStatusFailed);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.GetOutputStream().Printf("'%s' is not a known command.\n"
|
||||
"Try 'help' to see a current list of commands.\n"
|
||||
"Try '%shelp' to see a current list of commands.\n"
|
||||
"The closest match is '%s'. Help on it follows.\n\n",
|
||||
cmd_string.c_str(),
|
||||
m_interpreter.GetCommandPrefix(),
|
||||
sub_cmd_obj->GetCommandName());
|
||||
}
|
||||
}
|
||||
|
@ -183,8 +188,9 @@ CommandObjectHelp::DoExecute (Args& command, CommandReturnObject &result)
|
|||
else
|
||||
{
|
||||
result.AppendErrorWithFormat
|
||||
("'%s' is not a known command.\nTry 'help' to see a current list of commands.\n",
|
||||
command.GetArgumentAtIndex(0));
|
||||
("'%s' is not a known command.\nTry '%shelp' to see a current list of commands.\n",
|
||||
command.GetArgumentAtIndex(0),
|
||||
m_interpreter.GetCommandPrefix());
|
||||
result.SetStatus (eReturnStatusFailed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,11 +62,14 @@ public:
|
|||
switch (short_option)
|
||||
{
|
||||
case 'a':
|
||||
m_show_aliases = true;
|
||||
m_show_aliases = false;
|
||||
break;
|
||||
case 'u':
|
||||
m_show_user_defined = false;
|
||||
break;
|
||||
case 'h':
|
||||
m_show_hidden = true;
|
||||
break;
|
||||
default:
|
||||
error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
|
||||
break;
|
||||
|
@ -78,8 +81,9 @@ public:
|
|||
void
|
||||
OptionParsingStarting ()
|
||||
{
|
||||
m_show_aliases = false;
|
||||
m_show_aliases = true;
|
||||
m_show_user_defined = true;
|
||||
m_show_hidden = false;
|
||||
}
|
||||
|
||||
const OptionDefinition*
|
||||
|
@ -95,7 +99,8 @@ public:
|
|||
// Instance variables to hold the values for command options.
|
||||
|
||||
bool m_show_aliases;
|
||||
bool m_show_user_defined;
|
||||
bool m_show_user_defined;
|
||||
bool m_show_hidden;
|
||||
};
|
||||
|
||||
virtual Options *
|
||||
|
|
|
@ -927,6 +927,18 @@ Debugger::GetTopIOHandlerControlSequence(char ch)
|
|||
return m_input_reader_stack.GetTopIOHandlerControlSequence (ch);
|
||||
}
|
||||
|
||||
const char *
|
||||
Debugger::GetIOHandlerCommandPrefix()
|
||||
{
|
||||
return m_input_reader_stack.GetTopIOHandlerCommandPrefix();
|
||||
}
|
||||
|
||||
const char *
|
||||
Debugger::GetIOHandlerHelpPrologue()
|
||||
{
|
||||
return m_input_reader_stack.GetTopIOHandlerHelpPrologue();
|
||||
}
|
||||
|
||||
void
|
||||
Debugger::RunIOHandler (const IOHandlerSP& reader_sp)
|
||||
{
|
||||
|
|
|
@ -1170,17 +1170,25 @@ void
|
|||
CommandInterpreter::GetHelp (CommandReturnObject &result,
|
||||
uint32_t cmd_types)
|
||||
{
|
||||
const char * help_prologue = GetDebugger().GetIOHandlerHelpPrologue();
|
||||
if (help_prologue != NULL)
|
||||
{
|
||||
OutputFormattedHelpText(result.GetOutputStream(), NULL, help_prologue);
|
||||
}
|
||||
|
||||
CommandObject::CommandMap::const_iterator pos;
|
||||
size_t max_len = FindLongestCommandWord (m_command_dict);
|
||||
|
||||
if ( (cmd_types & eCommandTypesBuiltin) == eCommandTypesBuiltin )
|
||||
{
|
||||
|
||||
result.AppendMessage("The following is a list of built-in, permanent debugger commands:");
|
||||
result.AppendMessage("Debugger commands:");
|
||||
result.AppendMessage("");
|
||||
|
||||
for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos)
|
||||
{
|
||||
if (!(cmd_types & eCommandTypesHidden) && (pos->first.compare(0, 1, "_") == 0))
|
||||
continue;
|
||||
|
||||
OutputFormattedHelpText (result.GetOutputStream(), pos->first.c_str(), "--", pos->second->GetHelp(),
|
||||
max_len);
|
||||
}
|
||||
|
@ -1190,8 +1198,9 @@ CommandInterpreter::GetHelp (CommandReturnObject &result,
|
|||
|
||||
if (!m_alias_dict.empty() && ( (cmd_types & eCommandTypesAliases) == eCommandTypesAliases ))
|
||||
{
|
||||
result.AppendMessage("The following is a list of your current command abbreviations "
|
||||
"(see 'help command alias' for more info):");
|
||||
result.AppendMessageWithFormat("Current command abbreviations "
|
||||
"(type '%shelp command alias' for more info):\n",
|
||||
GetCommandPrefix());
|
||||
result.AppendMessage("");
|
||||
max_len = FindLongestCommandWord (m_alias_dict);
|
||||
|
||||
|
@ -1212,7 +1221,7 @@ CommandInterpreter::GetHelp (CommandReturnObject &result,
|
|||
|
||||
if (!m_user_dict.empty() && ( (cmd_types & eCommandTypesUserDef) == eCommandTypesUserDef ))
|
||||
{
|
||||
result.AppendMessage ("The following is a list of your current user-defined commands:");
|
||||
result.AppendMessage ("Current user-defined commands:");
|
||||
result.AppendMessage("");
|
||||
max_len = FindLongestCommandWord (m_user_dict);
|
||||
for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos)
|
||||
|
@ -1223,7 +1232,8 @@ CommandInterpreter::GetHelp (CommandReturnObject &result,
|
|||
result.AppendMessage("");
|
||||
}
|
||||
|
||||
result.AppendMessage("For more information on any particular command, try 'help <command-name>'.");
|
||||
result.AppendMessageWithFormat("For more information on any command, type '%shelp <command-name>'.\n",
|
||||
GetCommandPrefix());
|
||||
}
|
||||
|
||||
CommandObject *
|
||||
|
@ -2496,6 +2506,13 @@ CommandInterpreter::SourceInitFile (bool in_cwd, CommandReturnObject &result)
|
|||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
CommandInterpreter::GetCommandPrefix()
|
||||
{
|
||||
const char * prefix = GetDebugger().GetIOHandlerCommandPrefix();
|
||||
return prefix == NULL ? "" : prefix;
|
||||
}
|
||||
|
||||
PlatformSP
|
||||
CommandInterpreter::GetPlatform (bool prefer_target_platform)
|
||||
{
|
||||
|
@ -2885,6 +2902,66 @@ CommandInterpreter::SetSynchronous (bool value)
|
|||
m_synchronous_execution = value;
|
||||
}
|
||||
|
||||
void
|
||||
CommandInterpreter::OutputFormattedHelpText (Stream &strm,
|
||||
const char *prefix,
|
||||
const char *help_text)
|
||||
{
|
||||
const uint32_t max_columns = m_debugger.GetTerminalWidth();
|
||||
if (prefix == NULL)
|
||||
prefix = "";
|
||||
|
||||
size_t prefix_width = strlen(prefix);
|
||||
size_t line_width_max = max_columns - prefix_width;
|
||||
const char *help_text_end = help_text + strlen(help_text);
|
||||
const char *line_start = help_text;
|
||||
if (line_width_max < 16)
|
||||
line_width_max = help_text_end - help_text + prefix_width;
|
||||
|
||||
strm.IndentMore (prefix_width);
|
||||
while (line_start < help_text_end)
|
||||
{
|
||||
// Break each line at the first newline or last space/tab before
|
||||
// the maximum number of characters that fit on a line. Lines with no
|
||||
// natural break are left unbroken to wrap.
|
||||
const char *line_end = help_text_end;
|
||||
const char *line_scan = line_start;
|
||||
const char *line_scan_end = help_text_end;
|
||||
while (line_scan < line_scan_end)
|
||||
{
|
||||
char next = *line_scan;
|
||||
if (next == '\t' || next == ' ')
|
||||
{
|
||||
line_end = line_scan;
|
||||
line_scan_end = line_start + line_width_max;
|
||||
}
|
||||
else if (next == '\n' || next == '\0')
|
||||
{
|
||||
line_end = line_scan;
|
||||
break;
|
||||
}
|
||||
++line_scan;
|
||||
}
|
||||
|
||||
// Prefix the first line, indent subsequent lines to line up
|
||||
if (line_start == help_text)
|
||||
strm.Write (prefix, prefix_width);
|
||||
else
|
||||
strm.Indent();
|
||||
strm.Write (line_start, line_end - line_start);
|
||||
strm.EOL();
|
||||
|
||||
// When a line breaks at whitespace consume it before continuing
|
||||
line_start = line_end;
|
||||
char next = *line_start;
|
||||
if (next == '\n')
|
||||
++line_start;
|
||||
else while (next == ' ' || next == '\t')
|
||||
next = *(++line_start);
|
||||
}
|
||||
strm.IndentLess (prefix_width);
|
||||
}
|
||||
|
||||
void
|
||||
CommandInterpreter::OutputFormattedHelpText (Stream &strm,
|
||||
const char *word_text,
|
||||
|
@ -2892,79 +2969,9 @@ CommandInterpreter::OutputFormattedHelpText (Stream &strm,
|
|||
const char *help_text,
|
||||
size_t max_word_len)
|
||||
{
|
||||
const uint32_t max_columns = m_debugger.GetTerminalWidth();
|
||||
|
||||
int indent_size = max_word_len + strlen (separator) + 2;
|
||||
|
||||
strm.IndentMore (indent_size);
|
||||
|
||||
StreamString text_strm;
|
||||
text_strm.Printf ("%-*s %s %s", (int)max_word_len, word_text, separator, help_text);
|
||||
|
||||
size_t len = text_strm.GetSize();
|
||||
const char *text = text_strm.GetData();
|
||||
if (text[len - 1] == '\n')
|
||||
{
|
||||
text_strm.EOL();
|
||||
len = text_strm.GetSize();
|
||||
}
|
||||
|
||||
if (len < max_columns)
|
||||
{
|
||||
// Output it as a single line.
|
||||
strm.Printf ("%s", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to break it up into multiple lines.
|
||||
bool first_line = true;
|
||||
int text_width;
|
||||
size_t start = 0;
|
||||
size_t end = start;
|
||||
const size_t final_end = strlen (text);
|
||||
|
||||
while (end < final_end)
|
||||
{
|
||||
if (first_line)
|
||||
text_width = max_columns - 1;
|
||||
else
|
||||
text_width = max_columns - indent_size - 1;
|
||||
|
||||
// Don't start the 'text' on a space, since we're already outputting the indentation.
|
||||
if (!first_line)
|
||||
{
|
||||
while ((start < final_end) && (text[start] == ' '))
|
||||
start++;
|
||||
}
|
||||
|
||||
end = start + text_width;
|
||||
if (end > final_end)
|
||||
end = final_end;
|
||||
else
|
||||
{
|
||||
// If we're not at the end of the text, make sure we break the line on white space.
|
||||
while (end > start
|
||||
&& text[end] != ' ' && text[end] != '\t' && text[end] != '\n')
|
||||
end--;
|
||||
assert (end > 0);
|
||||
}
|
||||
|
||||
const size_t sub_len = end - start;
|
||||
if (start != 0)
|
||||
strm.EOL();
|
||||
if (!first_line)
|
||||
strm.Indent();
|
||||
else
|
||||
first_line = false;
|
||||
assert (start <= final_end);
|
||||
assert (start + sub_len <= final_end);
|
||||
if (sub_len > 0)
|
||||
strm.Write (text + start, sub_len);
|
||||
start = end + 1;
|
||||
}
|
||||
}
|
||||
strm.EOL();
|
||||
strm.IndentLess(indent_size);
|
||||
StreamString prefix_stream;
|
||||
prefix_stream.Printf (" %-*s %s ", (int)max_word_len, word_text, separator);
|
||||
OutputFormattedHelpText (strm, prefix_stream.GetData(), help_text);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in New Issue