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 @@ -34,10 +34,11 @@ ePlatformPluginCompletion = (1u << 6), eArchitectureCompletion = (1u << 7), eVariablePathCompletion = (1u << 8), + eRegisterCompletion = (1u << 9), // 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 << 9) + eCustomCompletion = (1u << 10) }; static bool InvokeCommonCompletionCallbacks( @@ -81,6 +82,9 @@ static void VariablePath(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); + + static void Registers(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 @@ -18,6 +18,7 @@ #include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Variable.h" +#include "lldb/Target/RegisterContext.h" #include "lldb/Utility/FileSpec.h" #include "lldb/Utility/StreamString.h" #include "lldb/Utility/TildeExpressionResolver.h" @@ -55,6 +56,7 @@ {ePlatformPluginCompletion, CommandCompletions::PlatformPluginNames}, {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, {eVariablePathCompletion, CommandCompletions::VariablePath}, + {eRegisterCompletion, CommandCompletions::Registers}, {eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -531,3 +533,20 @@ SearchFilter *searcher) { Variable::AutoComplete(interpreter.GetExecutionContext(), request); } + +void CommandCompletions::Registers(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + std::string reg_prefix = ""; + if (request.GetCursorArgumentPrefix().startswith("$")) + reg_prefix = "$"; + + RegisterContext *reg_ctx = + interpreter.GetExecutionContext().GetRegisterContext(); + const size_t reg_num = reg_ctx->GetRegisterCount(); + for (size_t reg_idx = 0; reg_idx < reg_num; ++reg_idx) { + const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_idx); + request.TryCompleteCurrentArg(reg_prefix + reg_info->name, + reg_info->alt_name); + } +} \ No newline at end of file diff --git a/lldb/source/Commands/CommandObjectRegister.cpp b/lldb/source/Commands/CommandObjectRegister.cpp --- a/lldb/source/Commands/CommandObjectRegister.cpp +++ b/lldb/source/Commands/CommandObjectRegister.cpp @@ -70,6 +70,17 @@ ~CommandObjectRegisterRead() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (!m_exe_ctx.HasProcessScope()) + return; + + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eRegisterCompletion, + request, nullptr); + } + Options *GetOptions() override { return &m_option_group; } bool DumpRegister(const ExecutionContext &exe_ctx, Stream &strm, @@ -323,6 +334,17 @@ ~CommandObjectRegisterWrite() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (!m_exe_ctx.HasProcessScope() || request.GetCursorIndex() != 0) + return; + + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eRegisterCompletion, + request, nullptr); + } + protected: bool DoExecute(Args &command, CommandReturnObject &result) override { DataExtractor reg_data; 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 @@ -414,3 +414,66 @@ # No completion for Qu because the candidate is # (anonymous namespace)::Quux(). self.complete_from_to('breakpoint set -n Qu', '') + + @skipIf(archs=no_match(['x86_64'])) + def test_register_read_and_write_on_x86(self): + """Test the completion of the commands register read and write on x86""" + + # The tab completion for "register read/write" won't work without a running process. + self.complete_from_to('register read ', + 'register read ') + self.complete_from_to('register write ', + 'register write ') + + self.build() + self.main_source_spec = lldb.SBFileSpec("main.cpp") + lldbutil.run_to_source_breakpoint(self, '// Break here', self.main_source_spec) + + # test cases for register read + self.complete_from_to('register read ', + ['rax', + 'rbx', + 'rcx']) + self.complete_from_to('register read r', + ['rax', + 'rbx', + 'rcx']) + self.complete_from_to('register read ra', + 'register read rax') + # register read can take multiple register names as arguments + self.complete_from_to('register read rax ', + ['rax', + 'rbx', + 'rcx']) + # complete with prefix '$' + self.completions_match('register read $rb', + ['$rbx', + '$rbp']) + self.completions_match('register read $ra', + ['$rax']) + self.complete_from_to('register read rax $', + ['\$rax', + '\$rbx', + '\$rcx']) + self.complete_from_to('register read $rax ', + ['rax', + 'rbx', + 'rcx']) + + # test cases for register write + self.complete_from_to('register write ', + ['rax', + 'rbx', + 'rcx']) + self.complete_from_to('register write r', + ['rax', + 'rbx', + 'rcx']) + self.complete_from_to('register write ra', + 'register write rax') + self.complete_from_to('register write rb', + ['rbx', + 'rbp']) + # register write can only take exact one register name as argument + self.complete_from_to('register write rbx ', + [])