From 0ec2a4dba95c084ae7de590ae9a8e25df2332507 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 12 Aug 2025 00:49:13 -0300 Subject: [PATCH] Try fix invalid memory access in SyntaxDefinitions. Added attach in Chat UI. Some minor improvements in Http pool implementation. --- include/eepp/network/http.hpp | 4 +- include/eepp/system/lock.hpp | 8 + include/eepp/ui/doc/foldrangeservice.hpp | 2 + .../eepp/ui/doc/syntaxdefinitionmanager.hpp | 5 +- include/eepp/ui/doc/textdocument.hpp | 7 +- include/eepp/ui/doc/textdocumentline.hpp | 34 +-- include/eepp/version.hpp | 2 +- include/eepp/window/engine.hpp | 13 +- src/eepp/graphics/fonttruetype.cpp | 2 +- src/eepp/network/http.cpp | 14 +- src/eepp/scene/node.cpp | 2 +- src/eepp/system/lock.cpp | 8 + src/eepp/ui/doc/documentview.cpp | 2 + src/eepp/ui/doc/foldrangeservice.cpp | 7 + src/eepp/ui/doc/syntaxdefinitionmanager.cpp | 214 ++++++++++-------- src/eepp/ui/doc/textdocument.cpp | 48 ++-- src/eepp/ui/uicodeeditor.cpp | 4 +- src/eepp/ui/uitextinput.cpp | 2 +- src/eepp/window/engine.cpp | 17 +- .../src/eterm/terminal/terminaldisplay.cpp | 2 +- src/tools/ecode/ecode.cpp | 7 +- src/tools/ecode/featureshealth.cpp | 4 +- .../plugins/aiassistant/aiassistantplugin.cpp | 2 + .../plugins/aiassistant/aiassistantplugin.hpp | 2 +- .../ecode/plugins/aiassistant/chatui.cpp | 173 +++++++++++++- .../ecode/plugins/aiassistant/chatui.hpp | 15 ++ .../autocomplete/autocompleteplugin.cpp | 6 +- .../ecode/plugins/debugger/debuggerplugin.cpp | 4 +- .../plugins/formatter/formatterplugin.cpp | 6 +- .../ecode/plugins/lsp/lspclientplugin.cpp | 2 +- .../ecode/plugins/lsp/lspclientserver.cpp | 2 +- .../ecode/plugins/plugincontextprovider.hpp | 2 + src/tools/ecode/universallocator.cpp | 6 +- src/tools/ecode/version.hpp | 2 +- 34 files changed, 443 insertions(+), 187 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 260e6ff5b..44b704719 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -597,11 +597,11 @@ class EE_API Http : NonCopyable { * @param host The scheme + hostname + port represented as an URI. * @param proxy The client proxy if any, scheme + hostname + post as URI. */ - Http* get( const URI& host, const URI& proxy = URI() ); + std::shared_ptr get( const URI& host, const URI& proxy = URI() ); protected: Mutex mMutex; - UnorderedMap mHttps; + UnorderedMap> mHttps; static std::string getHostKey( const URI& host, const URI& proxy ); diff --git a/include/eepp/system/lock.hpp b/include/eepp/system/lock.hpp index 77ef25333..8e3443671 100644 --- a/include/eepp/system/lock.hpp +++ b/include/eepp/system/lock.hpp @@ -4,6 +4,10 @@ #include #include +#ifdef EE_DEBUG +#include +#endif + namespace EE { namespace System { class Mutex; @@ -22,6 +26,10 @@ class EE_API Lock : NonCopyable { private: Mutex& mMutex; ///< Mutex to lock / unlock + +#ifdef EE_DEBUG + Clock mClock; +#endif }; class EE_API ConditionalLock : NonCopyable { diff --git a/include/eepp/ui/doc/foldrangeservice.hpp b/include/eepp/ui/doc/foldrangeservice.hpp index d55c4986a..808298e23 100644 --- a/include/eepp/ui/doc/foldrangeservice.hpp +++ b/include/eepp/ui/doc/foldrangeservice.hpp @@ -28,6 +28,8 @@ class EE_API FoldRangeServive { void findRegions(); + void findRegionsNative(); + void clear(); bool empty(); diff --git a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp index 0add2f70e..015b65c77 100644 --- a/include/eepp/ui/doc/syntaxdefinitionmanager.hpp +++ b/include/eepp/ui/doc/syntaxdefinitionmanager.hpp @@ -71,7 +71,7 @@ class EE_API SyntaxDefinitionManager { void loadFromFolder( const std::string& folderPath ); - const std::vector& getDefinitions() const; + const std::vector>& getDefinitions() const; const std::vector& getPreDefinitions() const; @@ -85,9 +85,10 @@ class EE_API SyntaxDefinitionManager { protected: SyntaxDefinitionManager( std::size_t reserveSpaceForLanguages = 12 ); - std::vector mDefinitions; + std::vector> mDefinitions; std::vector mPreDefinitions; std::map mPriorities; + mutable Mutex mMutex; std::optional getLanguageIndex( const std::string& langName ); }; diff --git a/include/eepp/ui/doc/textdocument.hpp b/include/eepp/ui/doc/textdocument.hpp index 7ada3e428..6768978a1 100644 --- a/include/eepp/ui/doc/textdocument.hpp +++ b/include/eepp/ui/doc/textdocument.hpp @@ -21,7 +21,6 @@ #include #include #include -#include #include using namespace EE::System; @@ -136,7 +135,7 @@ class EE_API TextDocument { void reset(); - void resetCursor(); + void resetSelection(); LoadStatus loadFromStream( IOStream& path ); @@ -199,7 +198,7 @@ class EE_API TextDocument { const TextDocumentLine& line( const size_t& index ) const; - size_t linesCount() const; + std::size_t linesCount() const; const TextDocumentLine& getCurrentLine() const; @@ -713,7 +712,7 @@ class EE_API TextDocument { UnorderedSet mClients; Mutex mClientsMutex; mutable Mutex mLinesMutex; - mutable std::shared_ptr mDocumentMutex; + 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 e910af241..fbd9c65ee 100644 --- a/include/eepp/ui/doc/textdocumentline.hpp +++ b/include/eepp/ui/doc/textdocumentline.hpp @@ -2,15 +2,17 @@ #define EE_UI_DOC_TEXTDOCUMENTLINE_HPP #include +#include +#include #include -#include -#include + +using namespace EE::System; namespace EE { namespace UI { namespace Doc { class EE_API TextDocumentLine { public: - TextDocumentLine( const String& text, std::shared_ptr docMutex ) : + TextDocumentLine( const String& text, std::shared_ptr docMutex ) : mText( text ), mDocMutex( docMutex ) { updateState(); } @@ -18,13 +20,13 @@ class EE_API TextDocumentLine { ~TextDocumentLine() { if ( mDocMutex ) { // Wait for any readers to finish before destruction - std::unique_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); } } void setText( String&& text ) { if ( mDocMutex ) { - std::unique_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); mText = std::move( text ); updateState(); } else { @@ -35,7 +37,7 @@ class EE_API TextDocumentLine { const String& getText() const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mText; } return mText; @@ -43,7 +45,7 @@ class EE_API TextDocumentLine { String getTextWithoutNewLine() const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mText.substr( 0, mText.size() - 1 ); } return mText.substr( 0, mText.size() - 1 ); @@ -51,7 +53,7 @@ class EE_API TextDocumentLine { String::StringBaseType operator[]( std::size_t index ) const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mText[index]; } return mText[index]; @@ -59,7 +61,7 @@ class EE_API TextDocumentLine { void append( const String& text ) { if ( mDocMutex ) { - std::unique_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); mText.append( text ); updateState(); } else { @@ -70,7 +72,7 @@ class EE_API TextDocumentLine { String substr( std::size_t pos = 0, std::size_t n = String::StringType::npos ) const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mText.substr( pos, n ); } return mText.substr( pos, n ); @@ -78,7 +80,7 @@ class EE_API TextDocumentLine { bool empty() const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mText.empty(); } return mText.empty(); @@ -86,7 +88,7 @@ class EE_API TextDocumentLine { size_t size() const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mText.size(); } return mText.size(); @@ -94,13 +96,11 @@ class EE_API TextDocumentLine { String::HashType getHash() const { return mHash; } - bool isAscii() const { - return ( mFlags & TextHints::AllAscii ) != 0; - } + bool isAscii() const { return ( mFlags & TextHints::AllAscii ) != 0; } Uint32 getTextHints() const { if ( mDocMutex ) { - std::shared_lock lock( *mDocMutex ); + Lock lock( *mDocMutex ); return mFlags; } return mFlags; @@ -110,7 +110,7 @@ class EE_API TextDocumentLine { String mText; String::HashType mHash; Uint32 mFlags{ 0 }; - std::shared_ptr mDocMutex; + std::shared_ptr mDocMutex; void updateState() { mHash = mText.getHash(); diff --git a/include/eepp/version.hpp b/include/eepp/version.hpp index 7f25ccf7c..4bb962417 100644 --- a/include/eepp/version.hpp +++ b/include/eepp/version.hpp @@ -6,7 +6,7 @@ #define EEPP_MAJOR_VERSION 2 #define EEPP_MINOR_VERSION 8 -#define EEPP_PATCH_LEVEL 7 +#define EEPP_PATCH_LEVEL 8 #define EEPP_CODENAME "Siddhi" /** The compiled version of the library */ diff --git a/include/eepp/window/engine.hpp b/include/eepp/window/engine.hpp index 959b39c77..34b8f2bc3 100644 --- a/include/eepp/window/engine.hpp +++ b/include/eepp/window/engine.hpp @@ -25,7 +25,11 @@ class EE_API Engine { static bool isEngineRunning(); - static bool isRunninMainThread(); + /** @return The id of the thread that was used to initialize the OpenGL Context. */ + static Uint64 getMainThreadId(); + + /** @returns True if the current thread is the main thread. */ + static bool isMainThread(); /** Creates a new window. */ EE::Window::Window* createWindow( WindowSettings Settings, @@ -130,12 +134,6 @@ class EE_API Engine { * for emscripten) */ bool isThreaded(); - /** @return The id of the thread that was used to initialize the OpenGL Context. */ - Uint32 getMainThreadId(); - - /** @returns True if the current thread is the main thread. */ - bool isMainThread() const; - /** @return The instance of platform class that provides some helpers for some platforms */ PlatformHelper* getPlatformHelper(); @@ -154,7 +152,6 @@ class EE_API Engine { std::map mWindows; EE::Window::Window* mWindow; bool mSharedGLContext; - Uint32 mMainThreadId; PlatformHelper* mPlatformHelper; Pack* mZip; DisplayManager* mDisplayManager; diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index 14bb6008d..f88b1df01 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -409,7 +409,7 @@ const Glyph& FontTrueType::getGlyph( Uint32 codePoint, unsigned int characterSiz const Glyph& FontTrueType::getGlyphByIndex( Uint32 index, unsigned int characterSize, bool bold, bool italic, Float outlineThickness, Page& page ) const { - eeASSERT( Engine::isRunninMainThread() ); + eeASSERT( Engine::isMainThread() ); // Get the page corresponding to the character size GlyphTable& glyphs = page.glyphs; diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 622161130..98c7b2270 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -569,7 +569,7 @@ Http::Response Http::request( const URI& uri, Request::Method method, const Time const Http::Request::ProgressCallback& progressCallback, const Http::Request::FieldTable& headers, const std::string& body, const bool& validateCertificate, const URI& proxy ) { - Http* http = sGlobalHttpPool.get( uri, proxy ); + auto http = sGlobalHttpPool.get( uri, proxy ); Request request( uri.getPathAndQuery(), method, body, validateCertificate, validateCertificate, true, true ); request.setProgressCallback( progressCallback ); @@ -601,7 +601,7 @@ void Http::requestAsync( const Http::AsyncResponseCallback& cb, const URI& uri, const Http::Request::ProgressCallback& progressCallback, const Http::Request::FieldTable& headers, const std::string& body, const bool& validateCertificate, const URI& proxy ) { - Http* http = sGlobalHttpPool.get( uri, proxy ); + auto http = sGlobalHttpPool.get( uri, proxy ); Request request( uri.getPathAndQuery(), method, body, validateCertificate, validateCertificate, true, true ); request.setProgressCallback( progressCallback ); @@ -1498,12 +1498,6 @@ Http::Pool::~Pool() { void Http::Pool::clear() { Lock l( mMutex ); - for ( auto& connection : mHttps ) { - Http* con = connection.second; - - eeSAFE_DELETE( con ); - } - mHttps.clear(); } @@ -1522,7 +1516,7 @@ bool Http::Pool::exists( const URI& host, const URI& proxy ) { return mHttps.find( getHostHash( host, proxy ) ) != mHttps.end(); } -Http* Http::Pool::get( const URI& host, const URI& proxy ) { +std::shared_ptr Http::Pool::get( const URI& host, const URI& proxy ) { { Lock l( mMutex ); auto hostInstance = mHttps.find( Http::Pool::getHostHash( host, proxy ) ); @@ -1532,7 +1526,7 @@ Http* Http::Pool::get( const URI& host, const URI& proxy ) { } } - Http* http = eeNew( Http, ( host.getHost(), host.getPort(), host.getScheme() == "https" ) ); + auto http = std::make_shared( host.getHost(), host.getPort(), host.getScheme() == "https" ); Lock l( mMutex ); mHttps[getHostHash( host, proxy )] = http; return http; diff --git a/src/eepp/scene/node.cpp b/src/eepp/scene/node.cpp index cbda44610..dd57e8a00 100644 --- a/src/eepp/scene/node.cpp +++ b/src/eepp/scene/node.cpp @@ -1592,7 +1592,7 @@ void Node::runOnMainThread( Actions::Runnable::RunnableFunc runnable, const Time bool Node::ensureMainThread( Actions::Runnable::RunnableFunc runnable, const Action::UniqueID& uniqueIdentifier ) { - if ( Engine::isRunninMainThread() ) { + if ( Engine::isMainThread() ) { runnable(); return true; } else { diff --git a/src/eepp/system/lock.cpp b/src/eepp/system/lock.cpp index b10e1e42d..5ab6d7021 100644 --- a/src/eepp/system/lock.cpp +++ b/src/eepp/system/lock.cpp @@ -1,5 +1,8 @@ #include #include +#ifdef EE_DEBUG +#include +#endif namespace EE { namespace System { @@ -9,6 +12,11 @@ Lock::Lock( Mutex& mutex ) : mMutex( mutex ) { Lock::~Lock() { mMutex.unlock(); +#ifdef EE_DEBUG + if ( EE::Window::Engine::isMainThread() && mClock.getElapsedTime().asMilliseconds() > 100.f ) { + eeASSERT( false ); + } +#endif } ConditionalLock::ConditionalLock( bool condition, Mutex* mutex ) : diff --git a/src/eepp/ui/doc/documentview.cpp b/src/eepp/ui/doc/documentview.cpp index 5d92d727c..5c0833717 100644 --- a/src/eepp/ui/doc/documentview.cpp +++ b/src/eepp/ui/doc/documentview.cpp @@ -153,6 +153,8 @@ DocumentView::LineWrapInfo DocumentView::computeLineBreaks( const TextDocument& bool keepIndentation, Uint32 tabWidth, Float whiteSpaceWidth, bool tabStops, Float initialXOffset ) { + if ( line >= doc.linesCount() ) + return {}; const auto& docLine = doc.line( line ); const auto& text = docLine.getText(); return computeLineBreaks( text.view().substr( 0, text.size() - 1 ), fontStyle, maxWidth, mode, diff --git a/src/eepp/ui/doc/foldrangeservice.cpp b/src/eepp/ui/doc/foldrangeservice.cpp index 0038adeea..c16beb7f7 100644 --- a/src/eepp/ui/doc/foldrangeservice.cpp +++ b/src/eepp/ui/doc/foldrangeservice.cpp @@ -215,6 +215,13 @@ void FoldRangeServive::findRegions() { return; } + findRegionsNative(); +} + +void FoldRangeServive::findRegionsNative() { + if ( !mEnabled || mDoc == nullptr || !canFold() ) + return; + switch ( mDoc->getSyntaxDefinition().getFoldRangeType() ) { case FoldRangeType::Braces: setFoldingRegions( findFoldingRangesBraces( mDoc ) ); diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 4f710e3cc..548531394 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -179,7 +180,10 @@ SyntaxDefinitionManager::SyntaxDefinitionManager( std::size_t reserveSpaceForLan if ( ms_singleton == nullptr ) ms_singleton = this; - mDefinitions.reserve( reserveSpaceForLanguages ); + { + Lock l( mMutex ); + mDefinitions.reserve( reserveSpaceForLanguages ); + } // Register some languages support. addPlainText(); @@ -196,7 +200,8 @@ SyntaxDefinitionManager::SyntaxDefinitionManager( std::size_t reserveSpaceForLan addXML(); } -const std::vector& SyntaxDefinitionManager::getDefinitions() const { +const std::vector>& +SyntaxDefinitionManager::getDefinitions() const { return mDefinitions; } @@ -337,7 +342,7 @@ bool SyntaxDefinitionManager::save( const std::string& path, } else { json j = json::array(); for ( const auto& d : mDefinitions ) - j.emplace_back( toJson( d ) ); + j.emplace_back( toJson( *d.get() ) ); return FileSystem::fileWrite( path, j.dump( 2 ) ); } return false; @@ -349,9 +354,10 @@ void SyntaxDefinitionManager::setLanguageExtensionsPriority( } std::optional SyntaxDefinitionManager::getLanguageIndex( const std::string& langName ) { + Lock l( mMutex ); size_t pos = 0; for ( const auto& def : mDefinitions ) { - if ( def.getLanguageName() == langName ) { + if ( def->getLanguageName() == langName ) { return pos; } ++pos; @@ -534,10 +540,11 @@ namespace EE { namespace UI { namespace Doc { namespace Language { } SyntaxDefinition& SyntaxDefinitionManager::add( SyntaxDefinition&& syntaxStyle ) { + Lock l( mMutex ); syntaxStyle.mLanguageIndex = mDefinitions.size(); syntaxStyle.compile(); - mDefinitions.emplace_back( std::move( syntaxStyle ) ); - return mDefinitions.back(); + mDefinitions.emplace_back( std::make_shared( std::move( syntaxStyle ) ) ); + return *mDefinitions.back().get(); } SyntaxPreDefinition& @@ -547,7 +554,7 @@ SyntaxDefinitionManager::addPreDefinition( SyntaxPreDefinition&& preDefinition ) } const SyntaxDefinition& SyntaxDefinitionManager::getPlainDefinition() const { - return mDefinitions[0]; + return *mDefinitions[0].get(); } SyntaxDefinition& SyntaxDefinitionManager::getByExtensionRef( const std::string& filePath ) { @@ -556,13 +563,14 @@ SyntaxDefinition& SyntaxDefinitionManager::getByExtensionRef( const std::string& const SyntaxDefinition& SyntaxDefinitionManager::getByLanguageName( const std::string_view& name ) const { + Lock l( mMutex ); for ( const auto& definition : mDefinitions ) { - if ( definition.getLanguageName() == name || - std::find_if( definition.getAlternativeNames().begin(), - definition.getAlternativeNames().end(), + if ( definition->getLanguageName() == name || + std::find_if( definition->getAlternativeNames().begin(), + definition->getAlternativeNames().end(), [name]( const std::string& altName ) { return altName == name; } ) != - definition.getAlternativeNames().end() ) - return definition; + definition->getAlternativeNames().end() ) + return *definition.get(); } for ( const auto& preDefinition : mPreDefinitions ) { @@ -575,24 +583,26 @@ SyntaxDefinitionManager::getByLanguageName( const std::string_view& name ) const } } - return mDefinitions[0]; + return *mDefinitions[0].get(); } const SyntaxDefinition& SyntaxDefinitionManager::getByLanguageIndex( const Uint32& index ) const { + Lock l( mMutex ); eeASSERT( index < mDefinitions.size() ); - return mDefinitions[index]; + return *mDefinitions[index].get(); } const SyntaxDefinition& SyntaxDefinitionManager::getByLanguageNameInsensitive( const std::string_view& name ) const { + Lock l( mMutex ); for ( const auto& definition : mDefinitions ) { - if ( String::iequals( definition.getLanguageName(), name ) || - std::find_if( definition.getAlternativeNames().begin(), - definition.getAlternativeNames().end(), + if ( String::iequals( definition->getLanguageName(), name ) || + std::find_if( definition->getAlternativeNames().begin(), + definition->getAlternativeNames().end(), [name]( const std::string& altName ) { return String::iequals( altName, name ); - } ) != definition.getAlternativeNames().end() ) - return definition; + } ) != definition->getAlternativeNames().end() ) + return *definition.get(); } for ( const auto& preDefinition : mPreDefinitions ) { @@ -605,27 +615,29 @@ SyntaxDefinitionManager::getByLanguageNameInsensitive( const std::string_view& n return preDefinition.load(); } - return mDefinitions[0]; + return *mDefinitions[0].get(); } const SyntaxDefinition& SyntaxDefinitionManager::getByLSPName( const std::string_view& name ) const { + Lock l( mMutex ); for ( const auto& definition : mDefinitions ) { - if ( definition.getLSPName() == name ) - return definition; + if ( definition->getLSPName() == name ) + return *definition.get(); } for ( const auto& preDefinition : mPreDefinitions ) { if ( preDefinition.getLSPName() == name ) return preDefinition.load(); } - return mDefinitions[0]; + return *mDefinitions[0].get(); } std::vector SyntaxDefinitionManager::getLanguageNames() const { + Lock l( mMutex ); std::unordered_set names; for ( auto& definition : mDefinitions ) { - if ( definition.isVisible() ) - names.insert( definition.getLanguageName() ); + if ( definition->isVisible() ) + names.insert( definition->getLanguageName() ); } for ( auto& preDefinition : mPreDefinitions ) names.insert( preDefinition.getLanguageName() ); @@ -638,10 +650,11 @@ std::vector SyntaxDefinitionManager::getLanguageNames() const { } std::vector SyntaxDefinitionManager::getExtensionsPatternsSupported() const { + Lock l( mMutex ); std::unordered_set exts; exts.reserve( mDefinitions.size() ); for ( auto& definition : mDefinitions ) - for ( auto& pattern : definition.getFiles() ) + for ( auto& pattern : definition->getFiles() ) exts.insert( pattern ); for ( auto& preDefinition : mPreDefinitions ) @@ -657,9 +670,10 @@ std::vector SyntaxDefinitionManager::getExtensionsPatternsSupported } const SyntaxDefinition* SyntaxDefinitionManager::getPtrByLSPName( const std::string& name ) const { + Lock l( mMutex ); for ( const auto& definition : mDefinitions ) { - if ( definition.getLSPName() == name ) - return &definition; + if ( definition->getLSPName() == name ) + return definition.get(); } for ( const auto& preDefinition : mPreDefinitions ) { if ( preDefinition.getLSPName() == name ) @@ -1014,12 +1028,16 @@ bool SyntaxDefinitionManager::loadFromStream( IOStream& stream, if ( addedLangs ) addedLangs->push_back( res.getLanguageName() ); res.mLanguageIndex = *pos; - mDefinitions[pos.value()] = std::move( res ); + Lock l( mMutex ); + mDefinitions[pos.value()] = + std::make_shared( std::move( res ) ); } else { if ( addedLangs ) addedLangs->push_back( res.getLanguageName() ); res.mLanguageIndex = mDefinitions.size(); - mDefinitions.emplace_back( std::move( res ) ); + Lock l( mMutex ); + mDefinitions.emplace_back( + std::make_shared( std::move( res ) ) ); } } } @@ -1031,12 +1049,16 @@ bool SyntaxDefinitionManager::loadFromStream( IOStream& stream, if ( addedLangs ) addedLangs->push_back( res.getLanguageName() ); res.mLanguageIndex = *pos; - mDefinitions[pos.value()] = std::move( res ); + Lock l( mMutex ); + mDefinitions[pos.value()] = + std::make_shared( std::move( res ) ); } else { if ( addedLangs ) addedLangs->push_back( res.getLanguageName() ); res.mLanguageIndex = mDefinitions.size(); - mDefinitions.emplace_back( std::move( res ) ); + Lock l( mMutex ); + mDefinitions.emplace_back( + std::make_shared( std::move( res ) ) ); } } } @@ -1104,19 +1126,22 @@ SyntaxDefinitionManager::languagesThatSupportExtension( std::string extension ) if ( extension[0] != '.' ) extension = '.' + extension; - for ( const auto& definition : mDefinitions ) { - for ( const auto& ext : definition.getFiles() ) { - if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || - String::endsWith( ext, "$" ) ) { - LuaPattern words( ext ); - int start, end; - if ( words.find( extension, start, end ) ) { - langs.insert( &definition ); + { + Lock l( mMutex ); + for ( const auto& definition : mDefinitions ) { + for ( const auto& ext : definition->getFiles() ) { + if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || + String::endsWith( ext, "$" ) ) { + LuaPattern words( ext ); + int start, end; + if ( words.find( extension, start, end ) ) { + langs.insert( definition.get() ); + break; + } + } else if ( extension == ext ) { + langs.insert( definition.get() ); break; } - } else if ( extension == ext ) { - langs.insert( &definition ); - break; } } } @@ -1158,23 +1183,26 @@ bool SyntaxDefinitionManager::extensionCanRepresentManyLanguages( std::string ex extension = '.' + extension; std::unordered_set count; - for ( const auto& definition : mDefinitions ) { - for ( const auto& ext : definition.getFiles() ) { - if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || - String::endsWith( ext, "$" ) ) { - LuaPattern words( ext ); - int start, end; - if ( words.find( extension, start, end ) ) { - count.insert( definition.getLanguageName() ); + { + Lock l( mMutex ); + for ( const auto& definition : mDefinitions ) { + for ( const auto& ext : definition->getFiles() ) { + if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || + String::endsWith( ext, "$" ) ) { + LuaPattern words( ext ); + int start, end; + if ( words.find( extension, start, end ) ) { + count.insert( definition->getLanguageName() ); + if ( count.size() > 1 ) + return true; + break; + } + } else if ( extension == ext ) { + count.insert( definition->getLanguageName() ); if ( count.size() > 1 ) return true; break; } - } else if ( extension == ext ) { - count.insert( definition.getLanguageName() ); - if ( count.size() > 1 ) - return true; - break; } } } @@ -1226,37 +1254,40 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri const SyntaxDefinition* def = nullptr; if ( !extension.empty() ) { - for ( const auto& definition : mDefinitions ) { - if ( &definition == &mDefinitions[0] ) // Ignore Plain text - continue; + { + Lock l( mMutex ); + for ( const auto& definition : mDefinitions ) { + if ( &definition == &mDefinitions[0] ) // Ignore Plain text + continue; - for ( const auto& ext : definition.getFiles() ) { - if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || - String::endsWith( ext, "$" ) ) { - LuaPattern words( ext ); - int start, end; - if ( words.find( fileName, start, end ) ) { - if ( hFileAsCPP && definition.getLSPName() == "c" && - ( ext == "%.h$" || ext == "%.h%.in$" ) ) + for ( const auto& ext : definition->getFiles() ) { + if ( String::startsWith( ext, "%." ) || String::startsWith( ext, "^" ) || + String::endsWith( ext, "$" ) ) { + LuaPattern words( ext ); + int start, end; + if ( words.find( fileName, start, end ) ) { + if ( hFileAsCPP && definition->getLSPName() == "c" && + ( ext == "%.h$" || ext == "%.h%.in$" ) ) + return getByLSPName( "cpp" ); + + if ( extHasMultipleLangs && !definition->hasExtensionPriority() ) { + def = definition.get(); + continue; + } + + return *definition.get(); + } + } else if ( extension == ext ) { + if ( hFileAsCPP && definition->getLSPName() == "c" && ext == ".h" ) return getByLSPName( "cpp" ); - if ( extHasMultipleLangs && !definition.hasExtensionPriority() ) { - def = &definition; + if ( extHasMultipleLangs && !definition->hasExtensionPriority() ) { + def = definition.get(); continue; } - return definition; + return *definition.get(); } - } else if ( extension == ext ) { - if ( hFileAsCPP && definition.getLSPName() == "c" && ext == ".h" ) - return getByLSPName( "cpp" ); - - if ( extHasMultipleLangs && !definition.hasExtensionPriority() ) { - def = &definition; - continue; - } - - return definition; } } } @@ -1294,19 +1325,22 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByExtension( const std::stri } } - return def != nullptr ? *def : mDefinitions[0]; + return def != nullptr ? *def : *mDefinitions[0].get(); } const SyntaxDefinition& SyntaxDefinitionManager::getByHeader( const std::string& header, bool /*hFileAsCPP*/ ) const { if ( !header.empty() ) { - for ( auto definition = mDefinitions.rbegin(); definition != mDefinitions.rend(); - ++definition ) { - for ( const auto& hdr : definition->getHeaders() ) { - LuaPattern words( hdr ); - int start, end; - if ( words.find( header, start, end ) ) { - return *definition; + { + Lock l( mMutex ); + for ( auto definition = mDefinitions.rbegin(); definition != mDefinitions.rend(); + ++definition ) { + for ( const auto& hdr : definition->get()->getHeaders() ) { + LuaPattern words( hdr ); + int start, end; + if ( words.find( header, start, end ) ) { + return *definition->get(); + } } } } @@ -1322,14 +1356,14 @@ const SyntaxDefinition& SyntaxDefinitionManager::getByHeader( const std::string& } } } - return mDefinitions[0]; + return *mDefinitions[0].get(); } const SyntaxDefinition& SyntaxDefinitionManager::find( const std::string& filePath, const std::string& header, bool hFileAsCPP ) { const SyntaxDefinition& def = getByHeader( header ); - if ( def.getLanguageName() == mDefinitions[0].getLanguageName() ) + if ( def.getLanguageName() == mDefinitions[0]->getLanguageName() ) return getByExtension( filePath, hFileAsCPP ); return def; } diff --git a/src/eepp/ui/doc/textdocument.cpp b/src/eepp/ui/doc/textdocument.cpp index 1e3b7f8e7..1cb71aeb9 100644 --- a/src/eepp/ui/doc/textdocument.cpp +++ b/src/eepp/ui/doc/textdocument.cpp @@ -119,7 +119,7 @@ bool TextDocument::isNonWord( String::StringBaseType ch ) const { TextDocument::TextDocument( bool verbose ) : mUUID( true ), mUndoStack( this ), - mDocumentMutex( std::make_shared() ), + mDocumentMutex( std::make_shared() ), mVerbose( verbose ), mAutoCloseBracketsPairs( { { '(', ')' }, { '[', ']' }, { '{', '}' }, { '\'', '\'' }, { '"', '"' }, { '`', '`' } } ), @@ -204,7 +204,7 @@ void TextDocument::reset() { notifyDocumentReset(); } -void TextDocument::resetCursor() { +void TextDocument::resetSelection() { auto cursor = sanitizeRange( getSelection() ); mSelection.clear(); mSelection.push_back( cursor ); @@ -1128,7 +1128,7 @@ const TextDocumentLine& TextDocument::line( const size_t& index ) const { return index >= linesCount() ? safeLine : mLines[index]; } -size_t TextDocument::linesCount() const { +std::size_t TextDocument::linesCount() const { Lock l( mLinesMutex ); return mLines.size(); } @@ -1703,42 +1703,58 @@ TextRange TextDocument::getLineRange( Int64 line ) const { } std::size_t TextDocument::getLineLength( Int64 line ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); - return mLines[line].size(); + return line >= (Int64)mLines.size() ? 0 : mLines[line].size(); } String TextDocument::getLineText( Int64 line ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); - return mLines[line].getText(); + return line >= (Int64)mLines.size() ? String() : mLines[line].getText(); } String TextDocument::getLineTextSubStr( Int64 line, std::size_t pos, std::size_t n ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); - return mLines[line].getText().substr( pos, n ); + return line >= (Int64)mLines.size() ? String() : mLines[line].getText().substr( pos, n ); } String::HashType TextDocument::getLineHash( Int64 line ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); - return mLines[line].getHash(); + return line >= (Int64)mLines.size() ? 0 : mLines[line].getHash(); } String TextDocument::getLineTextWithoutNewLine( Int64 line ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); - return mLines[line].getTextWithoutNewLine(); + return line >= (Int64)mLines.size() ? String() : mLines[line].getTextWithoutNewLine(); } void TextDocument::getLineTextToBuffer( Int64 line, String& buffer ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); + if ( line >= (Int64)mLines.size() ) { + buffer.clear(); + return; + } buffer = mLines[line].getText(); } std::string TextDocument::getLineTextUtf8( Int64 line ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); - return mLines[line].getText().toUtf8(); + return line >= (Int64)mLines.size() ? std::string() : mLines[line].getText().toUtf8(); } void TextDocument::getLineTextToBufferUtf8( Int64 line, std::string& buffer ) const { + eeASSERT( line < (Int64)linesCount() ); Lock l( mLinesMutex ); + if ( line >= (Int64)mLines.size() ) { + buffer.clear(); + return; + } buffer = mLines[line].getText().toUtf8(); } @@ -2065,12 +2081,12 @@ void TextDocument::moveToNextPage( Int64 pageSize ) { } void TextDocument::moveToStartOfDoc() { - resetCursor(); + resetSelection(); setSelection( startOfDoc() ); } void TextDocument::moveToEndOfDoc() { - resetCursor(); + resetSelection(); setSelection( endOfDoc() ); } @@ -3812,7 +3828,7 @@ void TextDocument::escape() { } else { auto prevSels = getSelections(); - resetCursor(); + resetSelection(); size_t lineCount = linesCount(); for ( size_t i = 0; i < lineCount; i++ ) { @@ -3841,7 +3857,7 @@ void TextDocument::unescape() { } else { auto prevSels = getSelections(); - resetCursor(); + resetSelection(); size_t lineCount = linesCount(); for ( size_t i = 0; i < lineCount; i++ ) { @@ -3874,7 +3890,7 @@ void TextDocument::toBase64() { } else { auto prevSels = getSelections(); - resetCursor(); + resetSelection(); selectAll(); const auto fullDoc = getText().toUtf8(); std::string newDoc; @@ -3904,7 +3920,7 @@ void TextDocument::fromBase64() { } else { auto prevSels = getSelections(); - resetCursor(); + resetSelection(); selectAll(); const auto fullDoc = getText().toUtf8(); std::string newDoc; @@ -3968,7 +3984,7 @@ void TextDocument::initializeCommands() { mCommands["toggle-line-comments"] = [this] { toggleLineComments(); }; mCommands["selection-to-upper"] = [this] { toUpperSelection(); }; mCommands["selection-to-lower"] = [this] { toLowerSelection(); }; - mCommands["reset-cursor"] = [this] { resetCursor(); }; + mCommands["reset-cursor"] = [this] { resetSelection(); }; mCommands["add-cursor-above"] = [this] { addCursorAbove(); }; mCommands["add-cursor-below"] = [this] { addCursorBelow(); }; mCommands["cursor-undo"] = [this] { cursorUndo(); }; diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 9d3863c74..d4928a961 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -1102,7 +1102,7 @@ Uint32 UICodeEditor::onTextInput( const TextInputEvent& event ) { } void UICodeEditor::updateIMELocation() { - if ( mDoc->isLoading() || mDoc->getActiveClient() != this || !Engine::isRunninMainThread() || + if ( mDoc->isLoading() || mDoc->getActiveClient() != this || !Engine::isMainThread() || mDocView.isFolded( mDoc->getSelection( true ).start().line() ) ) return; Rectf r( getScreenPosition( mDoc->getSelection( true ).start() ) ); @@ -4430,7 +4430,7 @@ void UICodeEditor::onCursorPosChange() { mLastActivity.restart(); sendCommonEvent( Event::OnCursorPosChange ); invalidateDraw(); - if ( !Engine::isRunninMainThread() ) + if ( !Engine::isMainThread() ) runOnMainThread( [this] { mDocView.ensureCursorVisibility(); } ); else mDocView.ensureCursorVisibility(); diff --git a/src/eepp/ui/uitextinput.cpp b/src/eepp/ui/uitextinput.cpp index 4418e3acf..d9229e18e 100644 --- a/src/eepp/ui/uitextinput.cpp +++ b/src/eepp/ui/uitextinput.cpp @@ -812,7 +812,7 @@ Uint32 UITextInput::onTextInput( const TextInputEvent& event ) { } void UITextInput::updateIMELocation() { - if ( mDoc.getActiveClient() != this || !Engine::isRunninMainThread() ) + if ( mDoc.getActiveClient() != this || !Engine::isMainThread() ) return; Vector2f cursor( eefloor( mScreenPos.x + mRealAlignOffset.x + mCurPos.x + mPaddingPx.Left ), mScreenPos.y + mRealAlignOffset.y + mCurPos.y + mPaddingPx.Top ); diff --git a/src/eepp/window/engine.cpp b/src/eepp/window/engine.cpp index 86f94843d..e9543937e 100644 --- a/src/eepp/window/engine.cpp +++ b/src/eepp/window/engine.cpp @@ -42,13 +42,14 @@ namespace EE { namespace Window { +static Uint64 sMainThreadId{ 0 }; + SINGLETON_DECLARE_IMPLEMENTATION( Engine ) Engine::Engine() : mBackend( NULL ), mWindow( NULL ), mSharedGLContext( true ), - mMainThreadId( 0 ), mPlatformHelper( NULL ), mZip( NULL ), mDisplayManager( NULL ) { @@ -162,7 +163,7 @@ EE::Window::Window* Engine::createWindow( WindowSettings Settings, ContextSettin if ( NULL != mWindow ) { Settings.Backend = mWindow->getWindowInfo()->WindowConfig.Backend; } else { - mMainThreadId = Thread::getCurrentThreadId(); + sMainThreadId = Thread::getCurrentThreadId(); } switch ( Settings.Backend ) { @@ -241,10 +242,6 @@ bool Engine::isEngineRunning() { return existsSingleton() && Engine::instance()->isRunning(); } -bool Engine::isRunninMainThread() { - return isEngineRunning() && Engine::instance()->isMainThread(); -} - bool Engine::isRunning() const { return NULL != mWindow; } @@ -370,12 +367,12 @@ bool Engine::isThreaded() { #endif } -Uint32 Engine::getMainThreadId() { - return mMainThreadId; +Uint64 Engine::getMainThreadId() { + return sMainThreadId; } -bool Engine::isMainThread() const { - return Thread::getCurrentThreadId() == Engine::instance()->getMainThreadId(); +bool Engine::isMainThread() { + return Thread::getCurrentThreadId() == Engine::getMainThreadId(); } PlatformHelper* Engine::getPlatformHelper() { diff --git a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp index e16e1d035..7f14dfdcc 100644 --- a/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp +++ b/src/modules/eterm/src/eterm/terminal/terminaldisplay.cpp @@ -1782,7 +1782,7 @@ void TerminalDisplay::initVBOs() { } Rectf TerminalDisplay::updateIMELocation() { - if ( !Engine::isRunninMainThread() ) + if ( !Engine::isMainThread() ) return {}; Float fontSize = mFont->getFontHeight( mFontSize ); Float spaceCharAdvanceX = mFont->getGlyph( 'A', mFontSize, false, false ).advance; diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index b6c49e049..cb1a00a8b 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -286,7 +286,7 @@ void App::setAppTitle( const std::string& title ) { if ( mCurWindowTitle != fullTitle ) { mCurWindowTitle = fullTitle; - if ( Engine::isRunninMainThread() ) { + if ( Engine::isMainThread() ) { mWindow->setTitle( fullTitle ); } else { mUISceneNode->runOnMainThread( [this, fullTitle] { mWindow->setTitle( fullTitle ); } ); @@ -2563,7 +2563,7 @@ void App::onCodeEditorCreated( UICodeEditor* editor, TextDocument& doc ) { } ); auto docChanged = [this]( const Event* event ) { - if ( !Engine::isRunninMainThread() ) + if ( !Engine::isMainThread() ) return; const DocEvent* synEvent = static_cast( event ); UICodeEditor* editor = event->getNode()->asType(); @@ -4180,7 +4180,8 @@ static void exportLanguages( const std::string& path, const std::string& langs, if ( !langs.empty() ) { if ( langs == "all" ) { - defs = sdm->getDefinitions(); + for ( const auto& def : sdm->getDefinitions() ) + defs.push_back( *def.get() ); } else { auto langss = String::split( langs, ',' ); for ( const auto& l : langss ) { diff --git a/src/tools/ecode/featureshealth.cpp b/src/tools/ecode/featureshealth.cpp index c54c4112b..c7539d26d 100644 --- a/src/tools/ecode/featureshealth.cpp +++ b/src/tools/ecode/featureshealth.cpp @@ -47,8 +47,8 @@ std::vector FeaturesHealth::getHealth( PluginManager std::set languages; for ( const auto& def : definitions ) - if ( def.isVisible() ) - languages.insert( def.getLSPName() ); + if ( def->isVisible() ) + languages.insert( def->getLSPName() ); for ( const auto& pdef : preDefinitions ) languages.insert( pdef.getLSPName() ); diff --git a/src/tools/ecode/plugins/aiassistant/aiassistantplugin.cpp b/src/tools/ecode/plugins/aiassistant/aiassistantplugin.cpp index 972f256ec..66f7a8288 100644 --- a/src/tools/ecode/plugins/aiassistant/aiassistantplugin.cpp +++ b/src/tools/ecode/plugins/aiassistant/aiassistantplugin.cpp @@ -23,6 +23,7 @@ static std::initializer_list AIAssistantCommandList = { "ai-prompt", "ai-add-chat", "ai-chat-history", + "ai-attach-file", "ai-clone-chat", "ai-settings", "ai-toggle-private-chat", @@ -336,6 +337,7 @@ void AIAssistantPlugin::loadAIAssistantConfig( const std::string& path, bool upd mKeyBindings["ai-show-menu"] = "mod+m"; mKeyBindings["ai-chat-toggle-role"] = "mod+shift+r"; mKeyBindings["ai-refresh-local-models"] = "mod+shift+l"; + mKeyBindings["ai-attach-file"] = "mod+shift+a"; } auto& kb = j["keybindings"]; diff --git a/src/tools/ecode/plugins/aiassistant/aiassistantplugin.hpp b/src/tools/ecode/plugins/aiassistant/aiassistantplugin.hpp index 812319784..2fed972a4 100644 --- a/src/tools/ecode/plugins/aiassistant/aiassistantplugin.hpp +++ b/src/tools/ecode/plugins/aiassistant/aiassistantplugin.hpp @@ -46,7 +46,7 @@ class AIAssistantPlugin : public PluginBase { void onSaveState( IniFile* state ) override; - void setConfig( AIAssistantConfig config ) { mConfig = std::move( config ); } + void setConfig( AIAssistantConfig&& config ) { mConfig = std::move( config ); } protected: LLMProviders mProviders; diff --git a/src/tools/ecode/plugins/aiassistant/chatui.cpp b/src/tools/ecode/plugins/aiassistant/chatui.cpp index f58b3d16a..7e06e3bb4 100644 --- a/src/tools/ecode/plugins/aiassistant/chatui.cpp +++ b/src/tools/ecode/plugins/aiassistant/chatui.cpp @@ -1,6 +1,7 @@ #include "chatui.hpp" #include "../../appconfig.hpp" #include "../../notificationcenter.hpp" +#include "../../projectdirectorytree.hpp" #include "../../widgetcommandexecuter.hpp" #include "aiassistantplugin.hpp" #include "chathistory.hpp" @@ -130,6 +131,17 @@ DropDownList.role_ui { .llm_chatui .image { tint: var(--font); } +.llm_chat_attach { + padding: 8dp 8dp 38dp 8dp; +} +.llm_chat_locate_input { + margin-bottom: 2dp; + padding: 0 0 0 4dp; +} +.llm_chat_attach_locate { + border-radius: 8dp; + margin-bottom: 4dp; +} @@ -143,9 +155,13 @@ DropDownList.role_ui { + + + + - + @@ -344,6 +360,8 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) : setCmd( "ai-chat-history", [this] { showChatHistory(); } ); + setCmd( "ai-attach-file", [this] { showAttachFile(); } ); + setCmd( "ai-toggle-private-chat", [this] { mChatPrivate->toggleSelection(); } ); setCmd( "ai-toggle-lock-chat", [this] { renameChat( mSummary, true ); } ); @@ -427,9 +445,14 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) : mChatHistory = find( "llm_chat_history" ); mChatHistory->onClick( [this]( auto ) { showChatHistory(); } ); + mChatAttach = find( "llm_attach" ); + mChatAttach->onClick( [this]( auto ) { showAttachFile(); } ); + if ( getPlugin() == nullptr ) return; + initAttachFile(); + auto providers = getPlugin()->getProviders(); setProviders( std::move( providers ) ); mCurModel = getDefaultModel(); @@ -462,6 +485,7 @@ LLMChatUI::LLMChatUI( PluginManager* manager ) : bindCmds( mChatInput, true ); appendShortcutToTooltip( mChatHistory, "ai-chat-history" ); + appendShortcutToTooltip( mChatAttach, "ai-attach-file" ); appendShortcutToTooltip( mChatRun, "ai-prompt" ); appendShortcutToTooltip( mChatStop, "ai-prompt" ); appendShortcutToTooltip( mChatAdd, "ai-add-chat" ); @@ -479,8 +503,8 @@ LLMChatUI::~LLMChatUI() { if ( getPlugin() ) { AIAssistantPlugin::AIAssistantConfig config; config.partition = getSplitter()->getSplitPartition(); - config.modelProvider = getCurModel().provider; - config.modelName = getCurModel().name; + config.modelProvider = mCurModel.provider; + config.modelName = mCurModel.name; getPlugin()->setConfig( std::move( config ) ); } } @@ -514,6 +538,7 @@ void LLMChatUI::bindCmds( UICodeEditor* editor, bool bindToChatUI ) { addKb( editor, "mod+m", "ai-show-menu", bindToChatUI ); addKb( editor, "mod+shift+r", "ai-chat-toggle-role", bindToChatUI ); addKb( editor, "mod+shift+l", "ai-refresh-local-models", bindToChatUI ); + addKb( editor, "mod+shift+a", "ai-attach-file", bindToChatUI ); if ( bindToChatUI ) addKb( editor, "mod+shift+return", "ai-add-chat", bindToChatUI ); @@ -539,6 +564,8 @@ void LLMChatUI::showChatHistory() { if ( plugin == nullptr ) return; + hideAttachFile(); + static const char* CHAT_HISTORY_LAYOUT = R"xml( @@ -1332,4 +1359,144 @@ void LLMChatUI::deleteOldConversations( int days ) { FileSystem::fileRemove( chat.file.getFilepath() ); } +void LLMChatUI::updateLocateBarColumns() { + Float width = eeceil( mLocateTable->getPixelsSize().getWidth() ); + width -= mLocateTable->getVerticalScrollBar()->getPixelsSize().getWidth(); + mLocateTable->setColumnsVisible( { 0, 1 } ); + mLocateTable->setColumnWidth( 0, eeceil( width * 0.5 ) ); + mLocateTable->setColumnWidth( 1, width - mLocateTable->getColumnWidth( 0 ) ); +} + +void LLMChatUI::showAttachFile() { + auto text = mLocateInput->getText(); + auto ctx = getPlugin()->getPluginContext(); + if ( !ctx->isDirTreeReady() ) { + mLocateTable->setModel( ProjectDirectoryTree::emptyModel( {}, ctx->getCurrentProject() ) ); + mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) ); + } else if ( !mLocateInput->getText().empty() ) { + ctx->getDirTree()->asyncMatchTree( + ProjectDirectoryTree::MatchType::Fuzzy, text, 100, + [this, text]( auto res ) { + mUISceneNode->runOnMainThread( [this, res] { + mLocateTable->setModel( res ); + mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) ); + mLocateTable->scrollToTop(); + updateLocateBarColumns(); + } ); + }, + ctx->getCurrentProject() ); + } else { + mLocateTable->setModel( ctx->getDirTree()->asModel( + 100, {}, ctx->getCurrentProject(), Image::getImageExtensionsSupported() ) ); + mLocateTable->getSelection().set( mLocateTable->getModel()->index( 0 ) ); + } + mLocateBarLayout->setVisible( true ); + mLocateInput->setFocus(); + updateLocateBarColumns(); +} + +void LLMChatUI::hideAttachFile() { + mLocateBarLayout->setVisible( false ); +} + +void LLMChatUI::initAttachFile() { + mLocateBarLayout = findByClass( "llm_chat_attach" ); + mLocateInput = findByClass( "llm_chat_locate_input" ); + mLocateTable = findByClass( "llm_chat_attach_locate" ); + mLocateTable->setHeadersVisible( false ); + + mLocateTable->on( Event::OnSizeChange, [this]( const Event* ) { updateLocateBarColumns(); } ); + + mLocateInput->on( Event::OnTextChanged, [this]( const Event* ) { + showAttachFile(); + updateLocateBarColumns(); + } ); + mLocateInput->on( Event::OnPressEnter, [this]( const Event* ) { + KeyEvent keyEvent( mLocateTable, Event::KeyDown, KEY_RETURN, SCANCODE_UNKNOWN, 0, 0 ); + mLocateTable->forceKeyDown( keyEvent ); + } ); + mLocateInput->on( Event::KeyDown, [this]( const Event* event ) { + const KeyEvent* keyEvent = static_cast( event ); + mLocateTable->forceKeyDown( *keyEvent ); + } ); + mLocateBarLayout->setCommand( "close-locatebar", [this] { + hideAttachFile(); + if ( mChatInput ) + mChatInput->setFocus(); + } ); + mLocateBarLayout->getKeyBindings().addKeybindsString( { + { "escape", "close-locatebar" }, + } ); + mLocateTable->on( Event::KeyDown, [this]( const Event* event ) { + const KeyEvent* keyEvent = static_cast( event ); + if ( keyEvent->getKeyCode() == KEY_ESCAPE ) + mLocateBarLayout->execute( "close-locatebar" ); + } ); + mLocateTable->on( Event::OnModelEvent, [this]( const Event* event ) { + const ModelEvent* modelEvent = static_cast( event ); + if ( modelEvent->getModelEventType() == ModelEventType::Open ) { + Variant vName( modelEvent->getModel()->data( + modelEvent->getModel()->index( modelEvent->getModelIndex().row(), 0 ), + ModelRole::Display ) ); + Variant vPath( modelEvent->getModel()->data( + modelEvent->getModel()->index( modelEvent->getModelIndex().row(), 1 ), + ModelRole::Display ) ); + + if ( !vPath.isValid() ) + return; + + std::string path( vPath.toString() ); + if ( path.empty() ) + return; + + Variant rangeStr( modelEvent->getModel()->data( + modelEvent->getModel()->index( modelEvent->getModelIndex().row(), 1 ), + ModelRole::Custom ) ); + + std::string prjPath( getPlugin()->getPluginContext()->getCurrentProject() ); + + if ( FileSystem::isRelativePath( path ) ) + path = prjPath + path; + + TextDocument doc; + if ( doc.loadFromFile( path ) == TextDocument::LoadStatus::Loaded ) { + std::string nameToDisplay = doc.getFilename(); + if ( !prjPath.empty() && String::startsWith( doc.getFilePath(), prjPath ) ) { + nameToDisplay = doc.getFilePath(); + FileSystem::filePathRemoveBasePath( prjPath, nameToDisplay ); + } + auto cdoc = mChatInput->getDocumentRef(); + cdoc->resetSelection(); + cdoc->moveToEndOfLine(); + const auto& lineComment = doc.getSyntaxDefinition().getComment(); + if ( lineComment.empty() ) { + cdoc->textInput( "\n" + nameToDisplay + ":\n" ); + } + cdoc->textInput( "\n```" + doc.getSyntaxDefinition().getLSPName() ); + if ( !lineComment.empty() ) { + cdoc->textInput( String::format( " %s %s", lineComment, nameToDisplay ) ); + } + auto lineToFold = cdoc->getSelection().end().line(); + if ( doc.linesCount() >= 1 && + !String::startsWith( doc.line( 0 ).getText(), "\n" ) ) { + cdoc->textInput( "\n" ); + } + cdoc->textInput( doc.getText() ); + if ( doc.linesCount() >= 1 && + doc.line( doc.linesCount() - 1 ).getText() != String( "\n" ) ) { + cdoc->textInput( "\n" ); + } + cdoc->textInput( "```\n" ); + cdoc->getFoldRangeService().findRegionsNative(); + if ( doc.linesCount() > 30 && + cdoc->getFoldRangeService().isFoldingRegionInLine( lineToFold ) ) { + mChatInput->toggleFoldUnfold( lineToFold ); + } + } + + mLocateBarLayout->execute( "close-locatebar" ); + } + } ); +} + } // namespace ecode diff --git a/src/tools/ecode/plugins/aiassistant/chatui.hpp b/src/tools/ecode/plugins/aiassistant/chatui.hpp index 396280f71..933862dcb 100644 --- a/src/tools/ecode/plugins/aiassistant/chatui.hpp +++ b/src/tools/ecode/plugins/aiassistant/chatui.hpp @@ -16,6 +16,7 @@ class UIScrollView; class UISceneNode; class UIDropDownList; class UIPushButton; +class UITableView; }} // namespace EE::UI namespace EE { namespace Graphics { @@ -28,6 +29,7 @@ using namespace EE::Graphics; namespace ecode { +class UIVLinearLayoutCommandExecuter; class AIAssistantPlugin; class LLMChat { @@ -103,9 +105,14 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter { UIPushButton* mChatHistory{ nullptr }; UIPushButton* mChatMore{ nullptr }; UIPushButton* mRefreshModels{ nullptr }; + UIPushButton* mChatAttach{ nullptr }; UISelectButton* mChatPrivate{ nullptr }; UIScrollView* mChatScrollView{ nullptr }; UIDropDownList* mModelDDL{ nullptr }; + UIVLinearLayoutCommandExecuter* mLocateBarLayout{ nullptr }; + UITextInput* mLocateInput{ nullptr }; + UITableView* mLocateTable{ nullptr }; + std::unique_ptr mRequest; std::unique_ptr mSummaryRequest; LLMProviders mProviders; @@ -175,6 +182,14 @@ class LLMChatUI : public UILinearLayout, public WidgetCommandExecuter { bool bindToChatUI = false, bool searchDefined = true ); void deleteOldConversations( int days ); + + void initAttachFile(); + + void updateLocateBarColumns(); + + void showAttachFile(); + + void hideAttachFile(); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp index e4e7f71f7..2e32c2bbd 100644 --- a/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp +++ b/src/tools/ecode/plugins/autocomplete/autocompleteplugin.cpp @@ -1407,8 +1407,12 @@ AutoCompletePlugin::SymbolsList AutoCompletePlugin::getDocumentSymbols( TextDocu auto lineCount = doc->linesCount(); std::string string; for ( Int64 i = 0; i < static_cast( lineCount ); i++ ) { - if ( doc->getLineLength( i ) > MAX_LINE_LENGTH ) + auto len = doc->getLineLength( i ); + if ( len == 0 || len > MAX_LINE_LENGTH ) { + if ( len == 0 ) // Line count must have changed + lineCount = doc->linesCount(); continue; + } doc->getLineTextToBufferUtf8( i, string ); for ( auto& match : pattern.gmatch( string ) ) { std::string matchStr( match[0] ); diff --git a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp index 1dda055eb..be4447d87 100644 --- a/src/tools/ecode/plugins/debugger/debuggerplugin.cpp +++ b/src/tools/ecode/plugins/debugger/debuggerplugin.cpp @@ -152,7 +152,7 @@ DebuggerPlugin::~DebuggerPlugin() { } if ( mSidePanel && mTab ) { - if ( Engine::isRunninMainThread() ) + if ( Engine::isMainThread() ) mSidePanel->removeTab( mTab ); else { auto sidePanel = mSidePanel; @@ -2341,7 +2341,7 @@ void DebuggerPlugin::setUIDebuggingState( StatusDebuggerController::State state } if ( mPanelBoxButtons.box ) { - if ( Engine::isRunninMainThread() ) + if ( Engine::isMainThread() ) updatePanelUIState( state ); else mPanelBoxButtons.box->runOnMainThread( [this, state] { updatePanelUIState( state ); } ); diff --git a/src/tools/ecode/plugins/formatter/formatterplugin.cpp b/src/tools/ecode/plugins/formatter/formatterplugin.cpp index c9351880e..40841e260 100644 --- a/src/tools/ecode/plugins/formatter/formatterplugin.cpp +++ b/src/tools/ecode/plugins/formatter/formatterplugin.cpp @@ -385,7 +385,7 @@ void FormatterPlugin::formatDoc( UICodeEditor* editor ) { std::shared_ptr doc = editor->getDocumentRef(); auto pos = doc->getSelection(); auto scroll = editor->getScroll(); - doc->resetCursor(); + doc->resetSelection(); doc->selectAll(); doc->setRunningTransaction( true ); doc->textInput( data, false ); @@ -425,7 +425,7 @@ void FormatterPlugin::runFormatter( UICodeEditor* editor, const Formatter& forma std::shared_ptr doc = editor->getDocumentRef(); TextPosition pos = doc->getSelection().start(); auto scroll = editor->getScroll(); - doc->resetCursor(); + doc->resetSelection(); doc->selectAll(); doc->setRunningTransaction( true ); doc->textInput( res.result, false ); @@ -471,7 +471,7 @@ void FormatterPlugin::runFormatter( UICodeEditor* editor, const Formatter& forma std::shared_ptr doc = editor->getDocumentRef(); TextPosition pos = doc->getSelection().start(); auto scroll = editor->getScroll(); - doc->resetCursor(); + doc->resetSelection(); doc->selectAll(); doc->setRunningTransaction( true ); doc->textInput( data, false ); diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index f742f826f..fb3f92667 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -520,7 +520,7 @@ void processFormattingResponse( const std::shared_ptr& doc, std::vector edits ) { TextRanges ranges = doc->getSelections(); - doc->resetCursor(); + doc->resetSelection(); doc->setRunningTransaction( true ); // Sort from bottom to top, this way we don't have to compute any position deltas diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 70477a9a6..c91c0c4d6 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1374,7 +1374,7 @@ void LSPClientServer::notifyServerInitialized() { } bool LSPClientServer::needsAsync() { - return Engine::isRunninMainThread(); + return Engine::isMainThread(); } bool LSPClientServer::isRunning() { diff --git a/src/tools/ecode/plugins/plugincontextprovider.hpp b/src/tools/ecode/plugins/plugincontextprovider.hpp index 160d7b3a5..295d80f9b 100644 --- a/src/tools/ecode/plugins/plugincontextprovider.hpp +++ b/src/tools/ecode/plugins/plugincontextprovider.hpp @@ -132,6 +132,8 @@ class PluginContextProvider { virtual std::string getDefaultFileDialogFolder() const = 0; virtual AppConfig& getConfig() = 0; + + virtual bool isDirTreeReady() const = 0; }; } // namespace ecode diff --git a/src/tools/ecode/universallocator.cpp b/src/tools/ecode/universallocator.cpp index 4a425eeb8..c9945f5d9 100644 --- a/src/tools/ecode/universallocator.cpp +++ b/src/tools/ecode/universallocator.cpp @@ -924,11 +924,11 @@ UniversalLocator::openFileTypeModel( const std::string& pattern ) { fileTypeNames.insert( def.getLanguageName() ); } for ( const auto& def : defs ) { - if ( !def.isVisible() ) + if ( !def->isVisible() ) continue; - if ( pattern.empty() || String::startsWith( String::toLower( def.getLanguageName() ), + if ( pattern.empty() || String::startsWith( String::toLower( def->getLanguageName() ), String::toLower( pattern ) ) ) - fileTypeNames.insert( def.getLanguageName() ); + fileTypeNames.insert( def->getLanguageName() ); } return ItemListOwnerModel::create( std::vector( std::make_move_iterator( fileTypeNames.begin() ), diff --git a/src/tools/ecode/version.hpp b/src/tools/ecode/version.hpp index b1be2763e..1db89b5fb 100644 --- a/src/tools/ecode/version.hpp +++ b/src/tools/ecode/version.hpp @@ -8,7 +8,7 @@ using namespace EE; #define ECODE_MAJOR_VERSION 0 #define ECODE_MINOR_VERSION 7 -#define ECODE_PATCH_LEVEL 2 +#define ECODE_PATCH_LEVEL 3 /* ECODE_COMMIT_NUMBER 9999 is used for official releases, nightly builds (pre-releases) will * contain the number of commits after the last official release */