/** * DebuggerClient.h * Copyright (c) 2013 ROBLOX Corp. All Rights Reserved. */ #pragma once //this will make sure that we can use qt with boost (else moc compiler errors) #ifndef QT_NO_KEYWORDS #define QT_NO_KEYWORDS #endif // 3rd Party Headers #include "boost/shared_ptr.hpp" // Roblox Headers #include "rbx/signal.h" #include "script/DebuggerManager.h" // Qt Headers #include #include // Roblox Studio Headers #include "RobloxScriptDoc.h" namespace RBX { class Script; class DataModel; class Heartbeat; class RunTransition; namespace Scripting { class ScriptDebugger; } } class QTextBlock; class QDockWidget; class ScriptTextEditor; class DebuggerToolTipWidget; class DebuggerClient; class IRobloxDoc; class QDebuggerClient; class QStringList; class DebuggerClientManager : public QObject { Q_OBJECT public: enum eStepMode { STEP_INTO, STEP_OUT, STEP_OVER }; static DebuggerClientManager& Instance(); void setDataModel(shared_ptr spDataModel); boost::shared_ptr getDataModel(); bool actionState(const QString &actionID, bool &enableState, bool &checkedState); bool handleAction(const QString &actionID, bool checkedState); void addDebuggerClient(DebuggerClient* pDebuggerClient); void removeDebuggerClient(DebuggerClient* pDebuggerClient); void setActiveDebuggerClient(DebuggerClient* pDebuggerClient); void syncBreakpointState(shared_ptr spBreakpoint); RBX::Reflection::Variant getWatchValue(boost::shared_ptr spWatch); void addWatch(const QString& watchKey); DebuggerClient* getOrCreateDebuggerClient(shared_ptr spScript); DebuggerClient* getDebuggerClient(shared_ptr spScript); const std::vector& getDebuggerClients(); DebuggerClient* getCurrentDebuggerClient() { return m_pCurrentDebuggerClient; } void breakpointEncounter(DebuggerClient *pDebuggerClient); void onRunTransition(RBX::RunTransition evt); //signals emitted rbx::signal clientActivated; rbx::signal clientDeactivated; rbx::signal clearAll; rbx::signal debuggingStarted; rbx::signal debuggingStopped; rbx::signal executionDataCleared; rbx::signal breakpointEncountered; rbx::signal)> breakpointAdded; rbx::signal)> breakpointRemoved; rbx::signal)> watchAdded; rbx::signal)> watchRemoved; rbx::signal debuggersListUpdated; private Q_SLOTS: void onStepAction(); void updateScriptDocument(); void updateDebuggersList(); private: DebuggerClientManager(); void displayBreakOnErrorDialog(); void updateDocumentData(bool openDoc = true, bool updateMarkers = true); void onDebuggerAdded(boost::shared_ptr spInstance); void onDebuggerRemoved(boost::shared_ptr spInstance); void startDebugging(RBX::RunTransition evt); void pauseDebugging(RBX::RunTransition evt); void stopDebugging(RBX::RunTransition evt); void resumeAllClients(bool forceAll = false); void pauseAllClients(bool forceAll = false); void resetAllClients(); Q_INVOKABLE void updateDebugActions(bool add); std::vector debuggerClients; boost::shared_ptr m_spDataModel; rbx::signals::connection m_cRunTransitionConnection; rbx::signals::connection m_cDebuggerAddedConnection; rbx::signals::connection m_cDebuggerRemovedConnection; DebuggerClient* m_pCurrentDebuggerClient; DebuggerClient* m_pActiveDebuggerClient; QAction* m_pSeparatorAction; QMutex m_debuggerClientMutex; bool m_bIgnorePauseExecution; bool m_bDebuggerListUpdateRequested; }; class DebuggerClient { public: struct BreakpointDetail { BreakpointDetail(int iLine, bool iIsEnabled) : line(iLine), isEnabled(iIsEnabled) {} int line; bool isEnabled; //std::string condition; }; typedef std::vector BreakpointDetails; DebuggerClient(boost::shared_ptr script); ~DebuggerClient(); void activate(); void deActivate(); void setDocument(IRobloxDoc *pDocument); boost::shared_ptr getScript(); boost::shared_ptr getScript(int frameNo); bool updateDocument(bool openDoc = true, bool updateMarkers = true); void setExternalDebuggerClient(DebuggerClient* pExtDebuggerClient); void highlightLine(int textEditLine); int getCurrentLine(); void setCurrentLine(int textEditLine); void setMarker(int textEditLine, const QString& marker, bool setBlockCurrent); void setCurrentFrame(int frame); int getCurrentFrame() { return m_CurrentFrame; } RBX::Scripting::ScriptDebugger::Stack getCurrentCallStack() { return m_CallStackAtPausedLine; } bool getValue(const QString& key, RBX::Reflection::Variant& value, int frame = -1); RBX::Reflection::Variant getWatchValue(boost::shared_ptr spWatch); void addWatch(const QString& watchKey); QString getSourceCodeAtLine(int breakpointLine); void syncBreakpointState(int breakpointLine); void syncScriptWithTextEditor(const BreakpointDetails& textEditBreakpoints); void syncTextEdtiorWithScript(); void toggleBreakpoint(int breakpointLine); void toggleBreakpointState(int breakpointLine); void pauseExecution(); void resumeExecution(); void resetExecution(); bool isDebugging(); bool isPaused(); bool hasError(); std::string getErrorMessage() { return m_ErrorMessage; } const RBX::Scripting::ScriptDebugger::PausedThreads& getPausedThreads(); int getCurrentThread(); void setCurrentThread(int threadID); bool isHighlightingRequired(); bool isTopFrameCurrent(); bool actionState(const QString &actionID, bool &enableState, bool &checkedState); bool handleAction(const QString &actionID, bool checkedState); //signals emitted rbx::signal debuggerResuming; private: bool isValid(); void onDebuggerResume(); void onEncounteredBreakpoint(int line); void onBreakpointAdded(shared_ptr spInstance); void onBreakpointRemoved(shared_ptr spInstance); void onWatchAdded(shared_ptr spInstance); void onWatchRemoved(shared_ptr spInstance); void onScriptErrorDetected(int errorLine, std::string errorMessage, RBX::Scripting::ScriptDebugger::Stack stack); void onScriptStopped(); bool isKeyDefined(boost::shared_ptr variables, const QString &wordUnderCursor, RBX::Reflection::Variant &value); boost::shared_ptr m_spScript; boost::shared_ptr m_spDataModel; boost::shared_ptr m_spDebugger; std::list m_cConnections; DebuggerClient* m_pExtDebuggerClient; QDebuggerClient* m_pQDebuggerClient; QStringList m_ScriptLines; RBX::Scripting::ScriptDebugger::Stack m_CallStackAtPausedLine; int m_CurrentFrame; std::string m_ErrorMessage; int m_currentThreadID; bool m_bIgnoreBreakpointAddRemove; bool m_bIsActive; }; class QDebuggerClient : public QObject { Q_OBJECT public: QDebuggerClient(DebuggerClient* pDebuggerClient, ScriptTextEditor* pTextEdit); ~QDebuggerClient(); void activate(); void deActivate(); QString getSourceCodeAtLine(int textEditLine); void updateBreakpointState(int textEditLine, RBXTextUserData::eBreakpointState state = RBXTextUserData::NO_BREAKPOINT); void highlightLine(int textEditLine); void clearLineHighlight(); void setCurrentLine(int textEditLine); void setMarker(int textEditLine, const QString& marker, bool setBlockCurrent); void setExternalDebuggerClient(DebuggerClient* pExtDebuggerClient) { m_pExtDebuggerClient = pExtDebuggerClient; } void updateEditor(); private Q_SLOTS: void onInsertBreakpoint(); void onDeleteBreakpoint(); void onToggleBreakpoint(int textEditLine); void onToggleBreakpointState(); void onAddWatch(); //slot methods to ensure public functions are called from main thread void highlightLine_MT(int textEditLine); void clearLineHighlight_MT(); void setCurrentLine_MT(int textEditLine); void setMarker_MT(int textEditLine, QString marker, bool setBlockCurrent); bool onShowToolTip(const QPoint& pos, const QString& key); void onContentsChanged(); void onUpdateContextualMenu(QMenu *pMenu, QPoint pos); private: void updateTextUserData(int textEditLineNumber, RBXTextUserData::eBreakpointState state); void modifyContextualMenu(QMenu* pContextualMenu); void cleanupContextMenu(); void setCurrentBlock(const QTextBlock &blockToSet); void highlightBlock(const QTextBlock &blockToHighlight); DebuggerClient::BreakpointDetails getBreakpointDetails(); void removeBreakpointState(); bool eventFilter(QObject *obj, QEvent *evt) ; DebuggerClient* m_pDebuggerClient; DebuggerClient* m_pExtDebuggerClient; QAction* m_pBreakpointMenuAction; QAction* m_pSeparatorAction1; QAction* m_pSeparatorAction2; ScriptTextEditor* m_pTextEdit; DebuggerToolTipWidget* m_pToolTipWidget; QList m_extraSelections; int m_lastBlockCount; bool m_isModifiedByKeyBoard; bool m_ignoreAutoRepeatEvent; };