diff --git a/include/eepp/ui/doc/foldrangeservice.hpp b/include/eepp/ui/doc/foldrangeservice.hpp index 6fca639c4..01377b02a 100644 --- a/include/eepp/ui/doc/foldrangeservice.hpp +++ b/include/eepp/ui/doc/foldrangeservice.hpp @@ -24,6 +24,8 @@ class EE_API FoldRangeServive { void clear(); + bool empty(); + std::optional find( Int64 docIdx ); void addFoldRegion( TextRange region ); diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index d7cd732fa..f982afcba 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -265,6 +265,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Float getLineNumberWidth() const; + Float getInternalGutterWidth() const; + virtual Float getGutterWidth() const; const bool& getShowLineNumber() const; @@ -698,7 +700,10 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Float getMinimapLineSpacing() const; - protected: + bool getShowFoldingRegion() const; + void setShowFoldingRegion(bool showFoldingRegion); + + protected: struct LastXOffset { TextPosition position{ 0, 0 }; Float offset{ 0.f }; @@ -714,6 +719,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mCursorVisible{ false }; bool mMouseDown{ false }; bool mShowLineNumber{ true }; + bool mShowFoldingRegion{ true }; bool mShowWhitespaces{ true }; bool mShowLineEndings{ false }; bool mLocked{ false }; @@ -739,6 +745,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mDisplayLockedIcon{ false }; bool mInvalidateOnLoaded{ false }; bool mUseDefaultStyle{ false }; + bool mFoldsVisible{ false }; std::atomic mHighlightWordProcessing{ false }; TextRange mLinkPosition; String mLink; @@ -749,6 +756,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { StyleSheetLength mLineSpacing{ 0.f, StyleSheetLength::Px }; Float mLineNumberPaddingLeft; Float mLineNumberPaddingRight; + Float mFoldRegionWidth; Color mLineNumberFontColor; Color mLineNumberActiveFontColor; Color mLineNumberBackgroundColor; diff --git a/src/eepp/core/debug.cpp b/src/eepp/core/debug.cpp index e54d4b023..00dd5f5a6 100644 --- a/src/eepp/core/debug.cpp +++ b/src/eepp/core/debug.cpp @@ -25,12 +25,12 @@ void eeREPORT_ASSERT( const char* File, int Line, const char* Exp ) { #else if ( PrintDebugInLog ) { - Log::instance()->writef( LogLevel::Assert, "%s file:%s line:%d", Exp, File, Line ); + Log::instance()->writef( LogLevel::Assert, "%s file:%s line:%d\n", Exp, File, Line ); if ( !Log::instance()->isLoggingToStdOut() ) - printf( "ASSERT: %s file:%s line:%d", Exp, File, Line ); + printf( "ASSERT: %s file:%s line:%d\n", Exp, File, Line ); } else { - printf( "ASSERT: %s file:%s line:%d", Exp, File, Line ); + printf( "ASSERT: %s file:%s line:%d\n", Exp, File, Line ); } #if defined( EE_COMPILER_GCC ) && !defined( EE_ARM ) && EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN && \ @@ -41,6 +41,7 @@ void eeREPORT_ASSERT( const char* File, int Line, const char* Exp ) { #endif #endif + } #ifndef EE_SILENT diff --git a/src/eepp/ui/doc/documentview.cpp b/src/eepp/ui/doc/documentview.cpp index df2c2fa05..b20303bc2 100644 --- a/src/eepp/ui/doc/documentview.cpp +++ b/src/eepp/ui/doc/documentview.cpp @@ -142,9 +142,9 @@ DocumentView::LineWrapInfo DocumentView::computeLineBreaks( const TextDocument& DocumentView::DocumentView( std::shared_ptr doc, FontStyleConfig fontStyle, Config config ) : - mDoc( std::move( doc ) ), - mFontStyle( std::move( fontStyle ) ), - mConfig( std::move( config ) ) {} + mDoc( std::move( doc ) ), mFontStyle( std::move( fontStyle ) ), mConfig( std::move( config ) ) { + invalidateCache(); +} bool DocumentView::isWrapEnabled() const { return mConfig.mode != LineWrapMode::NoWrap; @@ -249,11 +249,11 @@ void DocumentView::invalidateCache() { VisibleIndex DocumentView::toVisibleIndex( Int64 docIdx, bool retLast ) const { // eeASSERT( isLineVisible( docIdx ) ); - if ( isOneToOne() ) + if ( isOneToOne() || mDocLineToVisibleIndex.empty() ) return static_cast( docIdx ); auto idx = mDocLineToVisibleIndex[eeclamp( docIdx, 0ll, static_cast( mDocLineToVisibleIndex.size() - 1 ) )]; - if ( retLast ) { + if ( retLast && idx != static_cast( VisibleIndex::invalid ) ) { Int64 lastOfLine = mVisibleLines[idx].line(); Int64 visibleLinesCount = mVisibleLines.size(); for ( auto i = idx + 1; i < visibleLinesCount; i++ ) { @@ -442,7 +442,9 @@ void DocumentView::recomputeDocLineToVisibleIndex( Int64 fromVisibleIndex ) { for ( Int64 i = previousLineIdx + 1; i < visibleLine.line(); i++ ) mDocLineToVisibleIndex[i] = static_cast( VisibleIndex::invalid ); } - mDocLineToVisibleIndex[visibleLine.line()] = visibleIdx; + mDocLineToVisibleIndex[visibleLine.line()] = + isFolded( visibleLine.line(), true ) ? static_cast( VisibleIndex::invalid ) + : visibleIdx; previousLineIdx = visibleLine.line(); } } @@ -471,8 +473,8 @@ void DocumentView::unfoldRegion( Int64 foldDocIdx ) { if ( !foldRegion ) return; Int64 toDocIdx = foldRegion->end().line(); - changeVisibility( foldDocIdx + 1, toDocIdx, true ); removeFoldedRegion( *foldRegion ); + changeVisibility( foldDocIdx + 1, toDocIdx, true ); verifyStructuralConsistency(); if ( isOneToOne() ) clearCache(); @@ -484,12 +486,16 @@ bool DocumentView::isOneToOne() const { void DocumentView::changeVisibility( Int64 fromDocIdx, Int64 toDocIdx, bool visible ) { if ( visible ) { - auto it = std::lower_bound( mVisibleLines.begin(), mVisibleLines.end(), TextPosition{ fromDocIdx, 0 } ); Int64 oldIdxFrom = std::distance( mVisibleLines.begin(), it ); auto idxOffset = oldIdxFrom; for ( auto i = fromDocIdx; i <= toDocIdx; i++ ) { + if ( isFolded( i, true ) ) { + mVisibleLinesOffset[i] = computeOffsets( mDoc->line( i ).getText().view(), + mFontStyle, mConfig.tabWidth ); + continue; + } auto lb = isWrapEnabled() ? computeLineBreaks( *mDoc, i, mFontStyle, mMaxWidth, mConfig.mode, mConfig.keepIndentation, mConfig.tabWidth ) @@ -512,8 +518,10 @@ void DocumentView::changeVisibility( Int64 fromDocIdx, Int64 toDocIdx, bool visi } Int64 linesCount = mDoc->linesCount(); Int64 idxOffset = oldIdxTo - oldIdxFrom + 1; - for ( Int64 idx = toDocIdx + 1; idx < linesCount; idx++ ) - mDocLineToVisibleIndex[idx] -= idxOffset; + for ( Int64 idx = toDocIdx + 1; idx < linesCount; idx++ ) { + if ( mDocLineToVisibleIndex[idx] != static_cast( VisibleIndex::invalid ) ) + mDocLineToVisibleIndex[idx] -= idxOffset; + } } eeASSERT( mDocLineToVisibleIndex.size() == mDoc->linesCount() ); } @@ -551,8 +559,30 @@ void DocumentView::verifyStructuralConsistency() { invalidateCache(); - eeASSERT( visibleLines == mVisibleLines ); - eeASSERT( docLineToVisibleIndex == mDocLineToVisibleIndex ); + auto visibleLinesConsistency = visibleLines == mVisibleLines; + eeASSERT( visibleLinesConsistency ); + + if ( !visibleLinesConsistency && mVisibleLines.size() == visibleLines.size() ) { + for ( size_t i = 0; i < mVisibleLines.size(); i++ ) { + if ( mVisibleLines[i] != visibleLines[i] ) { + eeASSERT( mVisibleLines[i] == visibleLines[i] ); + break; + } + } + } + + bool docConsistency = docLineToVisibleIndex == mDocLineToVisibleIndex; + eeASSERT( docConsistency ); + + if ( !docConsistency && docLineToVisibleIndex.size() == mDocLineToVisibleIndex.size() ) { + for ( size_t i = 0; i < mDocLineToVisibleIndex.size(); i++ ) { + if ( mDocLineToVisibleIndex[i] != docLineToVisibleIndex[i] ) { + eeASSERT( mDocLineToVisibleIndex[i] == docLineToVisibleIndex[i] ); + break; + } + } + } + eeASSERT( visibleLinesOffset == mVisibleLinesOffset ); #endif } diff --git a/src/eepp/ui/doc/foldrangeservice.cpp b/src/eepp/ui/doc/foldrangeservice.cpp index e2f833741..9c141ad1d 100644 --- a/src/eepp/ui/doc/foldrangeservice.cpp +++ b/src/eepp/ui/doc/foldrangeservice.cpp @@ -27,6 +27,11 @@ void FoldRangeServive::clear() { mFoldingRegions.clear(); } +bool FoldRangeServive::empty() { + Lock l( mMutex ); + return mFoldingRegions.empty(); +} + std::optional FoldRangeServive::find( Int64 docIdx ) { Lock l( mMutex ); auto foldRegionIt = mFoldingRegions.find( docIdx ); @@ -48,13 +53,18 @@ bool FoldRangeServive::isFoldingRegionInLine( Int64 docIdx ) { } void FoldRangeServive::shiftFoldingRegions( Int64 fromLine, Int64 numLines ) { + // TODO: Optimize this Lock l( mMutex ); - for ( auto& [_, region] : mFoldingRegions ) { - if ( region.start().line() >= fromLine ) { - region.start().setLine( region.start().line() + numLines ); - region.end().setLine( region.end().line() + numLines ); + std::unordered_map foldingRegions; + for ( auto& foldingRegion : mFoldingRegions ) { + if ( foldingRegion.second.start().line() > fromLine ) { + foldingRegion.second.start().setLine( foldingRegion.second.start().line() + numLines ); + foldingRegion.second.end().setLine( foldingRegion.second.end().line() + numLines ); + foldingRegions[foldingRegion.second.start().line()] = foldingRegion.second; } + foldingRegions[foldingRegion.second.start().line()] = foldingRegion.second; } + mFoldingRegions = foldingRegions; } void FoldRangeServive::setFoldingRegions( std::vector regions ) { diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 83497bab4..ebb840b62 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -124,8 +124,9 @@ UICodeEditor::UICodeEditor( const std::string& elementTag, const bool& autoRegis mTabWidth( 4 ), mMouseWheelScroll( 50 ), mFontSize( mFontStyleConfig.getFontCharacterSize() ), - mLineNumberPaddingLeft( PixelDensity::dpToPx( 6 ) ), - mLineNumberPaddingRight( PixelDensity::dpToPx( 6 ) ), + mLineNumberPaddingLeft( PixelDensity::dpToPx( 4 ) ), + mLineNumberPaddingRight( PixelDensity::dpToPx( 4 ) ), + mFoldRegionWidth( PixelDensity::dpToPx( 12 ) ), mKeyBindings( getUISceneNode()->getWindow()->getInput() ), mFindLongestLineWidthUpdateFrequency( Seconds( 1 ) ), mPreviewColor( Color::Transparent ) { @@ -272,6 +273,8 @@ void UICodeEditor::draw() { if ( !mLocked && mHighlightCurrentLine ) { for ( const auto& sel : mDoc->getSelections() ) { + if ( mDocView.isFolded( sel.start().line(), true ) ) + continue; primitives.setColor( Color( mCurrentLineBackgroundColor ).blendAlpha( mAlpha ) ); Float height = 1; if ( mDocView.isWrappedLine( sel.start().line() ) ) @@ -350,7 +353,7 @@ void UICodeEditor::draw() { } if ( mPluginsGutterSpace > 0 ) { - Float curGutterPos = 0.f; + Float curGutterPos = 0; for ( auto& plugin : mPluginGutterSpaces ) { for ( auto gi = lineRange.first; gi <= lineRange.second; gi++ ) { if ( !mDocView.isLineVisible( gi ) ) @@ -379,7 +382,7 @@ void UICodeEditor::draw() { drawCursor( startScroll, lineHeight, sel.start() ); } - if ( mShowLineNumber ) { + if ( mShowLineNumber || mShowFoldingRegion ) { drawLineNumbers( lineRange, startScroll, { screenStart.x + mPluginsGutterSpace, screenStart.y }, lineHeight, getLineNumberWidth(), lineNumberDigits, charSize ); @@ -798,8 +801,13 @@ Float UICodeEditor::getLineNumberWidth() const { : 0.f; } +Float UICodeEditor::getInternalGutterWidth() const { + return getLineNumberWidth() + + ( !mShowFoldingRegion || mDoc->getFoldRangeService().empty() ? 0.f : mFoldRegionWidth ); +} + Float UICodeEditor::getGutterWidth() const { - return getLineNumberWidth() + mPluginsGutterSpace; + return getInternalGutterWidth() + mPluginsGutterSpace; } const bool& UICodeEditor::getShowLineNumber() const { @@ -1019,7 +1027,8 @@ Uint32 UICodeEditor::onTextInput( const TextInputEvent& event ) { } void UICodeEditor::updateIMELocation() { - if ( mDoc->getActiveClient() != this || !Engine::isRunninMainThread() ) + if ( mDoc->getActiveClient() != this || !Engine::isRunninMainThread() || + mDocView.isFolded( mDoc->getSelection( true ).start().line() ) ) return; Rectf r( getScreenPosition( mDoc->getSelection( true ).start() ) ); getUISceneNode()->getWindow()->getIME().setLocation( r.asInt() ); @@ -1450,6 +1459,12 @@ Uint32 UICodeEditor::onMouseMove( const Vector2i& position, const Uint32& flags checkMouseOverLink( position ); } + Vector2f localPos( convertToNodeSpace( position.asFloat() ) ); + bool oldFoldVisible = mFoldsVisible; + mFoldsVisible = localPos.x <= mPaddingPx.Left + getGutterWidth(); + if ( oldFoldVisible != mFoldsVisible ) + invalidateDraw(); + return UIWidget::onMouseMove( position, flags ); } @@ -1577,6 +1592,10 @@ Uint32 UICodeEditor::onMouseLeave( const Vector2i& position, const Uint32& flags mMinimapHover = false; invalidateDraw(); } + if ( mFoldsVisible ) { + invalidateDraw(); + mFoldsVisible = false; + } for ( auto& plugin : mPlugins ) if ( plugin->onMouseLeave( this, position, flags ) ) return UIWidget::onMouseLeave( position, flags ); @@ -1654,7 +1673,8 @@ UIScrollBar* UICodeEditor::getHScrollBar() const { void UICodeEditor::drawCursor( const Vector2f& startScroll, const Float& lineHeight, const TextPosition& cursor ) { - if ( mCursorVisible && !mLocked && isTextSelectionEnabled() ) { + if ( mCursorVisible && !mLocked && isTextSelectionEnabled() && + !mDocView.isFolded( cursor.line(), true ) ) { auto offset = getTextPositionOffset( cursor, lineHeight ); Vector2f cursorPos( startScroll.x + offset.x, startScroll.y + offset.y ); Primitives primitives; @@ -3712,7 +3732,12 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve const Float& fontSize ) { Primitives primitives; primitives.setColor( Color( mLineNumberBackgroundColor ).blendAlpha( mAlpha ) ); - primitives.drawRectangle( Rectf( screenStart, Sizef( lineNumberWidth, mSize.getHeight() ) ) ); + Float w = 0.f; + if ( mShowLineNumber ) + w += lineNumberWidth; + if ( mShowFoldingRegion && !mDoc->getFoldRangeService().empty() ) + w += mFoldRegionWidth; + primitives.drawRectangle( Rectf( screenStart, Sizef( w, mSize.getHeight() ) ) ); TextRange selection = mDoc->getSelection( true ); Float lineOffset = getLineOffset(); @@ -3730,28 +3755,34 @@ void UICodeEditor::drawLineNumbers( const DocumentLineRange& lineRange, const Ve Vector2f( screenStart.x + mLineNumberPaddingLeft, startScroll.y + mDocView.getLineYOffset( i, lineHeight ) + lineOffset ) ); - Text::draw( pos, lnPos, mFontStyleConfig.Font, fontSize, - ( i >= selection.start().line() && i <= selection.end().line() ) - ? mLineNumberActiveFontColor - : mLineNumberFontColor, - mFontStyleConfig.Style, mFontStyleConfig.OutlineThickness, - mFontStyleConfig.OutlineColor, mFontStyleConfig.ShadowColor, - mFontStyleConfig.ShadowOffset ); + if ( mShowLineNumber ) { + Text::draw( pos, lnPos, mFontStyleConfig.Font, fontSize, + ( i >= selection.start().line() && i <= selection.end().line() ) + ? mLineNumberActiveFontColor + : mLineNumberFontColor, + mFontStyleConfig.Style, mFontStyleConfig.OutlineThickness, + mFontStyleConfig.OutlineColor, mFontStyleConfig.ShadowColor, + mFontStyleConfig.ShadowOffset ); + } - if ( mDoc->getFoldRangeService().isFoldingRegionInLine( i ) ) { - Float dim = PixelDensity::dpToPx( 4 ); - lnPos = Vector2f( screenStart.x + lineNumberWidth - dim, - lnPos.y + lineHeight * 0.5f - dim * 0.5f ); + if ( mShowFoldingRegion && mFoldsVisible && + mDoc->getFoldRangeService().isFoldingRegionInLine( i ) ) { + Float dim = PixelDensity::dpToPx( 6 ); + Float center = ( mFoldRegionWidth - dim ) * 0.5f; + bool isFolded = mDocView.isFolded( i ); + Float dimH = isFolded ? dim : eeceil( dim * 0.75f ); + lnPos = Vector2f( screenStart.x + ( mShowLineNumber ? lineNumberWidth : 0.f ) + center, + lnPos.y + eeceil( ( lineHeight - dimH ) * 0.5f ) ); primitives.setColor( mLineNumberFontColor ); Triangle2f tri; - if ( mDocView.isFolded( i ) ) { + if ( isFolded ) { tri.V[0] = { 0, 0 }; tri.V[1] = { 0, dim }; tri.V[2] = { dim, dim * 0.5f }; } else { tri.V[0] = { 0, 0 }; tri.V[1] = { dim, 0 }; - tri.V[2] = { dim * 0.5f, dim }; + tri.V[2] = { dim * 0.5f, dim * 0.75f }; } tri.V[0] += lnPos; tri.V[1] += lnPos; @@ -4480,6 +4511,8 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&, for ( size_t i = 0; i < mDoc->getSelections().size(); ++i ) { const auto& selection = mDoc->getSelectionIndex( i ); + if ( mDocView.isFolded( selection.start().line(), true ) ) + continue; Float selectionY = rect.Top + ( static_cast( mDocView.getVisibleLineRange( selection.start() ).visibleIndex ) - @@ -4509,6 +4542,17 @@ Float UICodeEditor::getMinimapLineSpacing() const { return eemax( 1.f, eefloor( 2 * PixelDensity::getPixelDensity() * mMinimapConfig.scale ) ); } +bool UICodeEditor::getShowFoldingRegion() const { + return mShowFoldingRegion; +} + +void UICodeEditor::setShowFoldingRegion( bool showFoldingRegion ) { + if ( mShowFoldingRegion != showFoldingRegion ) { + mShowFoldingRegion = showFoldingRegion; + invalidateDraw(); + } +} + bool UICodeEditor::isMinimapFileTooLarge() const { return mDoc->linesCount() > 1 && mDoc->linesCount() > diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp index ea28aa23d..a6ed1caec 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp @@ -56,6 +56,7 @@ void LSPDocumentClient::onDocumentTextChanged( const DocumentContentChange& chan mServer->getThreadPool()->run( [this, change]() { mServer->processDidChangeQueue(); } ); requestSymbolsDelayed(); requestSemanticHighlightingDelayed(); + requestFoldRangeDelayed(); } void LSPDocumentClient::onDocumentUndoRedo( const TextDocument::UndoRedo& /*eventType*/ ) {}