diff --git a/include/eepp/ui/uicodeeditor.hpp b/include/eepp/ui/uicodeeditor.hpp index 0424fddd1..7fee3cbb3 100644 --- a/include/eepp/ui/uicodeeditor.hpp +++ b/include/eepp/ui/uicodeeditor.hpp @@ -64,7 +64,8 @@ class UICodeEditorPlugin { } virtual bool onMouseOver( UICodeEditor*, const Vector2i&, const Uint32& ) { return false; } virtual bool onMouseLeave( UICodeEditor*, const Vector2i&, const Uint32& ) { return false; } - virtual bool onCreateContextMenu( UICodeEditor*, const Vector2i&, const Uint32& ) { + virtual bool onCreateContextMenu( UICodeEditor*, UIPopUpMenu* /*menu*/, + const Vector2i& /*position*/, const Uint32& /*flags*/ ) { return false; } virtual void drawBeforeLineText( UICodeEditor*, const Int64&, Vector2f, const Float&, diff --git a/src/eepp/ui/uicodeeditor.cpp b/src/eepp/ui/uicodeeditor.cpp index 84dbe2cee..ecb7f951b 100644 --- a/src/eepp/ui/uicodeeditor.cpp +++ b/src/eepp/ui/uicodeeditor.cpp @@ -938,7 +938,7 @@ bool UICodeEditor::onCreateContextMenu( const Vector2i& position, const Uint32& createDefaultContextMenuOptions( menu ); for ( auto& plugin : mPlugins ) - if ( plugin->onCreateContextMenu( this, position, flags ) ) + if ( plugin->onCreateContextMenu( this, menu, position, flags ) ) return false; if ( menu->getCount() == 0 ) { @@ -1027,7 +1027,7 @@ Uint32 UICodeEditor::onMouseDown( const Vector2i& position, const Uint32& flags } } - if ( ( flags & EE_BUTTON_LMASK ) && isTextSelectionEnabled() && + if ( ( flags & ( EE_BUTTON_LMASK | EE_BUTTON_RMASK ) ) && isTextSelectionEnabled() && !getEventDispatcher()->isNodeDragging() && NULL != mFont && !mMouseDown && getEventDispatcher()->getMouseDownNode() == this ) { mMouseDown = true; diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index ca8c5e40c..1a8b018c1 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -92,9 +92,9 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std } if ( mKeyBindings.empty() ) - mKeyBindings["follow-symbol-under-cursor"] = "f2"; - if ( j.contains( "keybindings" ) && j["keybindings"].contains( "follow-symbol-under-cursor" ) ) - mKeyBindings["follow-symbol-under-cursor"] = j["keybindings"]["follow-symbol-under-cursor"]; + mKeyBindings["lsp-go-to-definition"] = "f2"; + if ( j.contains( "keybindings" ) && j["keybindings"].contains( "lsp-go-to-definition" ) ) + mKeyBindings["lsp-go-to-definition"] = j["keybindings"]["lsp-go-to-definition"]; if ( !j.contains( "servers" ) ) return; @@ -185,10 +185,11 @@ void LSPClientPlugin::onRegister( UICodeEditor* editor ) { editor->getKeyBindings().addKeybindString( kb.second, kb.first ); } - if ( editor->hasDocument() ) - editor->getDocument().setCommand( "follow-symbol-under-cursor", [&, editor]() { - mClientManager.followSymbolUnderCursor( editor->getDocumentRef().get() ); + if ( editor->hasDocument() ) { + editor->getDocument().setCommand( "lsp-go-to-definition", [&, editor]() { + mClientManager.goToDocumentDefinition( editor->getDocumentRef().get() ); } ); + } std::vector listeners; @@ -230,4 +231,44 @@ const PluginManager* LSPClientPlugin::getManager() const { return mManager; } +bool LSPClientPlugin::onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* menu, + const Vector2i& /*position*/, const Uint32& /*flags*/ ) { + auto* server = mClientManager.getOneLSPClientServer( editor ); + if ( !server ) + return false; + + menu->addSeparator(); + + auto addFn = [editor, server, menu]( const std::string& txtKey, const std::string& txtVal, + const std::string& cmd ) { + menu->add( editor->getUISceneNode()->i18n( txtKey, txtVal ) ) + ->setId( txtKey ) + ->on( Event::OnItemClicked, [server, editor, cmd]( const Event* event ) { + if ( !event->getNode()->isType( UI_TYPE_MENUITEM ) ) + return; + UIMenuItem* item = event->getNode()->asType(); + if ( String::startsWith( item->getId(), "lsp" ) ) { + server->getAndGoToLocation( editor->getDocument().getURI(), + editor->getDocument().getSelection().start(), cmd ); + } + } ); + }; + auto cap = server->getCapabilities(); + + if ( cap.definitionProvider ) + addFn( "lsp-go-to-definition", "Go To Definition", "textDocument/definition" ); + + if ( cap.declarationProvider ) + addFn( "lsp-go-to-declaration", "Go To Declaration", "textDocument/declaration" ); + + if ( cap.typeDefinitionProvider ) + addFn( "lsp-go-to-type-definition", "Go To Type Definition", + "textDocument/typeDefinition" ); + + if ( cap.implementationProvider ) + addFn( "lsp-go-to-implementation", "Go To Implementation", "textDocument/implementation" ); + + return false; +} + } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.hpp b/src/tools/ecode/plugins/lsp/lspclientplugin.hpp index 052018979..41d88a95c 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.hpp @@ -51,6 +51,9 @@ class LSPClientPlugin : public UICodeEditorPlugin { const PluginManager* getManager() const; + virtual bool onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* menu, + const Vector2i& position, const Uint32& flags ); + protected: const PluginManager* mManager{ nullptr }; std::shared_ptr mPool; diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index ec9d414b0..3d3c0ab1c 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -218,8 +218,8 @@ static void fromJson( LSPServerCapabilities& caps, const json& json ) { // it should not be sent unless such support is announced, but let's handle it anyway // so consider an object there as a (good?) sign that the server is suitably capable auto toBoolOrObject = []( const nlohmann::json& value, const std::string& valueName ) { - return value.contains( "valueName" ) && - ( value[valueName].get() || value[valueName].is_object() ); + return value.contains( valueName ) && + ( value[valueName].is_boolean() || value[valueName].is_object() ); }; auto sync = json["textDocumentSync"]; @@ -228,8 +228,10 @@ static void fromJson( LSPServerCapabilities& caps, const json& json ) { if ( sync.is_object() ) { auto syncObject = sync; auto save = syncObject["save"]; - if ( !save.empty() && ( save.is_object() || save.get() ) ) { - caps.textDocumentSync.save = { save["includeText"].get() }; + if ( save.is_boolean() ) { + caps.textDocumentSync.save.includeText = save.get(); + } else if ( save.is_object() && save.contains( "includeText" ) ) { + caps.textDocumentSync.save.includeText = save["includeText"].get(); } } @@ -410,6 +412,10 @@ bool LSPClientServer::registerDoc( const std::shared_ptr& doc ) { return true; } +const LSPServerCapabilities& LSPClientServer::getCapabilities() const { + return mCapabilities; +} + LSPClientServer::RequestHandle LSPClientServer::cancel( int reqid ) { if ( mHandlers.erase( reqid ) > 0 ) { auto params = json{ MEMBER_ID, reqid }; @@ -418,10 +424,8 @@ LSPClientServer::RequestHandle LSPClientServer::cancel( int reqid ) { return RequestHandle(); } -LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, - const GenericReplyHandler& h, - const GenericReplyHandler& eh, - const int id ) { +LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, const JsonReplyHandler& h, + const JsonReplyHandler& eh, const int id ) { RequestHandle ret; ret.mServer = this; @@ -460,8 +464,8 @@ LSPClientServer::RequestHandle LSPClientServer::write( const json& msg, return ret; } -LSPClientServer::RequestHandle LSPClientServer::send( const json& msg, const GenericReplyHandler& h, - const GenericReplyHandler& eh ) { +LSPClientServer::RequestHandle LSPClientServer::send( const json& msg, const JsonReplyHandler& h, + const JsonReplyHandler& eh ) { if ( mProcess.isAlive() ) { return write( msg, h, eh ); } else { @@ -569,8 +573,8 @@ const std::shared_ptr& LSPClientServer::getThreadPool() const { } LSPClientServer::RequestHandle LSPClientServer::documentSymbols( const URI& document, - const GenericReplyHandler& h, - const GenericReplyHandler& eh ) { + const JsonReplyHandler& h, + const JsonReplyHandler& eh ) { auto params = textDocumentParams( document ); return send( newRequest( "textDocument/documentSymbol", params ), h, eh ); } @@ -702,7 +706,9 @@ void LSPClientServer::readStdOut( const char* bytes, size_t n ) { continue; } +#ifndef EE_DEBUG try { +#endif auto res = json::parse( payload ); int msgid = -1; @@ -736,10 +742,12 @@ void LSPClientServer::readStdOut( const char* bytes, size_t n ) { Log::debug( "LSPClientServer::readStdOut server %s unexpected reply id: %d", mLSP.name.c_str(), msgid ); } +#ifndef EE_DEBUG } catch ( const json::exception& e ) { Log::debug( "LSPClientServer::readStdOut server %s said: Coudln't parse json err: %s", mLSP.name.c_str(), e.what() ); } +#endif } } @@ -753,7 +761,7 @@ void LSPClientServer::readStdErr( const char* bytes, size_t n ) { } if ( !msg.message.empty() ) { msg.type = LSPMessageType::Log; - Log::debug( "LSPClientServer::readStdOut server %s: %s", mLSP.name.c_str(), + Log::debug( "LSPClientServer::readStdErr server %s: %s", mLSP.name.c_str(), msg.message.c_str() ); } } @@ -812,13 +820,17 @@ void LSPClientServer::initialize() { write( newRequest( "initialize", params ), [&]( const json& resp ) { +#ifndef EE_DEBUG try { +#endif fromJson( mCapabilities, resp["capabilities"] ); +#ifndef EE_DEBUG } catch ( const json::exception& e ) { Log::warning( "LSPClientServer::initialize server %s error parsing capabilities: %s", mLSP.name.c_str(), e.what() ); } +#endif mReady = true; write( newRequest( "initialized" ) ); diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index 83d5e93ec..e3da34e46 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include @@ -25,7 +27,7 @@ class LSPClientServer { public: template using ReplyHandler = std::function; - using GenericReplyHandler = ReplyHandler; + using JsonReplyHandler = ReplyHandler; class RequestHandle { friend class LSPClientServer; @@ -49,20 +51,21 @@ class LSPClientServer { bool registerDoc( const std::shared_ptr& doc ); + const LSPServerCapabilities& getCapabilities() const; + LSPClientServerManager* getManager() const; const std::shared_ptr& getThreadPool() const; LSPClientServer::RequestHandle cancel( int id ); - LSPClientServer::RequestHandle send( const json& msg, const GenericReplyHandler& h = nullptr, - const GenericReplyHandler& eh = nullptr ); + LSPClientServer::RequestHandle send( const json& msg, const JsonReplyHandler& h = nullptr, + const JsonReplyHandler& eh = nullptr ); const LSPDefinition& getDefinition() const { return mLSP; } - LSPClientServer::RequestHandle documentSymbols( const URI& document, - const GenericReplyHandler& h, - const GenericReplyHandler& eh ); + LSPClientServer::RequestHandle documentSymbols( const URI& document, const JsonReplyHandler& h, + const JsonReplyHandler& eh ); LSPClientServer::RequestHandle didOpen( const URI& document, const std::string& text, int version ); @@ -110,6 +113,9 @@ class LSPClientServer { void workDoneProgress( const LSPWorkDoneProgressParams& workDoneParams ); + LSPClientServer::RequestHandle getAndGoToLocation( const URI& document, const TextPosition& pos, + const std::string& search ); + protected: LSPClientServerManager* mManager{ nullptr }; String::HashType mId; @@ -118,13 +124,13 @@ class LSPClientServer { Process mProcess; std::vector mDocs; std::map> mClients; - std::map> mHandlers; + std::map> mHandlers; Mutex mClientsMutex; bool mReady{ false }; struct QueueMessage { json msg; - GenericReplyHandler h; - GenericReplyHandler eh; + JsonReplyHandler h; + JsonReplyHandler eh; }; std::vector mQueuedMessages; std::string mReceive; @@ -137,9 +143,8 @@ class LSPClientServer { void readStdErr( const char* bytes, size_t n ); - LSPClientServer::RequestHandle write( const json& msg, const GenericReplyHandler& h = nullptr, - const GenericReplyHandler& eh = nullptr, - const int id = 0 ); + LSPClientServer::RequestHandle write( const json& msg, const JsonReplyHandler& h = nullptr, + const JsonReplyHandler& eh = nullptr, const int id = 0 ); void initialize(); @@ -150,9 +155,6 @@ class LSPClientServer { void processRequest( const json& msg ); void goToLocation( const json& res ); - - LSPClientServer::RequestHandle getAndGoToLocation( const URI& document, const TextPosition& pos, - const std::string& search ); }; } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp index 02b7ba2da..59df0b19f 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp @@ -153,7 +153,7 @@ void LSPClientServerManager::updateDirty() { closeLSPServer( server ); } -void LSPClientServerManager::followSymbolUnderCursor( TextDocument* doc ) { +void LSPClientServerManager::goToDocumentDefinition( TextDocument* doc ) { for ( auto& server : mClients ) { if ( server.second->hasDocument( doc ) ) { server.second->documentDefinition( doc->getURI(), doc->getSelection().start() ); @@ -173,4 +173,31 @@ const LSPWorkspaceFolder& LSPClientServerManager::getLSPWorkspaceFolder() const return mLSPWorkspaceFolder; } +std::vector LSPClientServerManager::getLSPClientServers( UICodeEditor* editor ) { + return getLSPClientServers( editor->getDocumentRef() ); +} + +std::vector +LSPClientServerManager::getLSPClientServers( const std::shared_ptr& doc ) { + std::vector servers; + for ( auto& server : mClients ) { + if ( server.second->hasDocument( doc.get() ) ) + servers.push_back( server.second.get() ); + } + return servers; +} + +LSPClientServer* LSPClientServerManager::getOneLSPClientServer( UICodeEditor* editor ) { + return getOneLSPClientServer( editor->getDocumentRef() ); +} + +LSPClientServer* +LSPClientServerManager::getOneLSPClientServer( const std::shared_ptr& doc ) { + for ( auto& server : mClients ) { + if ( server.second->hasDocument( doc.get() ) ) + return server.second.get(); + } + return nullptr; +} + } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp index aed5fd75f..1d16b223e 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp @@ -27,13 +27,21 @@ class LSPClientServerManager { void updateDirty(); - void followSymbolUnderCursor( TextDocument* doc ); + void goToDocumentDefinition( TextDocument* doc ); void didChangeWorkspaceFolders( const std::string& folder ); const LSPWorkspaceFolder& getLSPWorkspaceFolder() const; - protected: + std::vector getLSPClientServers( UICodeEditor* editor ); + + std::vector getLSPClientServers( const std::shared_ptr& doc ); + + LSPClientServer* getOneLSPClientServer( UICodeEditor* editor ); + + LSPClientServer* getOneLSPClientServer( const std::shared_ptr& doc ); + + protected: friend class LSPClientServer; LSPClientPlugin* mPlugin{ nullptr };