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:
Kate Stone 2015-01-15 00:52:41 +00:00
parent 64e033f9c4
commit a487aa4cdb
6 changed files with 134 additions and 92 deletions

View File

@ -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()

View File

@ -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
//------------------------------------------------------------------

View File

@ -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);
}
}

View File

@ -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 *

View File

@ -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)
{

View File

@ -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