From 443aead0df4fbbafe5d19c07b497dad8db84ddf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 2 Jan 2025 01:08:02 -0300 Subject: [PATCH] More WIP. --- bin/assets/plugins/debugger.json | 2 + include/eepp/scene/eventdispatcher.hpp | 5 +- include/eepp/ui/uicodeeditor.hpp | 20 ++++ src/eepp/scene/eventdispatcher.cpp | 8 ++ src/eepp/ui/uicodeeditor.cpp | 47 +++++++- src/tools/ecode/plugins/debugger/config.cpp | 4 +- .../ecode/plugins/debugger/dap/protocol.hpp | 18 ++- .../ecode/plugins/debugger/debuggerplugin.cpp | 106 ++++++++++++++++++ .../ecode/plugins/debugger/debuggerplugin.hpp | 18 +++ src/tools/ecode/plugins/plugin.cpp | 2 + src/tools/ecode/plugins/plugin.hpp | 2 + 11 files changed, 221 insertions(+), 11 deletions(-) diff --git a/bin/assets/plugins/debugger.json b/bin/assets/plugins/debugger.json index 370c77104..7ae5d63d8 100644 --- a/bin/assets/plugins/debugger.json +++ b/bin/assets/plugins/debugger.json @@ -6,6 +6,7 @@ "run": { "command": "gdb --interpreter=dap" }, + "languages": [ "cpp", "c", "d", "go", "objectivec", "fortran", "pascal", "rust" ], "configurations": [ { "name": "launch binary", @@ -27,6 +28,7 @@ "run": { "command": "lldb-dap" }, + "languages": [ "cpp", "c", "odin", "rust", "zig" ], "configurations": [ { "name": "launch binary", diff --git a/include/eepp/scene/eventdispatcher.hpp b/include/eepp/scene/eventdispatcher.hpp index 11eed7de3..a021f2d9b 100644 --- a/include/eepp/scene/eventdispatcher.hpp +++ b/include/eepp/scene/eventdispatcher.hpp @@ -118,6 +118,8 @@ class EE_API EventDispatcher { void setLastFocusNode( Node* lastFocusNode ); + bool isFirstPress() const; + protected: EE::Window::Window* mWindow; Input* mInput; @@ -134,7 +136,8 @@ class EE_API EventDispatcher { Vector2i mClickPos; Int32 mCbId; Uint32 mIMECbId; - bool mFirstPress; + bool mFirstPress{ false }; + bool mJustPressed{ false }; bool mDisableMousePress{ false }; bool mJustDisabledMousePress{ false }; Node* mNodeWasDragging; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 91dbdfeaa..eca850d6e 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -107,6 +107,18 @@ class UICodeEditorPlugin { virtual void drawTop( UICodeEditor* /*editor*/, const Vector2f& /*screenStart*/, const Sizef& /*size*/, const Float& /*fontSize*/ ) {}; + virtual void + drawLineNumbersBefore( UICodeEditor* /*editor*/, const DocumentLineRange& /*lineRange*/, + const Vector2f& /*startScroll*/, const Vector2f& /*screenStart*/, + const Float& /*lineHeight*/, const Float& /*lineNumberWidth*/, + const int& /*lineNumberDigits*/, const Float& /*fontSize*/ ) {} + + virtual void + drawLineNumbersAfter( UICodeEditor* /*editor*/, const DocumentLineRange& /*lineRange*/, + const Vector2f& /*startScroll*/, const Vector2f& /*screenStart*/, + const Float& /*lineHeight*/, const Float& /*lineNumberWidth*/, + const int& /*lineNumberDigits*/, const Float& /*fontSize*/ ) {} + Uint32 addOnReadyCallback( const OnReadyCb& cb ) { mOnReadyCallbacks[mReadyCbNum++] = cb; return mReadyCbNum; @@ -753,6 +765,14 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void setTabIndentAlignment( CharacterAlignment alignment ); + const DocumentView& getDocumentView() const { return mDocView; } + + Float getGutterLocalStartOffset( UICodeEditorPlugin* plugin ) const; + + Float getGutterSpace( UICodeEditorPlugin* plugin ) const; + + Float getPluginsGutterSpace() const; + protected: struct LastXOffset { TextPosition position{ 0, 0 }; diff --git a/src/eepp/scene/eventdispatcher.cpp b/src/eepp/scene/eventdispatcher.cpp index ca686f479..2b25daaac 100644 --- a/src/eepp/scene/eventdispatcher.cpp +++ b/src/eepp/scene/eventdispatcher.cpp @@ -118,11 +118,14 @@ void EventDispatcher::update( const Time& time ) { if ( NULL != mNodeDragging ) mNodeDragging->onCalculateDrag( mMousePos, mInput->getPressTrigger() ); + mJustPressed = false; + if ( mInput->getPressTrigger() ) { if ( !mFirstPress ) { mDownNode = mOverNode; mMouseDownPos = mMousePosi; mFirstPress = true; + mJustPressed = true; } if ( NULL != mOverNode ) { @@ -142,6 +145,7 @@ void EventDispatcher::update( const Time& time ) { mDownNode = mOverNode; mMouseDownPos = mMousePosi; mFirstPress = true; + mJustPressed = true; } mOverNode->onMouseDown( mMousePosi, mInput->getReleaseTrigger() ); sendMsg( mOverNode, NodeMessage::MouseDown, mInput->getReleaseTrigger() ); @@ -324,6 +328,10 @@ void EventDispatcher::setLastFocusNode( Node* lastFocusNode ) { mLastFocusNode = lastFocusNode; } +bool EventDispatcher::isFirstPress() const { + return mJustPressed; +} + Node* EventDispatcher::getMouseDownNode() const { return mDownNode; } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index ded330570..58eed33f6 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -1398,8 +1398,10 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags if ( localPos.y < mPluginsTopSpace ) return UIWidget::onMouseDown( position, flags ); + bool downOverGutter = localPos.x < mPaddingPx.Left + getGutterWidth(); + if ( flags & EE_BUTTON_LMASK ) { - if ( localPos.x < mPaddingPx.Left + getGutterWidth() ) { + if ( localPos.x > mPaddingPx.Left + getLineNumberWidth() && downOverGutter ) { if ( mDoc->getFoldRangeService().isFoldingRegionInLine( textScreenPos.line() ) ) { if ( mDocView.isFolded( textScreenPos.line() ) ) { mDocView.unfoldRegion( textScreenPos.line() ); @@ -1407,7 +1409,7 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags mDocView.foldRegion( textScreenPos.line() ); } } - } else if ( input->isModState( KEYMOD_LALT | KEYMOD_SHIFT ) ) { + } else if ( !downOverGutter && input->isModState( KEYMOD_LALT | KEYMOD_SHIFT ) ) { TextRange range( mDoc->getSelection().start(), textScreenPos ); range = mDoc->sanitizeRange( range ); TextRange nrange = range.normalized(); @@ -1418,16 +1420,17 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags ranges.push_back( TextRange{ pos, pos } ); } mDoc->addSelections( std::move( ranges ) ); - } else if ( input->isModState( KEYMOD_SHIFT ) ) { + } else if ( !downOverGutter && input->isModState( KEYMOD_SHIFT ) ) { mDoc->selectTo( textScreenPos ); - } else if ( input->isModState( KEYMOD_CTRL ) && + } else if ( !downOverGutter && input->isModState( KEYMOD_CTRL ) && checkMouseOverLink( position ).empty() ) { TextPosition pos( textScreenPos ); if ( !mDoc->selectionExists( pos ) ) mDoc->addSelection( { pos, pos } ); - } else if ( mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) { + } else if ( !downOverGutter && + mLastDoubleClick.getElapsedTime() < Milliseconds( 300.f ) ) { mDoc->selectLine(); - } else { + } else if ( !downOverGutter ) { mDoc->setSelection( textScreenPos ); } } else if ( !mDoc->hasSelection() ) { @@ -2899,6 +2902,28 @@ bool UICodeEditor::gutterSpaceExists( UICodeEditorPlugin* plugin ) const { return false; } +Float UICodeEditor::getGutterLocalStartOffset( UICodeEditorPlugin* plugin ) const { + Float offset = 0; + for ( const auto& space : mPluginGutterSpaces ) { + if ( space.plugin == plugin ) + return offset; + offset += space.space; + } + return offset; +} + +Float UICodeEditor::getGutterSpace( UICodeEditorPlugin* plugin ) const { + for ( const auto& space : mPluginGutterSpaces ) { + if ( space.plugin == plugin ) + return space.space; + } + return 0; +} + +Float UICodeEditor::getPluginsGutterSpace() const { + return mPluginsGutterSpace; +} + bool UICodeEditor::topSpaceExists( UICodeEditorPlugin* plugin ) const { for ( const auto& space : mPluginTopSpaces ) { if ( space.plugin == plugin ) @@ -3846,6 +3871,11 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve TextRange selection = mDoc->getSelection( true ); Float lineOffset = getLineOffset(); + for ( auto plugin : mPlugins ) { + plugin->drawLineNumbersBefore( this, lineRange, startScroll, screenStart, lineHeight, + lineNumberWidth, lineNumberDigits, fontSize ); + } + for ( int i = lineRange.first; i <= lineRange.second; i++ ) { if ( !mDocView.isLineVisible( i ) ) continue; @@ -3948,6 +3978,11 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve } } } + + for ( auto plugin : mPlugins ) { + plugin->drawLineNumbersAfter( this, lineRange, startScroll, screenStart, lineHeight, + lineNumberWidth, lineNumberDigits, fontSize ); + } } void UICodeEditor::drawColorPreview( const Vector2f& startScroll, const Float& lineHeight, diff --git a/src/tools/ecode/plugins/debugger/config.cpp b/src/tools/ecode/plugins/debugger/config.cpp index f33136ec4..2b5a021be 100644 --- a/src/tools/ecode/plugins/debugger/config.cpp +++ b/src/tools/ecode/plugins/debugger/config.cpp @@ -23,8 +23,8 @@ bool BusSettings::hasConnection() const { } ProtocolSettings::ProtocolSettings( const nlohmann::json& configuration ) : - linesStartAt1( true ), - columnsStartAt1( true ), + linesStartAt1( false ), + columnsStartAt1( false ), pathFormatURI( false ), redirectStderr( configuration.value( REDIRECT_STDERR, false ) ), redirectStdout( configuration.value( REDIRECT_STDOUT, false ) ), diff --git a/src/tools/ecode/plugins/debugger/dap/protocol.hpp b/src/tools/ecode/plugins/debugger/dap/protocol.hpp index 20e3dde6e..cec3faf65 100644 --- a/src/tools/ecode/plugins/debugger/dap/protocol.hpp +++ b/src/tools/ecode/plugins/debugger/dap/protocol.hpp @@ -1,8 +1,8 @@ #pragma once +#include #include #include -#include #include #include @@ -169,6 +169,8 @@ struct SourceBreakpoint { SourceBreakpoint( const int line ); json toJson() const; + + bool operator==( const SourceBreakpoint& other ) const { return line == other.line; } }; struct Breakpoint { @@ -474,4 +476,16 @@ struct GotoTarget { static std::vector parseList( const json& variables ); }; -} // namespace dap +} // namespace ecode::dap + +template <> struct std::hash { + std::size_t operator()( ecode::dap::SourceBreakpoint const& breakpoint ) const noexcept { + size_t h1 = std::hash()( breakpoint.line ); + size_t h2 = breakpoint.column ? std::hash()( *breakpoint.column ) : 0; + size_t h3 = breakpoint.condition ? std::hash()( *breakpoint.condition ) : 0; + size_t h4 = + breakpoint.hitCondition ? std::hash()( *breakpoint.hitCondition ) : 0; + size_t h5 = breakpoint.logMessage ? std::hash()( *breakpoint.logMessage ) : 0; + return hashCombine( h1, h2, h3, h4, h5 ); + } +}; diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 9c884689f..ed2880d32 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -4,6 +4,7 @@ #include "busprocess.hpp" #include "dap/debuggerclientdap.hpp" #include "statusdebuggercontroller.hpp" +#include #include #include #include @@ -151,6 +152,16 @@ void DebuggerPlugin::loadDAPConfig( const std::string& path, bool updateConfigFi dapTool.configurations.emplace_back( std::move( dapConfig ) ); } } + + if ( dap.contains( "languages" ) && dap["languages"].is_array() ) { + auto& languages = dap["languages"]; + dapTool.languagesSupported.reserve( languages.size() ); + for ( const auto& lang : languages ) { + if ( lang.is_string() ) + dapTool.languagesSupported.emplace_back( lang.get() ); + } + } + mDaps.emplace_back( std::move( dapTool ) ); } } @@ -381,6 +392,101 @@ void DebuggerPlugin::replaceKeysInJson( nlohmann::json& json ) { } } +void DebuggerPlugin::onRegisterEditor( UICodeEditor* editor ) { + editor->registerGutterSpace( this, PixelDensity::dpToPx( 8 ), 0 ); +} + +void DebuggerPlugin::onUnregisterEditor( UICodeEditor* editor ) { + editor->unregisterGutterSpace( this ); +} + +void DebuggerPlugin::drawLineNumbersBefore( UICodeEditor* editor, + const DocumentLineRange& lineRange, + const Vector2f& startScroll, + const Vector2f& screenStart, const Float& lineHeight, + const Float&, const int&, const Float& ) { + if ( !editor->getDocument().hasFilepath() ) + return; + auto docIt = mBreakpoints.find( editor->getDocument().getFilePath() ); + if ( docIt == mBreakpoints.end() || docIt->second.empty() ) + return; + const auto& breakpoints = docIt->second; + Primitives p; + Float lineOffset = editor->getLineOffset(); + p.setColor( Color( editor->getColorScheme().getEditorColor( SyntaxStyleTypes::Error ) ) + .blendAlpha( editor->getAlpha() ) ); + Float gutterSpace = editor->getGutterSpace( this ); + Float radius = PixelDensity::dpToPx( 3 ); + Float offset = editor->getGutterLocalStartOffset( this ); + + for ( const SourceBreakpoint& breakpoint : breakpoints ) { + if ( breakpoint.line >= lineRange.first && breakpoint.line <= lineRange.second ) { + if ( !editor->getDocumentView().isLineVisible( breakpoint.line ) ) + continue; + + auto lnPos( Vector2f( + screenStart.x - editor->getPluginsGutterSpace() + offset, + startScroll.y + + editor->getDocumentView().getLineYOffset( breakpoint.line, lineHeight ) + + lineOffset ) ); + + // p.setColor( Color::Gray ); + // p.drawRectangle( { lnPos, Sizef{ gutterSpace, lineHeight } } ); + + p.setColor( Color( editor->getColorScheme().getEditorColor( SyntaxStyleTypes::Error ) ) + .blendAlpha( editor->getAlpha() ) ); + + p.drawCircle( { lnPos.x + radius + eefloor( ( gutterSpace - radius ) * 0.5f ), + lnPos.y + lineHeight * 0.5f }, + radius ); + } + } +} + +bool DebuggerPlugin::onLineNumberClick( UICodeEditor* editor, Uint32 lineNumber ) { + if ( !editor->getDocument().hasFilepath() ) + return false; + if ( !isSupportedByAnyDebugger( editor->getDocument().getSyntaxDefinition().getLSPName() ) ) + return false; + Lock l( mBreakpointsMutex ); + auto& breakpoints = mBreakpoints[editor->getDocument().getFilePath()]; + auto breakpointIt = breakpoints.find( SourceBreakpoint( lineNumber ) ); + if ( breakpointIt != breakpoints.end() ) { + breakpoints.erase( breakpointIt ); + } else { + breakpoints.insert( SourceBreakpoint( lineNumber ) ); + } + editor->invalidateDraw(); + return true; +} + +bool DebuggerPlugin::onMouseDown( UICodeEditor* editor, const Vector2i& position, + const Uint32& flags ) { + if ( !( flags & EE_BUTTON_LMASK ) ) + return false; + Float offset = editor->getGutterLocalStartOffset( this ); + Vector2f localPos( editor->convertToNodeSpace( position.asFloat() ) ); + if ( localPos.x >= editor->getPixelsPadding().Left + offset && + localPos.x < editor->getPixelsPadding().Left + offset + editor->getGutterSpace( this ) && + localPos.y > editor->getPluginsTopSpace() ) { + if ( editor->getUISceneNode()->getEventDispatcher()->isFirstPress() ) { + auto cursorPos( editor->resolveScreenPosition( position.asFloat() ) ); + onLineNumberClick( editor, cursorPos.line() ); + } + return true; + } + return false; +} + +bool DebuggerPlugin::isSupportedByAnyDebugger( const std::string& language ) { + for ( const auto& dap : mDaps ) { + if ( std::any_of( dap.languagesSupported.begin(), dap.languagesSupported.end(), + [&language]( const auto& l ) { return l == language; } ) ) + return true; + } + return false; +} + void DebuggerPlugin::runConfig( const std::string& debugger, const std::string& configuration ) { auto debuggerIt = std::find_if( mDaps.begin(), mDaps.end(), [&debugger]( const DapTool& dap ) { return dap.name == debugger; diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 60db2f769..0776954ef 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -23,6 +23,7 @@ struct DapConfig { struct DapTool { std::string name; std::string url; + std::vector languagesSupported; DapRunConfig run; std::vector configurations; }; @@ -63,6 +64,8 @@ class DebuggerPlugin : public PluginBase { UIDropDownList* mUIDebuggerList{ nullptr }; UIDropDownList* mUIDebuggerConfList{ nullptr }; UIPushButton* mRunButton{ nullptr }; + UnorderedMap> mBreakpoints; + Mutex mBreakpointsMutex; DebuggerPlugin( PluginManager* pluginManager, bool sync ); @@ -91,6 +94,21 @@ class DebuggerPlugin : public PluginBase { void exitDebugger(); void replaceKeysInJson( nlohmann::json& json ); + + void onRegisterEditor( UICodeEditor* ) override; + + void onUnregisterEditor( UICodeEditor* ) override; + + void drawLineNumbersBefore( UICodeEditor* editor, const DocumentLineRange& lineRange, + const Vector2f& startScroll, const Vector2f& screenStart, + const Float& lineHeight, const Float& lineNumberWidth, + const int& lineNumberDigits, const Float& fontSize ) override; + + bool onLineNumberClick( UICodeEditor* editor, Uint32 lineNumber ); + + bool onMouseDown( UICodeEditor*, const Vector2i&, const Uint32& flags ) override; + + bool isSupportedByAnyDebugger( const std::string& language ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/plugin.cpp b/src/tools/ecode/plugins/plugin.cpp index 6a048d21e..550c10540 100644 --- a/src/tools/ecode/plugins/plugin.cpp +++ b/src/tools/ecode/plugins/plugin.cpp @@ -170,6 +170,8 @@ void PluginBase::onRegister( UICodeEditor* editor ) { onRegisterDocument( editor->getDocumentRef().get() ); } mEditorDocs[editor] = editor->getDocumentRef().get(); + + onRegisterEditor( editor ); } void PluginBase::onUnregister( UICodeEditor* editor ) { diff --git a/src/tools/ecode/plugins/plugin.hpp b/src/tools/ecode/plugins/plugin.hpp index 65be3a8ea..ad40a7f71 100644 --- a/src/tools/ecode/plugins/plugin.hpp +++ b/src/tools/ecode/plugins/plugin.hpp @@ -104,6 +104,8 @@ class PluginBase : public Plugin { virtual void onRegisterDocument( TextDocument* ) {}; + virtual void onRegisterEditor( UICodeEditor* ) {}; + virtual void onUnregisterEditor( UICodeEditor* ) {}; //! Usually used to unregister commands in a document