File indexing completed on 2024-04-28 04:37:15

0001 /*
0002     SPDX-FileCopyrightText: 1999-2001 John Birch <jbb@kdevelop.org>
0003     SPDX-FileCopyrightText: 2001 Bernd Gehrmann <bernd@kdevelop.org>
0004     SPDX-FileCopyrightText: 2006 Vladimir Prus <ghost@cs.msu.su>
0005     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0006     SPDX-FileCopyrightText: 2009 Niko Sams <niko.sams@gmail.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "debugcontroller.h"
0012 
0013 #include <QAction>
0014 
0015 #include <KActionCollection>
0016 #include <KLocalizedString>
0017 #include <KTextEditor/Document>
0018 #include <KTextEditor/MarkInterface>
0019 #include <KXMLGUIFactory>
0020 
0021 #include "../interfaces/idocument.h"
0022 #include "../interfaces/icore.h"
0023 #include "../interfaces/idocumentcontroller.h"
0024 #include "../interfaces/contextmenuextension.h"
0025 #include "../interfaces/context.h"
0026 #include "../language/interfaces/editorcontext.h"
0027 #include "../sublime/view.h"
0028 #include "../sublime/mainwindow.h"
0029 #include "../sublime/area.h"
0030 #include "../debugger/breakpoint/breakpointmodel.h"
0031 #include "../debugger/breakpoint/breakpointwidget.h"
0032 #include "../debugger/variable/variablewidget.h"
0033 #include "../debugger/framestack/framestackmodel.h"
0034 #include "../debugger/framestack/framestackwidget.h"
0035 #include "../debugger/variable/variablecollection.h"
0036 #include "core.h"
0037 #include "debug.h"
0038 #include "uicontroller.h"
0039 #include "iruncontroller.h"
0040 
0041 namespace KDevelop {
0042 
0043 class DebuggerToolFactoryBase : public KDevelop::IToolViewFactory
0044 {
0045 public:
0046     DebuggerToolFactoryBase(DebugController* controller, const QString& id, Qt::DockWidgetArea defaultArea)
0047         : m_controller(controller)
0048         , m_id(id)
0049         , m_defaultArea(defaultArea)
0050     {
0051     }
0052 
0053     QString id() const override
0054     {
0055         return m_id;
0056     }
0057 
0058     Qt::DockWidgetArea defaultPosition() const override
0059     {
0060         return m_defaultArea;
0061     }
0062 
0063     void viewCreated(Sublime::View* view) override
0064     {
0065         if (view->widget()->metaObject()->indexOfSignal("requestRaise()") != -1)
0066             QObject::connect(view->widget(), SIGNAL(requestRaise()), view, SLOT(requestRaise()));
0067     }
0068 
0069 protected:
0070     auto controller() const
0071     {
0072         return m_controller;
0073     }
0074 
0075 private:
0076     DebugController* const m_controller;
0077     const QString m_id;
0078     const Qt::DockWidgetArea m_defaultArea;
0079 };
0080 
0081 template<class T>
0082 class DebuggerToolFactory : public DebuggerToolFactoryBase
0083 {
0084 public:
0085     using DebuggerToolFactoryBase::DebuggerToolFactoryBase;
0086 
0087     QWidget* create(QWidget* parent = nullptr) override
0088     {
0089         return new T(controller(), parent);
0090     }
0091 };
0092 
0093 template<class T>
0094 class DebuggerToolWithoutToolbarFactory : public DebuggerToolFactory<T>
0095 {
0096 public:
0097     using DebuggerToolFactory<T>::DebuggerToolFactory;
0098 
0099     // At present, some debugger widgets (e.g. breakpoint) contain actions so that shortcuts
0100     // work, but they don't need any toolbar.  So, suppress toolbar action.
0101     QList<QAction*> toolBarActions(QWidget* viewWidget) const override
0102     {
0103         Q_UNUSED(viewWidget);
0104         return {};
0105     }
0106 };
0107 
0108 DebugController::DebugController(QObject *parent)
0109     : IDebugController(parent), KXMLGUIClient(),
0110       m_breakpointModel(new BreakpointModel(this)),
0111       m_variableCollection(new VariableCollection(this))
0112 {
0113     qCDebug(SHELL) << "creating debug controller";
0114 
0115     setComponentName(QStringLiteral("kdevdebugger"), i18n("Debugger"));
0116     setXMLFile(QStringLiteral("kdevdebuggershellui.rc"));
0117 
0118     if (const auto* mainWindow = Core::self()->uiControllerInternal()->activeSublimeWindow()) {
0119         connect(mainWindow, &Sublime::MainWindow::areaChanged, this, &DebugController::areaChanged);
0120     }
0121 
0122     auto* const documentController = KDevelop::ICore::self()->documentController();
0123     Q_ASSERT(documentController); // DebugController is created after DocumentController.
0124 
0125     // This constructor is invoked before controllers are initialized, and thus before any documents can be opened.
0126     // So our textDocumentCreated() slot will be invoked for all documents.
0127     Q_ASSERT(documentController->openDocuments().empty());
0128     connect(documentController, &IDocumentController::textDocumentCreated, this, &DebugController::textDocumentCreated);
0129 }
0130 
0131 void DebugController::initialize()
0132 {
0133     m_breakpointModel->load();
0134 }
0135 
0136 void DebugController::initializeUi()
0137 {
0138     if (m_uiInitialized) return;
0139     m_uiInitialized = true;
0140 
0141     if((Core::self()->setupFlags() & Core::NoUi)) return;
0142     setupActions();
0143 
0144     ICore::self()->uiController()->addToolView(
0145         i18nc("@title:window", "Frame Stack"),
0146         new DebuggerToolWithoutToolbarFactory<FramestackWidget>(this, QStringLiteral("org.kdevelop.debugger.StackView"),
0147                                                                 Qt::BottomDockWidgetArea));
0148 
0149     ICore::self()->uiController()->addToolView(
0150         i18nc("@title:window", "Breakpoints"),
0151         new DebuggerToolWithoutToolbarFactory<BreakpointWidget>(
0152             this, QStringLiteral("org.kdevelop.debugger.BreakpointsView"), Qt::BottomDockWidgetArea));
0153 
0154     ICore::self()->uiController()->addToolView(
0155         i18nc("@title:window", "Variables"),
0156         new DebuggerToolFactory<VariableWidget>(this, QStringLiteral("org.kdevelop.debugger.VariablesView"),
0157                                                 Qt::LeftDockWidgetArea));
0158 
0159     ICore::self()->uiController()->activeMainWindow()->guiFactory()->addClient(this);
0160 
0161     stateChanged(QStringLiteral("ended"));
0162 }
0163 
0164 
0165 void DebugController::cleanup()
0166 {
0167     if (m_currentSession) m_currentSession.data()->stopDebugger();
0168 }
0169 
0170 DebugController::~DebugController()
0171 {
0172     qCDebug(SHELL) << "destroying debug controller";
0173 
0174     // The longest possible time interval has been allotted for previous and
0175     // the current debug sessions to stop their debugger processes: stopDebugger()
0176     // was called on the sessions in addSession() and cleanup() respectively.
0177     // Now is the last safe moment for a DebugSession to finalize its state and
0178     // invoke DebugController::debuggerStateChanged(), which in turn schedules the
0179     // session's deletion. This finalization not only ensures that debug sessions
0180     // are destroyed, but also prevents crashes: a DebugSession's state transition
0181     // signals lead to accesses to DebugController and its children, such as a
0182     // call to BreakpointModel::updateState(). Therefore we must force all debug
0183     // sessions to synchronously finalize their states now.
0184     emit killAllDebuggersNow();
0185 
0186     qCDebug(SHELL) << "destroyed debug controller";
0187 }
0188 
0189 BreakpointModel* DebugController::breakpointModel()
0190 {
0191     return m_breakpointModel;
0192 }
0193 
0194 VariableCollection* DebugController::variableCollection()
0195 {
0196     return m_variableCollection;
0197 }
0198 
0199 void DebugController::textDocumentCreated(KDevelop::IDocument* document)
0200 {
0201     if (auto* const iface = qobject_cast<KTextEditor::MarkInterface*>(document->textDocument())) {
0202         iface->setMarkPixmap(KTextEditor::MarkInterface::Execution, *executionPointPixmap());
0203     }
0204 }
0205 
0206 IDebugSession* DebugController::currentSession()
0207 {
0208     return m_currentSession.data();
0209 }
0210 
0211 void DebugController::setupActions()
0212 {
0213     KActionCollection* ac = actionCollection();
0214 
0215     QAction* action = m_continueDebugger = new QAction(this);
0216     setContinueStartsDebug(true);
0217     ac->addAction(QStringLiteral("debug_continue"), action);
0218     connect(action, &QAction::triggered, this, &DebugController::run);
0219 
0220     #if 0
0221     m_restartDebugger = action = new QAction(QIcon::fromTheme("media-seek-backward"), i18n("&Restart"), this);
0222     action->setToolTip( i18n("Restart program") );
0223     action->setWhatsThis( i18n("Restarts applications from the beginning.") );
0224     action->setEnabled(false);
0225     connect(action, SIGNAL(triggered(bool)), this, SLOT(restartDebugger()));
0226     ac->addAction("debug_restart", action);
0227     #endif
0228 
0229     m_interruptDebugger = action = new QAction(QIcon::fromTheme(QStringLiteral("media-playback-pause")), i18nc("@action", "Interrupt"), this);
0230     action->setToolTip( i18nc("@info:tooltip", "Interrupt application") );
0231     action->setWhatsThis(i18nc("@info:whatsthis", "Interrupts the debugged process or current debugger command."));
0232     connect(action, &QAction::triggered, this, &DebugController::interruptDebugger);
0233     ac->addAction(QStringLiteral("debug_pause"), action);
0234 
0235     m_runToCursor = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-run-cursor")), i18nc("@action", "Run to &Cursor"), this);
0236     action->setToolTip( i18nc("@info:tooltip", "Run to cursor") );
0237     action->setWhatsThis(i18nc("@info:whatsthis", "Continues execution until the cursor position is reached."));
0238     connect(action, &QAction::triggered, this, &DebugController::runToCursor);
0239     ac->addAction(QStringLiteral("debug_runtocursor"), action);
0240 
0241 
0242     m_jumpToCursor = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-execute-to-cursor")), i18nc("@action", "Set E&xecution Position to Cursor"), this);
0243     action->setToolTip( i18nc("@info:tooltip", "Jump to cursor") );
0244     action->setWhatsThis(i18nc("@info:whatsthis", "Continue execution from the current cursor position."));
0245     connect(action, &QAction::triggered, this, &DebugController::jumpToCursor);
0246     ac->addAction(QStringLiteral("debug_jumptocursor"), action);
0247 
0248     m_stepOver = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-step-over")), i18nc("@action", "Step &Over"), this);
0249     ac->setDefaultShortcut( action, Qt::Key_F10);
0250     action->setToolTip( i18nc("@info:tooltip", "Step over the next line") );
0251     action->setWhatsThis( i18nc("@info:whatsthis", "Executes one line of source in the current source file. "
0252                                "If the source line is a call to a function the whole "
0253                                "function is executed and the app will stop at the line "
0254                                "following the function call.") );
0255     connect(action, &QAction::triggered, this, &DebugController::stepOver);
0256     ac->addAction(QStringLiteral("debug_stepover"), action);
0257 
0258 
0259     m_stepOverInstruction = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-step-instruction")), i18nc("@action", "Step over Ins&truction"), this);
0260     action->setToolTip( i18nc("@info:tooltip", "Step over instruction") );
0261     action->setWhatsThis(i18nc("@info:whatsthis", "Steps over the next assembly instruction."));
0262     connect(action, &QAction::triggered, this, &DebugController::stepOverInstruction);
0263     ac->addAction(QStringLiteral("debug_stepoverinst"), action);
0264 
0265 
0266     m_stepInto = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-step-into")), i18nc("@action", "Step &Into"), this);
0267     ac->setDefaultShortcut( action, Qt::Key_F11);
0268     action->setToolTip( i18nc("@info:tooltip", "Step into the next statement") );
0269     action->setWhatsThis( i18nc("@info:whatsthis", "Executes exactly one line of source. If the source line "
0270                                "is a call to a function then execution will stop after "
0271                                "the function has been entered.") );
0272     connect(action, &QAction::triggered, this, &DebugController::stepInto);
0273     ac->addAction(QStringLiteral("debug_stepinto"), action);
0274 
0275 
0276     m_stepIntoInstruction = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-step-into-instruction")), i18nc("@action", "Step into I&nstruction"), this);
0277     action->setToolTip( i18nc("@info:tooltip", "Step into instruction") );
0278     action->setWhatsThis(i18nc("@info:whatsthis", "Steps into the next assembly instruction."));
0279     connect(action, &QAction::triggered, this, &DebugController::stepIntoInstruction);
0280     ac->addAction(QStringLiteral("debug_stepintoinst"), action);
0281 
0282     m_stepOut = action = new QAction(QIcon::fromTheme(QStringLiteral("debug-step-out")), i18nc("@action", "Step O&ut"), this);
0283     ac->setDefaultShortcut( action, Qt::Key_F12);
0284     action->setToolTip( i18nc("@info:tooltip", "Step out of the current function") );
0285     action->setWhatsThis( i18nc("@whatsthis", "Executes the application until the currently executing "
0286                                "function is completed. The debugger will then display "
0287                                "the line after the original call to that function. If "
0288                                "program execution is in the outermost frame (i.e. in "
0289                                "main()) then this operation has no effect.") );
0290     connect(action, &QAction::triggered, this, &DebugController::stepOut);
0291     ac->addAction(QStringLiteral("debug_stepout"), action);
0292 
0293     m_toggleBreakpoint = action = new QAction(QIcon::fromTheme(QStringLiteral("breakpoint")), i18nc("@action", "Toggle Breakpoint"), this);
0294     ac->setDefaultShortcut( action, i18n("Ctrl+Alt+B") );
0295     action->setToolTip(i18nc("@info:tooltip", "Toggle breakpoint"));
0296     action->setWhatsThis(i18nc("@info:whatsthis", "Toggles the breakpoint at the current line in editor."));
0297     connect(action, &QAction::triggered, this, &DebugController::toggleBreakpoint);
0298     ac->addAction(QStringLiteral("debug_toggle_breakpoint"), action);
0299 
0300     m_showCurrentLine = action = new QAction(QIcon::fromTheme(QStringLiteral("go-jump")), i18nc("@action", "Show Current Line"), this);
0301     action->setToolTip(i18nc("@info:tooltip", "Show the current execution position"));
0302     action->setWhatsThis(i18nc("@info:whatsthis", "Jumps to the execution line in the editor."));
0303     connect(action, &QAction::triggered, this, &DebugController::showCurrentLine);
0304     ac->addAction(QStringLiteral("debug_showcurrentline"), action);
0305 }
0306 
0307 void DebugController::addSession(IDebugSession* session)
0308 {
0309     qCDebug(SHELL) << session;
0310     Q_ASSERT(session->variableController());
0311     Q_ASSERT(session->breakpointController());
0312     Q_ASSERT(session->frameStackModel());
0313 
0314     //TODO support multiple sessions
0315     if (m_currentSession) {
0316         m_currentSession.data()->stopDebugger();
0317     }
0318     m_currentSession = session;
0319 
0320     connect(session, &IDebugSession::stateChanged, this, &DebugController::debuggerStateChanged);
0321     connect(session, &IDebugSession::showStepInSource, this, &DebugController::showStepInSource);
0322     connect(session, &IDebugSession::clearExecutionPoint, this, &DebugController::clearExecutionPoint);
0323     connect(session, &IDebugSession::raiseFramestackViews, this, &DebugController::raiseFramestackViews);
0324     connect(this, &DebugController::killAllDebuggersNow, session, &IDebugSession::killDebuggerNow);
0325 
0326     updateDebuggerState(session->state(), session);
0327 
0328     emit currentSessionChanged(session);
0329 
0330     if((Core::self()->setupFlags() & Core::NoUi)) return;
0331 
0332 
0333     Sublime::MainWindow* mainWindow = Core::self()->uiControllerInternal()->activeSublimeWindow();
0334     auto oldArea = mainWindow->area();
0335     if (oldArea->objectName() != QLatin1String("debug")) {
0336         ICore::self()->uiController()->switchToArea(QStringLiteral("debug"), IUiController::ThisWindow);
0337         mainWindow->area()->setWorkingSet(oldArea->workingSet(), oldArea->workingSetPersistent(), oldArea);
0338     }
0339 }
0340 
0341 void DebugController::clearExecutionPoint()
0342 {
0343     const auto* const documentController = KDevelop::ICore::self()->documentController();
0344     if (!documentController) {
0345         qCDebug(SHELL) << "Cannot clear execution point without the document controller. "
0346                           "KDevelop must be exiting and the document controller already destroyed.";
0347         return;
0348     }
0349 
0350     // TODO KF6: rename lastExecMarkDocument to document and remove the document variable just below.
0351     auto* const lastExecMarkDocument = m_lastExecMarkDocument.data();
0352     auto* const document = qobject_cast<KTextEditor::MarkInterface*>(lastExecMarkDocument);
0353     // Do we even have a document with execution mark?
0354     if (!document) {
0355         return;
0356     }
0357     m_lastExecMarkDocument = nullptr;
0358 
0359     constexpr auto markTypeToRemove = KTextEditor::MarkInterface::Execution;
0360 
0361     // Is the execution mark still at the line it was added to?
0362     if (document->mark(m_lastExecMarkLine) & markTypeToRemove) {
0363         document->removeMark(m_lastExecMarkLine, markTypeToRemove);
0364         return;
0365     }
0366 
0367     // The execution mark is on some other line (probably because the document was edited
0368     // during the current debug session). Iterate over all marks in the document to find and remove it.
0369     const auto& marks = document->marks();
0370     const auto it = std::find_if(marks.begin(), marks.end(), [](const KTextEditor::Mark* mark) -> bool {
0371         // TODO: remove the following line once building KDevelop with Visual Studio 2019 is no longer supported.
0372         constexpr auto markTypeToRemove = KTextEditor::MarkInterface::Execution;
0373         return mark->type & markTypeToRemove;
0374     });
0375 
0376     if (it != marks.end()) {
0377         document->removeMark(it.key(), markTypeToRemove);
0378         return;
0379     }
0380 
0381     qCWarning(SHELL) << "failed to remove execution mark from"
0382                      << lastExecMarkDocument->url().toString(QUrl::PreferLocalFile);
0383 }
0384 
0385 void DebugController::showStepInSource(const QUrl &url, int lineNum)
0386 {
0387     if((Core::self()->setupFlags() & Core::NoUi)) return;
0388 
0389     clearExecutionPoint();
0390     qCDebug(SHELL) << url << lineNum;
0391 
0392     Q_ASSERT(qobject_cast<IDebugSession*>(sender()));
0393     QPair<QUrl,int> openUrl = static_cast<IDebugSession*>(sender())->convertToLocalUrl(qMakePair<QUrl,int>( url, lineNum ));
0394     KDevelop::IDocument* document = KDevelop::ICore::self()
0395         ->documentController()
0396         ->openDocument(openUrl.first, KTextEditor::Cursor(openUrl.second, 0), IDocumentController::DoNotFocus);
0397 
0398     if( !document )
0399         return;
0400 
0401     auto* const textDocument = document->textDocument();
0402     auto* const iface = qobject_cast<KTextEditor::MarkInterface*>(textDocument);
0403     if( !iface )
0404         return;
0405 
0406     m_lastExecMarkDocument = textDocument;
0407     m_lastExecMarkLine = lineNum;
0408     iface->addMark(lineNum, KTextEditor::MarkInterface::Execution);
0409 }
0410 
0411 
0412 void DebugController::debuggerStateChanged(KDevelop::IDebugSession::DebuggerState state)
0413 {
0414     Q_ASSERT(qobject_cast<IDebugSession*>(sender()));
0415     auto* session = static_cast<IDebugSession*>(sender());
0416     qCDebug(SHELL) << session << state << "current" << m_currentSession.data();
0417     if (session == m_currentSession.data()) {
0418         updateDebuggerState(state, session);
0419     }
0420 
0421     if (state == IDebugSession::EndedState) {
0422         if (session == m_currentSession.data()) {
0423             m_currentSession.clear();
0424             emit currentSessionChanged(nullptr);
0425             if (!Core::self()->shuttingDown()) {
0426                 Sublime::MainWindow* mainWindow = Core::self()->uiControllerInternal()->activeSublimeWindow();
0427                 if (mainWindow && mainWindow->area()->objectName() != QLatin1String("code")) {
0428                     auto oldArea = mainWindow->area();
0429                     QString workingSet = oldArea->workingSet();
0430                     ICore::self()->uiController()->switchToArea(QStringLiteral("code"), IUiController::ThisWindow);
0431                     mainWindow->area()->setWorkingSet(workingSet, oldArea->workingSetPersistent(), oldArea);
0432                 }
0433                 ICore::self()->uiController()->findToolView(i18nc("@title:window", "Debug"), nullptr, IUiController::Raise);
0434             }
0435         }
0436         session->deleteLater();
0437     }
0438 }
0439 
0440 void DebugController::updateDebuggerState(IDebugSession::DebuggerState state, IDebugSession *session)
0441 {
0442     Q_UNUSED(session);
0443     if((Core::self()->setupFlags() & Core::NoUi)) return;
0444 
0445     qCDebug(SHELL) << state;
0446     switch (state) {
0447         case IDebugSession::StoppedState:
0448         case IDebugSession::NotStartedState:
0449         case IDebugSession::StoppingState:
0450             qCDebug(SHELL) << "new state: stopped";
0451             stateChanged(QStringLiteral("stopped"));
0452             setContinueStartsDebug(true);
0453             //m_restartDebugger->setEnabled(session->restartAvailable());
0454             break;
0455         case IDebugSession::StartingState:
0456         case IDebugSession::PausedState:
0457             qCDebug(SHELL) << "new state: paused";
0458             stateChanged(QStringLiteral("paused"));
0459             setContinueStartsDebug(false);
0460             //m_restartDebugger->setEnabled(session->restartAvailable());
0461             break;
0462         case IDebugSession::ActiveState:
0463             qCDebug(SHELL) << "new state: active";
0464             stateChanged(QStringLiteral("active"));
0465             setContinueStartsDebug(false);
0466             //m_restartDebugger->setEnabled(false);
0467             break;
0468         case IDebugSession::EndedState:
0469             qCDebug(SHELL) << "new state: ended";
0470             stateChanged(QStringLiteral("ended"));
0471             setContinueStartsDebug(true);
0472             //m_restartDebugger->setEnabled(false);
0473             break;
0474     }
0475     if (state == IDebugSession::PausedState && ICore::self()->uiController()->activeMainWindow()) {
0476         ICore::self()->uiController()->activeMainWindow()->activateWindow();
0477     }
0478 }
0479 
0480 void DebugController::setContinueStartsDebug(bool startsDebug)
0481 {
0482     if (startsDebug) {
0483         m_continueDebugger->setText(i18nc("@action", "Debug Launch"));
0484         m_continueDebugger->setIcon(QIcon::fromTheme(QStringLiteral("debug-run")));
0485         m_continueDebugger->setToolTip(i18nc("@info:tooltip", "Debug current launch"));
0486         m_continueDebugger->setWhatsThis(i18nc("@info:whatsthis", "Executes the target or the program specified in "
0487                                               "currently active launch configuration inside a Debugger."));
0488     } else {
0489         m_continueDebugger->setText(i18nc("@action", "&Continue"));
0490         m_continueDebugger->setIcon(QIcon::fromTheme(QStringLiteral("media-playback-start")));
0491         m_continueDebugger->setToolTip(i18nc("@info:tooltip", "Continue application execution") );
0492         m_continueDebugger->setWhatsThis(i18nc("@info:whatsthis", "Continues the execution of your application in the "
0493                                               "debugger. This only takes effect when the application "
0494                                               "has been halted by the debugger (i.e. a breakpoint has "
0495                                               "been activated or the interrupt was pressed).") );
0496     }
0497 }
0498 
0499 ContextMenuExtension DebugController::contextMenuExtension(Context* context, QWidget* parent)
0500 {
0501     Q_UNUSED(parent);
0502 
0503     ContextMenuExtension menuExt;
0504 
0505     if( context->type() != Context::EditorContext )
0506         return menuExt;
0507 
0508     auto *econtext = dynamic_cast<KDevelop::EditorContext*>(context);
0509     if (!econtext)
0510         return menuExt;
0511 
0512     if (m_currentSession && m_currentSession.data()->isRunning()) {
0513         menuExt.addAction( KDevelop::ContextMenuExtension::DebugGroup, m_runToCursor);
0514     }
0515 
0516     if (econtext->url().isLocalFile()) {
0517         menuExt.addAction( KDevelop::ContextMenuExtension::DebugGroup, m_toggleBreakpoint);
0518     }
0519     return menuExt;
0520 }
0521 
0522 #if 0
0523 void DebugController::restartDebugger() {
0524     if (m_currentSession) {
0525         m_currentSession.data()->restartDebugger();
0526     }
0527 }
0528 #endif
0529 
0530 void DebugController::stopDebugger() {
0531     if (m_currentSession) {
0532         m_currentSession.data()->stopDebugger();
0533     }
0534 }
0535 void DebugController::interruptDebugger() {
0536     if (m_currentSession) {
0537         m_currentSession.data()->interruptDebugger();
0538     }
0539 }
0540 
0541 void DebugController::run() {
0542     if (m_currentSession) {
0543         m_currentSession.data()->run();
0544     } else {
0545         auto runController = ICore::self()->runController();
0546         if (runController->launchConfigurations().isEmpty()) {
0547             runController->showConfigurationDialog();
0548         }
0549         runController->executeDefaultLaunch(QStringLiteral("debug"));
0550     }
0551 }
0552 
0553 void DebugController::runToCursor() {
0554     if (m_currentSession) {
0555         m_currentSession.data()->runToCursor();
0556     }
0557 }
0558 void DebugController::jumpToCursor() {
0559     if (m_currentSession) {
0560         m_currentSession.data()->jumpToCursor();
0561     }
0562 }
0563 void DebugController::stepOver() {
0564     if (m_currentSession) {
0565         m_currentSession.data()->stepOver();
0566     }
0567 }
0568 void DebugController::stepIntoInstruction() {
0569     if (m_currentSession) {
0570         m_currentSession.data()->stepIntoInstruction();
0571     }
0572 }
0573 void DebugController::stepInto() {
0574     if (m_currentSession) {
0575         m_currentSession.data()->stepInto();
0576     }
0577 }
0578 void DebugController::stepOverInstruction() {
0579     if (m_currentSession) {
0580         m_currentSession.data()->stepOverInstruction();
0581     }
0582 }
0583 void DebugController::stepOut() {
0584     if (m_currentSession) {
0585         m_currentSession.data()->stepOut();
0586     }
0587 }
0588 
0589 void DebugController::areaChanged(Sublime::Area* newArea)
0590 {
0591     if (newArea->objectName() != QLatin1String("debug") && newArea->objectName() != QLatin1String("review")) {
0592         stopDebugger();
0593     }
0594 }
0595 
0596 void DebugController::toggleBreakpoint()
0597 {
0598     if (KDevelop::IDocument* document = KDevelop::ICore::self()->documentController()->activeDocument()) {
0599         KTextEditor::Cursor cursor = document->cursorPosition();
0600         if (!cursor.isValid()) return;
0601         breakpointModel()->toggleBreakpoint(document->url(), cursor);
0602     }
0603 }
0604 
0605 void DebugController::showCurrentLine()
0606 {
0607     const auto location = qMakePair(m_currentSession->currentUrl(), m_currentSession->currentLine());
0608 
0609     if (location.second != -1) {
0610         const auto localLocation = m_currentSession->convertToLocalUrl(location);
0611         ICore::self()->documentController()->openDocument(localLocation.first,
0612                                                           KTextEditor::Cursor(localLocation.second, 0),
0613                                                           IDocumentController::DefaultMode);
0614     }
0615 
0616 }
0617 
0618 const QPixmap* DebugController::executionPointPixmap()
0619 {
0620     constexpr int markPixmapSize = 32;
0621     static QPixmap pixmap=QIcon::fromTheme(QStringLiteral("go-next")).pixmap(QSize(markPixmapSize, markPixmapSize), QIcon::Normal, QIcon::Off);
0622     return &pixmap;
0623 }
0624 
0625 }
0626 
0627 #include "moc_debugcontroller.cpp"