Index: lldb/include/lldb/Interpreter/CommandCompletions.h =================================================================== --- lldb/include/lldb/Interpreter/CommandCompletions.h +++ lldb/include/lldb/Interpreter/CommandCompletions.h @@ -37,10 +37,12 @@ eRegisterCompletion = (1u << 9), eBreakpointCompletion = (1u << 10), eProcessPluginCompletion = (1u << 11), + eRemoteDiskFileCompletion = (1u << 12), + eRemoteDiskDirectoryCompletion = (1u << 13), // This item serves two purposes. It is the last element in the enum, so // you can add custom enums starting from here in your Option class. Also // if you & in this bit the base code will not process the option. - eCustomCompletion = (1u << 12) + eCustomCompletion = (1u << 14) }; static bool InvokeCommonCompletionCallbacks( @@ -62,6 +64,14 @@ StringList &matches, TildeExpressionResolver &Resolver); + static void RemoteDiskFiles(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher); + + static void RemoteDiskDirectories(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher); + static void SourceFiles(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); Index: lldb/include/lldb/Target/Platform.h =================================================================== --- lldb/include/lldb/Target/Platform.h +++ lldb/include/lldb/Target/Platform.h @@ -523,6 +523,9 @@ return UINT64_MAX; } + virtual void AutoCompleteDiskFileOrDirectory(CompletionRequest &request, + bool only_dir) {} + virtual uint64_t ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Status &error) { error.SetErrorStringWithFormat( Index: lldb/include/lldb/Utility/StringExtractorGDBRemote.h =================================================================== --- lldb/include/lldb/Utility/StringExtractorGDBRemote.h +++ lldb/include/lldb/Utility/StringExtractorGDBRemote.h @@ -90,6 +90,7 @@ eServerPacketType_vFile_stat, eServerPacketType_vFile_symlink, eServerPacketType_vFile_unlink, + eServerPacketType_vFile_autocomplete, // debug server packages eServerPacketType_QEnvironmentHexEncoded, eServerPacketType_QListThreadsInStopReply, Index: lldb/source/Commands/CommandCompletions.cpp =================================================================== --- lldb/source/Commands/CommandCompletions.cpp +++ lldb/source/Commands/CommandCompletions.cpp @@ -59,6 +59,9 @@ {eRegisterCompletion, CommandCompletions::Registers}, {eBreakpointCompletion, CommandCompletions::Breakpoints}, {eProcessPluginCompletion, CommandCompletions::ProcessPluginNames}, + {eRemoteDiskFileCompletion, CommandCompletions::RemoteDiskFiles}, + {eRemoteDiskDirectoryCompletion, + CommandCompletions::RemoteDiskDirectories}, {eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -472,6 +475,24 @@ DiskFilesOrDirectories(partial_file_name, true, matches, Resolver); } +void CommandCompletions::RemoteDiskFiles(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::PlatformSP platform_sp = + interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + if (platform_sp) + platform_sp->AutoCompleteDiskFileOrDirectory(request, false); +} + +void CommandCompletions::RemoteDiskDirectories(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::PlatformSP platform_sp = + interpreter.GetDebugger().GetPlatformList().GetSelectedPlatform(); + if (platform_sp) + platform_sp->AutoCompleteDiskFileOrDirectory(request, true); +} + void CommandCompletions::Modules(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { Index: lldb/source/Commands/CommandObjectPlatform.cpp =================================================================== --- lldb/source/Commands/CommandObjectPlatform.cpp +++ lldb/source/Commands/CommandObjectPlatform.cpp @@ -392,7 +392,8 @@ "or for a platform by name.", "platform settings", 0), m_options(), - m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', 0, + m_option_working_dir(LLDB_OPT_SET_1, false, "working-dir", 'w', + CommandCompletions::eRemoteDiskDirectoryCompletion, eArgTypePath, "The working directory for the platform.") { m_options.Append(&m_option_working_dir, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1); @@ -485,6 +486,15 @@ ~CommandObjectPlatformFOpen() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (request.GetCursorIndex() == 0) + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), + CommandCompletions::eRemoteDiskFileCompletion, request, nullptr); + } + bool DoExecute(Args &args, CommandReturnObject &result) override { PlatformSP platform_sp( GetDebugger().GetPlatformList().GetSelectedPlatform()); @@ -817,6 +827,19 @@ ~CommandObjectPlatformGetFile() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (request.GetCursorIndex() == 0) + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), + CommandCompletions::eRemoteDiskFileCompletion, request, nullptr); + else if (request.GetCursorIndex() == 1) + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, + request, nullptr); + } + bool DoExecute(Args &args, CommandReturnObject &result) override { // If the number of arguments is incorrect, issue an error message. if (args.GetArgumentCount() != 2) { @@ -882,6 +905,17 @@ ~CommandObjectPlatformGetSize() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (request.GetCursorIndex() != 0) + return; + + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eRemoteDiskFileCompletion, + request, nullptr); + } + bool DoExecute(Args &args, CommandReturnObject &result) override { // If the number of arguments is incorrect, issue an error message. if (args.GetArgumentCount() != 1) { @@ -927,6 +961,19 @@ ~CommandObjectPlatformPutFile() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (request.GetCursorIndex() == 0) + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eDiskFileCompletion, + request, nullptr); + else if (request.GetCursorIndex() == 1) + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), + CommandCompletions::eRemoteDiskFileCompletion, request, nullptr); + } + bool DoExecute(Args &args, CommandReturnObject &result) override { const char *src = args.GetArgumentAtIndex(0); const char *dst = args.GetArgumentAtIndex(1); Index: lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h =================================================================== --- lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h +++ lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.h @@ -127,6 +127,9 @@ lldb::user_id_t GetFileSize(const FileSpec &file_spec) override; + void AutoCompleteDiskFileOrDirectory(CompletionRequest &request, + bool only_dir) override; + Status PutFile(const FileSpec &source, const FileSpec &destination, uint32_t uid = UINT32_MAX, uint32_t gid = UINT32_MAX) override; Index: lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp =================================================================== --- lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp +++ lldb/source/Plugins/Platform/gdb-server/PlatformRemoteGDBServer.cpp @@ -661,6 +661,11 @@ return m_gdb_client.GetFileSize(file_spec); } +void PlatformRemoteGDBServer::AutoCompleteDiskFileOrDirectory( + CompletionRequest &request, bool only_dir) { + m_gdb_client.AutoCompleteDiskFileOrDirectory(request, only_dir); +} + uint64_t PlatformRemoteGDBServer::ReadFile(lldb::user_id_t fd, uint64_t offset, void *dst, uint64_t dst_len, Status &error) { Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h @@ -375,6 +375,9 @@ lldb::user_id_t GetFileSize(const FileSpec &file_spec); + void AutoCompleteDiskFileOrDirectory(CompletionRequest &request, + bool only_dir); + Status GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp @@ -2981,6 +2981,29 @@ return UINT64_MAX; } +void GDBRemoteCommunicationClient::AutoCompleteDiskFileOrDirectory( + CompletionRequest &request, bool only_dir) { + lldb_private::StreamString stream; + stream.PutCString("vFile:autocomplete:"); + stream.PutStringAsRawHex8(request.GetCursorArgumentPrefix()); + stream.PutChar(','); + stream.PutHex32(only_dir ? 1 : 0); + StringExtractorGDBRemote response; + if (SendPacketAndWaitForResponse(stream.GetString(), response, false) == + PacketResult::Success) { + if (response.GetChar() != 'F') + return; + + llvm::StringRef matches = response.GetStringRef().drop_front(); + // Skip the first 'F' charactor. + while (matches.size()) { + std::pair str_pair = matches.split(','); + request.AddCompletion(str_pair.first); + matches = str_pair.second; + } + } +} + Status GDBRemoteCommunicationClient::GetFilePermissions(const FileSpec &file_spec, uint32_t &file_permissions) { Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.h @@ -77,6 +77,8 @@ PacketResult Handle_vFile_MD5(StringExtractorGDBRemote &packet); + PacketResult Handle_vFile_autocomplete(StringExtractorGDBRemote &packet); + PacketResult Handle_qEcho(StringExtractorGDBRemote &packet); PacketResult Handle_qModuleInfo(StringExtractorGDBRemote &packet); Index: lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp =================================================================== --- lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp +++ lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerCommon.cpp @@ -25,6 +25,7 @@ #include "lldb/Host/Host.h" #include "lldb/Host/HostInfo.h" #include "lldb/Host/SafeMachO.h" +#include "lldb/Interpreter/CommandCompletions.h" #include "lldb/Interpreter/OptionArgParser.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/Platform.h" @@ -33,6 +34,7 @@ #include "lldb/Utility/Log.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" +#include "lldb/Utility/TildeExpressionResolver.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/Support/JSON.h" @@ -172,6 +174,9 @@ RegisterMemberFunctionHandler( StringExtractorGDBRemote::eServerPacketType_vFile_unlink, &GDBRemoteCommunicationServerCommon::Handle_vFile_unlink); + RegisterMemberFunctionHandler( + StringExtractorGDBRemote::eServerPacketType_vFile_autocomplete, + &GDBRemoteCommunicationServerCommon::Handle_vFile_autocomplete); } // Destructor @@ -723,6 +728,33 @@ return SendPacketNoLock(response.GetString()); } +GDBRemoteCommunication::PacketResult +GDBRemoteCommunicationServerCommon::Handle_vFile_autocomplete( + StringExtractorGDBRemote &packet) { + packet.SetFilePos(::strlen("vFile:autocomplete:")); + std::string path; + packet.GetHexByteStringTerminatedBy(path, ','); + if (packet.GetChar() != ',') + return SendErrorResponse(24); // FIXME: How to choose the error value? + const bool only_dir = (packet.GetHexMaxU32(false, 0) == 1); + + StringList matches; + StandardTildeExpressionResolver resolver; + if (only_dir) + CommandCompletions::DiskDirectories(path, matches, resolver); + else + CommandCompletions::DiskFiles(path, matches, resolver); + + StreamString response; + response.PutChar('F'); + for (const auto &match : matches) { + if (response.GetSize() > 1) + response.PutChar(','); + response.PutCString(match.c_str()); + } + return SendPacketNoLock(response.GetString()); +} + GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerCommon::Handle_qPlatform_shell( StringExtractorGDBRemote &packet) { Index: lldb/source/Utility/StringExtractorGDBRemote.cpp =================================================================== --- lldb/source/Utility/StringExtractorGDBRemote.cpp +++ lldb/source/Utility/StringExtractorGDBRemote.cpp @@ -334,7 +334,8 @@ return eServerPacketType_vFile_symlink; else if (PACKET_STARTS_WITH("vFile:unlink")) return eServerPacketType_vFile_unlink; - + else if (PACKET_STARTS_WITH("vFile:autocomplete")) + return eServerPacketType_vFile_autocomplete; } else { if (PACKET_STARTS_WITH("vAttach;")) return eServerPacketType_vAttach;