diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h --- a/lldb/include/lldb/Target/Thread.h +++ b/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 diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp --- a/lldb/source/Commands/CommandObjectThread.cpp +++ b/lldb/source/Commands/CommandObjectThread.cpp @@ -1985,6 +1985,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) { diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -1103,6 +1103,22 @@ 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()); + i++; + } +} + ThreadPlan *Thread::GetCurrentPlan() const { return GetPlans().GetCurrentPlan().get(); } 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 @@ -329,6 +329,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'].""" diff --git a/lldb/test/API/functionalities/completion/thread_plan_script.py b/lldb/test/API/functionalities/completion/thread_plan_script.py new file mode 100644 --- /dev/null +++ b/lldb/test/API/functionalities/completion/thread_plan_script.py @@ -0,0 +1,20 @@ +############################################################################# +# This script is just to provide a thread plan which won't be popped instantly +# for the completion test. The thread plan class below won't really do anything +# itself. + +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