From 0e50b9a43bf65714cc6669203aeb1c175878a7da Mon Sep 17 00:00:00 2001 From: Gongyu Deng Date: Mon, 11 May 2020 15:21:37 +0200 Subject: [PATCH] Complete breakpoint enable/disable/delete/modify with a list of breakpoint IDs Summary: 1. A new common completion `CommandCompletions::Breakpoints` to provide a list of the breakpoints of the current context; 2. Apply the completion above to the commands breakpoint enable/disable/delete/modify; 3. Unit test. Reviewers: teemperor, JDevlieghere Reviewed By: teemperor Tags: #lldb Differential Revision: https://reviews.llvm.org/D79666 --- .../lldb/Interpreter/CommandCompletions.h | 6 ++- lldb/source/Commands/CommandCompletions.cpp | 34 ++++++++++++++++- .../Commands/CommandObjectBreakpoint.cpp | 32 ++++++++++++++++ .../completion/TestCompletion.py | 37 +++++++++++++++++++ 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/lldb/include/lldb/Interpreter/CommandCompletions.h b/lldb/include/lldb/Interpreter/CommandCompletions.h index a6e025e72baf..dc2bf841620d 100644 --- a/lldb/include/lldb/Interpreter/CommandCompletions.h +++ b/lldb/include/lldb/Interpreter/CommandCompletions.h @@ -35,10 +35,11 @@ public: 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 @@ public: 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 index 1e903157c511..d4e4f6a5ebb5 100644 --- a/lldb/source/Commands/CommandCompletions.cpp +++ b/lldb/source/Commands/CommandCompletions.cpp @@ -57,6 +57,7 @@ bool CommandCompletions::InvokeCommonCompletionCallbacks( {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 @@ void CommandCompletions::Registers(CommandInterpreter &interpreter, 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 index e62a1154797e..11cded687913 100644 --- a/lldb/source/Commands/CommandObjectBreakpoint.cpp +++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp @@ -819,6 +819,14 @@ public: ~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 @@ public: ~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 @@ the second re-enables the first location."); ~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 @@ public: ~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 index 5e8f79934345..3a9c30a3e62a 100644 --- a/lldb/test/API/functionalities/completion/TestCompletion.py +++ b/lldb/test/API/functionalities/completion/TestCompletion.py @@ -477,3 +477,40 @@ class CommandLineCompletionTestCase(TestBase): # 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']) +