mirror of
https://github.com/SpartanJ/eepp.git
synced 2026-09-29 09:44:18 +03:00
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.
This commit is contained in:
@@ -34,6 +34,9 @@ class EE_API SyntaxHighlighter {
|
||||
const std::vector<SyntaxTokenPosition>& getLine( const size_t& index,
|
||||
bool mustTokenize = true );
|
||||
|
||||
void copyLineToBuffer( const size_t& index, std::vector<SyntaxTokenPosition>& buffer,
|
||||
bool mustTokenize = true );
|
||||
|
||||
Int64 getFirstInvalidLine() const;
|
||||
|
||||
Int64 getMaxWantedLine() const;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <eepp/ui/doc/textrange.hpp>
|
||||
#include <eepp/ui/doc/textundostack.hpp>
|
||||
#include <functional>
|
||||
#include <shared_mutex>
|
||||
#include <vector>
|
||||
|
||||
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<Client*> mClients;
|
||||
Mutex mClientsMutex;
|
||||
mutable Mutex mLinesMutex;
|
||||
mutable std::shared_ptr<std::shared_mutex> mDocumentMutex;
|
||||
TextFormat::Encoding mEncoding{ TextFormat::Encoding::UTF8 };
|
||||
TextFormat::LineEnding mLineEnding{ TextFormat::LineEnding::LF };
|
||||
std::atomic<bool> mLoading{ false };
|
||||
|
||||
@@ -2,74 +2,115 @@
|
||||
#define EE_UI_DOC_TEXTDOCUMENTLINE_HPP
|
||||
|
||||
#include <eepp/core/string.hpp>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
|
||||
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<std::shared_mutex> docMutex ) :
|
||||
mText( text ), mDocMutex( docMutex ) {
|
||||
updateState();
|
||||
}
|
||||
|
||||
~TextDocumentLine() {
|
||||
if ( mDocMutex ) {
|
||||
// Wait for any readers to finish before destruction
|
||||
std::unique_lock<std::shared_mutex> lock( *mDocMutex );
|
||||
}
|
||||
}
|
||||
|
||||
void setText( String&& text ) {
|
||||
mText = std::move( text );
|
||||
updateState();
|
||||
if ( mDocMutex ) {
|
||||
std::unique_lock<std::shared_mutex> 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<std::shared_mutex> lock( *mDocMutex );
|
||||
return mText;
|
||||
}
|
||||
return mText;
|
||||
}
|
||||
|
||||
const String& getText() const { return mText; }
|
||||
String getTextWithoutNewLine() const {
|
||||
if ( mDocMutex ) {
|
||||
std::shared_lock<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> 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<std::shared_mutex> lock( *mDocMutex );
|
||||
return mText.empty();
|
||||
}
|
||||
return mText.empty();
|
||||
}
|
||||
|
||||
bool empty() const { return mText.empty(); }
|
||||
size_t size() const {
|
||||
if ( mDocMutex ) {
|
||||
std::shared_lock<std::shared_mutex> 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<std::shared_mutex> lock( *mDocMutex );
|
||||
return mFlags;
|
||||
}
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
protected:
|
||||
String mText;
|
||||
String::HashType mHash;
|
||||
Uint32 mFlags{ 0 };
|
||||
std::shared_ptr<std::shared_mutex> mDocMutex;
|
||||
|
||||
void updateState() {
|
||||
mHash = mText.getHash();
|
||||
|
||||
@@ -363,6 +363,8 @@ class EE_API UICodeEditorSplitter {
|
||||
|
||||
UITabWidget* splitTabWidget( SplitDirection, UITabWidget* );
|
||||
|
||||
std::shared_ptr<TextDocument> getTextDocumentRef( TextDocument* doc );
|
||||
|
||||
protected:
|
||||
UISceneNode* mUISceneNode{ nullptr };
|
||||
std::shared_ptr<ThreadPool> mThreadPool;
|
||||
|
||||
@@ -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<SyntaxTokenPosition> mTokens;
|
||||
|
||||
UICodeEditor( const std::string& elementTag, const bool& autoRegisterBaseCommands = true,
|
||||
const bool& autoRegisterBaseKeybindings = true );
|
||||
|
||||
@@ -145,6 +145,8 @@ class EE_API UIConsole : public UIWidget,
|
||||
|
||||
TextDocument& getDoc();
|
||||
|
||||
Client::Type getTextDocumentClientType() { return TextDocument::Client::Core; }
|
||||
|
||||
protected:
|
||||
struct TextCache {
|
||||
Text text;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<VisibleIndex>( 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 };
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ namespace EE { namespace UI { namespace Doc {
|
||||
static std::vector<TextRange> findFoldingRangesBraces( TextDocument* doc ) {
|
||||
Clock c;
|
||||
std::vector<TextRange> regions;
|
||||
if ( doc->linesCount() <= 2 )
|
||||
auto lineCount = doc->linesCount();
|
||||
if ( lineCount <= 2 )
|
||||
return regions;
|
||||
const auto& braces = doc->getSyntaxDefinition().getFoldBraces();
|
||||
std::stack<TextPosition> 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<TextRange> findFoldingRangesIndentation( TextDocument* doc )
|
||||
Clock c;
|
||||
std::stack<TextPosition> indentStack;
|
||||
std::vector<TextRange> 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<TextRange> findFoldingRangesIndentation( TextDocument* doc )
|
||||
auto top = indentStack.top();
|
||||
indentStack.pop();
|
||||
regions.emplace_back( TextPosition( top.line() + 1, 0 ),
|
||||
TextPosition( static_cast<Int64>( doc->linesCount() ) - 1, 0 ) );
|
||||
TextPosition( static_cast<Int64>( lineCount ) - 1, 0 ) );
|
||||
}
|
||||
|
||||
Log::debug( "findFoldingRangesIndentation for \"%s\" took %s", doc->getFilePath(),
|
||||
@@ -102,9 +104,10 @@ static std::vector<TextRange> findFoldingRangesIndentation( TextDocument* doc )
|
||||
static std::vector<TextRange> findFoldingRangesMarkdown( TextDocument* doc ) {
|
||||
Clock c; // For performance logging, consistent with existing functions
|
||||
std::vector<TextRange> 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<TextRange> 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<TextRange> findFoldingRangesMarkdown( TextDocument* doc ) {
|
||||
while ( !sectionStack.empty() ) {
|
||||
auto [headingLine, _] = sectionStack.back();
|
||||
sectionStack.pop_back();
|
||||
if ( headingLine < static_cast<Int64>( doc->linesCount() ) ) {
|
||||
regions.emplace_back( TextPosition( headingLine, 0 ), // Start at heading
|
||||
TextPosition( doc->linesCount() - 1, 0 ) // End at document end
|
||||
if ( headingLine < static_cast<Int64>( 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<Int64>( 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<Int64>( lineCount ) ) {
|
||||
regions.emplace_back( TextPosition( codeBlockStart, 0 ), // Start at opening ```
|
||||
TextPosition( lineCount - 1, 0 ) // End at document end
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<SyntaxTokenPosition>& SyntaxHighlighter::getLine( const size_t
|
||||
static std::vector<SyntaxTokenPosition> 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<SyntaxTokenPosition>& 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<Int64>( mMaxWantedLine, index );
|
||||
return it->second.tokens;
|
||||
@@ -170,7 +171,7 @@ const std::vector<SyntaxTokenPosition>& 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<SyntaxTokenPosition>& SyntaxHighlighter::getLine( const size_t
|
||||
return mLines[index].tokens;
|
||||
}
|
||||
|
||||
void SyntaxHighlighter::copyLineToBuffer( const size_t& index,
|
||||
std::vector<SyntaxTokenPosition>& buffer,
|
||||
bool mustTokenize ) {
|
||||
static std::vector<SyntaxTokenPosition> 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<Int64>( 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<Int64>( 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 {
|
||||
|
||||
@@ -119,6 +119,7 @@ bool TextDocument::isNonWord( String::StringBaseType ch ) const {
|
||||
TextDocument::TextDocument( bool verbose ) :
|
||||
mUUID( true ),
|
||||
mUndoStack( this ),
|
||||
mDocumentMutex( std::make_shared<std::shared_mutex>() ),
|
||||
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<size_t>( 100, mLines.size() );
|
||||
guessTabsFn( 0, start );
|
||||
size_t start = eemin<size_t>( 100, linesCount() );
|
||||
|
||||
if ( !guessTabs && !guessSpaces ) {
|
||||
if ( start == 100 )
|
||||
guessTabsFn( start, eemin<size_t>( start + 100, mLines.size() ) );
|
||||
if ( !guessTabs && !guessSpaces )
|
||||
return;
|
||||
{
|
||||
Lock l( mLinesMutex );
|
||||
guessTabsFn( 0, start );
|
||||
|
||||
if ( !guessTabs && !guessSpaces ) {
|
||||
if ( start == 100 )
|
||||
guessTabsFn( start, eemin<size_t>( 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<Int64>( pos + 1 ) },
|
||||
{ curLine, static_cast<Int64>( mLines[i].getText().size() ) } } );
|
||||
{ curLine, static_cast<Int64>( 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<String> lines = text.split( '\n', true );
|
||||
Int64 linesAdd = eemax<Int64>( 0, static_cast<Int64>( 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<String> lines = text.split( '\n', true );
|
||||
linesAdd = eemax<Int64>( 0, static_cast<Int64>( 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<Int64>( 0, position.column() + (Int64)mLines[position.line()].size() ) );
|
||||
eemax<Int64>( 0, position.column() + (Int64)getLineLength( position.line() ) ) );
|
||||
}
|
||||
while ( position.line() < (Int64)mLines.size() - 1 &&
|
||||
while ( position.line() < (Int64)linesCount() - 1 &&
|
||||
position.column() >
|
||||
(Int64)eemax<Int64>( 0, (Int64)mLines[position.line()].size() - 1 ) ) {
|
||||
position.setColumn( position.column() - mLines[position.line()].size() );
|
||||
(Int64)eemax<Int64>( 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<Int64>( position.line(), 0UL, mLines.size() ? mLines.size() - 1 : 0 );
|
||||
auto lineCount = linesCount();
|
||||
Int64 line = eeclamp<Int64>( position.line(), 0UL, lineCount ? lineCount - 1 : 0 );
|
||||
Int64 col =
|
||||
eeclamp<Int64>( position.column(), 0UL, eemax<Int64>( 0, mLines[line].size() - 1 ) );
|
||||
eeclamp<Int64>( position.column(), 0UL, eemax<Int64>( 0, getLineLength( line ) - 1 ) );
|
||||
return { line, col };
|
||||
}
|
||||
|
||||
@@ -2877,7 +2966,7 @@ TextDocument::SearchResult TextDocument::find( const String& text, TextPosition
|
||||
std::vector<String> textLines = type == FindReplaceType::Normal ? text.split( '\n', true, true )
|
||||
: std::vector<String>{ 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<String> 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<Int64>( 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<Int64>( 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<Int64>( 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 ) );
|
||||
|
||||
@@ -1782,4 +1782,13 @@ void UICodeEditorSplitter::updateTabWidgetVisualSplitting() {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<TextDocument> UICodeEditorSplitter::getTextDocumentRef( TextDocument* doc ) {
|
||||
std::shared_ptr<TextDocument> ret;
|
||||
forEachEditor( [doc, &ret]( UICodeEditor* editor ) {
|
||||
if ( editor->getDocumentRef().get() == doc )
|
||||
ret = editor->getDocumentRef();
|
||||
} );
|
||||
return ret;
|
||||
}
|
||||
|
||||
}}} // namespace EE::UI::Tools
|
||||
|
||||
@@ -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<TextDocument> 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<String::StringBaseType> 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 );
|
||||
|
||||
@@ -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<TextDocument> 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<Int64>( 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<Int64>( 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;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <eepp/system/sys.hpp>
|
||||
#include <eepp/system/threadpool.hpp>
|
||||
#include <eepp/ui/uicodeeditor.hpp>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
using namespace EE;
|
||||
using namespace EE::System;
|
||||
@@ -125,7 +124,7 @@ class AutoCompletePlugin : public Plugin {
|
||||
Mutex mDocMutex;
|
||||
Time mUpdateFreq{ Seconds( 5 ) };
|
||||
std::unordered_map<UICodeEditor*, std::vector<Uint32>> mEditors;
|
||||
std::set<TextDocument*> mDocs;
|
||||
std::unordered_set<TextDocument*> mDocs;
|
||||
std::unordered_map<UICodeEditor*, TextDocument*> mEditorDocs;
|
||||
bool mDirty{ false };
|
||||
bool mReplacing{ false };
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -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<TextDocument> 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<TextDocument> 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 };
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<LSPWorkspaceFolder>& added,
|
||||
@@ -271,7 +273,7 @@ class LSPClientServer {
|
||||
std::unordered_map<TextDocument*, std::unique_ptr<LSPDocumentClient>> mClients;
|
||||
using HandlersMap = std::map<PluginIDType, std::pair<JsonReplyHandler, JsonReplyHandler>>;
|
||||
HandlersMap mHandlers;
|
||||
Mutex mClientsMutex;
|
||||
mutable Mutex mClientsMutex;
|
||||
Mutex mHandlersMutex;
|
||||
bool mReady{ false };
|
||||
bool mEnded{ false };
|
||||
|
||||
@@ -394,7 +394,7 @@ void LSPClientServerManager::sendSymbolReferenceBroadcast( const std::vector<LSP
|
||||
|
||||
auto curDoc = mPluginManager->getSplitter()->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::vector<LSP
|
||||
if ( TextDocument::LoadStatus::Loaded != doc->loadFromFile( 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 ) );
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <eepp/system/filesystem.hpp>
|
||||
#include <eepp/system/iostreamstring.hpp>
|
||||
#include <eepp/system/log.hpp>
|
||||
#include <eepp/system/scopedop.hpp>
|
||||
#include <eepp/ui/doc/syntaxhighlighter.hpp>
|
||||
#include <eepp/window/engine.hpp>
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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<LSPCodeLens> mCodeLens;
|
||||
bool mRunningSemanticTokens{ false };
|
||||
bool mWaitingSemanticTokensResponse{ false };
|
||||
bool mProcessingSemanticTokensResponse{ false };
|
||||
bool mShutdown{ false };
|
||||
bool mFirstHighlight{ true };
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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<std::string> captures;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user