From 5edee822d2f80e23c9e465394bac6d9c31468406 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Tue, 27 Aug 2019 11:32:22 +0000 Subject: [PATCH] [lldb] Allow partial completions to fix directory completion. On the command line we usually insert a space after a completion to indicate that the completion was successful. After the completion API refactoring, this also happens with directories which essentially breaks file path completion (as adding a space terminates the path and starts a new arg). This patch restores the old behavior by again allowing partial completions. Also extends the iohandler and SB API tests as the implementation for this is different in Editline and SB API. llvm-svn: 370043 --- lldb/include/lldb/Utility/CompletionRequest.h | 8 ++++++ .../completion/TestCompletion.py | 7 +++++ .../completion/TestIOHandlerCompletion.py | 14 ++++++++++ lldb/source/Commands/CommandCompletions.cpp | 27 ++++++++++++------- lldb/source/Host/common/Editline.cpp | 6 +++++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/lldb/include/lldb/Utility/CompletionRequest.h b/lldb/include/lldb/Utility/CompletionRequest.h index 88ee3344a3f5..28ac78a87565 100644 --- a/lldb/include/lldb/Utility/CompletionRequest.h +++ b/lldb/include/lldb/Utility/CompletionRequest.h @@ -17,7 +17,15 @@ namespace lldb_private { enum class CompletionMode { + // The current token has been completed. Normal, + // The current token has been partially completed. This means that we found + // a completion, but that the completed token is still incomplete. Examples + // for this are file paths, where we want to complete "/bi" to "/bin/", but + // the file path token is still incomplete after the completion. Clients + // should not indicate to the user that this is a full completion (e.g. by + // not inserting the usual trailing space after a successful completion). + Partial, // The full line has been rewritten by the completion. RewriteLine, }; diff --git a/lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py b/lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py index b95d490c8872..0b5b3a416a03 100644 --- a/lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py +++ b/lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py @@ -137,6 +137,13 @@ class CommandLineCompletionTestCase(TestBase): self.complete_from_to('log enable lldb expr -f ' + src_dir, ['main.cpp']) + @skipIfFreeBSD # timing out on the FreeBSD buildbot + def test_log_file(self): + # Complete in our source directory which contains a 'main.cpp' file. + src_dir = os.path.dirname(os.path.realpath(__file__)) + self.complete_from_to('log enable lldb expr -f ' + src_dir, + [src_dir + "/"]) + # @skipIfFreeBSD # timing out on the FreeBSD buildbot def test_infinite_loop_while_completing(self): diff --git a/lldb/packages/Python/lldbsuite/test/iohandler/completion/TestIOHandlerCompletion.py b/lldb/packages/Python/lldbsuite/test/iohandler/completion/TestIOHandlerCompletion.py index 26805bfb07cb..a6748c656195 100644 --- a/lldb/packages/Python/lldbsuite/test/iohandler/completion/TestIOHandlerCompletion.py +++ b/lldb/packages/Python/lldbsuite/test/iohandler/completion/TestIOHandlerCompletion.py @@ -2,6 +2,8 @@ Test completion in our IOHandlers. """ +import os + import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * @@ -48,6 +50,18 @@ class IOHandlerCompletionTest(TestBase): self.expect_string(prompt + "register") self.child.send("\n") + # Try tab completing directories and files. Also tests the partial + # completion where LLDB shouldn't print a space after the directory + # completion (as it didn't completed the full token). + dir_without_slashes = os.path.realpath(os.path.dirname(__file__)).rstrip("/") + self.child.send("file " + dir_without_slashes + "\t") + self.expect_string("iohandler/completion/") + # If we get a correct partial completion without a trailing space, then this + # should complete the current test file. + self.child.send("TestIOHandler\t") + self.expect_string("TestIOHandlerCompletion.py") + self.child.send("\n") + # Start tab completion and abort showing more commands with 'n'. self.child.send("\t") self.expect_string("More (Y/n/a)") diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp index a06f9a4db539..f00a35d3d81b 100644 --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -87,10 +87,9 @@ void CommandCompletions::SourceFiles(CommandInterpreter &interpreter, } static void DiskFilesOrDirectories(const llvm::Twine &partial_name, - bool only_directories, StringList &matches, + bool only_directories, + CompletionRequest &request, TildeExpressionResolver &Resolver) { - matches.Clear(); - llvm::SmallString<256> CompletionBuffer; llvm::SmallString<256> Storage; partial_name.toVector(CompletionBuffer); @@ -124,7 +123,7 @@ static void DiskFilesOrDirectories(const llvm::Twine &partial_name, for (const auto &S : MatchSet) { Resolved = S.getKey(); path::append(Resolved, path::get_separator()); - matches.AppendString(Resolved); + request.AddCompletion(Resolved, "", CompletionMode::Partial); } } return; @@ -136,7 +135,7 @@ static void DiskFilesOrDirectories(const llvm::Twine &partial_name, if (FirstSep == llvm::StringRef::npos) { // Make sure it ends with a separator. path::append(CompletionBuffer, path::get_separator()); - matches.AppendString(CompletionBuffer); + request.AddCompletion(CompletionBuffer, "", CompletionMode::Partial); return; } @@ -217,17 +216,27 @@ static void DiskFilesOrDirectories(const llvm::Twine &partial_name, path::append(CompletionBuffer, path::get_separator()); } - matches.AppendString(CompletionBuffer); + CompletionMode mode = + is_dir ? CompletionMode::Partial : CompletionMode::Normal; + request.AddCompletion(CompletionBuffer, "", mode); } } +static void DiskFilesOrDirectories(const llvm::Twine &partial_name, + bool only_directories, StringList &matches, + TildeExpressionResolver &Resolver) { + CompletionResult result; + std::string partial_name_str = partial_name.str(); + CompletionRequest request(partial_name_str, partial_name_str.size(), result); + DiskFilesOrDirectories(partial_name, only_directories, request, Resolver); + result.GetMatches(matches); +} + static void DiskFilesOrDirectories(CompletionRequest &request, bool only_directories) { StandardTildeExpressionResolver resolver; - StringList matches; DiskFilesOrDirectories(request.GetCursorArgumentPrefix(), only_directories, - matches, resolver); - request.AddCompletions(matches); + request, resolver); } void CommandCompletions::DiskFiles(CommandInterpreter &interpreter, diff --git a/lldb/source/Host/common/Editline.cpp b/lldb/source/Host/common/Editline.cpp index a2e8773f8f1f..0a95dffa40ee 100644 --- a/lldb/source/Host/common/Editline.cpp +++ b/lldb/source/Host/common/Editline.cpp @@ -954,6 +954,12 @@ unsigned char Editline::TabCommand(int ch) { el_insertstr(m_editline, to_add.c_str()); break; } + case CompletionMode::Partial: { + std::string to_add = completion.GetCompletion(); + to_add = to_add.substr(request.GetCursorArgumentPrefix().size()); + el_insertstr(m_editline, to_add.c_str()); + break; + } case CompletionMode::RewriteLine: { el_deletestr(m_editline, line_info->cursor - line_info->buffer); el_insertstr(m_editline, completion.GetCompletion().c_str()); -- 2.34.1