diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 3cf254c91..497983abc 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -3,10 +3,10 @@ { "name": "gdb", "url": "https://www.gnu.org/software/gdb", + "type": "cppdbg", "run": { "command": "gdb", - "command_arguments": "--interpreter=dap", - "type": "cppdbg" + "command_arguments": "--interpreter=dap" }, "languages": [ "cpp", "c", "d", "go", "objectivec", "fortran", "pascal", "rust" ], "configurations": [ @@ -26,10 +26,10 @@ { "name": "lldb-dap", "url": "https://github.com/llvm/llvm-project/blob/main/lldb/tools/lldb-dap/README.md", + "type": "cppdbg", "run": { "command": "lldb-dap", - "command_fallback": "lldb-vscode", - "type": "cppdbg" + "command_fallback": "lldb-vscode" }, "find": { "macos": "xcrun -f ${command}" diff --git a/include/eepp/ui/css/transitiondefinition.hpp b/include/eepp/ui/css/transitiondefinition.hpp index f8fb2583f..cc922e9d1 100644 --- a/include/eepp/ui/css/transitiondefinition.hpp +++ b/include/eepp/ui/css/transitiondefinition.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include using namespace EE::Math; diff --git a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp index b8f01d40c..514741b0e 100644 --- a/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerclientlistener.cpp @@ -86,6 +86,8 @@ std::unordered_map ModelVariableNode::nodeMap = class VariablesModel : public Model { public: + enum Columns { Name, Value, Type }; + VariablesModel( ModelVariableNode::NodePtr rootNode, UISceneNode* sceneNode ) : rootNode( rootNode ), mSceneNode( sceneNode ) {} @@ -124,7 +126,7 @@ class VariablesModel : public Model { return node.get() == parentNode.get(); } ) ); - return createIndex( row, 0, parentNode.get() ); + return createIndex( row, Columns::Name, parentNode.get() ); } size_t rowCount( const ModelIndex& index = ModelIndex() ) const override { @@ -146,11 +148,11 @@ class VariablesModel : public Model { std::string columnName( const size_t& colIdx ) const override { switch ( colIdx ) { - case 0: + case Columns::Name: return mSceneNode->i18n( "variable_name", "Variable Name" ); - case 1: + case Columns::Value: return mSceneNode->i18n( "value", "Value" ); - case 2: + case Columns::Type: return mSceneNode->i18n( "type", "Type" ); } return ""; @@ -184,6 +186,8 @@ class VariablesModel : public Model { class ThreadsModel : public Model { public: + enum Columns { ID }; + ThreadsModel( const std::vector& threads, UISceneNode* sceneNode ) : mThreads( threads ), mSceneNode( sceneNode ) {} @@ -192,17 +196,17 @@ class ThreadsModel : public Model { virtual std::string columnName( const size_t& colIdx ) const { switch ( colIdx ) { - case 0: + case Columns::ID: return mSceneNode->i18n( "thread_id", "Thread ID" ); } return ""; } virtual Variant data( const ModelIndex& modelIndex, ModelRole role ) const { - if ( role == ModelRole::Display && modelIndex.column() == 0 ) { + if ( role == ModelRole::Display && modelIndex.column() == Columns::ID ) { return Variant( String::format( "#%d (%s)", mThreads[modelIndex.row()].id, mThreads[modelIndex.row()].name.c_str() ) ); - } else if ( role == ModelRole::Icon && modelIndex.column() == 0 && + } else if ( role == ModelRole::Icon && modelIndex.column() == Columns::ID && mThreads[modelIndex.row()].id == mCurrentThreadId ) { static UIIcon* circleFilled = mSceneNode->findIcon( "circle-filled" ); return Variant( circleFilled ); @@ -258,6 +262,8 @@ class ThreadsModel : public Model { class StackModel : public Model { public: + enum Columns { ID, Name, SourceName, SourcePath, Line, Column }; + StackModel( StackTraceInfo&& stack, UISceneNode* sceneNode ) : mStack( std::move( stack ) ), mSceneNode( sceneNode ) {} @@ -270,15 +276,15 @@ class StackModel : public Model { virtual std::string columnName( const size_t& colIdx ) const { switch ( colIdx ) { - case 0: + case Columns::ID: return mSceneNode->i18n( "id", "ID" ); - case 1: + case Columns::Name: return mSceneNode->i18n( "name", "Name" ); - case 2: + case Columns::SourceName: return mSceneNode->i18n( "source_name", "Source Name" ); - case 3: + case Columns::SourcePath: return mSceneNode->i18n( "source_path", "Source Path" ); - case 4: + case Columns::Line: return mSceneNode->i18n( "line", "Line" ); } return ""; @@ -288,25 +294,25 @@ class StackModel : public Model { Lock l( mResourceLock ); if ( role == ModelRole::Display ) { switch ( modelIndex.column() ) { - case 0: + case Columns::ID: return Variant( String::toString( mStack.stackFrames[modelIndex.row()].id ) ); - case 1: + case Columns::Name: return Variant( mStack.stackFrames[modelIndex.row()].name.c_str() ); - case 2: + case Columns::SourceName: return mStack.stackFrames[modelIndex.row()].source ? Variant( mStack.stackFrames[modelIndex.row()].source->name ) : Variant(); - case 3: + case Columns::SourcePath: return mStack.stackFrames[modelIndex.row()].source ? Variant( mStack.stackFrames[modelIndex.row()].source->path ) : Variant(); - case 4: + case Columns::Line: return Variant( String::toString( mStack.stackFrames[modelIndex.row()].line ) ); - case 5: + case Columns::Column: return Variant( String::toString( mStack.stackFrames[modelIndex.row()].column ) ); } - } else if ( role == ModelRole::Icon && modelIndex.column() == 1 && + } else if ( role == ModelRole::Icon && modelIndex.column() == Columns::Name && mCurrentScopeId == mStack.stackFrames[modelIndex.row()].id ) { static UIIcon* circleFilled = mSceneNode->findIcon( "circle-filled" ); return Variant( circleFilled ); diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 7602599f6..e71878e24 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -151,6 +151,7 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi for ( const auto& dap : dapArr ) { DapTool dapTool; dapTool.name = dap.value( "name", "" ); + dapTool.type = dap.value( "type", "" ); dapTool.url = dap.value( "url", "" ); if ( dap.contains( "run" ) ) { @@ -241,6 +242,67 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi } } +void DebuggerPlugin::loadProjectConfiguration( const std::string& path ) { + std::string data; + if ( !FileSystem::fileGet( path, data ) ) + return; + + json j; + try { + j = json::parse( data, nullptr, true, true ); + } catch ( const json::exception& e ) { + Log::error( "DebuggerPlugin::loadProjectConfiguration - Error parsing config from path %s, " + "error: %s, config " + "file content:\n%s", + path.c_str(), e.what(), data.c_str() ); + return; + } + + if ( !j.contains( "configurations" ) || !j["configurations"].is_array() ) { + Log::warning( "DebuggerPlugin::loadProjectConfiguration: wrong format in %s", path ); + return; + } + + auto& confs = j["configurations"]; + + for ( const auto& conf : confs ) { + DapConfig dapConfig; + auto type = conf.value( "type", "" ); + if ( type != "cppdbg" ) + continue; + dapConfig.name = conf.value( "name", "" ); + if ( dapConfig.name.empty() ) + continue; + dapConfig.command = conf.value( "request", "" ); + if ( dapConfig.command != "launch" && dapConfig.command != "attach" ) + continue; + dapConfig.args = std::move( conf ); + + { + Lock l( mDapsMutex ); + mDapConfigs.emplace_back( std::move( dapConfig ) ); + } + } +} + +void DebuggerPlugin::loadProjectConfigurations() { + { + Lock l( mDapsMutex ); + mDapConfigs.clear(); + } + + mThreadPool->run( [this] { + std::string config = mProjectPath + ".vscode/launch.json"; + if ( FileSystem::fileExists( config ) ) + loadProjectConfiguration( config ); + config = mProjectPath + ".ecode/launch.json"; + if ( FileSystem::fileExists( config ) ) + loadProjectConfiguration( config ); + + getUISceneNode()->runOnMainThread( [this] { updateDebuggerConfigurationList(); } ); + } ); +} + PluginRequestHandle DebuggerPlugin::processMessage( const PluginMessage& msg ) { switch ( msg.type ) { case PluginMessageType::WorkspaceFolderChanged: { @@ -248,11 +310,16 @@ PluginRequestHandle DebuggerPlugin::processMessage( const PluginMessage& msg ) { if ( getUISceneNode() && mSidePanel ) { getUISceneNode()->runOnMainThread( [this] { - if ( mProjectPath.empty() ) + if ( mProjectPath.empty() ) { hideSidePanel(); + Lock l( mDapsMutex ); + mDapConfigs.clear(); + } } ); } + loadProjectConfigurations(); + mBreakpointsModel.reset(); updateUI(); mInitialized = true; @@ -304,6 +371,21 @@ void DebuggerPlugin::buildSidePanelTab() { getUISceneNode()->bind( "panel", mSidePanel ); static constexpr auto STYLE = R"html( + @@ -311,6 +393,13 @@ void DebuggerPlugin::buildSidePanelTab() { + + + + + + + )html"; @@ -324,6 +413,30 @@ void DebuggerPlugin::buildSidePanelTab() { mTabContents->bind( "debugger_list", mUIDebuggerList ); mTabContents->bind( "debugger_conf_list", mUIDebuggerConfList ); + mTabContents->bind( "panel_debugger_buttons", mPanelBoxButtons.box ); + mTabContents->bind( "panel_debugger_continue", mPanelBoxButtons.resume ); + mTabContents->bind( "panel_debugger_pause", mPanelBoxButtons.pause ); + mTabContents->bind( "panel_debugger_step_over", mPanelBoxButtons.stepOver ); + mTabContents->bind( "panel_debugger_step_into", mPanelBoxButtons.stepInto ); + mTabContents->bind( "panel_debugger_step_out", mPanelBoxButtons.stepOut ); + + mPanelBoxButtons.resume->onClick( + [this]( auto ) { getPluginContext()->runCommand( "debugger-continue-interrupt" ); } ); + + mPanelBoxButtons.pause->onClick( + [this]( auto ) { getPluginContext()->runCommand( "debugger-continue-interrupt" ); } ); + + mPanelBoxButtons.stepOver->onClick( + [this]( auto ) { getPluginContext()->runCommand( "debugger-step-over" ); } ); + + mPanelBoxButtons.stepInto->onClick( + [this]( auto ) { getPluginContext()->runCommand( "debugger-step-into" ); } ); + + mPanelBoxButtons.stepOut->onClick( + [this]( auto ) { getPluginContext()->runCommand( "debugger-step-out" ); } ); + + setUIDebuggingState( StatusDebuggerController::State::NotStarted ); + updateSidePanelTab(); } @@ -438,6 +551,17 @@ void DebuggerPlugin::updateDebuggerConfigurationList() { confNames.reserve( mDaps.size() ); for ( const auto& conf : debuggerIt->configurations ) confNames.emplace_back( conf.name ); + + { + Lock l( mDapsMutex ); + for ( const auto& conf : mDapConfigs ) { + if ( conf.args.value( "type", "" ) != debuggerIt->type ) + continue; + + confNames.emplace_back( conf.name ); + } + } + std::sort( confNames.begin(), confNames.end() ); mUIDebuggerConfList->getListBox()->addListBoxItems( confNames ); bool empty = mUIDebuggerConfList->getListBox()->isEmpty(); @@ -453,6 +577,7 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { static constexpr auto KEY_ENV = "${env}"; static constexpr auto KEY_STOPONENTRY = "${stopOnEntry}"; static constexpr auto KEY_WORKSPACEFOLDER = "${workspaceFolder}"; + static constexpr auto KEY_FILEDIRNAME = "${fileDirname}"; auto runConfig = getPluginContext()->getProjectBuildManager()->getCurrentRunConfig(); for ( auto& j : json ) { @@ -460,22 +585,24 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { replaceKeysInJson( j ); } else if ( j.is_string() ) { std::string val( j.get() ); - if ( runConfig && val == KEY_FILE ) { - j = runConfig->cmd; - } else if ( runConfig && val == KEY_ARGS ) { + + if ( runConfig && val == KEY_ARGS ) { auto argsArr = nlohmann::json::array(); auto args = Process::parseArgs( runConfig->args ); for ( const auto& arg : args ) argsArr.push_back( arg ); - j = argsArr; - } else if ( runConfig && val == KEY_CWD ) { - j = runConfig->workingDir; + j = std::move( argsArr ); + continue; } else if ( runConfig && val == KEY_ENV ) { j = nlohmann::json{}; - } else if ( runConfig && val == KEY_WORKSPACEFOLDER ) { - j = mProjectPath; } else if ( val == KEY_STOPONENTRY ) { j = false; + } else if ( runConfig ) { + String::replaceAll( val, KEY_FILE, runConfig->cmd ); + String::replaceAll( val, KEY_CWD, runConfig->workingDir ); + String::replaceAll( val, KEY_FILEDIRNAME, runConfig->workingDir ); + String::replaceAll( val, KEY_WORKSPACEFOLDER, mProjectPath ); + j = std::move( val ); } } } @@ -816,7 +943,12 @@ void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& [&configuration]( const DapConfig& conf ) { return conf.name == configuration; } ); if ( configIt == debuggerIt->configurations.end() ) { - return; + configIt = std::find_if( + mDapConfigs.begin(), mDapConfigs.end(), + [&configuration]( const DapConfig& conf ) { return conf.name == configuration; } ); + + if ( configIt == debuggerIt->configurations.end() ) + return; } ProtocolSettings protocolSettings; @@ -935,11 +1067,34 @@ StatusDebuggerController* DebuggerPlugin::getStatusDebuggerController() const { return static_cast( debuggerElement.get() ); } +void DebuggerPlugin::updatePanelUIState( StatusDebuggerController::State state ) { + mPanelBoxButtons.box->setVisible( state != StatusDebuggerController::State::NotStarted ); + mPanelBoxButtons.resume->setVisible( state != StatusDebuggerController::State::NotStarted ) + ->setEnabled( state == StatusDebuggerController::State::Paused ); + mPanelBoxButtons.pause->setVisible( state != StatusDebuggerController::State::NotStarted ) + ->setEnabled( state == StatusDebuggerController::State::Running ); + mPanelBoxButtons.stepOver->setVisible( state != StatusDebuggerController::State::NotStarted ) + ->setEnabled( state == StatusDebuggerController::State::Paused ); + mPanelBoxButtons.stepInto->setVisible( state != StatusDebuggerController::State::NotStarted ) + ->setEnabled( state == StatusDebuggerController::State::Paused ); + mPanelBoxButtons.stepOut->setVisible( state != StatusDebuggerController::State::NotStarted ) + ->setEnabled( state == StatusDebuggerController::State::Paused ); +} + void DebuggerPlugin::setUIDebuggingState( StatusDebuggerController::State state ) { mDebuggingState = state; + auto ctrl = getStatusDebuggerController(); - if ( ctrl ) + if ( ctrl ) { ctrl->setDebuggingState( state ); + } + + if ( mPanelBoxButtons.box ) { + if ( Engine::isRunninMainThread() ) + updatePanelUIState( state ); + else + mPanelBoxButtons.box->runOnMainThread( [this, state] { updatePanelUIState( state ); } ); + } } // Mouse Hover Tooltip diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 62d7dd7bc..8bf0ec2b5 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -24,6 +24,7 @@ struct DapConfig { struct DapTool { std::string name; std::string url; + std::string type; std::vector languagesSupported; DapRunConfig run; std::vector configurations; @@ -57,6 +58,7 @@ class DebuggerPlugin : public PluginBase { std::string mProjectPath; std::vector mDaps; + std::vector mDapConfigs; std::unique_ptr mDebugger; std::unique_ptr mListener; @@ -70,6 +72,7 @@ class DebuggerPlugin : public PluginBase { UnorderedMap> mBreakpoints; UnorderedSet mPendingBreakpoints; std::shared_ptr mBreakpointsModel; + Mutex mDapsMutex; Mutex mBreakpointsMutex; StatusDebuggerController::State mDebuggingState{ StatusDebuggerController::State::NotStarted }; @@ -86,6 +89,16 @@ class DebuggerPlugin : public PluginBase { std::string mOldMaxWidth; // End hover stuff + struct PanelBoxButtons { + UILinearLayout* box{ nullptr }; + UIPushButton* resume{ nullptr }; + UIPushButton* pause{ nullptr }; + UIPushButton* stepOver{ nullptr }; + UIPushButton* stepInto{ nullptr }; + UIPushButton* stepOut{ nullptr }; + }; + PanelBoxButtons mPanelBoxButtons; + DebuggerPlugin( PluginManager* pluginManager, bool sync ); void load( PluginManager* pluginManager ); @@ -151,6 +164,8 @@ class DebuggerPlugin : public PluginBase { void setUIDebuggingState( StatusDebuggerController::State state ); + void updatePanelUIState( StatusDebuggerController::State state ); + void hideTooltip( UICodeEditor* editor ); void displayTooltip( UICodeEditor* editor, const EvaluateInfo& resp, const Vector2f& position ); @@ -159,6 +174,10 @@ class DebuggerPlugin : public PluginBase { bool onMouseMove( UICodeEditor* editor, const Vector2i& position, const Uint32& flags ) override; + + void loadProjectConfiguration( const std::string& path ); + + void loadProjectConfigurations(); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/debugger/statusdebuggercontroller.cpp b/src/tools/ecode/plugins/debugger/statusdebuggercontroller.cpp index 2d9987ed3..133ec2ccd 100644 --- a/src/tools/ecode/plugins/debugger/statusdebuggercontroller.cpp +++ b/src/tools/ecode/plugins/debugger/statusdebuggercontroller.cpp @@ -141,13 +141,13 @@ void StatusDebuggerController::createContainer() { - - - - - - - + + + + + + + )xml";