More code folding work.

This commit is contained in:
Martín Lucas Golini
2024-05-28 00:42:06 -03:00
parent 396f1e2558
commit 0bd4de1d27
7 changed files with 137 additions and 41 deletions

View File

@@ -24,6 +24,8 @@ class EE_API FoldRangeServive {
void clear();
bool empty();
std::optional<TextRange> find( Int64 docIdx );
void addFoldRegion( TextRange region );

View File

@@ -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<size_t> 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;

View File

@@ -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

View File

@@ -142,9 +142,9 @@ DocumentView::LineWrapInfo DocumentView::computeLineBreaks( const TextDocument&
DocumentView::DocumentView( std::shared_ptr<TextDocument> 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<VisibleIndex>( docIdx );
auto idx = mDocLineToVisibleIndex[eeclamp(
docIdx, 0ll, static_cast<Int64>( mDocLineToVisibleIndex.size() - 1 ) )];
if ( retLast ) {
if ( retLast && idx != static_cast<Int64>( 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<Int64>( VisibleIndex::invalid );
}
mDocLineToVisibleIndex[visibleLine.line()] = visibleIdx;
mDocLineToVisibleIndex[visibleLine.line()] =
isFolded( visibleLine.line(), true ) ? static_cast<Int64>( 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<Int64>( 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
}

View File

@@ -27,6 +27,11 @@ void FoldRangeServive::clear() {
mFoldingRegions.clear();
}
bool FoldRangeServive::empty() {
Lock l( mMutex );
return mFoldingRegions.empty();
}
std::optional<TextRange> 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<Int64, TextRange> 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<TextRange> regions ) {

View File

@@ -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<Int64>( 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() >

View File

@@ -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*/ ) {}