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 @@ -44,10 +44,11 @@ eStopHookIDCompletion = (1u << 16), eThreadIndexCompletion = (1u << 17), eWatchPointIDCompletion = (1u << 18), + eBreakpointNameCompletion = (1u << 19), // 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 << 19) + eCustomCompletion = (1u << 20) }; static bool InvokeCommonCompletionCallbacks( @@ -101,6 +102,10 @@ static void Breakpoints(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); + static void BreakpointNames(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher); + static void ProcessPluginNames(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher); 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 @@ -70,6 +70,7 @@ {eStopHookIDCompletion, CommandCompletions::StopHookIDs}, {eThreadIndexCompletion, CommandCompletions::ThreadIndexes}, {eWatchPointIDCompletion, CommandCompletions::WatchPointIDs}, + {eBreakpointNameCompletion, CommandCompletions::BreakpointNames}, {eNoCompletion, nullptr} // This one has to be last in the list. }; @@ -617,13 +618,26 @@ } } +void CommandCompletions::BreakpointNames(CommandInterpreter &interpreter, + CompletionRequest &request, + SearchFilter *searcher) { + lldb::TargetSP target = interpreter.GetDebugger().GetSelectedTarget(); + if (!target) + return; + + std::vector name_list; + target->GetBreakpointNames(name_list); + + for (const std::string &name : name_list) + request.TryCompleteCurrentArg(name); +} + void CommandCompletions::ProcessPluginNames(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { PluginManager::AutoCompleteProcessName(request.GetCursorArgumentPrefix(), request); } - void CommandCompletions::DisassemblyFlavors(CommandInterpreter &interpreter, CompletionRequest &request, SearchFilter *searcher) { 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 @@ -2097,7 +2097,79 @@ return llvm::makeArrayRef(g_breakpoint_read_options); } - // Instance variables to hold the values for command options. + void HandleOptionArgumentCompletion( + CompletionRequest &request, OptionElementVector &opt_element_vector, + int opt_element_index, CommandInterpreter &interpreter) override { + 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; + + switch (GetDefinitions()[opt_defs_index].short_option) { + case 'f': + CommandCompletions::InvokeCommonCompletionCallbacks( + interpreter, CommandCompletions::eDiskFileCompletion, request, + nullptr); + break; + + case 'N': + llvm::Optional file_spec; + const llvm::StringRef dash_f("-f"); + for (int arg_idx = 0; arg_idx < opt_arg_pos; arg_idx++) { + if (dash_f == request.GetParsedLine().GetArgumentAtIndex(arg_idx)) { + file_spec.emplace( + request.GetParsedLine().GetArgumentAtIndex(arg_idx + 1)); + break; + } + } + if (!file_spec) + return; + + FileSystem::Instance().Resolve(*file_spec); + Status error; + StructuredData::ObjectSP input_data_sp = + StructuredData::ParseJSONFromFile(*file_spec, error); + if (!error.Success()) + return; + + StructuredData::Array *bkpt_array = input_data_sp->GetAsArray(); + if (!bkpt_array) + return; + + const size_t num_bkpts = bkpt_array->GetSize(); + for (size_t i = 0; i < num_bkpts; i++) { + StructuredData::ObjectSP bkpt_object_sp = + bkpt_array->GetItemAtIndex(i); + if (!bkpt_object_sp) + return; + + StructuredData::Dictionary *bkpt_dict = + bkpt_object_sp->GetAsDictionary(); + if (!bkpt_dict) + return; + + StructuredData::ObjectSP bkpt_data_sp = + bkpt_dict->GetValueForKey(Breakpoint::GetSerializationKey()); + if (!bkpt_data_sp) + return; + + bkpt_dict = bkpt_data_sp->GetAsDictionary(); + if (!bkpt_dict) + return; + + StructuredData::Array *names_array; + + if (!bkpt_dict->GetValueForKeyAsArray("Names", names_array)) + return; + + size_t num_names = names_array->GetSize(); + + for (size_t i = 0; i < num_names; i++) { + llvm::StringRef name; + if (names_array->GetItemAtIndexAsString(i, name)) + request.TryCompleteCurrentArg(name); + } + } + } + } std::string m_filename; std::vector m_names; 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 @@ -1041,7 +1041,7 @@ { eArgTypeBoolean, "boolean", CommandCompletions::eNoCompletion, { nullptr, false }, "A Boolean value: 'true' or 'false'" }, { eArgTypeBreakpointID, "breakpt-id", CommandCompletions::eNoCompletion, { BreakpointIDHelpTextCallback, false }, nullptr }, { eArgTypeBreakpointIDRange, "breakpt-id-list", CommandCompletions::eNoCompletion, { BreakpointIDRangeHelpTextCallback, false }, nullptr }, - { eArgTypeBreakpointName, "breakpoint-name", CommandCompletions::eNoCompletion, { BreakpointNameHelpTextCallback, false }, nullptr }, + { eArgTypeBreakpointName, "breakpoint-name", CommandCompletions::eBreakpointNameCompletion, { BreakpointNameHelpTextCallback, false }, nullptr }, { eArgTypeByteSize, "byte-size", CommandCompletions::eNoCompletion, { nullptr, false }, "Number of bytes to use." }, { eArgTypeClassName, "class-name", CommandCompletions::eNoCompletion, { nullptr, false }, "Then name of a class from the debug information in the program." }, { eArgTypeCommandName, "cmd-name", CommandCompletions::eNoCompletion, { nullptr, false }, "A debugger command (may be multiple words), without any options or arguments." }, 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 @@ -644,3 +644,21 @@ ['1', '2']) + def test_complete_breakpoint_with_names(self): + self.build() + target = self.dbg.CreateTarget(self.getBuildArtifact('a.out')) + self.assertTrue(target, VALID_TARGET) + + # test breakpoint read dedicated + self.complete_from_to('breakpoint read -N ', 'breakpoint read -N ') + self.complete_from_to('breakpoint read -f breakpoints.json -N ', ['mm']) + self.complete_from_to('breakpoint read -f breakpoints.json -N n', 'breakpoint read -f breakpoints.json -N n') + self.complete_from_to('breakpoint read -f breakpoints_invalid.json -N ', 'breakpoint read -f breakpoints_invalid.json -N ') + + # test common breapoint name completion + bp1 = target.BreakpointCreateByName('main', 'a.out') + self.assertTrue(bp1) + self.assertEqual(bp1.GetNumLocations(), 1) + self.complete_from_to('breakpoint set -N n', 'breakpoint set -N n') + self.assertTrue(bp1.AddNameWithErrorHandling("nn")) + self.complete_from_to('breakpoint set -N ', 'breakpoint set -N nn') diff --git a/lldb/test/API/functionalities/completion/breakpoints.json b/lldb/test/API/functionalities/completion/breakpoints.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/completion/breakpoints.json @@ -0,0 +1,34 @@ +[ + { + "Breakpoint": { + "BKPTOptions": { + "AutoContinue": false, + "ConditionText": "", + "EnabledState": true, + "IgnoreCount": 0, + "OneShotState": false + }, + "BKPTResolver": { + "Options": { + "NameMask": [ + 56 + ], + "Offset": 0, + "SkipPrologue": true, + "SymbolNames": [ + "main" + ] + }, + "Type": "SymbolName" + }, + "Hardware": false, + "Names": [ + "mm" + ], + "SearchFilter": { + "Options": {}, + "Type": "Unconstrained" + } + } + } +] diff --git a/lldb/test/API/functionalities/completion/breakpoints_invalid.json b/lldb/test/API/functionalities/completion/breakpoints_invalid.json new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/completion/breakpoints_invalid.json @@ -0,0 +1,6 @@ +[ + { + "Breakpoint": { + } + } +]