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 @@ -35,10 +35,11 @@ eArchitectureCompletion = (1u << 7), eVariablePathCompletion = (1u << 8), eRegisterCompletion = (1u << 9), + eBreakpointCompletion = (1u << 10), // 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 << 10) + eCustomCompletion = (1u << 11) }; static bool InvokeCommonCompletionCallbacks( @@ -85,6 +86,9 @@ static void Registers(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); + + static void Breakpoints(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 @@ -57,6 +57,7 @@ {eArchitectureCompletion, CommandCompletions::ArchitectureNames}, {eVariablePathCompletion, CommandCompletions::VariablePath}, {eRegisterCompletion, CommandCompletions::Registers}, + {eBreakpointCompletion, CommandCompletions::Breakpoints}, {eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -549,4 +550,35 @@ request.TryCompleteCurrentArg(reg_prefix + reg_info->name, reg_info->alt_name); } -} \ No newline at end of file +} + +void CommandCompletions::Breakpoints(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget(); + if (!target) + return; + + const BreakpointList &breakpoints = target->GetBreakpointList(); + + std::unique_lock lock; + target->GetBreakpointList().GetListMutex(lock); + + size_t num_breakpoints = breakpoints.GetSize(); + if (num_breakpoints == 0) + return; + + for (size_t i = 0; i < num_breakpoints; ++i) { + lldb::BreakpointSP bp = breakpoints.GetBreakpointAtIndex(i); + + StreamString s; + bp->GetDescription(&s, lldb::eDescriptionLevelBrief); + llvm::StringRef bp_info = s.GetString(); + + const size_t colon_pos = bp_info.find_first_of(':'); + if (colon_pos != llvm::StringRef::npos) + bp_info = bp_info.drop_front(colon_pos + 2); + + request.TryCompleteCurrentArg(std::to_string(bp->GetID()), bp_info); + } +} diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -819,6 +819,14 @@ ~CommandObjectBreakpointModify() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion, + request, nullptr); + } + Options *GetOptions() override { return &m_options; } protected: @@ -885,6 +893,14 @@ ~CommandObjectBreakpointEnable() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion, + request, nullptr); + } + protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target &target = GetSelectedOrDummyTarget(); @@ -993,6 +1009,14 @@ ~CommandObjectBreakpointDisable() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion, + request, nullptr); + } + protected: bool DoExecute(Args &command, CommandReturnObject &result) override { Target &target = GetSelectedOrDummyTarget(); @@ -1371,6 +1395,14 @@ ~CommandObjectBreakpointDelete() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CommandCompletions::InvokeCommonCompletionCallbacks( + GetCommandInterpreter(), CommandCompletions::eBreakpointCompletion, + request, nullptr); + } + Options *GetOptions() override { return &m_options; } class CommandOptions : public Options { 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 @@ -477,3 +477,40 @@ # register write can only take exact one register name as argument self.complete_from_to('register write rbx ', []) + + def test_breakpoint_enable_disable_delete_modify_with_ids(self): + """These four breakpoint subcommands should be completed with a list of breakpoint ids""" + + subcommands = ['enable', 'disable', 'delete', 'modify'] + + # The tab completion here is unavailable without a target + for subcommand in subcommands: + self.complete_from_to('breakpoint ' + subcommand + ' ', + 'breakpoint ' + subcommand + ' ') + + self.build() + target = self.dbg.CreateTarget(self.getBuildArtifact('a.out')) + self.assertTrue(target, VALID_TARGET) + + bp = target.BreakpointCreateByName('main', 'a.out') + self.assertTrue(bp) + self.assertEqual(bp.GetNumLocations(), 1) + + for subcommand in subcommands: + self.complete_from_to('breakpoint ' + subcommand + ' ', + ['1']) + + bp2 = target.BreakpointCreateByName('Bar', 'a.out') + self.assertTrue(bp2) + self.assertEqual(bp2.GetNumLocations(), 1) + + for subcommand in subcommands: + self.complete_from_to('breakpoint ' + subcommand + ' ', + ['1', + '2']) + + for subcommand in subcommands: + self.complete_from_to('breakpoint ' + subcommand + ' 1 ', + ['1', + '2']) +