diff --git a/src/eepp/ui/doc/languages/rust.cpp b/src/eepp/ui/doc/languages/rust.cpp new file mode 100644 index 000000000..a5e37ea71 --- /dev/null +++ b/src/eepp/ui/doc/languages/rust.cpp @@ -0,0 +1,61 @@ +#include +#include + +namespace EE { namespace UI { namespace Doc { namespace Language { + +void addRust() { + + SyntaxDefinitionManager::instance()->add( + + { "Rust", + { "%.rs$" }, + { + { { "//.-\n" }, "comment" }, + { { "/%*", "%*/" }, "comment" }, + { { "r#\"", "\"#", "\\" }, "string" }, + { { "\"", "\"", "\\" }, "string" }, + { { "'.'" }, "string" }, + { { "'[%a_][%a%d_]*" }, "keyword2" }, + { { "0[oO_][0-7]+" }, "number" }, + { { "-?0x[%x_]+" }, "number" }, + { { "-?%d+_%d" }, "number" }, + { { "-?%d+[%d%.eE]*f?" }, "number" }, + { { "-?%.?%d+f?" }, "number" }, + { { "[%+%-=/%*%^%%<>!~|&]" }, "operator" }, + { { "[%a_][%w_]*!%f[%[(]" }, "function" }, + { { "[%a_][%w_]*%f[(]" }, "function" }, + { { "[%a_][%w_]*" }, "symbol" }, + { { "%s+" }, "normal" }, + { { "%w+%f[%s]" }, "normal" }, + + }, + { + { "while", "keyword" }, { "for", "keyword" }, { "u16", "keyword2" }, + { "i16", "keyword2" }, { "usize", "keyword2" }, { "continue", "keyword" }, + { "char", "keyword2" }, { "i64", "keyword2" }, { "extern", "keyword" }, + { "fn", "keyword" }, { "if", "keyword" }, { "u8", "keyword2" }, + { "f32", "keyword2" }, { "await", "keyword" }, { "let", "keyword" }, + { "return", "keyword" }, { "Self", "keyword" }, { "Result", "literal" }, + { "false", "literal" }, { "dyn", "keyword" }, { "pub", "keyword" }, + { "i128", "keyword2" }, { "as", "keyword" }, { "Some", "literal" }, + { "enum", "keyword" }, { "f64", "keyword2" }, { "None", "literal" }, + { "async", "keyword" }, { "mut", "keyword" }, { "i32", "keyword2" }, + { "move", "keyword" }, { "f128", "keyword2" }, { "crate", "keyword" }, + { "i8", "keyword2" }, { "str", "keyword2" }, { "impl", "keyword" }, + { "in", "keyword" }, { "break", "keyword" }, { "else", "keyword" }, + { "isize", "keyword2" }, { "String", "keyword2" }, { "type", "keyword" }, + { "loop", "keyword" }, { "static", "keyword" }, { "match", "keyword" }, + { "Option", "literal" }, { "bool", "keyword2" }, { "mod", "keyword" }, + { "ref", "keyword" }, { "super", "keyword" }, { "self", "keyword" }, + { "trait", "keyword" }, { "struct", "keyword" }, { "true", "literal" }, + { "const", "keyword" }, { "u32", "keyword2" }, { "u64", "keyword2" }, + { "use", "keyword" }, { "unsafe", "keyword" }, { "where", "keyword" }, + + }, + "//", + {} + + } ); +} + +}}}} // namespace EE::UI::Doc::Language diff --git a/src/eepp/ui/doc/languages/rust.hpp b/src/eepp/ui/doc/languages/rust.hpp new file mode 100644 index 000000000..1d51eb30b --- /dev/null +++ b/src/eepp/ui/doc/languages/rust.hpp @@ -0,0 +1,10 @@ +#ifndef EE_UI_DOC_Rust +#define EE_UI_DOC_Rust + +namespace EE { namespace UI { namespace Doc { namespace Language { + +extern void addRust(); + +}}}} + +#endif diff --git a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp index 7285e123b..22cccf4e5 100644 --- a/src/eepp/ui/doc/syntaxdefinitionmanager.cpp +++ b/src/eepp/ui/doc/syntaxdefinitionmanager.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -1496,53 +1497,6 @@ static void addGo() { "//" } ); } -static void addRust() { - SyntaxDefinitionManager::instance()->add( - { "Rust", - { "%.rs$" }, - { - { { "//.-\n" }, "comment" }, - { { "/%*", "%*/" }, "comment" }, - { { "r#\"", "\"#", "\\" }, "string" }, - { { "\"", "\"", "\\" }, "string" }, - { { "`", "`", "\\" }, "string" }, - { { "'.'" }, "string" }, - { { "[%a_][%a%d_]*" }, "keyword2" }, - { { "0[oO_][0-7]+" }, "number" }, - { { "-?0x[%x_]+" }, "number" }, - { { "-?%d+_%d" }, "number" }, - { { "-?%d+[%d%.eE]*f?" }, "number" }, - { { "-?%.?%d+f?" }, "number" }, - { { "[%+%-=/%*%^%%<>!~|&]" }, "operator" }, - { { "[%a_][%w_]*%f[(]" }, "function" }, - { { "[%a_][%w_]*" }, "symbol" }, - }, - { - { "as", "keyword" }, { "async", "keyword" }, { "await", "keyword" }, - { "break", "keyword" }, { "const", "keyword" }, { "continue", "keyword" }, - { "crate", "keyword" }, { "dyn", "keyword" }, { "else", "keyword" }, - { "enum", "keyword" }, { "extern", "keyword" }, { "false", "keyword" }, - { "fn", "keyword" }, { "for", "keyword" }, { "if", "keyword" }, - { "impl", "keyword" }, { "in", "keyword" }, { "let", "keyword" }, - { "loop", "keyword" }, { "match", "keyword" }, { "mod", "keyword" }, - { "move", "keyword" }, { "mut", "keyword" }, { "pub", "keyword" }, - { "ref", "keyword" }, { "return", "keyword" }, { "Self", "keyword" }, - { "self", "keyword" }, { "static", "keyword" }, { "struct", "keyword" }, - { "super", "keyword" }, { "trait", "keyword" }, { "true", "keyword" }, - { "type", "keyword" }, { "unsafe", "keyword" }, { "use", "keyword" }, - { "where", "keyword" }, { "while", "keyword" }, { "i32", "keyword2" }, - { "i64", "keyword2" }, { "i128", "keyword2" }, { "i16", "keyword2" }, - { "i8", "keyword2" }, { "u8", "keyword2" }, { "u16", "keyword2" }, - { "u32", "keyword2" }, { "u64", "keyword2" }, { "usize", "keyword2" }, - { "isize", "keyword2" }, { "f32", "keyword2" }, { "f64", "keyword2" }, - { "f128", "keyword2" }, { "String", "keyword2" }, { "char", "keyword2" }, - { "&str", "keyword2" }, { "bool", "keyword2" }, { "true", "literal" }, - { "false", "literal" }, { "None", "literal" }, { "Some", "literal" }, - { "Option", "literal" }, { "Result", "literal" }, - }, - "//" } ); -} - static void addGDScript() { SyntaxDefinitionManager::instance()->add( diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index e216d2e26..f642b62d8 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1043,6 +1043,7 @@ void LSPClientServer::initialize() { workspace["applyEdit"] = true; workspace["executeCommand"] = json{ { "dynamicRegistration", true } }; workspace["workspaceFolders"] = true; + workspace["semanticTokens"] = json{ { "refreshSupport", true } }; json capabilities{ { "textDocument", @@ -1350,6 +1351,15 @@ LSPClientServer::LSPRequestHandle LSPClientServer::sendSync( const json& msg, return LSPRequestHandle(); } +void LSPClientServer::refreshSmenaticHighlighting() { + Lock l( mClientsMutex ); + for ( const auto& client : mClients ) { + if ( !client.second->isWaitingSemanticTokensResponse() && + !client.second->isRunningSemanticTokens() ) + client.second->requestSemanticHighlighting(); + } +} + LSPClientServer::LSPRequestHandle LSPClientServer::didOpen( const URI& document, const std::string& text, int version ) { auto params = textDocumentParams( textDocumentItem( document, mLSP.language, text, version ) ); @@ -1617,6 +1627,10 @@ void LSPClientServer::processRequest( const json& msg ) { } else if ( method == "window/workDoneProgress/create" ) { write( newEmptyResult( msgid ) ); return; + } else if ( method == "workspace/semanticTokens/refresh" ) { + refreshSmenaticHighlighting(); + write( newEmptyResult( msgid ) ); + return; } else if ( method == "client/registerCapability" ) { registerCapabilities( msg[MEMBER_PARAMS] ); write( newEmptyResult( msgid ) ); diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index aac60bcdd..1734d3e4a 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -298,6 +298,8 @@ class LSPClientServer { LSPClientServer::LSPRequestHandle sendSync( const json& msg, const JsonReplyHandler& h = nullptr, const JsonReplyHandler& eh = nullptr ); + + void refreshSmenaticHighlighting(); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp index 4a206b400..41144851d 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.cpp @@ -125,16 +125,17 @@ void LSPDocumentClient::requestSemanticHighlighting( bool reqFull ) { !cap.semanticTokenProvider.range ) return; + mWaitingSemanticTokensResponse = true; TextRange range; std::string reqId; bool delta = false; - if ( cap.semanticTokenProvider.range && !mFirstHighlight && !reqFull ) { - range = mDoc->getActiveClientVisibleRange(); - } else if ( mFirstHighlight || ( reqFull && !cap.semanticTokenProvider.fullDelta ) ) { - mFirstHighlight = false; - } else if ( cap.semanticTokenProvider.fullDelta ) { + if ( cap.semanticTokenProvider.fullDelta ) { delta = true; reqId = reqFull ? "" : mSemanticeResultId; + } else if ( cap.semanticTokenProvider.range && !mFirstHighlight && !reqFull ) { + range = mDoc->getActiveClientVisibleRange(); + } else if ( mFirstHighlight || reqFull ) { + mFirstHighlight = false; } LSPDocumentClient* docClient = this; @@ -144,8 +145,10 @@ void LSPDocumentClient::requestSemanticHighlighting( bool reqFull ) { mServer->documentSemanticTokensFull( mDoc->getURI(), delta, reqId, range, [docClient, uri, server, docModId]( const auto&, const LSPSemanticTokensDelta& deltas ) { - if ( server->hasDocument( uri ) ) + if ( server->hasDocument( uri ) ) { + docClient->mWaitingSemanticTokensResponse = false; docClient->processTokens( deltas, docModId ); + } } ); } @@ -160,12 +163,21 @@ void LSPDocumentClient::requestSemanticHighlightingDelayed( bool reqFull ) { return; UISceneNode* sceneNode = getUISceneNode(); if ( sceneNode ) { + mWaitingSemanticTokensResponse = true; sceneNode->removeActionsByTag( mTagSemanticTokens ); sceneNode->runOnMainThread( [this, reqFull]() { requestSemanticHighlighting( reqFull ); }, Seconds( 0.5f ), mTagSemanticTokens ); } } +bool LSPDocumentClient::isRunningSemanticTokens() const { + return mRunningSemanticTokens; +} + +bool LSPDocumentClient::isWaitingSemanticTokensResponse() const { + return mWaitingSemanticTokensResponse; +} + UISceneNode* LSPDocumentClient::getUISceneNode() { LSPClientServer* server = mServer; if ( !server || !server->getManager() || !server->getManager()->getPluginManager() || diff --git a/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp b/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp index 4428ab623..6f462643f 100644 --- a/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp +++ b/src/tools/ecode/plugins/lsp/lspdocumentclient.hpp @@ -57,6 +57,10 @@ class LSPDocumentClient : public TextDocument::Client { void requestSemanticHighlightingDelayed( bool reqFull = false ); + bool isRunningSemanticTokens() const; + + bool isWaitingSemanticTokensResponse() const; + protected: LSPClientServer* mServer{ nullptr }; LSPClientServerManager* mServerManager{ nullptr }; @@ -67,6 +71,7 @@ class LSPDocumentClient : public TextDocument::Client { std::string mSemanticeResultId; LSPSemanticTokensDelta mSemanticTokens; bool mRunningSemanticTokens{ false }; + bool mWaitingSemanticTokensResponse{ false }; bool mShutdown{ false }; bool mFirstHighlight{ true }; diff --git a/src/tools/ecode/plugins/pluginmanager.cpp b/src/tools/ecode/plugins/pluginmanager.cpp index 9afe7ecaf..8fe4a55ef 100644 --- a/src/tools/ecode/plugins/pluginmanager.cpp +++ b/src/tools/ecode/plugins/pluginmanager.cpp @@ -496,4 +496,63 @@ PluginManager* Plugin::getManager() const { return mManager; } +void PluginBase::onRegister( UICodeEditor* editor ) { + Lock l( mMutex ); + + std::vector listeners; + + listeners.push_back( + editor->addEventListener( Event::OnDocumentLoaded, [this]( const Event* event ) { + Lock l( mMutex ); + const DocEvent* docEvent = static_cast( event ); + onDocumentLoaded( docEvent->getDoc() ); + } ) ); + + listeners.push_back( + editor->addEventListener( Event::OnDocumentClosed, [this]( const Event* event ) { + { + Lock l( mMutex ); + const DocEvent* docEvent = static_cast( event ); + TextDocument* doc = docEvent->getDoc(); + mDocs.erase( doc ); + onDocumentClosed( doc ); + } + } ) ); + + listeners.push_back( + editor->addEventListener( Event::OnDocumentChanged, [&, editor]( const Event* ) { + TextDocument* oldDoc = mEditorDocs[editor]; + TextDocument* newDoc = editor->getDocumentRef().get(); + Lock l( mMutex ); + mDocs.erase( oldDoc ); + mEditorDocs[editor] = newDoc; + onDocumentChanged( editor, oldDoc ); + } ) ); + + onRegisterListeners( editor, listeners ); + + mEditors.insert( { editor, listeners } ); + mDocs.insert( editor->getDocumentRef().get() ); + mEditorDocs[editor] = editor->getDocumentRef().get(); +} + +void PluginBase::onUnregister( UICodeEditor* editor ) { + onBeforeUnregister( editor ); + if ( mShuttingDown ) + return; + Lock l( mMutex ); + TextDocument* doc = mEditorDocs[editor]; + auto cbs = mEditors[editor]; + for ( auto listener : cbs ) + editor->removeEventListener( listener ); + onUnregisterEditor( editor ); + mEditors.erase( editor ); + mEditorDocs.erase( editor ); + for ( auto editorIt : mEditorDocs ) + if ( editorIt.second == doc ) + return; + onUnregisterDocument( doc ); + mDocs.erase( doc ); +} + } // namespace ecode diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp index 918fb85ce..4fd78cde6 100644 --- a/src/tools/ecode/plugins/pluginmanager.hpp +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -421,6 +421,39 @@ class Plugin : public UICodeEditorPlugin { bool mShuttingDown{ false }; }; +class PluginBase : public Plugin { + public: + virtual void onRegister( UICodeEditor* ); + + virtual void onUnregister( UICodeEditor* ); + + protected: + //! Keep track of the registered editors + all the listeners registered to each editor + std::unordered_map> mEditors; + //! Keep track of the documents opened + std::set mDocs; + //! Documents and Editors mutex + Mutex mMutex; + //! Keep track of the document pointer of each editor + std::unordered_map mEditorDocs; + + virtual void onDocumentLoaded( TextDocument* ){}; + + virtual void onDocumentClosed( TextDocument* ){}; + + virtual void onDocumentChanged( UICodeEditor*, TextDocument* /*oldDoc*/ ){}; + + virtual void onRegisterListeners( UICodeEditor*, std::vector& /*listeners*/ ){}; + + //! Usually used to remove keybindings in an editor + virtual void onBeforeUnregister( UICodeEditor* ){}; + + virtual void onUnregisterEditor( UICodeEditor* ){}; + + //! Usually used to unregister commands in a document + virtual void onUnregisterDocument( TextDocument* ){}; +}; + } // namespace ecode #endif // ECODE_PLUGINMANAGER_HPP