diff --git a/lldb/include/lldb/Core/PluginManager.h b/lldb/include/lldb/Core/PluginManager.h --- a/lldb/include/lldb/Core/PluginManager.h +++ b/lldb/include/lldb/Core/PluginManager.h @@ -243,6 +243,9 @@ static const char *GetProcessPluginDescriptionAtIndex(uint32_t idx); + static void AutoCompleteProcessName(llvm::StringRef partial_name, + CompletionRequest &request); + // ScriptInterpreter static bool RegisterPlugin(ConstString name, const char *description, lldb::ScriptLanguage script_lang, diff --git a/lldb/include/lldb/Interpreter/CommandCompletions.h b/lldb/include/lldb/Interpreter/CommandCompletions.h --- a/lldb/include/lldb/Interpreter/CommandCompletions.h +++ b/lldb/include/lldb/Interpreter/CommandCompletions.h @@ -36,10 +36,11 @@ eVariablePathCompletion = (1u << 8), eRegisterCompletion = (1u << 9), eBreakpointCompletion = (1u << 10), + eProcessPluginCompletion = (1u << 11), // 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 << 11) + eCustomCompletion = (1u << 12) }; static bool InvokeCommonCompletionCallbacks( @@ -89,6 +90,10 @@ static void Breakpoints(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); + + static void ProcessPluginNames(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher); }; } // namespace lldb_private diff --git a/lldb/source/Commands/CommandCompletions.cpp b/lldb/source/Commands/CommandCompletions.cpp --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -58,6 +58,7 @@ {eVariablePathCompletion, CommandCompletions::VariablePath}, {eRegisterCompletion, CommandCompletions::Registers}, {eBreakpointCompletion, CommandCompletions::Breakpoints}, + {eProcessPluginCompletion, CommandCompletions::ProcessPluginNames}, {eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -582,3 +583,10 @@ request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info); } } + +void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(), + request); +} \ No newline at end of file diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -325,34 +325,38 @@ int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos; int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index; - // We are only completing the name option for now... - - // Are we in the name? - if (GetDefinitions()[opt_defs_index].short_option != 'n') - return; - - // Look to see if there is a -P argument provided, and if so use that - // plugin, otherwise use the default plugin. - - const char *partial_name = nullptr; - partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); - - PlatformSP platform_sp(interpreter.GetPlatform(true)); - if (!platform_sp) - return; - ProcessInstanceInfoList process_infos; - ProcessInstanceInfoMatch match_info; - if (partial_name) { - match_info.GetProcessInfo().GetExecutableFile().SetFile( - partial_name, FileSpec::Style::native); - match_info.SetNameMatchType(NameMatch::StartsWith); - } - platform_sp->FindProcesses(match_info, process_infos); - const size_t num_matches = process_infos.size(); - if (num_matches == 0) - return; - for (size_t i = 0; i < num_matches; ++i) { - request.AddCompletion(process_infos[i].GetNameAsStringRef()); + switch (GetDefinitions()[opt_defs_index].short_option) { + case 'n': { + // Look to see if there is a -P argument provided, and if so use that + // plugin, otherwise use the default plugin. + + const char *partial_name = nullptr; + partial_name = request.GetParsedLine().GetArgumentAtIndex(opt_arg_pos); + + PlatformSP platform_sp(interpreter.GetPlatform(true)); + if (!platform_sp) + return; + ProcessInstanceInfoList process_infos; + ProcessInstanceInfoMatch match_info; + if (partial_name) { + match_info.GetProcessInfo().GetExecutableFile().SetFile( + partial_name, FileSpec::Style::native); + match_info.SetNameMatchType(NameMatch::StartsWith); + } + platform_sp->FindProcesses(match_info, process_infos); + const size_t num_matches = process_infos.size(); + if (num_matches == 0) + return; + for (size_t i = 0; i < num_matches; ++i) { + request.AddCompletion(process_infos[i].GetNameAsStringRef()); + } + } break; + + case 'P': + CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, CommandCompletions::eProcessPluginCompletion, request, + nullptr); + break; } } diff --git a/lldb/source/Core/PluginManager.cpp b/lldb/source/Core/PluginManager.cpp --- a/lldb/source/Core/PluginManager.cpp +++ b/lldb/source/Core/PluginManager.cpp @@ -830,6 +830,14 @@ return GetProcessInstances().GetCallbackForName(name); } +void PluginManager::AutoCompleteProcessName(llvm::StringRef name, + CompletionRequest &request) { + for (const auto &instance : GetProcessInstances().GetInstances()) { + if (instance.name.GetStringRef().startswith(name)) + request.AddCompletion(instance.name.GetCString(), instance.description); + } +} + #pragma mark ScriptInterpreter struct ScriptInterpreterInstance diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -1080,7 +1080,7 @@ { eArgTypePermissionsNumber, "perms-numeric", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as an octal number (e.g. 755)." }, { eArgTypePermissionsString, "perms=string", CommandCompletions::eNoCompletion, { nullptr, false }, "Permissions given as a string value (e.g. rw-r-xr--)." }, { eArgTypePid, "pid", CommandCompletions::eNoCompletion, { nullptr, false }, "The process ID number." }, - { eArgTypePlugin, "plugin", CommandCompletions::eNoCompletion, { nullptr, false }, "Help text goes here." }, + { eArgTypePlugin, "plugin", CommandCompletions::eProcessPluginCompletion, { nullptr, false }, "Help text goes here." }, { eArgTypeProcessName, "process-name", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of the process." }, { eArgTypePythonClass, "python-class", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python class." }, { eArgTypePythonFunction, "python-function", CommandCompletions::eNoCompletion, { nullptr, false }, "The name of a Python function." }, diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py --- a/lldb/test/API/functionalities/completion/TestCompletion.py +++ b/lldb/test/API/functionalities/completion/TestCompletion.py @@ -85,6 +85,13 @@ ['mips', 'arm64']) + def test_process_plugin_completion(self): + subcommands = ['attach -P', 'connect -p', 'launch -p'] + + for subcommand in subcommands: + self.complete_from_to('process ' + subcommand + ' mac', + 'process ' + subcommand + ' mach-o-core') + def test_process_signal(self): # The tab completion for "process signal" won't work without a running process. self.complete_from_to('process signal ',