Skip to content

Commit b36910b

Browse files
committed
PyDebugger Plugin: implemented basic watch functionality, but does not allow expanding child members of a watched object. Also cleaned up some of the cruft
1 parent daf5128 commit b36910b

2 files changed

Lines changed: 170 additions & 151 deletions

File tree

‎PythonDebugger/PyDebugger.cpp‎

Lines changed: 109 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <wx/regex.h>
44
#include <backtracedlg.h>
55
#include <cbstyledtextctrl.h>
6+
#include <watchesdlg.h>
67

78
// Register the plugin with Code::Blocks.
89
// We are using an anonymous namespace so we don't litter the global one.
@@ -44,16 +45,7 @@ int ID_LangMenu_RunPiped = wxNewId();//XRCID("idPyDebuggerMenuDebug");
4445

4546
// events handling
4647
BEGIN_EVENT_TABLE(PyDebugger, cbDebuggerPlugin)
47-
// add any events you want to handle here
48-
//// EVT_MENU(ID_LangMenu_Run,PyDebugger::OnRun)
49-
//// EVT_MENU(ID_LangMenu_RunPiped,PyDebugger::OnDebugTarget)
50-
//// EVT_MENU(XRCID("idPyDebuggerMenuDebug"),PyDebugger::OnContinue)
51-
//// EVT_MENU(XRCID("idPyDebuggerMenuNext"),PyDebugger::OnNext)
52-
//// EVT_MENU(XRCID("idPyDebuggerMenuStep"),PyDebugger::OnStep)
53-
//// EVT_MENU(XRCID("idPyDebuggerMenuStop"),PyDebugger::OnStop)
5448
EVT_MENU(ID_LangMenu_DebugSendCommand,PyDebugger::OnSendCommand)
55-
EVT_MENU(ID_LangMenu_ShowWatch,PyDebugger::OnViewWatch)
56-
EVT_MENU(ID_LangMenu_UpdateWatch,PyDebugger::OnUpdateWatch)
5749
EVT_END_PROCESS(ID_PipedProcess, PyDebugger::OnTerminatePipedProcess)
5850
EVT_TIMER(ID_TimerPollDebugger, PyDebugger::OnTimer)
5951
END_EVENT_TABLE()
@@ -183,63 +175,19 @@ wxString PyDebugger::AssembleBreakpointCommands()
183175
}
184176
return commands;
185177
}
186-
178+
187179

188180
wxString PyDebugger::AssembleWatchCommands()
189-
{ //TODO: get this working
190-
wxString commands;
191-
wxString watchtext=m_WatchDlg->m_WatchText->GetValue();
192-
wxRegEx re,re1,re2,re3,re4;
193-
//Not sure regexes are really required, something simpler would also work...
194-
re1.Compile(_T("^ *([^\\t]*)(\\t.*)*"),wxRE_ADVANCED); // evaluate expression: syntax "expr = value" expr may contain == so regex must only match on single =
195-
re2.Compile(_T("^ *([^\\t]*)\\: *(\\t.*)*$"),wxRE_ADVANCED); // evaluate members: syntax "objname:"
196-
re3.Compile(_T("^ *[\\.\\*]"),wxRE_ADVANCED); // subordinates of object in the form ".varname\tvalue" and errors will be deleted
197-
re4.Compile(_T("^ *(\\#.*)"),wxRE_ADVANCED); // comment line syntax "# blah blah blah" no command processing
198-
199-
//pull one line at a time and look for a regex match in each line
200-
while( watchtext.Len()>0 )
201-
{
202-
int watchtextpos=watchtext.Find(_T("\n"));
203-
wxString watchline;
204-
if(watchtextpos<0) // not found
205-
{
206-
watchline=watchtext;
207-
watchtext=_("");
208-
} else
209-
{
210-
watchline=(watchtextpos==0?_T(""):watchtext.Mid(0,watchtextpos));
211-
watchtext=watchtext.Mid(watchtextpos+1);
212-
}
213-
wxString expr, cmd;
214-
if(watchline.Len()==0)
215-
continue;
216-
if(re4.Matches(watchline))
217-
{
218-
expr=re4.GetMatch(watchline,1);
219-
expr.Replace(_T("\t"),_T("\001"));
220-
cmd=_T("pc ")+expr+_T("\n");
221-
commands+=cmd;
222-
continue;
223-
}
224-
if(re3.Matches(watchline))
225-
continue;
226-
if(re2.Matches(watchline)&&re2.GetMatchCount()>=2)
227-
{
228-
expr=re2.GetMatch(watchline,1);
229-
cmd=_T("pm1 ")+expr+_T("\n")+_T("pm2 ")+expr+_T("\n");
230-
commands+=cmd;
231-
continue;
232-
}
233-
if(re1.Matches(watchline)&&re1.GetMatchCount()>=2)
234-
{
235-
expr=re1.GetMatch(watchline,1);
236-
if(expr==_T(""))
237-
continue;
238-
cmd=_T("ps ")+expr+_T("\n");
239-
commands+=cmd;
240-
continue;
241-
}
242-
}
181+
{
182+
wxString commands;
183+
184+
for (unsigned int i=0;i<m_watchlist.size();++i)
185+
{ //TODO: ITERATE INTO CHILD WATCHES
186+
PythonWatch *w=m_watchlist[i].get();
187+
wxString s;
188+
w->GetSymbol(s);
189+
commands+=_T("ps ")+s+_T("\n");
190+
}
243191
return commands;
244192
}
245193

@@ -251,7 +199,7 @@ wxString PyDebugger::AssembleAliasCommands()
251199
commands+=_T("alias pm1 print '%1: \\001',;print type(%1)\n");
252200
commands+=_T("alias pm2 for k in %1.__dict__.keys(): print '...',k,'\\001',type(k),'\\001',%1.__dict__[k],'\\002',\n");
253201
//Print variable name, type and value
254-
commands+=_T("alias ps print '%1 \\001',;print type(%1), '\\001', %1\n");
202+
commands+=_T("alias ps print '%1\\001',;print str(type(%1))+'\\001',;print %1\n");
255203
//Print comment
256204
commands+=_T("alias pc print '%1'\n");
257205

@@ -320,17 +268,34 @@ void PyDebugger::OnTimer(wxTimerEvent& event)
320268
wxString exprresult=reprompt.GetMatch(m_outdebugbuf.Mid(m_debugbufpos),1);
321269
if(cmd.type==DBGCMDTYPE_WATCHEXPRESSION)
322270
{
271+
/*commands+=_T("alias ps print '%1 \\001',;print type(%1), '\\001', %1\n");*/
323272
// exprresult;
324273
if(reprompt.GetMatchCount()>1)
325-
{
274+
{
275+
exprresult.RemoveLast();
326276
exprresult.Replace(_T("\t"),_T("\\t"),true);
327277
exprresult.Replace(_T("\n"),_T("\\n"),true);
328-
exprresult.Replace(_T(" \001 "),_T("\t"),true);
329-
exprresult.Replace(_T(" \002"),_T("\n"),true);
330-
if(exprresult.Mid(exprresult.Len()-2,2)==_T("\\n"))
331-
exprresult=exprresult.Mid(0,exprresult.Len()-2);
332-
m_watchstr+=exprresult+_T("\n");
333-
// m_WatchDlg->UpdateVariable(exprresult); //TODO: implement this
278+
wxString symbol=exprresult.BeforeFirst(_T('\001'));
279+
exprresult=exprresult.AfterFirst(_T('\001'));
280+
wxString type=exprresult.BeforeFirst(_T('\001'));
281+
wxString value=exprresult.AfterFirst(_T('\001'));
282+
for(int i=0;i<m_watchlist.size();++i)
283+
{
284+
PythonWatch *p;
285+
wxString s;
286+
m_watchlist[i].get()->GetSymbol(s);
287+
if (s==symbol)
288+
p=m_watchlist[i].get();
289+
else
290+
p=dynamic_cast<PythonWatch*>(m_watchlist[i]->FindChild(symbol));
291+
if(p)
292+
{
293+
p->SetType(type);
294+
p->SetValue(value);
295+
}
296+
}
297+
DebuggerManager &dbg_manager = *Manager::Get()->GetDebuggerManager();
298+
dbg_manager.GetWatchesDialog()->UpdateWatches();
334299
}
335300
}
336301
if(cmd.type==DBGCMDTYPE_WATCHTOOLTIP)
@@ -476,9 +441,6 @@ void PyDebugger::OnTimer(wxTimerEvent& event)
476441
m_outprogbuf=_T("");
477442
m_progbufpos=0;
478443
m_TimerPollDebugger.Stop();
479-
if(m_watchstr!=_T(""))
480-
m_WatchDlg->m_WatchText->SetValue(m_watchstr);
481-
m_watchstr=_T("");
482444
}
483445
}
484446
}
@@ -534,7 +496,6 @@ bool PyDebugger::Debug(bool breakOnEntry)
534496
m_outbuf=_T("");
535497
m_outdebugbuf=_T("");
536498
m_outprogbuf=_T("");
537-
m_watchstr=_T("");
538499
ReadPluginConfig();
539500
if(!m_RunTarget)
540501
{
@@ -725,7 +686,7 @@ const cbBreakpoint* PyDebugger::GetBreakpoint(int index) const
725686

726687

727688
cbBreakpoint* PyDebugger::AddBreakpoint(const wxString& file, int line)
728-
{
689+
{
729690
if(!IsPythonFile(file))
730691
return NULL;
731692
for (BPList::iterator itr=m_bplist.begin(); itr!=m_bplist.end(); ++itr)
@@ -800,6 +761,68 @@ void PyDebugger::DeleteAllBreakpoints()
800761
}
801762

802763

764+
cbWatch* PyDebugger::AddWatch(const wxString& symbol)
765+
{
766+
PythonWatch::Pointer pwatch(new PythonWatch(symbol));
767+
m_watchlist.push_back(pwatch);
768+
769+
if(IsRunning())
770+
DispatchCommands(_T("ps ")+symbol+_T("\n"),DBGCMDTYPE_WATCHEXPRESSION);
771+
return pwatch.get();
772+
}
773+
774+
void PyDebugger::DeleteWatch(cbWatch *watch)
775+
{
776+
//this will delete the root node of watch
777+
//TODO: Why do we need to delete root node?
778+
cbWatch *root_watch = GetRootWatch(watch);
779+
unsigned int i;
780+
for (i=0;i<m_watchlist.size();++i)
781+
{
782+
if (m_watchlist[i].get()==root_watch)
783+
break;
784+
}
785+
if(i==m_watchlist.size())
786+
return;
787+
788+
m_watchlist.erase(m_watchlist.begin()+i);
789+
}
790+
791+
bool PyDebugger::HasWatch(cbWatch *watch)
792+
{
793+
cbWatch *root_watch = GetRootWatch(watch);
794+
unsigned int i;
795+
for (i=0;i<m_watchlist.size();++i)
796+
{
797+
if (m_watchlist[i].get()==root_watch)
798+
break;
799+
}
800+
return i<m_watchlist.size();
801+
}
802+
803+
void PyDebugger::ShowWatchProperties(cbWatch *watch)
804+
{
805+
}
806+
807+
bool PyDebugger::SetWatchValue(cbWatch *watch, const wxString &value)
808+
{
809+
return false;
810+
}
811+
812+
void PyDebugger::ExpandWatch(cbWatch *watch)
813+
{
814+
}
815+
816+
void PyDebugger::CollapseWatch(cbWatch *watch)
817+
{
818+
}
819+
820+
void PyDebugger::OnWatchesContextMenu(wxMenu &menu, const cbWatch &watch, wxObject *property)
821+
{
822+
}
823+
824+
825+
803826
int PyDebugger::GetStackFrameCount() const
804827
{
805828
return m_stackinfo.frames.size();
@@ -913,30 +936,6 @@ void PyDebugger::OnRun(wxCommandEvent& event)
913936
}
914937

915938

916-
void PyDebugger::OnViewWatch(wxCommandEvent& event)
917-
{
918-
// This toggles display of watch
919-
CodeBlocksDockEvent evt(event.IsChecked() ? cbEVT_SHOW_DOCK_WINDOW : cbEVT_HIDE_DOCK_WINDOW);
920-
evt.pWindow = m_WatchDlg;
921-
Manager::Get()->ProcessEvent(evt);
922-
923-
//TODO: Any required watch processing before watch is shown
924-
// if (event.IsChecked())
925-
// DoWatches();
926-
}
927-
928-
929-
void PyDebugger::OnUpdateWatch(wxCommandEvent& event)
930-
{
931-
if(m_DebuggerActive)
932-
{
933-
wxString wcommands=AssembleWatchCommands();
934-
DispatchCommands(wcommands,DBGCMDTYPE_WATCHEXPRESSION,true);
935-
}
936-
}
937-
938-
939-
940939
// constructor
941940
PyDebugger::PyDebugger()
942941
{
@@ -949,7 +948,6 @@ PyDebugger::PyDebugger()
949948
}
950949
m_DebuggerActive=false;
951950
m_RunTargetSelected=false;
952-
m_WatchDlg=NULL;
953951

954952
}
955953

@@ -977,31 +975,15 @@ void PyDebugger::OnAttachReal()
977975
// (see: does not need) this plugin...
978976
this->ReadPluginConfig();
979977
this->UpdateConfig();
980-
// m_HasDebugLog = Manager::Get()->GetConfigManager(_T("debugger"))->ReadBool(_T("debug_log"), false);
981-
// MessageManager* msgMan = Manager::Get()->GetMessageManager();
982978
m_DebugLog = new TextCtrlLogger(true);
983-
// m_DebugLogPageIndex = msgMan->AddLog(m_DebugLog, _("PyDebugger"));
984-
m_WatchDlg = new DebuggerWatch(Manager::Get()->GetAppWindow(), this);
985979

986980
Manager::Get()->RegisterEventSink(cbEVT_EDITOR_TOOLTIP, new cbEventFunctor<PyDebugger, CodeBlocksEvent>(this, &PyDebugger::OnValueTooltip));
987981

988982
CodeBlocksLogEvent evtlog(cbEVT_ADD_LOG_WINDOW,m_DebugLog, _("PyDebugger"));
989-
Manager::Get()->ProcessEvent(evtlog);
990-
CodeBlocksDockEvent evt(cbEVT_ADD_DOCK_WINDOW);
991-
evt.name = _T("Python Watch");
992-
evt.title = _T("Python Watch");
993-
evt.pWindow = m_WatchDlg;
994-
evt.dockSide = CodeBlocksDockEvent::dsFloating;
995-
evt.desiredSize.Set(150, 250);
996-
evt.floatingSize.Set(150, 250);
997-
evt.minimumSize.Set(150, 150);
998-
Manager::Get()->ProcessEvent(evt);
983+
Manager::Get()->ProcessEvent(evtlog);
984+
999985
DebuggerManager &dbg_manager = *Manager::Get()->GetDebuggerManager();
1000-
dbg_manager.RegisterDebugger(this, _T("python"));
1001-
// set log image
1002-
// wxString prefix = ConfigManager::GetDataFolder() + _T("/images/");
1003-
//bmp = cbLoadBitmap(prefix + _T("contents_16x16.png"), wxBITMAP_TYPE_PNG);
1004-
//Manager::Get()->GetMessageManager()->SetLogImage(m_pDbgLog, bmp);
986+
dbg_manager.RegisterDebugger(this, _T("python"));
1005987

1006988
}
1007989

@@ -1013,21 +995,14 @@ void PyDebugger::OnReleaseReal(bool appShutDown)
1013995
// NOTE: after this function, the inherited member variable
1014996
// m_IsAttached will be FALSE...
1015997

1016-
//TODO: Handle destruction of any running python processes? (Maybe not necessary...)
1017-
//TODO: Destroy any other objects that aren't cleaned up by the framework?
1018-
1019-
if (m_WatchDlg)
1020-
{
1021-
CodeBlocksDockEvent evt(cbEVT_REMOVE_DOCK_WINDOW);
1022-
evt.pWindow = m_WatchDlg;
1023-
Manager::Get()->ProcessEvent(evt);
1024-
m_WatchDlg->Destroy();
1025-
}
1026-
m_WatchDlg = 0L;
998+
//TODO: Handle destruction of any running python debugger processes? (Maybe not necessary...)
1027999

10281000
CodeBlocksLogEvent evt(cbEVT_REMOVE_LOG_WINDOW,m_DebugLog);
10291001
Manager::Get()->ProcessEvent(evt);
10301002
m_DebugLog = 0L;
1003+
1004+
DebuggerManager &dbg_manager = *Manager::Get()->GetDebuggerManager();
1005+
dbg_manager.UnregisterDebugger(this);
10311006

10321007
}
10331008

@@ -1050,8 +1025,6 @@ void PyDebugger::OnValueTooltip(CodeBlocksEvent& event)
10501025
return;
10511026
if (!IsStopped())
10521027
return;
1053-
// if (!Manager::Get()->GetConfigManager(_T("debugger"))->ReadBool(_T("eval_tooltip"), false))
1054-
// return;
10551028

10561029
EditorBase* base = event.GetEditor();
10571030
cbEditor* ed = base && base->IsBuiltinEditor() ? static_cast<cbEditor*>(base) : 0;
@@ -1125,8 +1098,8 @@ void PyDebugger::CreateMenu()
11251098
// LangMenu->Append(XRCID("idPyDebuggerMenuStep"),_T("Step &Into"),_T(""));
11261099
// LangMenu->Append(XRCID("idPyDebuggerMenuStop"),_T("&Stop"),_T(""));
11271100
LangMenu->Append(ID_LangMenu_DebugSendCommand,_T("&Send Debugger Command"),_T(""));
1128-
LangMenu->Append(ID_LangMenu_ShowWatch,_T("Toggle &Watch"),_T(""),wxITEM_CHECK);
1129-
LangMenu->Append(ID_LangMenu_UpdateWatch,_T("&Update Watch"),_T(""));
1101+
// LangMenu->Append(ID_LangMenu_ShowWatch,_T("Toggle &Watch"),_T(""),wxITEM_CHECK);
1102+
// LangMenu->Append(ID_LangMenu_UpdateWatch,_T("&Update Watch"),_T(""));
11301103
}
11311104

11321105

0 commit comments

Comments
 (0)