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