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"