Index: lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py =================================================================== --- lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py +++ lldb/packages/Python/lldbsuite/test/functionalities/completion/TestCompletion.py @@ -409,3 +409,27 @@ # No completion for Qu because the candidate is # (anonymous namespace)::Quux(). self.complete_from_to('breakpoint set -n Qu', '') + + def test_target_select_and_delete(self): + self.build() + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + + self.complete_from_to('target select ', + ['0', + '1', + '2']) + + self.complete_from_to('target select 0 ', + []) + + self.complete_from_to('target delete ', + ['0', + '1', + '2']) + + self.complete_from_to('target delete 1 ', + ['0', + '1', + '2']) Index: lldb/source/Commands/CommandObjectTarget.cpp =================================================================== --- lldb/source/Commands/CommandObjectTarget.cpp +++ lldb/source/Commands/CommandObjectTarget.cpp @@ -60,7 +60,8 @@ static void DumpTargetInfo(uint32_t target_idx, Target *target, const char *prefix_cstr, - bool show_stopped_process_status, Stream &strm) { + bool show_stopped_process_status, + bool print_target_index, Stream &strm) { const ArchSpec &target_arch = target->GetArchitecture(); Module *exe_module = target->GetExecutableModulePointer(); @@ -72,8 +73,13 @@ if (!exe_valid) ::strcpy(exe_path, ""); - strm.Printf("%starget #%u: %s", prefix_cstr ? prefix_cstr : "", target_idx, - exe_path); + if (prefix_cstr) + strm << prefix_cstr; + + if (print_target_index) + strm.Printf("target #%u: ", target_idx); + + strm << exe_path; uint32_t properties = 0; if (target_arch.IsValid()) { @@ -125,14 +131,36 @@ TargetSP target_sp(target_list.GetTargetAtIndex(i)); if (target_sp) { bool is_selected = target_sp.get() == selected_target_sp.get(); + bool print_target_index = true; DumpTargetInfo(i, target_sp.get(), is_selected ? "* " : " ", - show_stopped_process_status, strm); + show_stopped_process_status, print_target_index, strm); } } } return num_targets; } +static void CompleteTargetIndex(CompletionRequest &request, + Debugger &debugger) { + TargetList &target_list = debugger.GetTargetList(); + size_t number_of_targets = target_list.GetNumTargets(); + for (size_t target_i = 0; target_i < number_of_targets; ++target_i) { + // The completion is only the target index. + std::string completion = std::to_string(target_i); + + // Generate the description. + TargetSP target_sp(target_list.GetTargetAtIndex(target_i)); + StreamString stream; + bool show_stopped_process_status = false; + bool print_thread_index = false; + DumpTargetInfo(target_i, target_sp.get(), "", show_stopped_process_status, + print_thread_index, stream); + llvm::StringRef description = stream.GetString().rtrim(); + + request.TryCompleteCurrentArg(completion, description); + } +} + // Note that the negation in the argument name causes a slightly confusing // mapping of the enum values. static constexpr OptionEnumValueElement g_dependents_enumaration[] = { @@ -524,6 +552,14 @@ ~CommandObjectTargetSelect() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (request.GetCursorIndex() != 0) + return; + CompleteTargetIndex(request, GetDebugger()); + } + protected: bool DoExecute(Args &args, CommandReturnObject &result) override { if (args.GetArgumentCount() == 1) { @@ -604,6 +640,12 @@ Options *GetOptions() override { return &m_option_group; } + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + CompleteTargetIndex(request, GetDebugger()); + } + protected: bool DoExecute(Args &args, CommandReturnObject &result) override { const size_t argc = args.GetArgumentCount();