From db2c90fe3bf9a279b5fd478cb723f9c94e03c02b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 19 Mar 2024 01:02:33 -0300 Subject: [PATCH] Improve performance in document longest line detection. Fix crash in SyntaxHighlighter when the syntax is changed while doing async highlighting. Improve syntax definition detection in SyntaxDefinitionManager. Fix invalid memory access in UICodeEditor::getXOffsetColSanitized. --- .../eepp/ui/doc/syntaxdefinitionmanager.hpp | 7 +++ include/eepp/ui/doc/syntaxhighlighter.hpp | 2 + include/eepp/ui/uicodeeditor.hpp | 5 ++- include/eepp/ui/uitreeview.hpp | 2 +- src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 14 ++++++ src/eepp/ui/doc/syntaxhighlighter.cpp | 17 ++++++-- src/eepp/ui/doc/syntaxtokenizer.cpp | 4 +- src/eepp/ui/doc/textdocument.cpp | 2 +- src/eepp/ui/uicodeeditor.cpp | 43 +++++++++++++------ src/tools/ecode/ecode.cpp | 4 ++ 10 files changed, 79 insertions(+), 21 deletions(-) diff --git a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp index a50dc6515..1d308c5bf 100644 --- a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp +++ b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp @@ -78,10 +78,17 @@ class EE_API SyntaxDefinitionManager { /* empty = all */ bool save( const std::string& path, const std::vector& def = {} ); + void setLanguageExtensionsPriority( const std::map& priorities ); + + const std::map& getLanguageExtensionsPriority() { + return mPriorities; + } + protected: SyntaxDefinitionManager(); std::vector mDefinitions; + std::map mPriorities; std::optional getLanguageIndex( const std::string& langName ); }; diff --git a/include/eepp/ui/doc/syntaxhighlighter.hpp b/include/eepp/ui/doc/syntaxhighlighter.hpp index 25119f0d6..af5eddc44 100644 --- a/include/eepp/ui/doc/syntaxhighlighter.hpp +++ b/include/eepp/ui/doc/syntaxhighlighter.hpp @@ -77,6 +77,8 @@ class EE_API SyntaxHighlighter { Int64 mFirstInvalidLine; Int64 mMaxWantedLine; Int64 mMaxTokenizationLength{ 0 }; + std::mutex mAsyncTokenizeMutex; + std::condition_variable mAsyncTokenizeConf; bool mTokenizeAsync{ false }; bool mStopTokenizing{ false }; }; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 9b816b71c..ccb4d100c 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -708,6 +708,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Uint32 mLineBreakingColumn{ 100 }; TextRange mMatchingBrackets; Float mLongestLineWidth{ 0 }; + size_t mLongestLineIndex{ 0 }; Time mFindLongestLineWidthUpdateFrequency; Clock mLongestLineWidthLastUpdate; Clock mLastActivity; @@ -761,7 +762,9 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { void invalidateLinesCache(); - virtual void findLongestLine(); + void findLongestLine(); + + std::pair findLongestLineInRange( const TextRange& range ); virtual Uint32 onFocus(); diff --git a/include/eepp/ui/uitreeview.hpp b/include/eepp/ui/uitreeview.hpp index 452d5aa6a..9a5e1ea2c 100644 --- a/include/eepp/ui/uitreeview.hpp +++ b/include/eepp/ui/uitreeview.hpp @@ -136,7 +136,7 @@ class EE_API UITreeView : public UIAbstractTableView { void traverseTree( TreeViewCallback ) const; - mutable UnorderedMap mViewMetadata; + mutable std::unordered_map mViewMetadata; virtual size_t getItemCount() const; diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 944e4a6e0..99a202e60 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -1841,6 +1841,11 @@ bool SyntaxDefinitionManager::save( const std::string& path, return false; } +void SyntaxDefinitionManager::setLanguageExtensionsPriority( + const std::map& priorities ) { + mPriorities = priorities; +} + std::optional SyntaxDefinitionManager::getLanguageIndex( const std::string& langName ) { size_t pos = 0; for ( const auto& def : mDefinitions ) { @@ -2276,6 +2281,15 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri std::string fileName( FileSystem::fileNameFromPath( filePath ) ); bool extHasMultipleLangs = extensionCanRepresentManyLanguages( extension ); + auto priorityLanguage = mPriorities.end(); + if ( extHasMultipleLangs ) { + priorityLanguage = mPriorities.find( extension ); + const SyntaxDefinition* def = nullptr; + if ( priorityLanguage != mPriorities.end() && + ( def = getPtrByLSPName( priorityLanguage->second ) ) ) { + return *def; + } + } // Use the filename instead if ( extension.empty() ) diff --git a/src/eepp/ui/doc/syntaxhighlighter.cpp b/src/eepp/ui/doc/syntaxhighlighter.cpp index f4f9af324..4840a17af 100644 --- a/src/eepp/ui/doc/syntaxhighlighter.cpp +++ b/src/eepp/ui/doc/syntaxhighlighter.cpp @@ -35,6 +35,11 @@ void SyntaxHighlighter::changeDoc( TextDocument* doc ) { } void SyntaxHighlighter::reset() { + if ( mTokenizeAsync ) { + mStopTokenizing = true; + std::unique_lock lock( mAsyncTokenizeMutex ); + mAsyncTokenizeConf.wait( lock, [this]() { return !mTokenizeAsync; } ); + } Lock l( mLinesMutex ); mLines.clear(); mFirstInvalidLine = 0; @@ -129,10 +134,14 @@ void SyntaxHighlighter::tokenizeAsync( std::shared_ptr pool, return; mTokenizeAsync = true; pool->run( [this, onDone] { - for ( size_t i = mFirstInvalidLine; i < mDoc->linesCount() && !mStopTokenizing; i++ ) - getLine( i ); - mStopTokenizing = false; - mTokenizeAsync = false; + { + std::unique_lock lock( mAsyncTokenizeMutex ); + for ( size_t i = mFirstInvalidLine; i < mDoc->linesCount() && !mStopTokenizing; i++ ) + getLine( i ); + mStopTokenizing = false; + mTokenizeAsync = false; + mAsyncTokenizeConf.notify_all(); + } if ( onDone ) onDone(); } ); diff --git a/src/eepp/ui/doc/syntaxtokenizer.cpp b/src/eepp/ui/doc/syntaxtokenizer.cpp index c8cb4a8ba..b64f74751 100644 --- a/src/eepp/ui/doc/syntaxtokenizer.cpp +++ b/src/eepp/ui/doc/syntaxtokenizer.cpp @@ -263,9 +263,9 @@ _tokenize( const SyntaxDefinition& syntax, const std::string& text, const Syntax } bool matched = false; + size_t patternsCount = curState.currentSyntax->getPatterns().size(); - for ( size_t patternIndex = 0; patternIndex < curState.currentSyntax->getPatterns().size(); - patternIndex++ ) { + for ( size_t patternIndex = 0; patternIndex < patternsCount; patternIndex++ ) { const SyntaxPattern& pattern = curState.currentSyntax->getPatterns()[patternIndex]; if ( i != 0 && pattern.patterns[0][0] == '^' ) continue; diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index f4425f546..5cea882f6 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -2259,7 +2259,7 @@ const SyntaxDefinition& TextDocument::getSyntaxDefinition() const { } void TextDocument::setSyntaxDefinition( const SyntaxDefinition& definition ) { - if ( &mSyntaxDefinition != &definition ) { + if ( mSyntaxDefinition.getLSPName() != definition.getLSPName() ) { mSyntaxDefinition = definition; notifySyntaxDefinitionChange(); } diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index f72c8ade9..f377d1e52 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -1554,12 +1554,28 @@ void UICodeEditor::onPaddingChange() { invalidateEditor( false ); } +std::pair UICodeEditor::findLongestLineInRange( const TextRange& range ) { + std::pair curRange{ mLongestLineIndex, mLongestLineWidth }; + Float longestLineWidth = mLongestLineWidth; + if ( mHorizontalScrollBarEnabled ) { + for ( Int64 lineIndex = range.start().line(); lineIndex <= range.end().line(); + lineIndex++ ) { + Float lineWidth = getLineWidth( lineIndex ); + if ( lineWidth > longestLineWidth ) { + curRange.first = lineIndex; + curRange.second = lineWidth; + longestLineWidth = lineWidth; + } + } + } + return curRange; +} + void UICodeEditor::findLongestLine() { if ( mHorizontalScrollBarEnabled ) { - mLongestLineWidth = 0; - for ( size_t lineIndex = 0; lineIndex < mDoc->linesCount(); lineIndex++ ) { - mLongestLineWidth = eemax( mLongestLineWidth, getLineWidth( lineIndex ) ); - } + auto range = findLongestLineInRange( mDoc->getDocRange() ); + mLongestLineIndex = range.first; + mLongestLineWidth = range.second; } } @@ -1581,9 +1597,6 @@ Float UICodeEditor::getLineWidth( const Int64& lineIndex ) { void UICodeEditor::updateScrollBar() { int notVisibleLineCount = (int)mDoc->linesCount() - (int)getViewPortLineCount().y; - if ( mLongestLineWidthDirty && mFont ) - updateLongestLineWidth(); - mHScrollBar->setEnabled( false ); mHScrollBar->setVisible( false ); @@ -1700,11 +1713,14 @@ void UICodeEditor::updateEditor() { mDirtyScroll = false; } -void UICodeEditor::onDocumentTextChanged( const DocumentContentChange& ) { +void UICodeEditor::onDocumentTextChanged( const DocumentContentChange& change ) { invalidateDraw(); checkMatchingBrackets(); sendCommonEvent( Event::OnTextChanged ); - invalidateLongestLineWidth(); + + auto range = findLongestLineInRange( change.range ); + mLongestLineIndex = range.first; + mLongestLineWidth = range.second; } void UICodeEditor::onDocumentCursorChange( const Doc::TextPosition& ) { @@ -2004,8 +2020,11 @@ template Float UICodeEditor::getTextWidth( const StringTyp Float UICodeEditor::getXOffsetColSanitized( TextPosition position ) const { position.setLine( eeclamp( position.line(), 0L, mDoc->linesCount() - 1 ) ); // This is different from sanitizePosition, sinze allows the last character. - position.setColumn( eeclamp( position.column(), 0L, - eemax( 0, mDoc->line( position.line() ).size() ) ) ); + position.setColumn( + eeclamp( position.column(), 0L, + eemax( 0, position.line() < static_cast( mDoc->linesCount() ) + ? mDoc->line( position.line() ).size() + : 0 ) ) ); return getXOffsetCol( position ); } @@ -2402,8 +2421,8 @@ void UICodeEditor::setEnableColorPickerOnSelection( const bool& enableColorPicke void UICodeEditor::setSyntaxDefinition( const SyntaxDefinition& definition ) { std::string oldLang( mDoc->getSyntaxDefinition().getLanguageName() ); - mDoc->setSyntaxDefinition( definition ); mDoc->getHighlighter()->reset(); + mDoc->setSyntaxDefinition( definition ); invalidateDraw(); DocSyntaxDefEvent event( this, mDoc.get(), Event::OnDocumentSyntaxDefinitionChange, oldLang, mDoc->getSyntaxDefinition().getLanguageName() ); diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index a4deb6bbd..a7bfe1ee3 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -2323,6 +2323,8 @@ void App::createDocManyLangsAlert( UICodeEditor* editor ) { docAlert->close(); editor->setFocus(); mConfig.languagesExtensions.priorities[ext] = lang->getLSPName(); + SyntaxDefinitionManager::instance()->setLanguageExtensionsPriority( + mConfig.languagesExtensions.priorities ); } ); } @@ -3672,6 +3674,8 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe Clock defClock; SyntaxDefinitionManager::createSingleton(); + SyntaxDefinitionManager::instance()->setLanguageExtensionsPriority( + mConfig.languagesExtensions.priorities ); Log::info( "Syntax definitions loaded in %.2f ms.", defClock.getElapsedTimeAndReset().asMilliseconds() );