diff options
author | Raphael Isemann <teemperor@gmail.com> | 2018-09-13 21:26:00 +0000 |
---|---|---|
committer | Raphael Isemann <teemperor@gmail.com> | 2018-09-13 21:26:00 +0000 |
commit | 7f88829ceabef9ea9ff4743e8ba67f676d8c0968 (patch) | |
tree | a6811b305d3e768aedfd9f73b41c1b95f5b2d651 /lldb/source | |
parent | 9b3e7c365c3cd09954b7d3b4445d7b462bb3ec02 (diff) | |
download | llvm-7f88829ceabef9ea9ff4743e8ba67f676d8c0968.zip llvm-7f88829ceabef9ea9ff4743e8ba67f676d8c0968.tar.gz llvm-7f88829ceabef9ea9ff4743e8ba67f676d8c0968.tar.bz2 |
Add support for descriptions with command completions.
Summary:
This patch adds a framework for adding descriptions to the command completions we provide.
It also adds descriptions for completed top-level commands so that we can test this code.
Completions are in general supposed to be displayed alongside the completion itself. The descriptions
can be used to provide additional information about the completion to the user. Examples for descriptions
are function signatures when completing function calls in the expression command or the binary name
when providing completion for a symbol.
There is still some boilerplate code from the old completion API left in LLDB (mostly because the respective
APIs are reused for non-completion related purposes, so the CompletionRequest doesn't make sense to be
used), so that's why I still had to change some function signatures. Also, as the old API only passes around a
list of matches, and the descriptions are for these functions just another list, I had to add some code that
essentially just ensures that both lists are always the same side (e.g. all the manual calls to
`descriptions->AddString(X)` below a `matches->AddString(Y)` call).
The initial command descriptions that come with this patch are just reusing the existing
short help that is already added in LLDB.
An example completion with descriptions looks like this:
```
(lldb) pl
Available completions:
platform -- Commands to manage and create platforms.
plugin -- Commands for managing LLDB plugins.
```
Reviewers: #lldb, jingham
Reviewed By: #lldb, jingham
Subscribers: jingham, JDevlieghere, lldb-commits
Differential Revision: https://reviews.llvm.org/D51175
llvm-svn: 342181
Diffstat (limited to 'lldb/source')
-rw-r--r-- | lldb/source/API/SBCommandInterpreter.cpp | 31 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectMultiword.cpp | 7 | ||||
-rw-r--r-- | lldb/source/Core/IOHandler.cpp | 40 | ||||
-rw-r--r-- | lldb/source/Expression/REPL.cpp | 4 | ||||
-rw-r--r-- | lldb/source/Host/common/Editline.cpp | 44 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandInterpreter.cpp | 75 | ||||
-rw-r--r-- | lldb/source/Utility/CompletionRequest.cpp | 43 |
7 files changed, 169 insertions, 75 deletions
diff --git a/lldb/source/API/SBCommandInterpreter.cpp b/lldb/source/API/SBCommandInterpreter.cpp index cbb514a..58182d0 100644 --- a/lldb/source/API/SBCommandInterpreter.cpp +++ b/lldb/source/API/SBCommandInterpreter.cpp @@ -269,6 +269,16 @@ void SBCommandInterpreter::HandleCommandsFromFile( int SBCommandInterpreter::HandleCompletion( const char *current_line, const char *cursor, const char *last_char, int match_start_point, int max_return_elements, SBStringList &matches) { + SBStringList dummy_descriptions; + return HandleCompletionWithDescriptions( + current_line, cursor, last_char, match_start_point, max_return_elements, + matches, dummy_descriptions); +} + +int SBCommandInterpreter::HandleCompletionWithDescriptions( + const char *current_line, const char *cursor, const char *last_char, + int match_start_point, int max_return_elements, SBStringList &matches, + SBStringList &descriptions) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API)); int num_completions = 0; @@ -296,13 +306,15 @@ int SBCommandInterpreter::HandleCompletion( match_start_point, max_return_elements); if (IsValid()) { - lldb_private::StringList lldb_matches; + lldb_private::StringList lldb_matches, lldb_descriptions; num_completions = m_opaque_ptr->HandleCompletion( current_line, cursor, last_char, match_start_point, max_return_elements, - lldb_matches); + lldb_matches, lldb_descriptions); - SBStringList temp_list(&lldb_matches); - matches.AppendList(temp_list); + SBStringList temp_matches_list(&lldb_matches); + matches.AppendList(temp_matches_list); + SBStringList temp_descriptions_list(&lldb_descriptions); + descriptions.AppendList(temp_descriptions_list); } if (log) log->Printf( @@ -312,6 +324,17 @@ int SBCommandInterpreter::HandleCompletion( return num_completions; } +int SBCommandInterpreter::HandleCompletionWithDescriptions( + const char *current_line, uint32_t cursor_pos, int match_start_point, + int max_return_elements, SBStringList &matches, + SBStringList &descriptions) { + const char *cursor = current_line + cursor_pos; + const char *last_char = current_line + strlen(current_line); + return HandleCompletionWithDescriptions( + current_line, cursor, last_char, match_start_point, max_return_elements, + matches, descriptions); +} + int SBCommandInterpreter::HandleCompletion(const char *current_line, uint32_t cursor_pos, int match_start_point, diff --git a/lldb/source/Commands/CommandObjectMultiword.cpp b/lldb/source/Commands/CommandObjectMultiword.cpp index 19fcf60..9e67cf7 100644 --- a/lldb/source/Commands/CommandObjectMultiword.cpp +++ b/lldb/source/Commands/CommandObjectMultiword.cpp @@ -193,9 +193,10 @@ int CommandObjectMultiword::HandleCompletion(CompletionRequest &request) { auto arg0 = request.GetParsedLine()[0].ref; if (request.GetCursorIndex() == 0) { - StringList new_matches; - AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches); - request.AddCompletions(new_matches); + StringList new_matches, descriptions; + AddNamesMatchingPartialString(m_subcommand_dict, arg0, new_matches, + &descriptions); + request.AddCompletions(new_matches, descriptions); if (new_matches.GetSize() == 1 && new_matches.GetStringAtIndex(0) != nullptr && diff --git a/lldb/source/Core/IOHandler.cpp b/lldb/source/Core/IOHandler.cpp index 236eb8d..4c29ced 100644 --- a/lldb/source/Core/IOHandler.cpp +++ b/lldb/source/Core/IOHandler.cpp @@ -172,12 +172,10 @@ IOHandlerConfirm::IOHandlerConfirm(Debugger &debugger, llvm::StringRef prompt, IOHandlerConfirm::~IOHandlerConfirm() = default; -int IOHandlerConfirm::IOHandlerComplete(IOHandler &io_handler, - const char *current_line, - const char *cursor, - const char *last_char, - int skip_first_n_matches, - int max_matches, StringList &matches) { +int IOHandlerConfirm::IOHandlerComplete( + IOHandler &io_handler, const char *current_line, const char *cursor, + const char *last_char, int skip_first_n_matches, int max_matches, + StringList &matches, StringList &descriptions) { if (current_line == cursor) { if (m_default_response) { matches.AppendString("y"); @@ -223,12 +221,10 @@ void IOHandlerConfirm::IOHandlerInputComplete(IOHandler &io_handler, } } -int IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, - const char *current_line, - const char *cursor, - const char *last_char, - int skip_first_n_matches, - int max_matches, StringList &matches) { +int IOHandlerDelegate::IOHandlerComplete( + IOHandler &io_handler, const char *current_line, const char *cursor, + const char *last_char, int skip_first_n_matches, int max_matches, + StringList &matches, StringList &descriptions) { switch (m_completion) { case Completion::None: break; @@ -236,14 +232,16 @@ int IOHandlerDelegate::IOHandlerComplete(IOHandler &io_handler, case Completion::LLDBCommand: return io_handler.GetDebugger().GetCommandInterpreter().HandleCompletion( current_line, cursor, last_char, skip_first_n_matches, max_matches, - matches); - + matches, descriptions); case Completion::Expression: { + CompletionResult result; CompletionRequest request(current_line, current_line - cursor, - skip_first_n_matches, max_matches, matches); + skip_first_n_matches, max_matches, result); CommandCompletions::InvokeCommonCompletionCallbacks( io_handler.GetDebugger().GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion, request, nullptr); + result.GetMatches(matches); + result.GetDescriptions(descriptions); size_t num_matches = request.GetNumberOfMatches(); if (num_matches > 0) { @@ -432,17 +430,15 @@ int IOHandlerEditline::FixIndentationCallback(Editline *editline, *editline_reader, lines, cursor_position); } -int IOHandlerEditline::AutoCompleteCallback(const char *current_line, - const char *cursor, - const char *last_char, - int skip_first_n_matches, - int max_matches, - StringList &matches, void *baton) { +int IOHandlerEditline::AutoCompleteCallback( + const char *current_line, const char *cursor, const char *last_char, + int skip_first_n_matches, int max_matches, StringList &matches, + StringList &descriptions, void *baton) { IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; if (editline_reader) return editline_reader->m_delegate.IOHandlerComplete( *editline_reader, current_line, cursor, last_char, skip_first_n_matches, - max_matches, matches); + max_matches, matches, descriptions); return 0; } #endif diff --git a/lldb/source/Expression/REPL.cpp b/lldb/source/Expression/REPL.cpp index a441e38..50d4a09 100644 --- a/lldb/source/Expression/REPL.cpp +++ b/lldb/source/Expression/REPL.cpp @@ -453,7 +453,7 @@ void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) { int REPL::IOHandlerComplete(IOHandler &io_handler, const char *current_line, const char *cursor, const char *last_char, int skip_first_n_matches, int max_matches, - StringList &matches) { + StringList &matches, StringList &descriptions) { matches.Clear(); llvm::StringRef line(current_line, cursor - current_line); @@ -466,7 +466,7 @@ int REPL::IOHandlerComplete(IOHandler &io_handler, const char *current_line, const char *lldb_current_line = line.substr(1).data(); return debugger.GetCommandInterpreter().HandleCompletion( lldb_current_line, cursor, last_char, skip_first_n_matches, max_matches, - matches); + matches, descriptions); } // Strip spaces from the line and see if we had only spaces diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index 9c25832..71b0332 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -853,19 +853,45 @@ unsigned char Editline::BufferEndCommand(int ch) { return CC_NEWLINE; } +//------------------------------------------------------------------------------ +/// Prints completions and their descriptions to the given file. Only the +/// completions in the interval [start, end) are printed. +//------------------------------------------------------------------------------ +static void PrintCompletion(FILE *output_file, size_t start, size_t end, + StringList &completions, StringList &descriptions) { + // This is an 'int' because of printf. + int max_len = 0; + + for (size_t i = start; i < end; i++) { + const char *completion_str = completions.GetStringAtIndex(i); + max_len = std::max((int)strlen(completion_str), max_len); + } + + for (size_t i = start; i < end; i++) { + const char *completion_str = completions.GetStringAtIndex(i); + const char *description_str = descriptions.GetStringAtIndex(i); + + fprintf(output_file, "\n\t%-*s", max_len, completion_str); + + // Print the description if we got one. + if (strlen(description_str)) + fprintf(output_file, " -- %s", description_str); + } +} + unsigned char Editline::TabCommand(int ch) { if (m_completion_callback == nullptr) return CC_ERROR; const LineInfo *line_info = el_line(m_editline); - StringList completions; + StringList completions, descriptions; int page_size = 40; const int num_completions = m_completion_callback( line_info->buffer, line_info->cursor, line_info->lastchar, 0, // Don't skip any matches (start at match zero) -1, // Get all the matches - completions, m_completion_callback_baton); + completions, descriptions, m_completion_callback_baton); if (num_completions == 0) return CC_ERROR; @@ -893,10 +919,8 @@ unsigned char Editline::TabCommand(int ch) { int num_elements = num_completions + 1; fprintf(m_output_file, "\n" ANSI_CLEAR_BELOW "Available completions:"); if (num_completions < page_size) { - for (int i = 1; i < num_elements; i++) { - completion_str = completions.GetStringAtIndex(i); - fprintf(m_output_file, "\n\t%s", completion_str); - } + PrintCompletion(m_output_file, 1, num_elements, completions, + descriptions); fprintf(m_output_file, "\n"); } else { int cur_pos = 1; @@ -906,10 +930,10 @@ unsigned char Editline::TabCommand(int ch) { int endpoint = cur_pos + page_size; if (endpoint > num_elements) endpoint = num_elements; - for (; cur_pos < endpoint; cur_pos++) { - completion_str = completions.GetStringAtIndex(cur_pos); - fprintf(m_output_file, "\n\t%s", completion_str); - } + + PrintCompletion(m_output_file, cur_pos, endpoint, completions, + descriptions); + cur_pos = endpoint; if (cur_pos >= num_elements) { fprintf(m_output_file, "\n"); diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 6482227..6cc5da5 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -794,20 +794,23 @@ void CommandInterpreter::LoadCommandDictionary() { } int CommandInterpreter::GetCommandNamesMatchingPartialString( - const char *cmd_str, bool include_aliases, StringList &matches) { - AddNamesMatchingPartialString(m_command_dict, cmd_str, matches); + const char *cmd_str, bool include_aliases, StringList &matches, + StringList &descriptions) { + AddNamesMatchingPartialString(m_command_dict, cmd_str, matches, + &descriptions); if (include_aliases) { - AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches); + AddNamesMatchingPartialString(m_alias_dict, cmd_str, matches, + &descriptions); } return matches.GetSize(); } -CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, - bool include_aliases, - bool exact, - StringList *matches) const { +CommandObjectSP +CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, bool include_aliases, + bool exact, StringList *matches, + StringList *descriptions) const { CommandObjectSP command_sp; std::string cmd = cmd_str; @@ -848,8 +851,8 @@ CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, // empty CommandObjectSP and the list of matches. if (HasCommands()) { - num_cmd_matches = - AddNamesMatchingPartialString(m_command_dict, cmd_str, *matches); + num_cmd_matches = AddNamesMatchingPartialString(m_command_dict, cmd_str, + *matches, descriptions); } if (num_cmd_matches == 1) { @@ -860,8 +863,8 @@ CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, } if (include_aliases && HasAliases()) { - num_alias_matches = - AddNamesMatchingPartialString(m_alias_dict, cmd_str, *matches); + num_alias_matches = AddNamesMatchingPartialString(m_alias_dict, cmd_str, + *matches, descriptions); } if (num_alias_matches == 1) { @@ -872,8 +875,8 @@ CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, } if (HasUserCommands()) { - num_user_matches = - AddNamesMatchingPartialString(m_user_dict, cmd_str, *matches); + num_user_matches = AddNamesMatchingPartialString(m_user_dict, cmd_str, + *matches, descriptions); } if (num_user_matches == 1) { @@ -898,6 +901,8 @@ CommandObjectSP CommandInterpreter::GetCommandSP(llvm::StringRef cmd_str, } } else if (matches && command_sp) { matches->AppendString(cmd_str); + if (descriptions) + descriptions->AppendString(command_sp->GetHelp()); } return command_sp; @@ -997,10 +1002,12 @@ CommandObjectSP CommandInterpreter::GetCommandSPExact(llvm::StringRef cmd_str, return ret_val; } -CommandObject *CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, - StringList *matches) const { +CommandObject * +CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, + StringList *matches, + StringList *descriptions) const { CommandObject *command_obj = - GetCommandSP(cmd_str, false, true, matches).get(); + GetCommandSP(cmd_str, false, true, matches, descriptions).get(); // If we didn't find an exact match to the command string in the commands, // look in the aliases. @@ -1008,7 +1015,7 @@ CommandObject *CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, if (command_obj) return command_obj; - command_obj = GetCommandSP(cmd_str, true, true, matches).get(); + command_obj = GetCommandSP(cmd_str, true, true, matches, descriptions).get(); if (command_obj) return command_obj; @@ -1023,10 +1030,12 @@ CommandObject *CommandInterpreter::GetCommandObject(llvm::StringRef cmd_str, if (command_obj) { if (matches) matches->AppendString(command_obj->GetCommandName()); + if (descriptions) + descriptions->AppendString(command_obj->GetHelp()); return command_obj; } - return GetCommandSP(cmd_str, true, false, matches).get(); + return GetCommandSP(cmd_str, true, false, matches, descriptions).get(); } bool CommandInterpreter::CommandExists(llvm::StringRef cmd) const { @@ -1712,16 +1721,17 @@ int CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) { if (request.GetCursorIndex() == -1) { // We got nothing on the command line, so return the list of commands bool include_aliases = true; - StringList new_matches; - num_command_matches = - GetCommandNamesMatchingPartialString("", include_aliases, new_matches); - request.AddCompletions(new_matches); + StringList new_matches, descriptions; + num_command_matches = GetCommandNamesMatchingPartialString( + "", include_aliases, new_matches, descriptions); + request.AddCompletions(new_matches, descriptions); } else if (request.GetCursorIndex() == 0) { // The cursor is in the first argument, so just do a lookup in the // dictionary. - StringList new_matches; - CommandObject *cmd_obj = GetCommandObject( - request.GetParsedLine().GetArgumentAtIndex(0), &new_matches); + StringList new_matches, new_descriptions; + CommandObject *cmd_obj = + GetCommandObject(request.GetParsedLine().GetArgumentAtIndex(0), + &new_matches, &new_descriptions); if (num_command_matches == 1 && cmd_obj && cmd_obj->IsMultiwordObject() && new_matches.GetStringAtIndex(0) != nullptr && @@ -1733,12 +1743,13 @@ int CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) { look_for_subcommand = true; num_command_matches = 0; new_matches.DeleteStringAtIndex(0); + new_descriptions.DeleteStringAtIndex(0); request.GetParsedLine().AppendArgument(llvm::StringRef()); request.SetCursorIndex(request.GetCursorIndex() + 1); request.SetCursorCharPosition(0); } } - request.AddCompletions(new_matches); + request.AddCompletions(new_matches, new_descriptions); num_command_matches = request.GetNumberOfMatches(); } @@ -1762,12 +1773,13 @@ int CommandInterpreter::HandleCompletionMatches(CompletionRequest &request) { int CommandInterpreter::HandleCompletion( const char *current_line, const char *cursor, const char *last_char, - int match_start_point, int max_return_elements, StringList &matches) { + int match_start_point, int max_return_elements, StringList &matches, + StringList &descriptions) { llvm::StringRef command_line(current_line, last_char - current_line); + CompletionResult result; CompletionRequest request(command_line, cursor - current_line, - match_start_point, max_return_elements, matches); - + match_start_point, max_return_elements, result); // Don't complete comments, and if the line we are completing is just the // history repeat character, substitute the appropriate history line. const char *first_arg = request.GetParsedLine().GetArgumentAtIndex(0); @@ -1777,6 +1789,7 @@ int CommandInterpreter::HandleCompletion( else if (first_arg[0] == CommandHistory::g_repeat_char) { if (auto hist_str = m_command_history.FindString(first_arg)) { matches.InsertStringAtIndex(0, *hist_str); + descriptions.InsertStringAtIndex(0, "Previous command history event"); return -2; } else return 0; @@ -1787,6 +1800,8 @@ int CommandInterpreter::HandleCompletion( lldbassert(max_return_elements == -1); int num_command_matches = HandleCompletionMatches(request); + result.GetMatches(matches); + result.GetDescriptions(descriptions); if (num_command_matches <= 0) return num_command_matches; @@ -1794,6 +1809,7 @@ int CommandInterpreter::HandleCompletion( if (request.GetParsedLine().GetArgumentCount() == 0) { // If we got an empty string, insert nothing. matches.InsertStringAtIndex(0, ""); + descriptions.InsertStringAtIndex(0, ""); } else { // Now figure out if there is a common substring, and if so put that in // element 0, otherwise put an empty string in element 0. @@ -1815,6 +1831,7 @@ int CommandInterpreter::HandleCompletion( common_prefix.push_back(' '); } matches.InsertStringAtIndex(0, common_prefix.c_str()); + descriptions.InsertStringAtIndex(0, ""); } return num_command_matches; } diff --git a/lldb/source/Utility/CompletionRequest.cpp b/lldb/source/Utility/CompletionRequest.cpp index c88747e..096661b 100644 --- a/lldb/source/Utility/CompletionRequest.cpp +++ b/lldb/source/Utility/CompletionRequest.cpp @@ -16,11 +16,10 @@ CompletionRequest::CompletionRequest(llvm::StringRef command_line, unsigned raw_cursor_pos, int match_start_point, int max_return_elements, - StringList &matches) + CompletionResult &result) : m_command(command_line), m_raw_cursor_pos(raw_cursor_pos), m_match_start_point(match_start_point), - m_max_return_elements(max_return_elements), m_matches(&matches) { - matches.Clear(); + m_max_return_elements(max_return_elements), m_result(result) { // We parse the argument up to the cursor, so the last argument in // parsed_line is the one containing the cursor, and the cursor is after the @@ -36,8 +35,6 @@ CompletionRequest::CompletionRequest(llvm::StringRef command_line, m_cursor_char_position = strlen(m_partial_parsed_line.GetArgumentAtIndex(m_cursor_index)); - matches.Clear(); - const char *cursor = command_line.data() + raw_cursor_pos; if (raw_cursor_pos > 0 && cursor[-1] == ' ') { // We are just after a space. If we are in an argument, then we will @@ -58,3 +55,39 @@ CompletionRequest::CompletionRequest(llvm::StringRef command_line, } } } + +std::string CompletionResult::Completion::GetUniqueKey() const { + + // We build a unique key for this pair of completion:description. We + // prefix the key with the length of the completion string. This prevents + // that we could get any collisions from completions pairs such as these: + // "foo:", "bar" would be "foo:bar", but will now be: "4foo:bar" + // "foo", ":bar" would be "foo:bar", but will now be: "3foo:bar" + + std::string result; + result.append(std::to_string(m_completion.size())); + result.append(m_completion); + result.append(m_descripton); + return result; +} + +void CompletionResult::AddResult(llvm::StringRef completion, + llvm::StringRef description) { + Completion r(completion, description); + + // Add the completion if we haven't seen the same value before. + if (m_added_values.insert(r.GetUniqueKey()).second) + m_results.push_back(r); +} + +void CompletionResult::GetMatches(StringList &matches) const { + matches.Clear(); + for (const Completion &completion : m_results) + matches.AppendString(completion.m_completion); +} + +void CompletionResult::GetDescriptions(StringList &descriptions) const { + descriptions.Clear(); + for (const Completion &completion : m_results) + descriptions.AppendString(completion.m_descripton); +} |