Index: lldb/include/lldb/Target/Thread.h =================================================================== --- lldb/include/lldb/Target/Thread.h +++ lldb/include/lldb/Target/Thread.h @@ -19,6 +19,7 @@ #include "lldb/Target/RegisterCheckpoint.h" #include "lldb/Target/StackFrameList.h" #include "lldb/Utility/Broadcaster.h" +#include "lldb/Utility/CompletionRequest.h" #include "lldb/Utility/Event.h" #include "lldb/Utility/StructuredData.h" #include "lldb/Utility/UserID.h" @@ -911,6 +912,12 @@ // Thread Plan accessors: + /// Format the thread plan information for auto completion. + /// + /// \param[in] request + /// The reference to the completion handler. + void AutoCompleteThreadPlans(CompletionRequest &request) const; + /// Gets the plan which will execute next on the plan stack. /// /// \return Index: lldb/source/Commands/CommandObjectThread.cpp =================================================================== --- lldb/source/Commands/CommandObjectThread.cpp +++ lldb/source/Commands/CommandObjectThread.cpp @@ -1978,6 +1978,15 @@ ~CommandObjectThreadPlanDiscard() override = default; + void + HandleArgumentCompletion(CompletionRequest &request, + OptionElementVector &opt_element_vector) override { + if (!m_exe_ctx.HasThreadScope() || request.GetCursorIndex()) + return; + + m_exe_ctx.GetThreadPtr()->AutoCompleteThreadPlans(request); + } + bool DoExecute(Args &args, CommandReturnObject &result) override { Thread *thread = m_exe_ctx.GetThreadPtr(); if (args.GetArgumentCount() != 1) { Index: lldb/source/Target/Thread.cpp =================================================================== --- lldb/source/Target/Thread.cpp +++ lldb/source/Target/Thread.cpp @@ -1103,6 +1103,21 @@ discarded_plan_sp->GetThread().GetID()); } +void Thread::AutoCompleteThreadPlans(CompletionRequest &request) const { + const ThreadPlanStack &plans = GetPlans(); + if (!plans.AnyPlans()) + return; + + // Iterate from the second plan (index: 1) to skip the base plan. + ThreadPlanSP p; + uint32_t i = 1; + while (p = plans.GetPlanByIndex(i, false)) { + StreamString strm; + p->GetDescription(&strm, eDescriptionLevelInitial); + request.TryCompleteCurrentArg(std::to_string(i++), strm.GetString()); + } +} + ThreadPlan *Thread::GetCurrentPlan() const { return GetPlans().GetCurrentPlan().get(); } Index: lldb/test/API/functionalities/completion/TestCompletion.py =================================================================== --- lldb/test/API/functionalities/completion/TestCompletion.py +++ lldb/test/API/functionalities/completion/TestCompletion.py @@ -316,6 +316,19 @@ ['target.process.thread.step-avoid-regexp', 'target.process.thread.trace-thread']) + def test_thread_plan_discard(self): + self.build() + (_, _, thread, _) = lldbutil.run_to_source_breakpoint(self, + 'ptr_foo', lldb.SBFileSpec("main.cpp")) + self.assertTrue(thread) + self.complete_from_to('thread plan discard ', 'thread plan discard ') + + source_path = os.path.join(self.getSourceDir(), "thread_plan_script.py") + self.runCmd("command script import '%s'"%(source_path)) + self.runCmd("thread step-scripted -C thread_plan_script.PushPlanStack") + self.complete_from_to('thread plan discard ', 'thread plan discard 1') + self.runCmd('thread plan discard 1') + def test_target_space(self): """Test that 'target ' completes to ['create', 'delete', 'list', 'modules', 'select', 'stop-hook', 'variable'].""" Index: lldb/test/API/functionalities/completion/thread_plan_script.py =================================================================== --- /dev/null +++ lldb/test/API/functionalities/completion/thread_plan_script.py @@ -0,0 +1,15 @@ +import lldb + +class PushPlanStack: + + def __init__(self, thread_plan, dict): + pass + + def explains_stop(self, event): + return False + + def should_stop(self, event): + return True + + def should_step(self): + return True