1 /*
2  * This file is part of the Code::Blocks IDE and licensed under the GNU Lesser General Public License, version 3
3  * http://www.gnu.org/licenses/lgpl-3.0.html
4  *
5  * $Revision: 11638 $
6  * $Id: cbplugin.cpp 11638 2019-04-20 16:57:54Z fuscated $
7  * $HeadURL: svn://svn.code.sf.net/p/codeblocks/code/branches/release-20.xx/src/sdk/cbplugin.cpp $
8  */
9 
10 #include "sdk_precomp.h"
11 
12 #ifndef CB_PRECOMP
13     #include <wx/frame.h> // wxFrame
14     #include <wx/menu.h>
15     #include <wx/process.h>
16 
17     #include "cbeditor.h"
18     #include "cbplugin.h"
19     #include "cbproject.h"
20     #include "compiler.h" // GetSwitches
21     #include "configmanager.h"
22     #include "debuggermanager.h"
23     #include "editorcolourset.h"
24     #include "editormanager.h"
25     #include "infowindow.h"
26     #include "logmanager.h"
27     #include "macrosmanager.h"
28     #include "manager.h"
29     #include "projectbuildtarget.h"
30     #include "projectmanager.h"
31     #include "sdk_events.h"
32 #endif
33 
34 #include <wx/toolbar.h>
35 
36 #include "annoyingdialog.h"
37 #include "cbdebugger_interfaces.h"
38 #include "cbstyledtextctrl.h"
39 #include "ccmanager.h"
40 #include "debuggermanager.h"
41 #include "editor_hooks.h"
42 #include "loggers.h"
43 
44 #ifndef __WXMSW__
45     #include <errno.h>
46     // needed for the kill system call
47     #include <signal.h>
48     #include <sys/types.h>
49 #endif
50 
cbPlugin()51 cbPlugin::cbPlugin() :
52     m_Type(ptNone),
53     m_IsAttached(false)
54 {
55     SetEvtHandlerEnabled(false);
56 }
57 
~cbPlugin()58 cbPlugin::~cbPlugin()
59 {
60 }
61 
Attach()62 void cbPlugin::Attach()
63 {
64     if (m_IsAttached)
65         return;
66     wxWindow* window = Manager::Get()->GetAppWindow();
67     if (window)
68     {
69         // push ourself in the application's event handling chain...
70         window->PushEventHandler(this);
71     }
72     m_IsAttached = true;
73     OnAttach();
74     SetEvtHandlerEnabled(true);
75 
76     CodeBlocksEvent event(cbEVT_PLUGIN_ATTACHED);
77     event.SetPlugin(this);
78     // post event in the host's event queue
79     Manager::Get()->ProcessEvent(event);
80 }
81 
Release(bool appShutDown)82 void cbPlugin::Release(bool appShutDown)
83 {
84     if (!m_IsAttached)
85         return;
86     m_IsAttached = false;
87     SetEvtHandlerEnabled(false);
88     OnRelease(appShutDown);
89 
90     CodeBlocksEvent event(cbEVT_PLUGIN_RELEASED);
91     event.SetPlugin(this);
92     // ask the host to process this event immediately
93     // it must be done this way, because if the host references
94     // us (through event.GetEventObject()), we might not be valid at that time
95     // (while, now, we are...)
96     Manager::Get()->ProcessEvent(event);
97 
98     if (appShutDown)
99         return; // nothing more to do, if the app is shutting down
100 
101     wxWindow* window = Manager::Get()->GetAppWindow();
102     if (window)
103     {
104         // remove ourself from the application's event handling chain...
105         window->RemoveEventHandler(this);
106     }
107 }
108 
NotImplemented(const wxString & log) const109 void cbPlugin::NotImplemented(const wxString& log) const
110 {
111     Manager::Get()->GetLogManager()->DebugLog(log + _T(" : not implemented"));
112 }
113 
114 /////
115 ///// cbCompilerPlugin
116 /////
117 
cbCompilerPlugin()118 cbCompilerPlugin::cbCompilerPlugin()
119 {
120     m_Type = ptCompiler;
121 }
122 
123 /////
124 ///// cbDebuggerPlugin
125 /////
126 
cbDebuggerPlugin(const wxString & guiName,const wxString & settingsName)127 cbDebuggerPlugin::cbDebuggerPlugin(const wxString &guiName, const wxString &settingsName) :
128     m_pCompiler(nullptr),
129     m_WaitingCompilerToFinish(false),
130     m_StartType(StartTypeUnknown),
131     m_ActiveConfig(0),
132     m_LogPageIndex(-1),
133     m_lastLineWasNormal(true),
134     m_guiName(guiName),
135     m_settingsName(settingsName)
136 {
137     m_Type = ptDebugger;
138 }
139 
140 
OnAttach()141 void cbDebuggerPlugin::OnAttach()
142 {
143     Manager::Get()->GetDebuggerManager()->RegisterDebugger(this);
144 
145     OnAttachReal();
146 
147     typedef cbEventFunctor<cbDebuggerPlugin, CodeBlocksEvent> Event;
148 
149     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_OPEN, new Event(this, &cbDebuggerPlugin::OnEditorOpened));
150     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_ACTIVATE, new Event(this, &cbDebuggerPlugin::OnProjectActivated));
151     Manager::Get()->RegisterEventSink(cbEVT_PROJECT_CLOSE, new Event(this, &cbDebuggerPlugin::OnProjectClosed));
152 
153     Manager::Get()->RegisterEventSink(cbEVT_COMPILER_FINISHED, new Event(this, &cbDebuggerPlugin::OnCompilerFinished));
154 
155     m_StartType = StartTypeUnknown;
156 
157     if (SupportsFeature(cbDebuggerFeature::ValueTooltips))
158         RegisterValueTooltip();
159 }
160 
OnRelease(bool appShutDown)161 void cbDebuggerPlugin::OnRelease(bool appShutDown)
162 {
163     Manager::Get()->RemoveAllEventSinksFor(this);
164 
165     OnReleaseReal(appShutDown);
166 
167     Manager::Get()->GetDebuggerManager()->UnregisterDebugger(this);
168 }
169 
BuildMenu(cb_unused wxMenuBar * menuBar)170 void cbDebuggerPlugin::BuildMenu(cb_unused wxMenuBar* menuBar)
171 {
172     if (!IsAttached())
173         return;
174     Manager::Get()->GetDebuggerManager()->GetMenu();
175 }
176 
GetEditorWordAtCaret(const wxPoint * mousePosition)177 wxString cbDebuggerPlugin::GetEditorWordAtCaret(const wxPoint* mousePosition)
178 {
179     cbEditor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
180     if (!ed)
181         return wxEmptyString;
182     cbStyledTextCtrl* stc = ed->GetControl();
183     if (!stc)
184         return wxEmptyString;
185 
186     wxString selected_text = stc->GetSelectedText();
187     if (selected_text != wxEmptyString)
188     {
189         selected_text.Trim(true);
190         selected_text.Trim(false);
191 
192         wxString::size_type pos = selected_text.find(wxT('\n'));
193         if (pos != wxString::npos)
194         {
195             selected_text.Remove(pos, selected_text.length() - pos);
196             selected_text.Trim(true);
197             selected_text.Trim(false);
198         }
199         // check if the mouse is over the selected text
200         if (mousePosition)
201         {
202             int startPos = stc->GetSelectionStart();
203             int endPos = stc->GetSelectionEnd();
204             int mousePos = stc->PositionFromPointClose(mousePosition->x, mousePosition->y);
205             if (mousePos == wxSCI_INVALID_POSITION)
206                 return wxEmptyString;
207             else if (startPos <= mousePos && mousePos <= endPos)
208                 return selected_text;
209             else
210                 return wxEmptyString;
211         }
212         else
213             return selected_text;
214     }
215 
216     if (mousePosition)
217     {
218         int pos = stc->PositionFromPoint(*mousePosition);
219         int start = stc->WordStartPosition(pos, true);
220         int end = stc->WordEndPosition(pos, true);
221         selected_text = stc->GetTextRange(start, end);
222     }
223     else
224     {
225         int start = stc->WordStartPosition(stc->GetCurrentPos(), true);
226         int end = stc->WordEndPosition(stc->GetCurrentPos(), true);
227         selected_text = stc->GetTextRange(start, end);
228     }
229     return selected_text;
230 }
231 
BuildModuleMenu(const ModuleType type,wxMenu * menu,cb_unused const FileTreeData * data)232 void cbDebuggerPlugin::BuildModuleMenu(const ModuleType type, wxMenu* menu, cb_unused const FileTreeData* data)
233 {
234     if (!IsAttached())
235         return;
236     // we 're only interested in editor menus
237     // we 'll add a "debug watches" entry only when the debugger is running...
238     if (type != mtEditorManager || !menu)
239         return;
240     cbDebuggerPlugin *active_plugin = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
241     if (active_plugin != this)
242         return;
243 
244     wxString word;
245     if (IsRunning())
246     {
247         // has to have a word under the caret...
248         word = GetEditorWordAtCaret();
249     }
250     Manager::Get()->GetDebuggerManager()->BuildContextMenu(*menu, word, IsRunning());
251 }
252 
BuildToolBar(cb_unused wxToolBar * toolBar)253 bool cbDebuggerPlugin::BuildToolBar(cb_unused wxToolBar* toolBar)
254 {
255     return false;
256 }
257 
ToolMenuEnabled() const258 bool cbDebuggerPlugin::ToolMenuEnabled() const
259 {
260     cbProject* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
261 
262     bool en = (prj && !prj->GetCurrentlyCompilingTarget()) || IsAttachedToProcess();
263     return IsRunning() && en;
264 }
265 
GetActiveConfig()266 cbDebuggerConfiguration& cbDebuggerPlugin::GetActiveConfig()
267 {
268     DebuggerManager::RegisteredPlugins &allPlugins = Manager::Get()->GetDebuggerManager()->GetAllDebuggers();
269 
270     DebuggerManager::RegisteredPlugins::iterator it = allPlugins.find(this);
271     if (it == allPlugins.end())
272         cbAssert(false);
273     cbDebuggerConfiguration *config = it->second.GetConfiguration(m_ActiveConfig);
274     if (!config)
275         return *it->second.GetConfigurations().front();
276     else
277         return *config;
278 }
279 
SetActiveConfig(int index)280 void cbDebuggerPlugin::SetActiveConfig(int index)
281 {
282     m_ActiveConfig = index;
283 }
284 
GetIndexOfActiveConfig() const285 int cbDebuggerPlugin::GetIndexOfActiveConfig() const
286 {
287     return m_ActiveConfig;
288 }
289 
ClearActiveMarkFromAllEditors()290 void cbDebuggerPlugin::ClearActiveMarkFromAllEditors()
291 {
292     EditorManager* edMan = Manager::Get()->GetEditorManager();
293     for (int i = 0; i < edMan->GetEditorsCount(); ++i)
294     {
295         cbEditor* ed = edMan->GetBuiltinEditor(i);
296         if (ed)
297             ed->SetDebugLine(-1);
298     }
299 }
300 
SyncEditor(const wxString & filename,int line,bool setMarker)301 cbDebuggerPlugin::SyncEditorResult cbDebuggerPlugin::SyncEditor(const wxString& filename, int line, bool setMarker)
302 {
303     if (setMarker)
304     {
305         EditorManager* edMan = Manager::Get()->GetEditorManager();
306         for (int i = 0; i < edMan->GetEditorsCount(); ++i)
307         {
308             cbEditor* ed = edMan->GetBuiltinEditor(i);
309             if (ed)
310                 ed->SetDebugLine(-1);
311         }
312     }
313     FileType ft = FileTypeOf(filename);
314     if (ft != ftSource && ft != ftHeader && ft != ftResource && ft != ftTemplateSource)
315     {
316         // if the line is >= 0 and ft == ftOther assume, that we are in header without extension
317         if (line < 0 || ft != ftOther)
318         {
319             ShowLog(false);
320             Log(_("Unknown file: ") + filename, Logger::error);
321             InfoWindow::Display(_("Unknown file"), _("File: ") + filename, 5000);
322 
323             return SyncFileUnknown; // don't try to open unknown files
324         }
325     }
326 
327     cbProject* project = Manager::Get()->GetProjectManager()->GetActiveProject();
328     ProjectFile* f = project ? project->GetFileByFilename(filename, false, true) : nullptr;
329 
330     wxString unixfilename = UnixFilename(filename);
331     wxFileName fname(unixfilename);
332 
333     if (project && fname.IsRelative())
334         fname.MakeAbsolute(project->GetBasePath());
335 
336     // gdb can't work with spaces in filenames, so we have passed it the shorthand form (C:\MYDOCU~1 etc)
337     // revert this change now so the file can be located and opened...
338     // we do this by calling GetLongPath()
339     cbEditor* ed = Manager::Get()->GetEditorManager()->Open(fname.GetLongPath());
340     if (ed)
341     {
342         ed->Show(true);
343         if (f && !ed->GetProjectFile())
344             ed->SetProjectFile(f);
345         ed->GotoLine(line - 1, false);
346         if (setMarker)
347             ed->SetDebugLine(line - 1);
348         return SyncOk;
349     }
350     else
351     {
352         ShowLog(false);
353         Log(_("Cannot open file: ") + filename, Logger::error);
354         InfoWindow::Display(_("Cannot open file"), _("File: ") + filename, 5000);
355 
356         return SyncFileNotFound;
357     }
358 }
359 
HasBreakpoint(cbDebuggerPlugin & plugin,wxString const & filename,int line)360 inline bool HasBreakpoint(cbDebuggerPlugin &plugin, wxString const &filename, int line)
361 {
362     int count = plugin.GetBreakpointsCount();
363     for (int ii = 0; ii < count; ++ii)
364     {
365         const cb::shared_ptr<cbBreakpoint> &b = plugin.GetBreakpoint(ii);
366 
367         if (b->GetLocation() == filename && b->GetLine() == line)
368             return true;
369     }
370     return false;
371 }
372 
EditorLinesAddedOrRemoved(cbEditor * editor,int startline,int lines)373 void cbDebuggerPlugin::EditorLinesAddedOrRemoved(cbEditor* editor, int startline, int lines)
374 {
375     // here we keep the breakpoints in sync with the editors
376     // (whenever lines are added or removed)
377     if (!editor || lines == 0)
378         return;
379 
380     const wxString& filename = editor->GetFilename();
381 
382     std::vector<int> breakpoints_for_file;
383     int count = GetBreakpointsCount();
384     for (int ii = 0; ii < count; ++ii)
385     {
386         const cb::shared_ptr<cbBreakpoint> &b = GetBreakpoint(ii);
387 
388         if (b->GetLocation() == filename)
389         {
390             breakpoints_for_file.push_back(ii);
391         }
392     }
393 
394     if (lines < 0)
395     {
396         // removed lines
397         // make "lines" positive, for easier reading below
398         lines = -lines;
399         int endline = startline + lines - 1;
400 
401         std::vector<cb::shared_ptr<cbBreakpoint> > to_remove;
402 
403         for (std::vector<int>::iterator it = breakpoints_for_file.begin(); it != breakpoints_for_file.end(); ++it)
404         {
405             const cb::shared_ptr<cbBreakpoint> &b = GetBreakpoint(*it);
406             if (b->GetLine() > endline)
407                 ShiftBreakpoint(*it, -lines);
408             else if (b->GetLine() >= startline && b->GetLine() <= endline)
409                 to_remove.push_back(b);
410         }
411 
412         for (std::vector<cb::shared_ptr<cbBreakpoint> >::iterator it = to_remove.begin(); it != to_remove.end(); ++it)
413             DeleteBreakpoint(*it);
414     }
415     else
416     {
417         for (std::vector<int>::iterator it = breakpoints_for_file.begin(); it != breakpoints_for_file.end(); ++it)
418         {
419             const cb::shared_ptr<cbBreakpoint> &b = GetBreakpoint(*it);
420             if (b->GetLine() > startline)
421                 ShiftBreakpoint(*it, lines);
422         }
423     }
424 }
425 
OnEditorOpened(CodeBlocksEvent & event)426 void cbDebuggerPlugin::OnEditorOpened(CodeBlocksEvent& event)
427 {
428     // when an editor opens, look if we have breakpoints for it
429     // and notify it...
430     EditorBase* ed = event.GetEditor();
431     if (ed && ed->IsBuiltinEditor())
432     {
433         cbEditor *editor = static_cast<cbEditor*>(ed);
434         editor->RefreshBreakpointMarkers();
435 
436         if (IsRunning())
437         {
438             wxString filename;
439             int line;
440             GetCurrentPosition(filename, line);
441 
442             wxFileName edFileName(ed->GetFilename());
443             edFileName.Normalize();
444 
445             wxFileName dbgFileName(filename);
446             dbgFileName.Normalize();
447             if (dbgFileName.GetFullPath().IsSameAs(edFileName.GetFullPath()) && line != -1)
448             {
449                 editor->SetDebugLine(line - 1);
450             }
451         }
452     }
453     event.Skip(); // must do
454 }
455 
OnProjectActivated(CodeBlocksEvent & event)456 void cbDebuggerPlugin::OnProjectActivated(CodeBlocksEvent& event)
457 {
458     // allow others to catch this
459     event.Skip();
460 
461     if(this != Manager::Get()->GetDebuggerManager()->GetActiveDebugger())
462         return;
463     // when a project is activated and it's not the actively debugged project,
464     // ask the user to end debugging or re-activate the debugged project.
465 
466     if (!IsRunning())
467         return;
468 
469     if (event.GetProject() != GetProject() && GetProject())
470     {
471         wxString msg = _("You can't change the active project while you 're actively debugging another.\n"
472                         "Do you want to stop debugging?\n\n"
473                         "Click \"Yes\" to stop debugging now or click \"No\" to re-activate the debuggee.");
474         if (cbMessageBox(msg, _("Warning"), wxICON_WARNING | wxYES_NO) == wxID_YES)
475         {
476             Stop();
477         }
478         else
479         {
480             Manager::Get()->GetProjectManager()->SetProject(GetProject());
481         }
482     }
483 }
484 
OnProjectClosed(CodeBlocksEvent & event)485 void cbDebuggerPlugin::OnProjectClosed(CodeBlocksEvent& event)
486 {
487     // allow others to catch this
488     event.Skip();
489 
490     if(this != Manager::Get()->GetDebuggerManager()->GetActiveDebugger())
491         return;
492     CleanupWhenProjectClosed(event.GetProject());
493 
494     // when a project closes, make sure it's not the actively debugged project.
495     // if so, end debugging immediately!
496     if (!IsRunning())
497         return;
498 
499     if (event.GetProject() == GetProject())
500     {
501         AnnoyingDialog dlg(_("Project closed while debugging message"),
502                            _("The project you were debugging has closed.\n"
503                              "(The application most likely just finished.)\n"
504                              "The debugging session will terminate immediately."),
505                             wxART_WARNING, AnnoyingDialog::OK);
506         dlg.ShowModal();
507         Stop();
508         ResetProject();
509     }
510 }
511 
512 
513 
ShowLog(bool clear)514 void cbDebuggerPlugin::ShowLog(bool clear)
515 {
516     TextCtrlLogger *log = Manager::Get()->GetDebuggerManager()->GetLogger();
517     if (log)
518     {
519         // switch to the debugger log
520         CodeBlocksLogEvent eventSwitchLog(cbEVT_SWITCH_TO_LOG_WINDOW, log);
521         Manager::Get()->ProcessEvent(eventSwitchLog);
522         CodeBlocksLogEvent eventShowLog(cbEVT_SHOW_LOG_MANAGER);
523         Manager::Get()->ProcessEvent(eventShowLog);
524 
525         if (clear)
526             log->Clear();
527     }
528 }
529 
Log(const wxString & msg,Logger::level level)530 void cbDebuggerPlugin::Log(const wxString& msg, Logger::level level)
531 {
532     if (IsAttached())
533     {
534         Manager::Get()->GetLogManager()->Log((m_lastLineWasNormal ? wxEmptyString : wxT("\n")) + msg, m_LogPageIndex,
535                                              level);
536         m_lastLineWasNormal = true;
537     }
538 }
539 
DebugLog(const wxString & msg,Logger::level level)540 void cbDebuggerPlugin::DebugLog(const wxString& msg, Logger::level level)
541 {
542     // gdb debug messages
543     if (IsAttached() && HasDebugLog())
544     {
545         Manager::Get()->GetLogManager()->Log((!m_lastLineWasNormal ? wxT("[debug]") : wxT("\n[debug]")) + msg,
546                                              m_LogPageIndex, level);
547         m_lastLineWasNormal = false;
548     }
549 }
550 
HasDebugLog() const551 bool cbDebuggerPlugin::HasDebugLog() const
552 {
553     return cbDebuggerCommonConfig::GetFlag(cbDebuggerCommonConfig::ShowDebuggersLog);
554 }
555 
ClearLog()556 void cbDebuggerPlugin::ClearLog()
557 {
558     Manager::Get()->GetDebuggerManager()->GetLogger()->Clear();
559 }
560 
SetupLog(int normalIndex)561 void cbDebuggerPlugin::SetupLog(int normalIndex)
562 {
563     m_LogPageIndex = normalIndex;
564 }
565 
SwitchToDebuggingLayout()566 void cbDebuggerPlugin::SwitchToDebuggingLayout()
567 {
568     CodeBlocksLayoutEvent queryEvent(cbEVT_QUERY_VIEW_LAYOUT);
569 
570     const cbDebuggerConfiguration &config = GetActiveConfig();
571 
572     wxString perspectiveName;
573     switch (cbDebuggerCommonConfig::GetPerspective())
574     {
575     case cbDebuggerCommonConfig::OnePerDebugger:
576         perspectiveName = GetGUIName();
577         break;
578     case cbDebuggerCommonConfig::OnePerDebuggerConfig:
579         perspectiveName = GetGUIName() + wxT(":") + config.GetName();
580         break;
581     case cbDebuggerCommonConfig::OnlyOne:
582     default:
583         perspectiveName = _("Debugging");
584     }
585 
586     CodeBlocksLayoutEvent switchEvent(cbEVT_SWITCH_VIEW_LAYOUT, perspectiveName);
587 
588     Manager::Get()->GetLogManager()->DebugLog(F(_("Switching layout to \"%s\""), switchEvent.layout.wx_str()));
589 
590     // query the current layout
591     Manager::Get()->ProcessEvent(queryEvent);
592     m_PreviousLayout = queryEvent.layout;
593 
594     // switch to debugging layout
595     Manager::Get()->ProcessEvent(switchEvent);
596 
597     ShowLog(false);
598 }
599 
SwitchToPreviousLayout()600 void cbDebuggerPlugin::SwitchToPreviousLayout()
601 {
602     CodeBlocksLayoutEvent switchEvent(cbEVT_SWITCH_VIEW_LAYOUT, m_PreviousLayout);
603 
604     wxString const &name = !switchEvent.layout.IsEmpty() ? switchEvent.layout : wxString(_("Code::Blocks default"));
605 
606     Manager::Get()->GetLogManager()->DebugLog(F(_("Switching layout to \"%s\""), name.wx_str()));
607 
608     // switch to previous layout
609     Manager::Get()->ProcessEvent(switchEvent);
610 }
611 
GetDebuggee(wxString & pathToDebuggee,wxString & workingDirectory,ProjectBuildTarget * target)612 bool cbDebuggerPlugin::GetDebuggee(wxString &pathToDebuggee, wxString &workingDirectory, ProjectBuildTarget* target)
613 {
614     if (!target)
615         return false;
616 
617     wxString out;
618     switch (target->GetTargetType())
619     {
620         case ttExecutable:
621         case ttConsoleOnly:
622         case ttNative:
623             {
624                 out = UnixFilename(target->GetOutputFilename());
625                 Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
626                 wxFileName f(out);
627                 f.MakeAbsolute(target->GetParentProject()->GetBasePath());
628                 out = f.GetFullPath();
629                 Log(_("Adding file: ") + out);
630                 ConvertDirectory(out);
631             }
632             break;
633 
634         case ttStaticLib:
635         case ttDynamicLib:
636             // check for hostapp
637             if (target->GetHostApplication().IsEmpty())
638             {
639                 cbMessageBox(_("You must select a host application to \"run\" a library..."));
640                 return false;
641             }
642             out = UnixFilename(target->GetHostApplication());
643             Manager::Get()->GetMacrosManager()->ReplaceEnvVars(out); // apply env vars
644             Log(_("Adding file: ") + out);
645             ConvertDirectory(out);
646             break;
647 
648         case ttCommandsOnly: // fall through:
649         default:
650             Log(_("Unsupported target type (Project -> Properties -> Build Targets -> Type)"), Logger::error);
651             return false;
652     }
653     if (out.empty())
654     {
655         Log(_("Couldn't find the path to the debuggee!"), Logger::error);
656         return false;
657     }
658 
659     workingDirectory = target->GetWorkingDir();
660     Manager::Get()->GetMacrosManager()->ReplaceEnvVars(workingDirectory);
661     wxFileName cd(workingDirectory);
662     if (cd.IsRelative())
663         cd.MakeAbsolute(target->GetParentProject()->GetBasePath());
664     workingDirectory = cd.GetFullPath();
665 
666     pathToDebuggee = out;
667     return true;
668 }
669 
EnsureBuildUpToDate(StartType startType)670 bool cbDebuggerPlugin::EnsureBuildUpToDate(StartType startType)
671 {
672     m_StartType = startType;
673     m_WaitingCompilerToFinish = false;
674 
675     // compile project/target (if not attaching to a PID)
676     if (!IsAttachedToProcess())
677     {
678         // should we build to make sure project is up-to-date?
679         if (!cbDebuggerCommonConfig::GetFlag(cbDebuggerCommonConfig::AutoBuild))
680         {
681             m_WaitingCompilerToFinish = false;
682             m_pCompiler = nullptr;
683             return true;
684         }
685 
686         // make sure the target is compiled
687         const std::vector<cbCompilerPlugin*> &compilers = Manager::Get()->GetPluginManager()->GetCompilerPlugins();
688         if (compilers.empty())
689             m_pCompiler = nullptr;
690         else
691             m_pCompiler = compilers.front();
692         if (m_pCompiler)
693         {
694             // is the compiler already running?
695             if (m_pCompiler->IsRunning())
696             {
697                 Log(_("Compiler in use..."));
698                 Log(_("Aborting debugging session"));
699                 cbMessageBox(_("The compiler is currently in use. Aborting debugging session..."),
700                              _("Compiler running"), wxICON_WARNING);
701                 return false;
702             }
703 
704             Log(_("Building to ensure sources are up-to-date"));
705             m_WaitingCompilerToFinish = true;
706             m_pCompiler->Build();
707             // now, when the build is finished, DoDebug will be launched in OnCompilerFinished()
708         }
709     }
710     return true;
711 }
712 
OnCompilerFinished(cb_unused CodeBlocksEvent & event)713 void cbDebuggerPlugin::OnCompilerFinished(cb_unused CodeBlocksEvent& event)
714 {
715     if (m_WaitingCompilerToFinish)
716     {
717         m_WaitingCompilerToFinish = false;
718         bool compilerFailed = false;
719         // only proceed if build succeeded
720         if (m_pCompiler && m_pCompiler->GetExitCode() != 0)
721         {
722             AnnoyingDialog dlg(_("Debug anyway?"), _("Build failed, do you want to debug the program?"),
723                                wxART_QUESTION, AnnoyingDialog::YES_NO, AnnoyingDialog::rtNO);
724             if (dlg.ShowModal() != AnnoyingDialog::rtYES)
725             {
726                 ProjectManager *manager = Manager::Get()->GetProjectManager();
727                 if (manager->GetIsRunning() && manager->GetIsRunning() == this)
728                     manager->SetIsRunning(nullptr);
729                 compilerFailed = true;
730             }
731         }
732         ShowLog(false);
733         if (!CompilerFinished(compilerFailed, m_StartType))
734         {
735             ProjectManager *manager = Manager::Get()->GetProjectManager();
736             if (manager->GetIsRunning() && manager->GetIsRunning() == this)
737                 manager->SetIsRunning(nullptr);
738         }
739     }
740 }
741 
742 #ifndef __WXMSW__
743 namespace
744 {
MakeSleepCommand()745 wxString MakeSleepCommand()
746 {
747     return wxString::Format(wxT("sleep %lu"), 80000000 + ::wxGetProcessId());
748 }
749 
750 struct ConsoleInfo
751 {
ConsoleInfo__anon9ed1e1a60111::ConsoleInfo752     ConsoleInfo(const wxString &path = wxEmptyString, int pid = -1) : ttyPath(path), sleepPID(pid) {}
753 
IsValid__anon9ed1e1a60111::ConsoleInfo754     bool IsValid() const { return !ttyPath.empty() && sleepPID > 0; }
755 
756     wxString ttyPath;
757     int sleepPID;
758 };
759 
GetConsoleTty(int consolePID)760 ConsoleInfo GetConsoleTty(int consolePID)
761 {
762     // execute the ps x -o command  and read PS output to get the /dev/tty field
763     wxArrayString psOutput;
764     wxArrayString psErrors;
765 
766     int result = wxExecute(wxT("ps x -o tty,pid,command"), psOutput, psErrors, wxEXEC_SYNC);
767     if (result != 0)
768         return ConsoleInfo();
769 
770     // find task with our unique sleep time
771     const wxString &uniqueSleepTimeStr = MakeSleepCommand();
772 
773     // search the output of "ps pid" command
774     int count = psOutput.GetCount();
775     for (int i = count - 1; i > -1; --i)
776     {
777         // find the pts/# or tty/# or whatever it's called
778         // by searching the output of "ps x -o tty,pid,command" command.
779         // The output of ps looks like:
780         // TT       PID   COMMAND
781         // pts/0    13342 /bin/sh ./run.sh
782         // pts/0    13343 /home/pecanpecan/devel/trunk/src/devel/codeblocks
783         // pts/0    13361 /usr/bin/gdb -nx -fullname -quiet -args ./conio
784         // pts/0    13362 xterm -font -*-*-*-*-*-*-20-*-*-*-*-*-*-* -T Program Console -e sleep 93343
785         // pts/2    13363 sleep 93343
786         // ?        13365 /home/pecan/proj/conio/conio
787         // pts/1    13370 ps x -o tty,pid,command
788 
789         const wxString &psCmd = psOutput.Item(i);
790         if (psCmd.Contains(uniqueSleepTimeStr))
791         {
792             // Extract the pid for the line
793             long pidForLine;
794             if (psCmd.AfterFirst(' ').Trim(false).BeforeFirst(' ').Trim(true).ToLong(&pidForLine))
795             {
796                 // Check if we are at the correct line. It is possible that there are two lines which contain the
797                 // "sleep" string. One for the sleep process and one for the terminal process. We want to skip the
798                 // line for the terminal process.
799                 if (pidForLine != consolePID)
800                     return ConsoleInfo(wxT("/dev/") + psCmd.BeforeFirst(' '), pidForLine);
801             }
802         }
803     }
804     return ConsoleInfo();
805 }
806 
807 struct ConsoleProcessTerminationInfo
808 {
ConsoleProcessTerminationInfo__anon9ed1e1a60111::ConsoleProcessTerminationInfo809     ConsoleProcessTerminationInfo() : status(-1), terminated(false) {}
810 
FailedToStart__anon9ed1e1a60111::ConsoleProcessTerminationInfo811     bool FailedToStart() const { return terminated && status != 0; }
812 
813     int status;
814     bool terminated;
815 };
816 
817 struct ConsoleProcess : wxProcess
818 {
ConsoleProcess__anon9ed1e1a60111::ConsoleProcess819     ConsoleProcess(cb::shared_ptr<ConsoleProcessTerminationInfo> cptInfo) : info(cptInfo) {}
OnTerminate__anon9ed1e1a60111::ConsoleProcess820     void OnTerminate(int pid, int status) override
821     {
822         info->terminated = true;
823         info->status = status;
824     }
825     cb::shared_ptr<ConsoleProcessTerminationInfo> info;
826 };
827 
828 } // namespace
829 #endif
830 
RunNixConsole(wxString & consoleTty)831 int cbDebuggerPlugin::RunNixConsole(wxString &consoleTty)
832 {
833     consoleTty = wxEmptyString;
834 #ifndef __WXMSW__
835     // Start a terminal and put the shell to sleep with -e sleep 80000.
836     // Fetch the terminal's tty, so we can tell the debugger what TTY to use,
837     // thus redirecting program's stdin/stdout/stderr to the terminal.
838 
839     wxString cmd;
840     int consolePid = 0;
841     // Use the terminal specified by the user in the Settings -> Environment.
842     wxString term = Manager::Get()->GetConfigManager(_T("app"))->Read(_T("/console_terminal"), DEFAULT_CONSOLE_TERM);
843 
844     term.Replace(_T("$TITLE"), wxString(wxT("'"))+_("Program Console")+wxT("'"));
845     cmd << term << _T(" ");
846 
847     const wxString &sleepCommand = MakeSleepCommand();
848     cmd << sleepCommand;
849 
850     Manager::Get()->GetMacrosManager()->ReplaceEnvVars(cmd);
851 
852     // The lifetime of wxProcess objects is very uncertain, so we are using a shared pointer to
853     // prevent us accessing deleted objects.
854     cb::shared_ptr<ConsoleProcessTerminationInfo> processInfo(new ConsoleProcessTerminationInfo);
855     ConsoleProcess *process = new ConsoleProcess(processInfo);
856     consolePid = wxExecute(cmd, wxEXEC_ASYNC, process);
857     if (consolePid <= 0)
858         return -1;
859 
860     // Try to find the TTY. We're using a loop, because some slow machines might make the check fail due
861     // to a slow starting terminal.
862     for (int ii = 0; ii < 100; ++ii)
863     {
864         // First, wait for the terminal to settle down, else PS won't see the sleep task
865         Manager::Yield();
866         ::wxMilliSleep(200);
867 
868         // Try to detect if the terminal command is present or its parameters are valid.
869         if (processInfo->FailedToStart() /*&& ii > 0*/)
870         {
871             Log(F(wxT("Failed to execute terminal command: '%s' (exit code: %d)"),
872                   cmd.wx_str(), processInfo->status), Logger::error);
873             break;
874         }
875 
876         // Try to find tty path and pid for the sleep command we've just executed.
877         const ConsoleInfo &info = GetConsoleTty(consolePid);
878 
879         // If there is no sleep command yet, do another iteration after a small delay.
880         if (!info.IsValid())
881             continue;
882 
883         // Try to find if the console window is still alive. Newer terminals like gnome-terminal
884         // try to be easier on resources and use a shared server process. For these terminals the
885         // spawned terminal process exits immediately, but the sleep command is still executed.
886         // If we detect such case we will return the PID for the sleep command instead of the PID
887         // for the terminal.
888         if (kill(consolePid, 0) == -1 && errno == ESRCH) {
889             DebugLog(F(wxT("Using sleep command's PID as console PID %d, TTY %s"),
890                        info.sleepPID, info.ttyPath.wx_str()));
891             consoleTty = info.ttyPath;
892             return info.sleepPID;
893         }
894         else
895         {
896             DebugLog(F(wxT("Using terminal's PID as console PID %d, TTY %s"), info.sleepPID, info.ttyPath.wx_str()));
897             consoleTty = info.ttyPath;
898             return consolePid;
899         }
900     }
901     // failed to find the console tty
902     if (consolePid != 0)
903         ::wxKill(consolePid);
904 #endif // !__WWXMSW__
905     return -1;
906 }
907 
MarkAsStopped()908 void cbDebuggerPlugin::MarkAsStopped()
909 {
910     Manager::Get()->GetProjectManager()->SetIsRunning(nullptr);
911 }
912 
BringCBToFront()913 void cbDebuggerPlugin::BringCBToFront()
914 {
915     wxWindow* app = Manager::Get()->GetAppWindow();
916     if (app)
917         app->Raise();
918 }
919 
UpdateWatches(const std::vector<cb::shared_ptr<cbWatch>> & watches)920 void cbDebuggerPlugin::UpdateWatches(const std::vector<cb::shared_ptr<cbWatch>> &watches)
921 {
922     for (const cb::shared_ptr<cbWatch>& watch : watches)
923         UpdateWatch(watch);
924 }
925 
RegisterValueTooltip()926 void cbDebuggerPlugin::RegisterValueTooltip()
927 {
928     typedef cbEventFunctor<cbDebuggerPlugin, CodeBlocksEvent> Event;
929     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_TOOLTIP, new Event(this, &cbDebuggerPlugin::ProcessValueTooltip));
930     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_TOOLTIP_CANCEL,
931                                       new Event(this, &cbDebuggerPlugin::CancelValueTooltip));
932 }
933 
ShowValueTooltip(cb_unused int style)934 bool cbDebuggerPlugin::ShowValueTooltip(cb_unused int style)
935 {
936     return false;
937 }
938 
939 // Default implementation does nothing
OnValueTooltip(cb_unused const wxString & token,cb_unused const wxRect & evalRect)940 void cbDebuggerPlugin::OnValueTooltip(cb_unused const wxString& token, cb_unused const wxRect& evalRect)
941 {
942 }
943 
ProcessValueTooltip(CodeBlocksEvent & event)944 void cbDebuggerPlugin::ProcessValueTooltip(CodeBlocksEvent& event)
945 {
946     event.Skip();
947     if (cbDebuggerCommonConfig::GetFlag(cbDebuggerCommonConfig::RequireCtrlForTooltips))
948     {
949         if (!wxGetKeyState(WXK_CONTROL))
950             return;
951     }
952 
953     if (Manager::Get()->GetDebuggerManager()->GetInterfaceFactory()->IsValueTooltipShown())
954         return;
955 
956     if (!ShowValueTooltip(event.GetInt()))
957         return;
958 
959     EditorBase* base = event.GetEditor();
960     cbEditor* ed = base && base->IsBuiltinEditor() ? static_cast<cbEditor*>(base) : nullptr;
961     if (!ed)
962         return;
963 
964     if (ed->IsContextMenuOpened())
965         return;
966 
967     // get rid of other calltips (if any) [for example the code completion one, at this time we
968     // want the debugger value call/tool-tip to win and be shown]
969     if (ed->GetControl()->CallTipActive())
970         ed->GetControl()->CallTipCancel();
971 
972     wxPoint pt;
973     pt.x = event.GetX();
974     pt.y = event.GetY();
975 
976     const wxString &token = GetEditorWordAtCaret(&pt);
977     if (!token.empty())
978     {
979         pt = ed->GetControl()->ClientToScreen(pt);
980         OnValueTooltip(token, wxRect(pt.x - 5, pt.y, 10, 10));
981     }
982 }
983 
CancelValueTooltip(cb_unused CodeBlocksEvent & event)984 void cbDebuggerPlugin::CancelValueTooltip(cb_unused CodeBlocksEvent& event)
985 {
986     Manager::Get()->GetDebuggerManager()->GetInterfaceFactory()->HideValueTooltip();
987 }
988 /////
989 ///// cbToolPlugin
990 /////
991 
cbToolPlugin()992 cbToolPlugin::cbToolPlugin()
993 {
994     m_Type = ptTool;
995 }
996 
997 /////
998 ///// cbMimePlugin
999 /////
1000 
cbMimePlugin()1001 cbMimePlugin::cbMimePlugin()
1002 {
1003     m_Type = ptMime;
1004 }
1005 
1006 /////
1007 ///// cbCodeCompletionPlugin
1008 /////
1009 
cbCodeCompletionPlugin()1010 cbCodeCompletionPlugin::cbCodeCompletionPlugin()
1011 {
1012     m_Type = ptCodeCompletion;
1013 }
1014 
DoAutocomplete(cb_unused const CCToken & token,cb_unused cbEditor * ed)1015 void cbCodeCompletionPlugin::DoAutocomplete(cb_unused const CCToken& token, cb_unused cbEditor* ed)
1016 {
1017     // do nothing: allow (wx)Scintilla to handle the insert
1018 }
1019 
DoAutocomplete(const wxString & token,cbEditor * ed)1020 void cbCodeCompletionPlugin::DoAutocomplete(const wxString& token, cbEditor* ed)
1021 {
1022     DoAutocomplete(CCToken(-1, token), ed);
1023 }
1024 
IsProviderFor(cbEditor * ed)1025 bool cbCodeCompletionPlugin::IsProviderFor(cbEditor* ed)
1026 {
1027     return (Manager::Get()->GetCCManager()->GetProviderFor(ed) == this);
1028 }
1029 
1030 /////
1031 ///// cbWizardPlugin
1032 /////
1033 
cbWizardPlugin()1034 cbWizardPlugin::cbWizardPlugin()
1035 {
1036     m_Type = ptWizard;
1037 }
1038 
1039 /////
1040 ///// cbSmartIndentPlugin
1041 /////
1042 
cbSmartIndentPlugin()1043 cbSmartIndentPlugin::cbSmartIndentPlugin()
1044 {
1045     m_Type = ptSmartIndent;
1046 }
1047 
OnAttach()1048 void cbSmartIndentPlugin::OnAttach()
1049 {
1050     m_FunctorId = EditorHooks::RegisterHook( new EditorHooks::cbSmartIndentEditorHookFunctor(this) );
1051     Manager::Get()->RegisterEventSink(cbEVT_EDITOR_CC_DONE, new cbEventFunctor<cbSmartIndentPlugin, CodeBlocksEvent>(this, &cbSmartIndentPlugin::OnCCDoneEvent));
1052 }
1053 
OnRelease(cb_unused bool appShutDown)1054 void cbSmartIndentPlugin::OnRelease(cb_unused bool appShutDown)
1055 {
1056     EditorHooks::UnregisterHook(m_FunctorId);
1057 }
1058 
AutoIndentEnabled() const1059 bool cbSmartIndentPlugin::AutoIndentEnabled()const
1060 {
1061     return Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/auto_indent"), true);
1062 }
1063 
SmartIndentEnabled() const1064 bool cbSmartIndentPlugin::SmartIndentEnabled()const
1065 {
1066     return Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/smart_indent"), true);
1067 }
1068 
BraceSmartIndentEnabled() const1069 bool cbSmartIndentPlugin::BraceSmartIndentEnabled()const
1070 {
1071     return Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/brace_smart_indent"), true);
1072 }
1073 
BraceCompletionEnabled() const1074 bool cbSmartIndentPlugin::BraceCompletionEnabled()const
1075 {
1076     return Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/brace_completion"), true);
1077 }
1078 
SelectionBraceCompletionEnabled() const1079 bool cbSmartIndentPlugin::SelectionBraceCompletionEnabled()const
1080 {
1081     return Manager::Get()->GetConfigManager(_T("editor"))->ReadBool(_T("/selection_brace_completion"), false);
1082 }
1083 
Indent(cbStyledTextCtrl * stc,wxString & indent) const1084 void cbSmartIndentPlugin::Indent(cbStyledTextCtrl* stc, wxString &indent)const
1085 {
1086     if (stc->GetUseTabs())
1087         indent << _T('\t'); // 1 tab
1088     else
1089         indent << wxString(_T(' '), stc->GetTabWidth()); // n spaces
1090 }
1091 
Indent(cbStyledTextCtrl * stc,wxString & indent,int posInLine) const1092 bool cbSmartIndentPlugin::Indent(cbStyledTextCtrl* stc, wxString &indent, int posInLine)const
1093 {
1094     if (posInLine >= 0)
1095     {
1096         if (stc->GetUseTabs())
1097             indent = wxString(_T('\t'), posInLine/stc->GetTabWidth());
1098         else
1099             indent = wxString(_T(' '), posInLine); // n spaces
1100         return true;
1101     }
1102     return false;
1103 }
1104 
GetLastNonCommentWord(cbEditor * ed,int position,unsigned int NumberOfWords) const1105 wxString cbSmartIndentPlugin::GetLastNonCommentWord(cbEditor* ed, int position, unsigned int NumberOfWords)const
1106 {
1107     cbStyledTextCtrl* stc = ed->GetControl();
1108     if ( !stc )
1109         return wxEmptyString;
1110 
1111     if ( position == -1 )
1112         position = stc->GetCurrentPos();
1113 
1114 
1115     wxString str;
1116     str.Empty();
1117     int count = 0;
1118     bool foundlf = false; // For the rare case of CR's without LF's
1119     while (position)
1120     {
1121         wxChar c = stc->GetCharAt(--position);
1122         int style = stc->GetStyleAt(position);
1123         bool inComment = stc->IsComment(style);
1124         if (c == _T('\n'))
1125         {
1126             count++;
1127             foundlf = true;
1128         }
1129         else if (c == _T('\r') && !foundlf)
1130             count++;
1131         else
1132             foundlf = false;
1133 
1134         if (count > 1) return str;
1135         if (!inComment && c != _T(' ') && c != _T('\t') && c != _T('\n') && c != _T('\r') )
1136         {
1137             int startpos = stc->WordStartPosition( position, true );
1138             for ( unsigned int i = 1; i < NumberOfWords ; ++i )
1139                 startpos = stc->WordStartPosition( startpos - 1, true );
1140             int endpos = stc->WordEndPosition(startpos, true);
1141             str = stc->GetTextRange(startpos, endpos);
1142             return str;
1143         }
1144     }
1145     return str;
1146 }
1147 
GetLastNonWhitespaceChar(cbEditor * ed,int position) const1148 wxChar cbSmartIndentPlugin::GetLastNonWhitespaceChar(cbEditor* ed, int position)const
1149 {
1150     return GetLastNonWhitespaceChars(ed, position, 1)[0];
1151 }
1152 
GetLastNonWhitespaceChars(cbEditor * ed,int position,unsigned int NumberOfChars) const1153 wxString cbSmartIndentPlugin::GetLastNonWhitespaceChars(cbEditor* ed, int position, unsigned int NumberOfChars)const
1154 {
1155     cbStyledTextCtrl* stc = ed->GetControl();
1156     if ( !stc )
1157         return wxEmptyString;
1158 
1159     if (position == -1)
1160         position = stc->GetCurrentPos();
1161 
1162     int count = 0; // Used to count the number of blank lines
1163     bool foundlf = false; // For the rare case of CR's without LF's
1164     while (position)
1165     {
1166         wxChar c = stc->GetCharAt(--position);
1167         int style = stc->GetStyleAt(position);
1168         bool inComment = stc->IsComment(style);
1169         if (c == _T('\n'))
1170         {
1171             count++;
1172             foundlf = true;
1173         }
1174         else if (c == _T('\r') && !foundlf)
1175             count++;
1176         else
1177             foundlf = false;
1178         if (count > 1) return wxEmptyString;
1179         if (!inComment && c != _T(' ') && c != _T('\t') && c != _T('\n') && c != _T('\r'))
1180             return stc->GetTextRange(position-NumberOfChars+1, position+1);
1181     }
1182     return wxEmptyString;
1183 }
1184 
GetNextNonWhitespaceCharOnLine(cbStyledTextCtrl * stc,int position,int * pos) const1185 wxChar cbSmartIndentPlugin::GetNextNonWhitespaceCharOnLine(cbStyledTextCtrl* stc, int position, int *pos)const
1186 {
1187     if (position == -1)
1188         position = stc->GetCurrentPos();
1189 
1190     while (position < stc->GetLength())
1191     {
1192         wxChar c = stc->GetCharAt(position);
1193         if ( c == _T('\n') || c ==  _T('\r') )
1194         {
1195             if ( pos ) *pos = position;
1196             return 0;
1197         }
1198         if ( c !=  _T(' ') && c != _T('\t') )
1199         {
1200             if ( pos ) *pos = position;
1201             return c;
1202         }
1203         position++;
1204     }
1205 
1206     return 0;
1207 }
1208 
FindBlockStart(cbStyledTextCtrl * stc,int position,wxChar blockStart,wxChar blockEnd,cb_unused bool skipNested) const1209 int cbSmartIndentPlugin::FindBlockStart(cbStyledTextCtrl* stc, int position, wxChar blockStart, wxChar blockEnd, cb_unused bool skipNested)const
1210 {
1211     int lvl = 0;
1212     wxChar b = stc->GetCharAt(position);
1213     while (b)
1214     {
1215         if (b == blockEnd)
1216             ++lvl;
1217         else if (b == blockStart)
1218         {
1219             if (lvl == 0)
1220                 return position;
1221             --lvl;
1222         }
1223         --position;
1224         b = stc->GetCharAt(position);
1225     }
1226     return -1;
1227 }
1228 
FindBlockStart(cbStyledTextCtrl * stc,int position,wxString blockStart,wxString blockEnd,bool CaseSensitive) const1229 int cbSmartIndentPlugin::FindBlockStart(cbStyledTextCtrl* stc, int position, wxString blockStart, wxString blockEnd, bool CaseSensitive)const
1230 {
1231     int pos = position;
1232     int pb, pe;
1233     int lvl = 0;
1234 
1235     int flags = wxSCI_FIND_WHOLEWORD;
1236     if ( CaseSensitive )
1237         flags |= wxSCI_FIND_MATCHCASE;
1238 
1239     do
1240     {
1241         pb =  stc->FindText(pos, 0, blockStart, flags);
1242         pe =  stc->FindText(pos, 0, blockEnd, flags);
1243         if ( pe > pb )
1244         {
1245             pos = pe;
1246             ++lvl;
1247             continue;
1248         }
1249         pos = pb;
1250         if ( lvl == 0 ) return pb;
1251         --lvl;
1252     }
1253     while( pos != -1 );
1254 
1255     return -1;
1256 }
1257 
1258 //ToDo: Is this c++ only?
GetFirstBraceInLine(cbStyledTextCtrl * stc,int string_style) const1259 int cbSmartIndentPlugin::GetFirstBraceInLine(cbStyledTextCtrl* stc, int string_style)const
1260 {
1261     int curr_position = stc->GetCurrentPos();
1262     int position = curr_position;
1263     int min_brace_position = position;
1264     int closing_braces = 0;
1265     bool found_brace = false;
1266     bool has_braces = false;
1267 
1268     while (position)
1269     {
1270         wxChar c = stc->GetCharAt(--position);
1271 
1272         int style = stc->GetStyleAt(position);
1273         if (style == string_style)
1274             continue;
1275 
1276         if (c == _T(';'))
1277         {
1278             found_brace = false;
1279             break;
1280         }
1281         else if (c == _T(')'))
1282         {
1283             ++closing_braces;
1284             has_braces = true;
1285         }
1286         else if (c == _T('('))
1287         {
1288             has_braces = true;
1289             if (closing_braces > 0)
1290                 --closing_braces;
1291             else if (!found_brace)
1292             {
1293                 min_brace_position = position + 1;
1294                 found_brace = true;
1295                 break;
1296             }
1297         }
1298         else if (c == _T('\n') && position + 1 != curr_position && !has_braces)
1299         {
1300             break;
1301         }
1302     }
1303 
1304     if (!found_brace)
1305         return -1;
1306 
1307     int tab_characters = 0;
1308 
1309     while (position)
1310     {
1311         wxChar c = stc->GetCharAt(--position);
1312         if (c == _T('\n') && position + 1 != curr_position)
1313         {
1314             break;
1315         }
1316         else if (c == _T('\t'))
1317             ++tab_characters;
1318     }
1319 
1320     if (stc->GetUseTabs())
1321     {
1322         position -= tab_characters * stc->GetTabWidth();
1323     }
1324     return min_brace_position - position - 1;
1325 }
1326 
GetNextNonWhitespaceCharOfLine(cbStyledTextCtrl * stc,int position,int * pos) const1327 wxChar cbSmartIndentPlugin::GetNextNonWhitespaceCharOfLine(cbStyledTextCtrl* stc, int position, int *pos)const
1328 {
1329     if (position == -1)
1330         position = stc->GetCurrentPos();
1331 
1332     while (position < stc->GetLength())
1333     {
1334         wxChar c = stc->GetCharAt(position);
1335         if ( c == _T('\n') || c ==  _T('\r') )
1336         {
1337             if ( pos ) *pos = position;
1338             return 0;
1339         }
1340         if ( c !=  _T(' ') && c != _T('\t') )
1341         {
1342             if ( pos ) *pos = position;
1343             return c;
1344         }
1345         position++;
1346     }
1347 
1348     return 0;
1349 }
1350 
OnCCDoneEvent(CodeBlocksEvent & event)1351 void cbSmartIndentPlugin::OnCCDoneEvent(CodeBlocksEvent& event)
1352 {
1353     EditorBase* eb = event.GetEditor();
1354     if (eb && eb->IsBuiltinEditor())
1355     {
1356         cbEditor* ed = static_cast<cbEditor *>(eb);
1357         OnCCDone(ed);
1358     }
1359 }
1360