From cec73de2eb8c2c1bd7df5a42142922079cfddedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 10 Aug 2025 01:25:10 -0300 Subject: [PATCH] Fix a couple of bugs related with use-after-free. Try to make some of the text document and syntax highlighter functionality more thread-safe. --- include/eepp/ui/doc/syntaxhighlighter.hpp | 3 + include/eepp/ui/doc/textdocument.hpp | 24 ++ include/eepp/ui/doc/textdocumentline.hpp | 115 +++++--- .../eepp/ui/tools/uicodeeditorsplitter.hpp | 2 + include/eepp/ui/uicodeeditor.hpp | 8 +- include/eepp/ui/uiconsole.hpp | 2 + include/eepp/ui/uitextinput.hpp | 2 + src/eepp/graphics/fonttruetype.cpp | 2 +- src/eepp/ui/doc/documentview.cpp | 6 +- src/eepp/ui/doc/foldrangeservice.cpp | 30 +- src/eepp/ui/doc/syntaxhighlighter.cpp | 74 ++++- src/eepp/ui/doc/textdocument.cpp | 269 ++++++++++++------ src/eepp/ui/tools/uicodeeditorsplitter.cpp | 9 + src/eepp/ui/uicodeeditor.cpp | 47 +-- .../autocomplete/autocompleteplugin.cpp | 25 +- .../autocomplete/autocompleteplugin.hpp | 3 +- .../ecode/plugins/debugger/debuggerplugin.hpp | 1 + .../ecode/plugins/linter/linterplugin.cpp | 12 +- .../ecode/plugins/lsp/lspclientserver.cpp | 9 + .../ecode/plugins/lsp/lspclientserver.hpp | 4 +- .../plugins/lsp/lspclientservermanager.cpp | 8 +- .../ecode/plugins/lsp/lspdocumentclient.cpp | 17 +- .../ecode/plugins/lsp/lspdocumentclient.hpp | 2 + .../ecode/plugins/xmltools/xmltoolsplugin.hpp | 1 + src/tools/ecode/projectsearch.cpp | 10 +- src/tools/ecode/projectsearch.hpp | 4 +- 26 files changed, 469 insertions(+), 220 deletions(-) diff --git a/include/eepp/ui/doc/syntaxhighlighter.hpp b/include/eepp/ui/doc/syntaxhighlighter.hpp index 0ccac8dd1..73806fbe9 100644 --- a/include/eepp/ui/doc/syntaxhighlighter.hpp +++ b/include/eepp/ui/doc/syntaxhighlighter.hpp @@ -34,6 +34,9 @@ class EE_API SyntaxHighlighter { const std::vector& getLine( const size_t& index, bool mustTokenize = true ); + void copyLineToBuffer( const size_t& index, std::vector& buffer, + bool mustTokenize = true ); + Int64 getFirstInvalidLine() const; Int64 getMaxWantedLine() const; diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 25aa6e85f..7ada3e428 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include using namespace EE::System; @@ -84,6 +85,8 @@ class EE_API TextDocument { class EE_API Client { public: + enum Type { Core, Auxiliary }; + virtual ~Client(); virtual void onDocumentLoaded( TextDocument* ) {}; virtual void onDocumentTextChanged( const DocumentContentChange& ) = 0; @@ -99,6 +102,7 @@ class EE_API TextDocument { virtual void onDocumentDirtyOnFileSystem( TextDocument* ) = 0; virtual void onDocumentMoved( TextDocument* ) = 0; virtual void onDocumentReset( TextDocument* ) = 0; + virtual Client::Type getTextDocumentClientType() = 0; virtual void onDocumentReloaded( TextDocument* doc ) { onDocumentClosed( doc ); @@ -273,6 +277,22 @@ class EE_API TextDocument { TextRange getLineRange( Int64 line ) const; + String getLineText( Int64 line ) const; + + String getLineTextSubStr( Int64 line, std::size_t pos, std::size_t n ) const; + + String::HashType getLineHash( Int64 line ) const; + + String getLineTextWithoutNewLine( Int64 line ) const; + + std::size_t getLineLength( Int64 ) const; + + void getLineTextToBuffer( Int64 line, String& buffer ) const; + + std::string getLineTextUtf8( Int64 line ) const; + + void getLineTextToBufferUtf8( Int64 line, std::string& buffer ) const; + void deleteTo( const size_t& cursorIdx, TextPosition position ); void deleteTo( const size_t& cursorIdx, int offset ); @@ -297,6 +317,8 @@ class EE_API TextDocument { void unregisterClient( Client* client ); + std::size_t clientOfTypeCount( Client::Type type ); + void moveToPreviousChar(); void moveToNextChar(); @@ -690,6 +712,8 @@ class EE_API TextDocument { TextRanges mSelection; UnorderedSet mClients; Mutex mClientsMutex; + mutable Mutex mLinesMutex; + mutable std::shared_ptr mDocumentMutex; TextFormat::Encoding mEncoding{ TextFormat::Encoding::UTF8 }; TextFormat::LineEnding mLineEnding{ TextFormat::LineEnding::LF }; std::atomic mLoading{ false }; diff --git a/include/eepp/ui/doc/textdocumentline.hpp b/include/eepp/ui/doc/textdocumentline.hpp index cdcc82e94..e910af241 100644 --- a/include/eepp/ui/doc/textdocumentline.hpp +++ b/include/eepp/ui/doc/textdocumentline.hpp @@ -2,74 +2,115 @@ #define EE_UI_DOC_TEXTDOCUMENTLINE_HPP #include +#include +#include +#include namespace EE { namespace UI { namespace Doc { class EE_API TextDocumentLine { public: - TextDocumentLine( const String& text ) : mText( text ) { updateState(); } + TextDocumentLine( const String& text, std::shared_ptr docMutex ) : + mText( text ), mDocMutex( docMutex ) { + updateState(); + } + + ~TextDocumentLine() { + if ( mDocMutex ) { + // Wait for any readers to finish before destruction + std::unique_lock lock( *mDocMutex ); + } + } void setText( String&& text ) { - mText = std::move( text ); - updateState(); + if ( mDocMutex ) { + std::unique_lock lock( *mDocMutex ); + mText = std::move( text ); + updateState(); + } else { + mText = std::move( text ); + updateState(); + } } - void setText( const String& text ) { - mText = text; - updateState(); + const String& getText() const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mText; + } + return mText; } - const String& getText() const { return mText; } + String getTextWithoutNewLine() const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mText.substr( 0, mText.size() - 1 ); + } + return mText.substr( 0, mText.size() - 1 ); + } - String getTextWithoutNewLine() const { return mText.substr( 0, mText.size() - 1 ); } - - void operator=( const std::string& right ) { setText( right ); } - - String::StringBaseType operator[]( std::size_t index ) const { return mText[index]; } - - void insertChar( const unsigned int& pos, const String::StringBaseType& tchar ) { - mText.insert( mText.begin() + pos, tchar ); - updateState(); + String::StringBaseType operator[]( std::size_t index ) const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mText[index]; + } + return mText[index]; } void append( const String& text ) { - mText.append( text ); - updateState(); - } - - void append( const String::StringBaseType& code ) { - mText.append( code ); - updateState(); + if ( mDocMutex ) { + std::unique_lock lock( *mDocMutex ); + mText.append( text ); + updateState(); + } else { + mText.append( text ); + updateState(); + } } String substr( std::size_t pos = 0, std::size_t n = String::StringType::npos ) const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mText.substr( pos, n ); + } return mText.substr( pos, n ); } - String::Iterator insert( String::Iterator p, const String::StringBaseType& c ) { - auto it = mText.insert( p, c ); - updateState(); - return it; + bool empty() const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mText.empty(); + } + return mText.empty(); } - bool empty() const { return mText.empty(); } + size_t size() const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mText.size(); + } + return mText.size(); + } - size_t size() const { return mText.size(); } + String::HashType getHash() const { return mHash; } - size_t length() const { return mText.length(); } + bool isAscii() const { + return ( mFlags & TextHints::AllAscii ) != 0; + } - const String::HashType& getHash() const { return mHash; } - - std::string toUtf8() const { return mText.toUtf8(); } - - bool isAscii() const { return ( mFlags & TextHints::AllAscii ) != 0; } - - Uint32 getTextHints() const { return mFlags; } + Uint32 getTextHints() const { + if ( mDocMutex ) { + std::shared_lock lock( *mDocMutex ); + return mFlags; + } + return mFlags; + } protected: String mText; String::HashType mHash; Uint32 mFlags{ 0 }; + std::shared_ptr mDocMutex; void updateState() { mHash = mText.getHash(); diff --git a/include/eepp/ui/tools/uicodeeditorsplitter.hpp b/include/eepp/ui/tools/uicodeeditorsplitter.hpp index 60be8ba04..7298cce50 100644 --- a/include/eepp/ui/tools/uicodeeditorsplitter.hpp +++ b/include/eepp/ui/tools/uicodeeditorsplitter.hpp @@ -363,6 +363,8 @@ class EE_API UICodeEditorSplitter { UITabWidget* splitTabWidget( SplitDirection, UITabWidget* ); + std::shared_ptr getTextDocumentRef( TextDocument* doc ); + protected: UISceneNode* mUISceneNode{ nullptr }; std::shared_ptr mThreadPool; diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 6e8f5b2c0..f34af8e0b 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -779,10 +779,6 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { Float getPluginsGutterSpace() const; - void setEnableFlashCursor( bool enable ) { mEnableFlashCursor = enable; } - - bool isEnabledFlashCursor() { return mEnableFlashCursor; } - void setCursorVisible( bool visible ); bool isCursorVisible() const; @@ -802,6 +798,8 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool usesTabStops() { return mTabStops; } + Client::Type getTextDocumentClientType() { return TextDocument::Client::Core; } + protected: struct LastXOffset { TextPosition position{ 0, 0 }; @@ -845,7 +843,6 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { bool mFoldsAlwaysVisible{ false }; bool mFoldsVisible{ false }; bool mFoldsIsFirst{ true }; - bool mEnableFlashCursor{ false }; bool mDisableCursorBlinkingAfterAMinuteOfInactivity{ true }; bool mAllowSelectingTextFromGutter{ true }; bool mTabStops{ false }; @@ -931,6 +928,7 @@ class EE_API UICodeEditor : public UIWidget, public TextDocument::Client { String::HashType mTagFoldRange{ 0 }; Uint32 mTabIndentCharacter{ 187 /*'ยป'*/ }; CharacterAlignment mTabIndentAlignment{ CharacterAlignment::Center }; + std::vector mTokens; UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands = true, const bool& autoRegisterBaseKeybindings = true ); diff --git a/include/eepp/ui/uiconsole.hpp b/include/eepp/ui/uiconsole.hpp index 8cac7eac2..83231ee24 100644 --- a/include/eepp/ui/uiconsole.hpp +++ b/include/eepp/ui/uiconsole.hpp @@ -145,6 +145,8 @@ class EE_API UIConsole : public UIWidget, TextDocument& getDoc(); + Client::Type getTextDocumentClientType() { return TextDocument::Client::Core; } + protected: struct TextCache { Text text; diff --git a/include/eepp/ui/uitextinput.hpp b/include/eepp/ui/uitextinput.hpp index 28b94f770..7a966ca39 100644 --- a/include/eepp/ui/uitextinput.hpp +++ b/include/eepp/ui/uitextinput.hpp @@ -123,6 +123,8 @@ class EE_API UITextInput : public UITextView, public TextDocument::Client { void setSelectAllDocOnTabNavigate( bool selectAllDocOnTabNavigate ); + Client::Type getTextDocumentClientType() { return TextDocument::Client::Core; } + protected: TextDocument mDoc; Float mWaitCursorTime; diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index cd2a38166..14bb6008d 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -1161,7 +1161,7 @@ Glyph FontTrueType::loadGlyphByIndex( Uint32 index, unsigned int characterSize, page.texture->update( pixelPtr, w, h, x, y ); if ( scale < 1.f ) - eeFree( pixelPtr ); + eeSAFE_DELETE_ARRAY( pixelPtr ); } // Delete the FT glyph diff --git a/src/eepp/ui/doc/documentview.cpp b/src/eepp/ui/doc/documentview.cpp index c64914b97..5d92d727c 100644 --- a/src/eepp/ui/doc/documentview.cpp +++ b/src/eepp/ui/doc/documentview.cpp @@ -363,7 +363,7 @@ DocumentView::VisibleLineRange DocumentView::getVisibleLineRange( const TextPosi Int64 fromCol = mVisibleLines[mid].column(); Int64 toCol = mid + 1 <= toIdx ? mVisibleLines[mid + 1].column() - ( allowVisualLineEnd ? 0 : 1 ) - : mDoc->line( pos.line() ).size(); + : mDoc->getLineLength( pos.line() ); if ( pos.column() >= fromCol && pos.column() <= toCol ) { // If it's between the limits we must check if it fits into the previous one @@ -371,7 +371,7 @@ DocumentView::VisibleLineRange DocumentView::getVisibleLineRange( const TextPosi Int64 fromCol = mVisibleLines[mid - 1].column(); Int64 toCol = mid <= toIdx ? mVisibleLines[mid].column() - ( allowVisualLineEnd ? 0 : 1 ) - : mDoc->line( pos.line() ).size(); + : mDoc->getLineLength( pos.line() ); info.visibleIndex = static_cast( mid - 1 ); info.range = { { pos.line(), fromCol }, { pos.line(), toCol } }; @@ -411,7 +411,7 @@ TextRange DocumentView::getVisibleIndexRange( VisibleIndex visibleIndex ) const mVisibleLines[idx + 1].line() == start.line() ) { end.setColumn( mVisibleLines[idx + 1].column() ); } else { - end.setColumn( mDoc->line( start.line() ).size() ); + end.setColumn( mDoc->getLineLength( start.line() ) ); } return { start, end }; } diff --git a/src/eepp/ui/doc/foldrangeservice.cpp b/src/eepp/ui/doc/foldrangeservice.cpp index e677ec835..0038adeea 100644 --- a/src/eepp/ui/doc/foldrangeservice.cpp +++ b/src/eepp/ui/doc/foldrangeservice.cpp @@ -10,12 +10,13 @@ namespace EE { namespace UI { namespace Doc { static std::vector findFoldingRangesBraces( TextDocument* doc ) { Clock c; std::vector regions; - if ( doc->linesCount() <= 2 ) + auto lineCount = doc->linesCount(); + if ( lineCount <= 2 ) return regions; const auto& braces = doc->getSyntaxDefinition().getFoldBraces(); std::stack braceStack; auto highlighter = doc->getHighlighter(); - for ( size_t lineIdx = 0; lineIdx < doc->linesCount(); lineIdx++ ) { + for ( size_t lineIdx = 0; lineIdx < lineCount; lineIdx++ ) { const auto& line = doc->line( lineIdx ).getText(); size_t lineLength = line.length(); for ( size_t colIdx = 0; colIdx < lineLength; colIdx++ ) { @@ -63,12 +64,13 @@ static std::vector findFoldingRangesIndentation( TextDocument* doc ) Clock c; std::stack indentStack; std::vector regions; - if ( doc->linesCount() <= 2 ) + auto lineCount = doc->linesCount(); + if ( lineCount <= 2 ) return regions; const auto& braces = doc->getSyntaxDefinition().getFoldBraces(); int currentIndent = 0; - for ( size_t lineIdx = 0; lineIdx < doc->linesCount(); lineIdx++ ) { + for ( size_t lineIdx = 0; lineIdx < lineCount; lineIdx++ ) { const auto& line = doc->line( lineIdx ).getText(); int newIndent = countLeadingSpaces( line ); if ( newIndent > currentIndent ) { @@ -91,7 +93,7 @@ static std::vector findFoldingRangesIndentation( TextDocument* doc ) auto top = indentStack.top(); indentStack.pop(); regions.emplace_back( TextPosition( top.line() + 1, 0 ), - TextPosition( static_cast( doc->linesCount() ) - 1, 0 ) ); + TextPosition( static_cast( lineCount ) - 1, 0 ) ); } Log::debug( "findFoldingRangesIndentation for \"%s\" took %s", doc->getFilePath(), @@ -102,9 +104,10 @@ static std::vector findFoldingRangesIndentation( TextDocument* doc ) static std::vector findFoldingRangesMarkdown( TextDocument* doc ) { Clock c; // For performance logging, consistent with existing functions std::vector regions; + auto lineCount = doc->linesCount(); // Early return for small documents, as in other folding functions - if ( doc->linesCount() <= 2 ) + if ( lineCount <= 2 ) return regions; // Stack to track open heading sections: (line number, heading level) @@ -115,7 +118,7 @@ static std::vector findFoldingRangesMarkdown( TextDocument* doc ) { Int64 codeBlockStart = -1; // Process each line - for ( size_t lineIdx = 0; lineIdx < doc->linesCount(); lineIdx++ ) { + for ( size_t lineIdx = 0; lineIdx < lineCount; lineIdx++ ) { const String& lineText = doc->line( lineIdx ).getText(); String::View trimmed = String::trim( lineText.view() ); // Remove leading and trailing whitespace @@ -170,18 +173,17 @@ static std::vector findFoldingRangesMarkdown( TextDocument* doc ) { while ( !sectionStack.empty() ) { auto [headingLine, _] = sectionStack.back(); sectionStack.pop_back(); - if ( headingLine < static_cast( doc->linesCount() ) ) { - regions.emplace_back( TextPosition( headingLine, 0 ), // Start at heading - TextPosition( doc->linesCount() - 1, 0 ) // End at document end + if ( headingLine < static_cast( lineCount ) ) { + regions.emplace_back( TextPosition( headingLine, 0 ), // Start at heading + TextPosition( lineCount - 1, 0 ) // End at document end ); } } // Close any unterminated code block - if ( inCodeBlock && codeBlockStart != -1 && - codeBlockStart < static_cast( doc->linesCount() ) ) { - regions.emplace_back( TextPosition( codeBlockStart, 0 ), // Start at opening ``` - TextPosition( doc->linesCount() - 1, 0 ) // End at document end + if ( inCodeBlock && codeBlockStart != -1 && codeBlockStart < static_cast( lineCount ) ) { + regions.emplace_back( TextPosition( codeBlockStart, 0 ), // Start at opening ``` + TextPosition( lineCount - 1, 0 ) // End at document end ); } diff --git a/src/eepp/ui/doc/syntaxhighlighter.cpp b/src/eepp/ui/doc/syntaxhighlighter.cpp index 2dff0bf93..97a7f06d2 100644 --- a/src/eepp/ui/doc/syntaxhighlighter.cpp +++ b/src/eepp/ui/doc/syntaxhighlighter.cpp @@ -51,18 +51,18 @@ void SyntaxHighlighter::invalidate( Int64 lineIndex ) { } TokenizedLine SyntaxHighlighter::tokenizeLine( const size_t& line, const SyntaxState& state ) { - auto& ln = mDoc->line( line ); TokenizedLine tokenizedLine; tokenizedLine.initState = state; - tokenizedLine.hash = ln.getHash(); - if ( mMaxTokenizationLength != 0 && (Int64)ln.size() > mMaxTokenizationLength ) { - Int64 textSize = ln.size(); + tokenizedLine.hash = mDoc->getLineHash( line ); + auto len = mDoc->getLineLength( line ); + if ( mMaxTokenizationLength != 0 && (Int64)len > mMaxTokenizationLength ) { + Int64 textSize = len; SyntaxTokenLen pos = 0; while ( textSize > 0 ) { SyntaxTokenLen chunkSize = textSize > mMaxTokenizationLength ? mMaxTokenizationLength : textSize; SyntaxTokenPosition token{ SyntaxStyleTypes::Normal, pos, chunkSize }; - token.len = ln.size(); + token.len = len; tokenizedLine.tokens.emplace_back( token ); textSize -= chunkSize; pos += chunkSize; @@ -71,7 +71,8 @@ TokenizedLine SyntaxHighlighter::tokenizeLine( const size_t& line, const SyntaxS tokenizedLine.updateSignature(); return tokenizedLine; } - auto res = SyntaxTokenizer::tokenizePosition( mDoc->getSyntaxDefinition(), ln.toUtf8(), state ); + auto res = SyntaxTokenizer::tokenizePosition( mDoc->getSyntaxDefinition(), + mDoc->getLineTextUtf8( line ), state ); tokenizedLine.tokens = std::move( res.first ); tokenizedLine.state = std::move( res.second ); tokenizedLine.updateSignature(); @@ -94,7 +95,7 @@ void SyntaxHighlighter::moveHighlight( const Int64& fromLine, const Int64& /*toL auto lineIt = mLines.find( i - numLines ); if ( lineIt != mLines.end() ) { const auto& line = lineIt->second; - if ( line.hash == mDoc->line( i ).getHash() ) { + if ( line.hash == mDoc->getLineHash( i ) ) { auto nl = mLines.extract( lineIt ); nl.key() = i; mLines.insert( std::move( nl ) ); @@ -104,7 +105,7 @@ void SyntaxHighlighter::moveHighlight( const Int64& fromLine, const Int64& /*toL } else if ( numLines < 0 ) { for ( Int64 i = fromLine; i < linesCount; i++ ) { auto lineIt = mLines.find( i - numLines ); - if ( lineIt != mLines.end() && lineIt->second.hash == mDoc->line( i ).getHash() ) { + if ( lineIt != mLines.end() && lineIt->second.hash == mDoc->getLineHash( i ) ) { auto nl = mLines.extract( lineIt ); nl.key() = i; mLines[i] = std::move( nl.mapped() ); @@ -153,7 +154,7 @@ const std::vector& SyntaxHighlighter::getLine( const size_t static std::vector noHighlightVector = { { SyntaxStyleTypes::Normal, 0, 0 } }; if ( mDoc->getSyntaxDefinition().getPatterns().empty() ) { - noHighlightVector[0].len = mDoc->line( index ).size(); + noHighlightVector[0].len = mDoc->getLineLength( index ); return noHighlightVector; } @@ -162,7 +163,7 @@ const std::vector& SyntaxHighlighter::getLine( const size_t auto it = mLines.find( index ); bool needsTokenize = it == mLines.end() || - ( index < mDoc->linesCount() && mDoc->line( index ).getHash() != it->second.hash ); + ( index < mDoc->linesCount() && mDoc->getLineHash( index ) != it->second.hash ); if ( !needsTokenize ) { mMaxWantedLine = eemax( mMaxWantedLine, index ); return it->second.tokens; @@ -170,7 +171,7 @@ const std::vector& SyntaxHighlighter::getLine( const size_t } if ( !mustTokenize ) { - noHighlightVector[0].len = mDoc->line( index ).size(); + noHighlightVector[0].len = mDoc->getLineLength( index ); return noHighlightVector; } @@ -190,6 +191,52 @@ const std::vector& SyntaxHighlighter::getLine( const size_t return mLines[index].tokens; } +void SyntaxHighlighter::copyLineToBuffer( const size_t& index, + std::vector& buffer, + bool mustTokenize ) { + static std::vector noHighlightVector = { + { SyntaxStyleTypes::Normal, 0, 0 } }; + if ( mDoc->getSyntaxDefinition().getPatterns().empty() ) { + noHighlightVector[0].len = mDoc->getLineLength( index ); + buffer = noHighlightVector; + return; + } + + { + Lock l( mLinesMutex ); + auto it = mLines.find( index ); + bool needsTokenize = + it == mLines.end() || + ( index < mDoc->linesCount() && mDoc->getLineHash( index ) != it->second.hash ); + if ( !needsTokenize ) { + mMaxWantedLine = eemax( mMaxWantedLine, index ); + buffer = it->second.tokens; + return; + } + } + + if ( !mustTokenize ) { + noHighlightVector[0].len = mDoc->getLineLength( index ); + buffer = noHighlightVector; + return; + } + + SyntaxState prevState; + if ( index > 0 ) { + Lock l( mLinesMutex ); + auto prevIt = mLines.find( index - 1 ); + if ( prevIt != mLines.end() ) + prevState = prevIt->second.state; + } + auto tokenizedLine = tokenizeLine( index, prevState ); + + Lock l( mLinesMutex ); + mLines[index] = std::move( tokenizedLine ); + mTokenizerLines[index] = mLines[index]; + mMaxWantedLine = eemax( mMaxWantedLine, index ); + buffer = mLines[index].tokens; +} + Int64 SyntaxHighlighter::getFirstInvalidLine() const { return mFirstInvalidLine; } @@ -224,7 +271,7 @@ bool SyntaxHighlighter::updateDirty( int visibleLinesCount ) { Lock l( mLinesMutex ); const auto& it = mLines.find( index ); mustTokenize = it == mLines.end() || - it->second.hash != mDoc->line( index ).getHash() || + it->second.hash != mDoc->getLineHash( index ) || it->second.initState != state; } @@ -307,8 +354,7 @@ void SyntaxHighlighter::mergeLine( const size_t& line, const TokenizedLine& toke { mLinesMutex.lock(); auto found = mTokenizerLines.find( line ); - if ( found != mTokenizerLines.end() && - mDoc->line( line ).getHash() == found->second.hash ) { + if ( found != mTokenizerLines.end() && mDoc->getLineHash( line ) == found->second.hash ) { tline = found->second; mLinesMutex.unlock(); } else { diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 48d0f02fb..1e3b7f8e7 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -119,6 +119,7 @@ bool TextDocument::isNonWord( String::StringBaseType ch ) const { TextDocument::TextDocument( bool verbose ) : mUUID( true ), mUndoStack( this ), + mDocumentMutex( std::make_shared() ), mVerbose( verbose ), mAutoCloseBracketsPairs( { { '(', ')' }, { '[', ']' }, { '{', '}' }, { '\'', '\'' }, { '"', '"' }, { '`', '`' } } ), @@ -185,8 +186,12 @@ void TextDocument::reset() { mSelection.clear(); mSelection.push_back( { { 0, 0 }, { 0, 0 } } ); mLastSelection = 0; - mLines.clear(); - mLines.emplace_back( String( "\n" ) ); + { + Lock l( mLinesMutex ); + mLines.clear(); + mLines.emplace_back( String( "\n" ), mDocumentMutex ); + } + { Lock l( mSyntaxDefinitionMutex ); mSyntaxDefinition = SyntaxDefinitionManager::instance()->getPlainDefinition(); @@ -301,7 +306,12 @@ TextDocument::LoadStatus TextDocument::loadFromStream( IOStream& file, std::stri Clock clock; if ( callReset ) reset(); - mLines.clear(); + + { + Lock l( mLinesMutex ); + mLines.clear(); + } + MD5::Context md5Ctx; if ( file.isOpen() ) { const size_t BLOCK_SIZE = EE_1MB; @@ -362,7 +372,7 @@ TextDocument::LoadStatus TextDocument::loadFromStream( IOStream& file, std::stri char lastChar = lineBuffer[lineBufferSize - 1]; if ( lastChar == '\n' || lastChar == '\r' ) { - if ( mLines.empty() ) { + if ( linesCount() == 0 ) { if ( lineBufferSize > 1 && lineBuffer[lineBufferSize - 2] == '\r' && lastChar == '\n' ) { mLineEnding = TextFormat::LineEnding::CRLF; @@ -389,10 +399,14 @@ TextDocument::LoadStatus TextDocument::loadFromStream( IOStream& file, std::stri lineBuffer[lineBuffer.size() - 1] = '\n'; } - mLines.push_back( lineBuffer ); + { + Lock l( mLinesMutex ); + mLines.emplace_back( lineBuffer, mDocumentMutex ); + } lineBuffer.resize( 0 ); } else if ( consume <= 0 && pending - read == 0 ) { - mLines.push_back( lineBuffer ); + Lock l( mLinesMutex ); + mLines.emplace_back( lineBuffer, mDocumentMutex ); } if ( consume < 0 ) { @@ -408,15 +422,18 @@ TextDocument::LoadStatus TextDocument::loadFromStream( IOStream& file, std::stri }; } - if ( !mLines.empty() ) { - const String& lastLine = mLines[mLines.size() - 1].getText(); + auto lineCount = linesCount(); + if ( lineCount != 0 ) { + Lock l( mLinesMutex ); + const String& lastLine = mLines[lineCount - 1].getText(); if ( lastLine[lastLine.size() - 1] == '\n' ) { - mLines.push_back( String( "\n" ) ); + mLines.emplace_back( String( "\n" ), mDocumentMutex ); } else { - mLines[mLines.size() - 1].append( "\n" ); + mLines[lineCount - 1].append( "\n" ); } } else { - mLines.push_back( String( "\n" ) ); + Lock l( mLinesMutex ); + mLines.emplace_back( String( "\n" ), mDocumentMutex ); } if ( mAutoDetectIndentType ) @@ -465,14 +482,18 @@ void TextDocument::guessIndentType() { } }; - size_t start = eemin( 100, mLines.size() ); - guessTabsFn( 0, start ); + size_t start = eemin( 100, linesCount() ); - if ( !guessTabs && !guessSpaces ) { - if ( start == 100 ) - guessTabsFn( start, eemin( start + 100, mLines.size() ) ); - if ( !guessTabs && !guessSpaces ) - return; + { + Lock l( mLinesMutex ); + guessTabsFn( 0, start ); + + if ( !guessTabs && !guessSpaces ) { + if ( start == 100 ) + guessTabsFn( start, eemin( start + 100, linesCount() ) ); + if ( !guessTabs && !guessSpaces ) + return; + } } if ( guessTabs >= guessSpaces ) { @@ -781,7 +802,7 @@ bool TextDocument::save( const std::string& path ) { } bool TextDocument::save( IOStream& stream, bool keepUndoRedoStatus ) { - if ( !stream.isOpen() || mLines.empty() ) + if ( !stream.isOpen() || linesCount() == 0 ) return false; BoolScopedOp op( mDoingTextInput, true ); const std::string whitespaces( " \t\f\v\n\r" ); @@ -814,9 +835,9 @@ bool TextDocument::save( IOStream& stream, bool keepUndoRedoStatus ) { } } - size_t lastLine = mLines.size() - 1; + size_t lastLine = linesCount() - 1; for ( size_t i = 0; i <= lastLine; i++ ) { - std::string text( mLines[i].toUtf8() ); + std::string text( getLineTextUtf8( i ) ); if ( !keepUndoRedoStatus && mTrimTrailingWhitespaces && text.size() > 1 && whitespaces.find( text[text.size() - 2] ) != std::string::npos ) { @@ -824,11 +845,11 @@ bool TextDocument::save( IOStream& stream, bool keepUndoRedoStatus ) { Int64 curLine = i; if ( pos != std::string::npos ) { remove( 0, { { curLine, static_cast( pos + 1 ) }, - { curLine, static_cast( mLines[i].getText().size() ) } } ); + { curLine, static_cast( getLineLength( i ) ) } } ); } else { remove( 0, { startOfLine( { curLine, 0 } ), { endOfLine( { curLine, 0 } ) } } ); } - text = mLines[i].toUtf8(); + text = getLineTextUtf8( i ); } if ( i == lastLine ) { @@ -1096,18 +1117,19 @@ const TextRange& TextDocument::getSelection() const { } TextDocumentLine& TextDocument::line( const size_t& index ) { - static TextDocumentLine safeLine = TextDocumentLine( "" ); - eeASSERT( index < mLines.size() ); - return index >= mLines.size() ? safeLine : mLines[index]; + static TextDocumentLine safeLine = TextDocumentLine( "", nullptr ); + eeASSERT( index < linesCount() ); + return index >= linesCount() ? safeLine : mLines[index]; } const TextDocumentLine& TextDocument::line( const size_t& index ) const { - static TextDocumentLine safeLine = TextDocumentLine( "" ); - eeASSERT( index < mLines.size() ); - return index >= mLines.size() ? safeLine : mLines[index]; + static TextDocumentLine safeLine = TextDocumentLine( "", nullptr ); + eeASSERT( index < linesCount() ); + return index >= linesCount() ? safeLine : mLines[index]; } size_t TextDocument::linesCount() const { + Lock l( mLinesMutex ); return mLines.size(); } @@ -1129,6 +1151,7 @@ std::string TextDocument::getHashHexString() const { String TextDocument::getText( const TextRange& range ) const { TextRange nrange = sanitizeRange( range.normalized() ); + Lock l( mLinesMutex ); if ( nrange.start().line() == nrange.end().line() ) { return mLines[nrange.start().line()].substr( nrange.start().column(), nrange.end().column() - nrange.start().column() ); @@ -1206,11 +1229,13 @@ String::StringBaseType TextDocument::getCurrentChar() const { String::StringBaseType TextDocument::getChar( const TextPosition& position ) const { auto pos = sanitizePosition( position ); + Lock l( mLinesMutex ); return mLines[pos.line()][pos.column()]; } String::StringBaseType TextDocument::getCharFromUnsanitizedPosition( const TextPosition& position ) const { + Lock l( mLinesMutex ); return mLines[position.line()][position.column()]; } @@ -1241,23 +1266,28 @@ TextPosition TextDocument::insert( const size_t& cursorIdx, TextPosition positio } position = sanitizePosition( position ); - size_t lineCount = mLines.size(); + size_t lineCount = linesCount(); + Int64 linesAdd = 0; - String before = mLines[position.line()].substr( 0, position.column() ); - String after = mLines[position.line()].substr( position.column() ); - std::vector lines = text.split( '\n', true ); - Int64 linesAdd = eemax( 0, static_cast( lines.size() ) - 1 ); - for ( auto i = 0; i < linesAdd; i++ ) - lines[i] = lines[i] + "\n"; - lines[0] = before + lines[0]; - lines[lines.size() - 1] = lines[lines.size() - 1] + after; + { + Lock l( mLinesMutex ); + String before = mLines[position.line()].substr( 0, position.column() ); + String after = mLines[position.line()].substr( position.column() ); + std::vector lines = text.split( '\n', true ); + linesAdd = eemax( 0, static_cast( lines.size() ) - 1 ); + for ( auto i = 0; i < linesAdd; i++ ) + lines[i] = lines[i] + "\n"; + lines[0] = before + lines[0]; + lines[lines.size() - 1] = lines[lines.size() - 1] + after; - mLines[position.line()] = TextDocumentLine( lines[0] ); - notifyLineChanged( position.line() ); + mLines[position.line()] = TextDocumentLine( lines[0], mDocumentMutex ); + notifyLineChanged( position.line() ); - for ( Int64 i = 1; i < (Int64)lines.size(); i++ ) { - mLines.insert( mLines.begin() + position.line() + i, TextDocumentLine( lines[i] ) ); - notifyLineChanged( position.line() + i ); + for ( Int64 i = 1; i < (Int64)lines.size(); i++ ) { + mLines.insert( mLines.begin() + position.line() + i, + TextDocumentLine( lines[i], mDocumentMutex ) ); + notifyLineChanged( position.line() + i ); + } } TextPosition cursor = positionOffset( position, text.size() ); @@ -1272,8 +1302,8 @@ TextPosition TextDocument::insert( const size_t& cursorIdx, TextPosition positio notifyTextChanged( { { position, position }, text } ); - if ( lineCount != mLines.size() ) { - notifyLineCountChanged( lineCount, mLines.size() ); + if ( lineCount != linesCount() ) { + notifyLineCountChanged( lineCount, linesCount() ); } if ( mSelection.size() > 1 ) { @@ -1315,12 +1345,12 @@ TextPosition TextDocument::insert( const size_t& cursorIdx, TextPosition positio size_t TextDocument::remove( const size_t& cursorIdx, TextRange range ) { if ( !range.hasSelection() ) return 0; - size_t lineCount = mLines.size(); + size_t lineCount = linesCount(); mUndoStack.clearRedoStack(); size_t linesRemoved = remove( cursorIdx, sanitizeRange( range.normalized() ), mUndoStack.getUndoStackContainer(), mTimer.getElapsedTime() ); - if ( lineCount != mLines.size() ) { - notifyLineCountChanged( lineCount, mLines.size() ); + if ( lineCount != linesCount() ) { + notifyLineCountChanged( lineCount, linesCount() ); } return linesRemoved; } @@ -1352,26 +1382,29 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, bool deletedAcrossNewLine = false; // First delete all the lines in between the first and last one. - if ( range.start().line() + 1 < range.end().line() ) { - mLines.erase( mLines.begin() + range.start().line() + 1, - mLines.begin() + range.end().line() ); - linesRemoved = range.end().line() - ( range.start().line() + 1 ); - range.end().setLine( range.start().line() + 1 ); + { + Lock l( mLinesMutex ); + if ( range.start().line() + 1 < range.end().line() ) { + mLines.erase( mLines.begin() + range.start().line() + 1, + mLines.begin() + range.end().line() ); + linesRemoved = range.end().line() - ( range.start().line() + 1 ); + range.end().setLine( range.start().line() + 1 ); + } } if ( range.start().line() == range.end().line() ) { // Delete within same line. TextDocumentLine& line = this->line( range.start().line() ); bool wholeLineIsSelected = - range.start().column() == 0 && range.end().column() == (Int64)line.length(); + range.start().column() == 0 && range.end().column() == (Int64)line.size(); if ( wholeLineIsSelected ) { - line = "\n"; + line.setText( "\n" ); } else { auto beforeSelection = line.substr( 0, range.start().column() ); auto afterSelection = !line.empty() && range.end().column() < (Int64)line.size() - ? line.substr( range.end().column(), line.length() - range.end().column() ) + ? line.substr( range.end().column(), line.size() - range.end().column() ) : ""; if ( !beforeSelection.empty() && beforeSelection[beforeSelection.size() - 1] == '\n' ) @@ -1389,7 +1422,7 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, auto beforeSelection = firstLine.substr( 0, range.start().column() ); auto afterSelection = !secondLine.empty() && range.end().column() < (Int64)secondLine.size() ? secondLine.substr( range.end().column(), - secondLine.length() - range.end().column() ) + secondLine.size() - range.end().column() ) : ""; if ( !beforeSelection.empty() && beforeSelection[beforeSelection.size() - 1] == '\n' ) @@ -1398,13 +1431,18 @@ size_t TextDocument::remove( const size_t& cursorIdx, TextRange range, afterSelection += '\n'; firstLine.setText( beforeSelection + afterSelection ); + + Lock l( mLinesMutex ); mLines.erase( mLines.begin() + range.end().line() ); linesRemoved += 1; deletedAcrossNewLine = true; } - if ( mLines.empty() ) - mLines.emplace_back( String( "\n" ) ); + { + Lock l( mLinesMutex ); + if ( mLines.empty() ) + mLines.emplace_back( String( "\n" ), mDocumentMutex ); + } if ( mSelection.size() > 1 ) { auto oriNm( originalRange.normalized() ); @@ -1477,12 +1515,12 @@ TextPosition TextDocument::positionOffset( TextPosition position, int columnOffs while ( position.line() > 0 && position.column() < 0 ) { position.setLine( position.line() - 1 ); position.setColumn( - eemax( 0, position.column() + (Int64)mLines[position.line()].size() ) ); + eemax( 0, position.column() + (Int64)getLineLength( position.line() ) ) ); } - while ( position.line() < (Int64)mLines.size() - 1 && + while ( position.line() < (Int64)linesCount() - 1 && position.column() > - (Int64)eemax( 0, (Int64)mLines[position.line()].size() - 1 ) ) { - position.setColumn( position.column() - mLines[position.line()].size() ); + (Int64)eemax( 0, (Int64)getLineLength( position.line() ) - 1 ) ) { + position.setColumn( position.column() - getLineLength( position.line() ) ); position.setLine( position.line() + 1 ); } return sanitizePosition( position ); @@ -1493,7 +1531,7 @@ TextPosition TextDocument::positionOffset( TextPosition position, TextPosition o } bool TextDocument::replaceLine( const Int64& lineNum, const String& text ) { - if ( lineNum >= 0 && lineNum < (Int64)mLines.size() ) { + if ( lineNum >= 0 && lineNum < (Int64)linesCount() ) { TextRange oldSelection = getSelection(); setSelection( { startOfLine( { lineNum, 0 } ), endOfLine( { lineNum, 0 } ) } ); textInput( text, false ); @@ -1629,7 +1667,7 @@ TextPosition TextDocument::startOfLine( TextPosition position ) const { TextPosition TextDocument::endOfLine( TextPosition position ) const { position = sanitizePosition( position ); - return TextPosition( position.line(), mLines[position.line()].size() - 1 ); + return TextPosition( position.line(), getLineLength( position.line() ) - 1 ); } TextPosition TextDocument::startOfContent( TextPosition start ) { @@ -1652,7 +1690,8 @@ TextPosition TextDocument::startOfDoc() const { } TextPosition TextDocument::endOfDoc() const { - return TextPosition( mLines.size() - 1, mLines[mLines.size() - 1].size() - 1 ); + auto lineCount = linesCount(); + return TextPosition( lineCount - 1, getLineLength( lineCount - 1 ) - 1 ); } TextRange TextDocument::getDocRange() const { @@ -1663,6 +1702,46 @@ TextRange TextDocument::getLineRange( Int64 line ) const { return { startOfLine( { line, 0 } ), endOfLine( { line, 0 } ) }; } +std::size_t TextDocument::getLineLength( Int64 line ) const { + Lock l( mLinesMutex ); + return mLines[line].size(); +} + +String TextDocument::getLineText( Int64 line ) const { + Lock l( mLinesMutex ); + return mLines[line].getText(); +} + +String TextDocument::getLineTextSubStr( Int64 line, std::size_t pos, std::size_t n ) const { + Lock l( mLinesMutex ); + return mLines[line].getText().substr( pos, n ); +} + +String::HashType TextDocument::getLineHash( Int64 line ) const { + Lock l( mLinesMutex ); + return mLines[line].getHash(); +} + +String TextDocument::getLineTextWithoutNewLine( Int64 line ) const { + Lock l( mLinesMutex ); + return mLines[line].getTextWithoutNewLine(); +} + +void TextDocument::getLineTextToBuffer( Int64 line, String& buffer ) const { + Lock l( mLinesMutex ); + buffer = mLines[line].getText(); +} + +std::string TextDocument::getLineTextUtf8( Int64 line ) const { + Lock l( mLinesMutex ); + return mLines[line].getText().toUtf8(); +} + +void TextDocument::getLineTextToBufferUtf8( Int64 line, std::string& buffer ) const { + Lock l( mLinesMutex ); + buffer = mLines[line].getText().toUtf8(); +} + void TextDocument::deleteTo( const size_t& cursorIdx, int offset ) { eeASSERT( cursorIdx < mSelection.size() ); BoolScopedOpOptional op( !mDoingTextInput, mDoingTextInput, true ); @@ -1902,6 +1981,15 @@ void TextDocument::unregisterClient( Client* client ) { setActiveClient( nullptr ); } +std::size_t TextDocument::clientOfTypeCount( TextDocument::Client::Type type ) { + Lock l( mClientsMutex ); + std::size_t count = 0; + for ( const auto& client : mClients ) + if ( client->getTextDocumentClientType() == type ) + count++; + return count; +} + void TextDocument::moveToPreviousChar() { for ( size_t i = 0; i < mSelection.size(); ++i ) { if ( mSelection[i].hasSelection() ) { @@ -2380,7 +2468,7 @@ void TextDocument::moveLinesDown() { TextRange range = getSelectionIndex( i ).normalized(); bool swap = getSelectionIndex( i ).normalized() != getSelection(); appendLineIfLastLine( i, range.end().line() + 1 ); - if ( range.end().line() < (Int64)mLines.size() - 1 ) { + if ( range.end().line() < (Int64)linesCount() - 1 ) { auto text = line( range.end().line() + 1 ); remove( i, { { range.end().line() + 1, 0 }, { range.end().line() + 2, 0 } } ); insert( i, { range.start().line(), 0 }, text.getText() ); @@ -2399,7 +2487,7 @@ bool TextDocument::hasRedo() const { } void TextDocument::appendLineIfLastLine( const size_t& cursorIdx, Int64 line ) { - if ( line >= (Int64)mLines.size() - 1 ) { + if ( line >= (Int64)linesCount() - 1 ) { insert( cursorIdx, endOfDoc(), "\n" ); } } @@ -2433,8 +2521,8 @@ void TextDocument::deleteTo( const size_t& cursorIdx, TextPosition position ) { } void TextDocument::print() const { - for ( size_t i = 0; i < mLines.size(); i++ ) - printf( "%s", mLines[i].toUtf8().c_str() ); + for ( size_t i = 0; i < linesCount(); i++ ) + printf( "%s", getLineTextUtf8( i ).c_str() ); } TextRange TextDocument::sanitizeRange( const TextRange& range ) const { @@ -2455,9 +2543,9 @@ TextRanges TextDocument::sanitizeRange( const TextRanges& ranges ) const { } bool TextDocument::isValidPosition( const TextPosition& position ) const { - return !( position.line() < 0 || position.line() > (Int64)mLines.size() - 1 || + return !( position.line() < 0 || position.line() > (Int64)linesCount() - 1 || position.column() < 0 || - position.column() > (Int64)mLines[position.line()].size() - 1 ); + position.column() > (Int64)getLineLength( position.line() ) - 1 ); } bool TextDocument::isValidRange( const TextRange& range ) const { @@ -2498,9 +2586,10 @@ bool TextDocument::isSaving() const { } TextPosition TextDocument::sanitizePosition( const TextPosition& position ) const { - Int64 line = eeclamp( position.line(), 0UL, mLines.size() ? mLines.size() - 1 : 0 ); + auto lineCount = linesCount(); + Int64 line = eeclamp( position.line(), 0UL, lineCount ? lineCount - 1 : 0 ); Int64 col = - eeclamp( position.column(), 0UL, eemax( 0, mLines[line].size() - 1 ) ); + eeclamp( position.column(), 0UL, eemax( 0, getLineLength( line ) - 1 ) ); return { line, col }; } @@ -2877,7 +2966,7 @@ TextDocument::SearchResult TextDocument::find( const String& text, TextPosition std::vector textLines = type == FindReplaceType::Normal ? text.split( '\n', true, true ) : std::vector{ text }; - if ( textLines.empty() || textLines.size() > mLines.size() ) + if ( textLines.empty() || textLines.size() > linesCount() ) return {}; from = sanitizePosition( from ); @@ -2907,7 +2996,7 @@ TextDocument::SearchResult TextDocument::find( const String& text, TextPosition if ( initPos < from || initPos > to ) return find( text, range.result.end(), caseSensitive, wholeWord, type, restrictRange ); - String currentLine( mLines[initPos.line()].getText() ); + String currentLine( getLineText( initPos.line() ) ); if ( TextPosition( initPos.line(), (Int64)currentLine.size() - 1 ) > to ) return find( text, range.result.end(), caseSensitive, wholeWord, type, restrictRange ); @@ -2930,7 +3019,7 @@ TextDocument::SearchResult TextDocument::find( const String& text, TextPosition if ( initPos < from || initPos > to ) return find( text, range.result.end(), caseSensitive, wholeWord, type, restrictRange ); - const String& lastLine = mLines[initPos.line()].getText(); + const auto lastLine = getLineText( initPos.line() ); const String& curSearch = textLines[textLines.size() - 1]; if ( TextPosition( initPos.line(), (Int64)curSearch.size() - 1 ) > to ) @@ -2944,7 +3033,7 @@ TextDocument::SearchResult TextDocument::find( const String& text, TextPosition String::startsWith( String( lastLine ).toLower(), String( curSearch ).toLower() ) ) ) { TextRange foundRange( range.result.start(), TextPosition( initPos.line(), curSearch.size() ) ); - if ( foundRange.end().column() == (Int64)mLines[foundRange.end().line()].size() ) + if ( foundRange.end().column() == (Int64)getLineLength( foundRange.end().line() ) ) foundRange.setEnd( positionOffset( foundRange.end(), 1 ) ); range.result = foundRange; return range; @@ -2960,7 +3049,7 @@ TextDocument::SearchResult TextDocument::findLast( const String& text, TextPosit FindReplaceType type, TextRange restrictRange ) { std::vector textLines = text.split( '\n', true, true ); - if ( textLines.empty() || textLines.size() > mLines.size() ) + if ( textLines.empty() || textLines.size() > linesCount() ) return {}; from = sanitizePosition( from ); @@ -3013,7 +3102,7 @@ TextDocument::SearchResult TextDocument::findLast( const String& text, TextPosit if ( initPos < from || initPos > to ) return findLast( text, range.result.end(), caseSensitive, wholeWord, type, restrictRange ); - const String& lastLine = mLines[initPos.line()].getText(); + const auto lastLine = getLineText( initPos.line() ); const String& curSearch = textLines[textLines.size() - 1]; if ( TextPosition( initPos.line(), (Int64)curSearch.size() - 1 ) > to ) @@ -3114,7 +3203,7 @@ int TextDocument::replaceAll( const String& text, const String& replace, const b if ( found.isValid() ) { if ( numCaptures && numCaptures <= found.captures.size() ) { String finalReplace( replace ); - std::string l( line( found.captures[0].start().line() ).toUtf8() ); + std::string l( getLineTextUtf8( found.captures[0].start().line() ) ); for ( size_t i = 0; i < numCaptures; i++ ) { String matchSubStr( replace.substr( matchList[i].start, matchList[i].end - matchList[i].start ) ); // $1 $2 ... @@ -3198,7 +3287,7 @@ TextPosition TextDocument::replace( String search, const String& replace, TextPo if ( found.isValid() ) { if ( numCaptures && numCaptures == found.captures.size() ) { String finalReplace( replace ); - std::string l( line( found.captures[0].start().line() ).toUtf8() ); + std::string l( getLineTextUtf8( found.captures[0].start().line() ) ); for ( size_t i = 0; i < numCaptures; i++ ) { String matchSubStr( replace.substr( matchList[i].start, matchList[i].end - matchList[i].start ) ); // $1 $2 ... @@ -3542,7 +3631,7 @@ void TextDocument::toggleLineComments() { std::size_t startSpaces = std::string::npos; for ( Int64 i = selection.start().line(); i <= selection.end().line(); i++ ) { - const String& text = mLines[i].getText(); + const auto text = getLineText( i ); auto firstNonSpace = text.find_first_not_of( " \t\n" ); if ( firstNonSpace != std::string::npos && text[firstNonSpace] != '\n' ) { startSpaces = eemin( startSpaces, firstNonSpace ); @@ -3562,7 +3651,7 @@ void TextDocument::toggleLineComments() { bool swap = prevStart != range.start(); for ( Int64 lineIdx = selection.start().line(); lineIdx <= selection.end().line(); lineIdx++ ) { - const String& text = mLines[lineIdx].getText(); + const auto text = getLineText( lineIdx ); auto pos = text.find( commentText, startSpaces ); if ( pos != std::string::npos ) { remove( cursorIdx, { { lineIdx, static_cast( pos ) }, @@ -3725,10 +3814,10 @@ void TextDocument::escape() { resetCursor(); - size_t linesCount = mLines.size(); - for ( size_t i = 0; i < linesCount; i++ ) { + size_t lineCount = linesCount(); + for ( size_t i = 0; i < lineCount; i++ ) { Int64 lineIdx = static_cast( i ); - String newText( String::escape( mLines[i].getTextWithoutNewLine() ) ); + String newText( String::escape( getLineTextWithoutNewLine( i ) ) ); setSelection( 0, { { lineIdx, 0 }, { lineIdx, (Int64)line( i ).size() - 1 } } ); deleteTo( 0, 0 ); setSelection( 0, insert( 0, getSelectionIndex( 0 ).start(), newText ) ); @@ -3754,10 +3843,10 @@ void TextDocument::unescape() { resetCursor(); - size_t linesCount = mLines.size(); - for ( size_t i = 0; i < linesCount; i++ ) { + size_t lineCount = linesCount(); + for ( size_t i = 0; i < lineCount; i++ ) { Int64 lineIdx = static_cast( i ); - String newText( String::unescape( mLines[i].getTextWithoutNewLine() ) ); + String newText( String::unescape( getLineTextWithoutNewLine( i ) ) ); setSelection( 0, { { lineIdx, 0 }, { lineIdx, (Int64)line( i ).size() - 1 } } ); deleteTo( 0, 0 ); setSelection( 0, insert( 0, getSelectionIndex( 0 ).start(), newText ) ); diff --git a/src/eepp/ui/tools/uicodeeditorsplitter.cpp b/src/eepp/ui/tools/uicodeeditorsplitter.cpp index 31cab5bed..e47337f54 100644 --- a/src/eepp/ui/tools/uicodeeditorsplitter.cpp +++ b/src/eepp/ui/tools/uicodeeditorsplitter.cpp @@ -1782,4 +1782,13 @@ void UICodeEditorSplitter::updateTabWidgetVisualSplitting() { } } +std::shared_ptr UICodeEditorSplitter::getTextDocumentRef( TextDocument* doc ) { + std::shared_ptr ret; + forEachEditor( [doc, &ret]( UICodeEditor* editor ) { + if ( editor->getDocumentRef().get() == doc ) + ret = editor->getDocumentRef(); + } ); + return ret; +} + }}} // namespace EE::UI::Tools diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index fcbcc8468..9d3863c74 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -204,13 +204,18 @@ UICodeEditor::~UICodeEditor() { Sys::sleep( Milliseconds( 0.1 ) ); mDocView.setDocument( nullptr ); - if ( mDoc.use_count() == 1 ) { + std::size_t clientsOfTypeCount = mDoc->clientOfTypeCount( TextDocument::Client::Type::Core ); + long useCount = mDoc.use_count(); + + if ( clientsOfTypeCount == 1 || useCount == 1 ) { getUISceneNode()->removeActionsByTag( mTagFoldRange ); DocEvent event( this, mDoc.get(), Event::OnDocumentClosed ); sendEvent( &event ); mDoc->unregisterClient( this ); - mDoc.reset(); + + if ( useCount == 1 ) + mDoc.reset(); } else { mDoc->unregisterClient( this ); } @@ -973,9 +978,12 @@ TextDocument& UICodeEditor::getDocument() { void UICodeEditor::setDocument( std::shared_ptr doc ) { if ( mDoc.get() != doc.get() ) { URI oldDocURI = mDoc->getURI(); + std::size_t clientsOfTypeCount = + mDoc->clientOfTypeCount( TextDocument::Client::Type::Core ); + long useCount = mDoc.use_count(); mDoc->unregisterClient( this ); mDocView.setDocument( nullptr ); - if ( mDoc.use_count() == 1 ) + if ( clientsOfTypeCount == 1 || useCount == 1 ) onDocumentClosed( mDoc.get() ); mDoc = doc; mDoc->registerClient( this ); @@ -1187,8 +1195,6 @@ void UICodeEditor::setCodeEditorFlags( std::string flags, bool enable ) { mFoldsAlwaysVisible = enable; } else if ( "foldsvisible" == flag ) { mFoldsVisible = enable; - } else if ( "flashcursor" == flag ) { - mEnableFlashCursor = enable; } else if ( "defaultstyle" == flag ) { mUseDefaultStyle = enable; } else if ( !enable && "editorfeatures" == flag ) { @@ -1242,8 +1248,6 @@ std::string UICodeEditor::getCodeEditorFlags( bool enabled ) const { flags += "foldsalwaysvisible|"; if ( mFoldsVisible == enabled ) flags += "foldsvisible|"; - if ( mEnableFlashCursor == enabled ) - flags += "flashcursor|"; if ( mUseDefaultStyle == enabled ) flags += "defaultstyle|"; @@ -1956,9 +1960,9 @@ Float UICodeEditor::getLineWidth( const Int64& docLine ) { Float width = 0; if ( !isMonospaceLine ) { - auto& line = mDoc->line( docLine ); auto found = mLinesWidthCache.find( docLine ); - if ( found != mLinesWidthCache.end() && line.getHash() == found->second.first ) + if ( found != mLinesWidthCache.end() && + mDoc->getLineHash( docLine ) == found->second.first ) return found->second.second; } @@ -2211,7 +2215,7 @@ void UICodeEditor::onDocumentLineMove( const Int64& fromLine, const Int64& toLin auto lineIt = mLinesWidthCache.find( i - numLines ); if ( lineIt != mLinesWidthCache.end() ) { const auto& line = lineIt->second; - if ( line.first == mDoc->line( i ).getHash() ) { + if ( line.first == mDoc->getLineHash( i ) ) { auto nl = mLinesWidthCache.extract( lineIt ); nl.key() = i; mLinesWidthCache.insert( std::move( nl ) ); @@ -2222,7 +2226,7 @@ void UICodeEditor::onDocumentLineMove( const Int64& fromLine, const Int64& toLin for ( Int64 i = fromLine; i < linesCount; i++ ) { auto lineIt = mLinesWidthCache.find( i - numLines ); if ( lineIt != mLinesWidthCache.end() && - lineIt->second.first == mDoc->line( i ).getHash() ) { + lineIt->second.first == mDoc->getLineHash( i ) ) { auto nl = mLinesWidthCache.extract( lineIt ); nl.key() = i; mLinesWidthCache[i] = std::move( nl.mapped() ); @@ -2906,7 +2910,7 @@ std::string UICodeEditor::getPropertyString( const PropertyDefinition* propertyD case PropertyId::LineWrapType: return DocumentView::fromLineWrapType( getLineWrapType() ); case PropertyId::Text: - return mDoc->line( 0 ).toUtf8(); + return mDoc->getLineTextUtf8( 0 ); default: return UIWidget::getPropertyString( propertyDef, propertyIndex ); } @@ -3030,12 +3034,13 @@ void UICodeEditor::checkMatchingBrackets() { static const std::vector close{ '}', ')', ']' }; mMatchingBrackets = TextRange(); TextPosition pos = mDoc->sanitizePosition( mDoc->getSelection().start() ); - TextDocumentLine& line = mDoc->line( pos.line() ); - auto isOpenIt = std::find( open.begin(), open.end(), line[pos.column()] ); - auto isCloseIt = std::find( close.begin(), close.end(), line[pos.column()] ); + auto searchChar = mDoc->getChar( pos ); + auto isOpenIt = std::find( open.begin(), open.end(), searchChar ); + auto isCloseIt = std::find( close.begin(), close.end(), searchChar ); if ( ( isOpenIt == open.end() && isCloseIt == close.end() ) && pos.column() > 0 ) { - isOpenIt = std::find( open.begin(), open.end(), line[pos.column() - 1] ); - isCloseIt = std::find( close.begin(), close.end(), line[pos.column() - 1] ); + searchChar = mDoc->getChar( TextPosition( pos.line(), pos.column() - 1 ) ); + isOpenIt = std::find( open.begin(), open.end(), searchChar ); + isCloseIt = std::find( close.begin(), close.end(), searchChar ); if ( isOpenIt != open.end() ) { pos.setColumn( pos.column() - 1 ); } else if ( isCloseIt != close.end() ) { @@ -3786,7 +3791,9 @@ void UICodeEditor::drawLineText( const Int64& line, Vector2f position, const Flo const Float& lineHeight, const DocumentViewLineRange& visibleLineRange ) { Vector2f originalPosition( position ); - const auto& tokens = mDoc->getHighlighter()->getLine( line ); + // const auto& tokens = mDoc->getHighlighter()->getLine( line ); + mDoc->getHighlighter()->copyLineToBuffer( line, mTokens ); + const auto& tokens = mTokens; const auto& docLine = mDoc->line( line ); const String& strLine = docLine.getText(); Primitives primitives; @@ -4827,7 +4834,9 @@ void UICodeEditor::drawMinimap( const Vector2f& start, const DocumentLineRange&, if ( mHighlightWord.isEmpty() && !selectionString.empty() ) drawWordMatch( selectionString, line ); - const auto& tokens = mDoc->getHighlighter()->getLine( line, false ); + // const auto& tokens = mDoc->getHighlighter()->getLine( line, false ); + mDoc->getHighlighter()->copyLineToBuffer( line, mTokens, false ); + const auto& tokens = mTokens; const auto& text = mDoc->line( line ).getText(); Int64 pos = 0; bool wrappedLine = mDocView.isWrappedLine( line ); diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp index 835726817..e4e7f71f7 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp @@ -149,7 +149,7 @@ void AutoCompletePlugin::load( PluginManager* pluginManager ) { Log::error( "AutoCompletePlugin::load - Error parsing config from path %s, error: %s, config " "file content:\n%s", - path.c_str(), e.what(), data.c_str() ); + path, e.what(), data ); // Recreate it j = json::parse( "{\n \"config\":{},\n \"keybindings\":{},\n}\n", nullptr, true, true ); } @@ -610,7 +610,7 @@ void AutoCompletePlugin::updateDocCache( TextDocument* doc ) { lang.insert( lang.end(), d.second.symbols.begin(), d.second.symbols.end() ); } } - Log::debug( "Dictionary for %s updated in: %.2fms", doc->getFilename().c_str(), + Log::debug( "Dictionary for %s updated in: %.2fms", doc->getFilename(), clock.getElapsedTime().asMilliseconds() ); } @@ -624,7 +624,7 @@ void AutoCompletePlugin::updateLangCache( const std::string& langName ) { if ( d.first->getSyntaxDefinition().getLanguageName() == langName ) lang.insert( lang.end(), d.second.symbols.begin(), d.second.symbols.end() ); } - Log::debug( "Lang dictionary for %s updated in: %.2fms", langName.c_str(), + Log::debug( "Lang dictionary for %s updated in: %.2fms", langName, clock.getElapsedTime().asMilliseconds() ); } @@ -882,7 +882,7 @@ AutoCompletePlugin::processSignatureHelp( const LSPSignatureHelp& signatureHelp while ( 0 != doc.replaceAll( " ", " " ) ) ; - nsig.label = doc.line( 0 ).getTextWithoutNewLine(); + nsig.label = doc.getLineTextWithoutNewLine( 0 ); for ( const auto& param : parameters ) { auto res = doc.find( param ); @@ -1394,17 +1394,22 @@ void AutoCompletePlugin::resetSignatureHelp() { AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocument* doc ) { static constexpr auto MAX_LINE_LENGTH = EE_1KB * 10; - LuaPattern pattern( mSymbolPattern ); AutoCompletePlugin::SymbolsList symbols; + std::shared_ptr docRef = + getPluginContext()->getSplitter()->getTextDocumentRef( doc ); // acquire a doc + if ( docRef == nullptr ) + return symbols; + LuaPattern pattern( mSymbolPattern ); if ( doc->linesCount() == 0 || doc->isHuge() || mShuttingDown ) return symbols; std::string current( getPartialSymbol( doc ) ); TextPosition end = doc->getSelection().end(); - for ( Int64 i = 0; i < static_cast( doc->linesCount() ); i++ ) { - const auto& line = doc->line( i ); - if ( line.size() > MAX_LINE_LENGTH ) + auto lineCount = doc->linesCount(); + std::string string; + for ( Int64 i = 0; i < static_cast( lineCount ); i++ ) { + if ( doc->getLineLength( i ) > MAX_LINE_LENGTH ) continue; - auto string = line.toUtf8(); + doc->getLineTextToBufferUtf8( i, string ); for ( auto& match : pattern.gmatch( string ) ) { std::string matchStr( match[0] ); // Ignore the symbol if is actually the current symbol being written @@ -1416,7 +1421,7 @@ AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocu } ) ) symbols.push_back( std::move( matchStr ) ); } - if ( mShuttingDown ) + if ( mShuttingDown || mDocs.find( doc ) == mDocs.end() ) break; } return symbols; diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp index 9d17d9c4a..9b656ed00 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.hpp @@ -10,7 +10,6 @@ #include #include #include -#include #include using namespace EE; using namespace EE::System; @@ -125,7 +124,7 @@ class AutoCompletePlugin : public Plugin { Mutex mDocMutex; Time mUpdateFreq{ Seconds( 5 ) }; std::unordered_map> mEditors; - std::set mDocs; + std::unordered_set mDocs; std::unordered_map mEditorDocs; bool mDirty{ false }; bool mReplacing{ false }; diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp index 0500de21a..ee0c14302 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.hpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.hpp @@ -152,6 +152,7 @@ class DebuggerPlugin : public PluginBase { virtual void onDocumentDirtyOnFileSystem( TextDocument* ) {} virtual void onDocumentMoved( TextDocument* ) {} virtual void onDocumentReset( TextDocument* ) {} + Client::Type getTextDocumentClientType() { return TextDocument::Client::Auxiliary; } virtual void onDocumentLineMove( const Int64& fromLine, const Int64& toLine, const Int64& numLines ) { diff --git a/src/tools/ecode/plugins/linter/linterplugin.cpp b/src/tools/ecode/plugins/linter/linterplugin.cpp index bf934fba7..8b0b518d7 100644 --- a/src/tools/ecode/plugins/linter/linterplugin.cpp +++ b/src/tools/ecode/plugins/linter/linterplugin.cpp @@ -544,7 +544,7 @@ PluginRequestHandle LinterPlugin::processMessage( const PluginMessage& notificat match.range = diag.range; match.text = diag.message; match.type = getLinterTypeFromSeverity( diag.severity ); - match.lineCache = doc->line( match.range.start().line() ).getHash(); + match.lineCache = doc->getLineHash( match.range.start().line() ); match.origin = MatchOrigin::Diagnostics; match.diagnostic = std::move( diag ); matches[match.range.start().line()].emplace_back( std::move( match ) ); @@ -916,7 +916,7 @@ void LinterPlugin::runLinter( std::shared_ptr doc, const Linter& l linterMatch.range.setStart( { line > 0 ? line - 1 : 0, col > 0 ? col - 1 : 0 } ); - const String& text = doc->line( linterMatch.range.start().line() ).getText(); + String text = doc->getLineText( linterMatch.range.start().line() ); size_t minCol = text.find_first_not_of( " \t\f\v\n\r", linterMatch.range.start().column() ); if ( minCol == String::InvalidPos ) @@ -937,7 +937,7 @@ void LinterPlugin::runLinter( std::shared_ptr doc, const Linter& l linterMatch.range.setEnd( endPos ); linterMatch.range = linterMatch.range.normalized(); - linterMatch.lineCache = doc->line( linterMatch.range.start().line() ).getHash(); + linterMatch.lineCache = doc->getLineHash( linterMatch.range.start().line() ); bool skip = false; if ( linter.deduplicate && matches.find( line - 1 ) != matches.end() ) { @@ -1033,7 +1033,7 @@ void LinterPlugin::drawAfterLineText( UICodeEditor* editor, const Int64& index, if ( isDuplicate ) continue; - if ( match.lineCache != doc->line( index ).getHash() ) + if ( match.lineCache != doc->getLineHash( index ) ) return; Text line( "", editor->getFont(), editor->getFontSize() ); @@ -1133,7 +1133,7 @@ void LinterPlugin::minimapDrawBefore( UICodeEditor* editor, const DocumentLineRa for ( const auto& matches : matchIt->second ) { for ( const auto& match : matches.second ) { if ( match.range.intersectsLineRange( docLineRange ) ) { - if ( match.lineCache != doc->line( match.range.start().line() ).getHash() ) + if ( match.lineCache != doc->getLineHash( match.range.start().line() ) ) return; Color col( editor->getColorScheme() .getEditorSyntaxStyle( getMatchString( match.type ) ) @@ -1449,7 +1449,7 @@ void LinterPlugin::registerNativeLinters() { doc->previousWordBoundary( match.range.start(), false ) }; match.text = result.description(); match.type = getLinterTypeFromSeverity( LSPDiagnosticSeverity::Error ); - match.lineCache = doc->line( match.range.start().line() ).getHash(); + match.lineCache = doc->getLineHash( match.range.start().line() ); match.origin = MatchOrigin::Linter; matches[line] = { match }; } diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 08239bd25..70477a9a6 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1655,6 +1655,15 @@ bool LSPClientServer::hasDocument( const URI& uri ) const { return false; } +bool LSPClientServer::hasDocumentClient( LSPDocumentClient* cl ) const { + Lock l( mClientsMutex ); + for ( const auto& client : mClients ) { + if ( client.second && client.second.get() == cl ) + return true; + } + return false; +} + bool LSPClientServer::hasDocuments() const { return !mDocs.empty(); } diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index 5ed0b7cda..96eabe619 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -141,6 +141,8 @@ class LSPClientServer { bool hasDocument( const URI& uri ) const; + bool hasDocumentClient( LSPDocumentClient* client ) const; + bool hasDocuments() const; LSPRequestHandle didChangeWorkspaceFolders( const std::vector& added, @@ -271,7 +273,7 @@ class LSPClientServer { std::unordered_map> mClients; using HandlersMap = std::map>; HandlersMap mHandlers; - Mutex mClientsMutex; + mutable Mutex mClientsMutex; Mutex mHandlersMutex; bool mReady{ false }; bool mEnded{ false }; diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp index cc2b9693d..5e266d3c6 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp @@ -394,7 +394,7 @@ void LSPClientServerManager::sendSymbolReferenceBroadcast( const std::vectorgetSplitter()->findDocFromURI( r.uri ); if ( curDoc ) { - ProjectSearch::ResultData::Result rs( curDoc->line( r.range.start().line() ).getText(), + ProjectSearch::ResultData::Result rs( curDoc->getLineText( r.range.start().line() ), r.range, -1, -1 ); rd.results.emplace_back( std::move( rs ) ); @@ -406,14 +406,14 @@ void LSPClientServerManager::sendSymbolReferenceBroadcast( const std::vectorloadFromFile( fspath ) ) continue; - lineText = doc->line( r.range.start().line() ).getText(); + lineText = doc->getLineText( r.range.start().line() ); tmpDocs.insert( { std::move( fspath ), std::move( doc ) } ); } else { - lineText = foundDoc->second->line( r.range.start().line() ).getText(); + lineText = foundDoc->second->getLineText( r.range.start().line() ); } - ProjectSearch::ResultData::Result rs( lineText, r.range, -1, -1 ); + ProjectSearch::ResultData::Result rs( std::move( lineText ), r.range, -1, -1 ); rd.results.emplace_back( std::move( rs ) ); } diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp index 978239c3a..9a7aaebf2 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ LSPDocumentClient::~LSPDocumentClient() { if ( nullptr != sceneNode && 0 != mTagSemanticTokens ) sceneNode->removeActionsByTag( mTagSemanticTokens ); mShutdown = true; - while ( mRunningSemanticTokens ) + while ( mRunningSemanticTokens || mProcessingSemanticTokensResponse ) Sys::sleep( Milliseconds( 0.1f ) ); } @@ -174,8 +175,9 @@ void LSPDocumentClient::requestSemanticHighlighting( bool reqFull ) { Uint64 docModId = mDoc->getModificationId(); mServer->documentSemanticTokensFull( mDoc->getURI(), delta, reqId, range, - [docClient, uri, server, docModId]( const auto&, LSPSemanticTokensDelta&& deltas ) { - if ( server->hasDocument( uri ) ) { + [docClient, uri, server, docModId, this]( const auto&, LSPSemanticTokensDelta&& deltas ) { + BoolScopedOp op( mProcessingSemanticTokensResponse, true ); + if ( server->hasDocumentClient( docClient ) && server->hasDocument( uri ) ) { docClient->mWaitingSemanticTokensResponse = false; docClient->processTokens( std::move( deltas ), docModId ); } @@ -285,7 +287,7 @@ void LSPDocumentClient::processTokens( LSPSemanticTokensDelta&& tokens, if ( docModificationId != mDoc->getModificationId() ) return requestSemanticHighlightingDelayed(); - mRunningSemanticTokens = true; + BoolScopedOp op( mRunningSemanticTokens, true ); if ( !tokens.resultId.empty() ) mSemanticeResultId = tokens.resultId; @@ -297,6 +299,9 @@ void LSPDocumentClient::processTokens( LSPSemanticTokensDelta&& tokens, curTokens.begin() + edit.start + edit.deleteCount ); } curTokens.insert( curTokens.begin() + edit.start, edit.data.begin(), edit.data.end() ); + + if ( mShutdown ) + return; } if ( !tokens.data.empty() ) { @@ -304,8 +309,6 @@ void LSPDocumentClient::processTokens( LSPSemanticTokensDelta&& tokens, } highlight(); - - mRunningSemanticTokens = false; } void LSPDocumentClient::highlight() { @@ -351,7 +354,7 @@ void LSPDocumentClient::highlight() { } else { line->tokens.push_back( { SyntaxStyleTypes::Normal, start, len } ); } - line->hash = mDoc->line( currentLine ).getHash(); + line->hash = mDoc->getLineHash( currentLine ); line->updateSignature(); auto curSignature = mDoc->getHighlighter()->getTokenizedLineSignature( lastLine ); diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp index af796c7d3..a9c216a00 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp @@ -39,6 +39,7 @@ class LSPDocumentClient : public TextDocument::Client, public FoldRangeProvider virtual void onDocumentMoved( TextDocument* ); virtual void onDocumentReloaded( TextDocument* ); virtual void onDocumentReset( TextDocument* ); + Client::Type getTextDocumentClientType() { return TextDocument::Client::Auxiliary; } void notifyOpen(); @@ -82,6 +83,7 @@ class LSPDocumentClient : public TextDocument::Client, public FoldRangeProvider std::vector mCodeLens; bool mRunningSemanticTokens{ false }; bool mWaitingSemanticTokensResponse{ false }; + bool mProcessingSemanticTokensResponse{ false }; bool mShutdown{ false }; bool mFirstHighlight{ true }; diff --git a/src/tools/ecode/plugins/xmltools/xmltoolsplugin.hpp b/src/tools/ecode/plugins/xmltools/xmltoolsplugin.hpp index 9d8c346e6..d1ebc0736 100644 --- a/src/tools/ecode/plugins/xmltools/xmltoolsplugin.hpp +++ b/src/tools/ecode/plugins/xmltools/xmltoolsplugin.hpp @@ -84,6 +84,7 @@ class XMLToolsPlugin : public PluginBase { virtual void onDocumentDirtyOnFileSystem( TextDocument* ) {} virtual void onDocumentMoved( TextDocument* ) {}; virtual void onDocumentReset( TextDocument* ) { mSelections.clear(); } + Client::Type getTextDocumentClientType() { return TextDocument::Client::Auxiliary; } protected: TextDocument* mDoc{ nullptr }; diff --git a/src/tools/ecode/projectsearch.cpp b/src/tools/ecode/projectsearch.cpp index a390b8668..025393008 100644 --- a/src/tools/ecode/projectsearch.cpp +++ b/src/tools/ecode/projectsearch.cpp @@ -74,7 +74,7 @@ searchInFileHorspool( const std::string& file, const std::string& text, const bo totNl += countNewLines( fileText, lSearchRes, searchRes ); String str( textLine( caseSensitive ? fileText : fileTextOriginal, searchRes, relCol ) ); - res.push_back( { str, + res.push_back( { std::move( str ), { { (Int64)totNl, (Int64)relCol }, { (Int64)totNl, (Int64)( relCol + String::utf8Length( text ) ) } }, searchRes, @@ -168,11 +168,11 @@ ProjectSearch::fileResFromDoc( const std::string& string, bool caseSensitive, bo for ( const auto& r : res ) { ProjectSearch::ResultData::Result f; f.position = r.result; - const auto& line = doc->line( r.result.start().line() ); - if ( line.size() > EE_1KB ) - f.line = line.substr( 0, EE_1KB ); + auto lineLen = doc->getLineLength( r.result.start().line() ); + if ( lineLen > EE_1KB ) + f.line = doc->getLineTextSubStr( r.result.start().line(), 0, EE_1KB ); else - f.line = line.getTextWithoutNewLine(); + f.line = doc->getLineTextWithoutNewLine( r.result.start().line() ); f.start = r.result.start().column(); f.end = r.result.end().column(); std::vector captures; diff --git a/src/tools/ecode/projectsearch.hpp b/src/tools/ecode/projectsearch.hpp index cce6a14ee..eb962c903 100644 --- a/src/tools/ecode/projectsearch.hpp +++ b/src/tools/ecode/projectsearch.hpp @@ -39,8 +39,8 @@ class ProjectSearch { struct ResultData { struct Result { - Result( const String& line, const TextRange& pos, Int64 s, Int64 e ) : - line( line ), position( pos ), start( s ), end( e ) {} + Result( String&& line, const TextRange& pos, Int64 s, Int64 e ) : + line( std::move( line ) ), position( pos ), start( s ), end( e ) {} Result() {} String line; TextRange position;