diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 4b87d0de1..58a5a6271 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -273,21 +273,37 @@ PluginRequestHandle LSPClientPlugin::processWorkspaceSymbol( const PluginMessage json jmsg = msg.asJSON(); std::string query = jmsg.value( "query", "" ); + PluginRequestHandle hdl; + for ( const auto server : servers ) { - server->workspaceSymbol( - query, [&, query]( const PluginIDType& id, LSPSymbolInformationList&& info ) { - if ( !query.empty() ) { - for ( auto& i : info ) { - if ( i.score == 0.f ) - i.score = String::fuzzyMatch( i.name, query ); + if ( Engine::instance()->isMainThread() ) { + server->workspaceSymbolAsync( + query, [&, query]( const PluginIDType& id, LSPSymbolInformationList&& info ) { + if ( !query.empty() ) { + for ( auto& i : info ) { + if ( i.score == 0.f ) + i.score = String::fuzzyMatch( i.name, query ); + } } - } - mManager->sendResponse( this, PluginMessageType::WorkspaceSymbol, - PluginMessageFormat::SymbolInformation, &info, id ); - } ); + mManager->sendResponse( this, PluginMessageType::WorkspaceSymbol, + PluginMessageFormat::SymbolInformation, &info, id ); + } ); + } else { + hdl = server->workspaceSymbol( + query, [&, query]( const PluginIDType& id, LSPSymbolInformationList&& info ) { + if ( !query.empty() ) { + for ( auto& i : info ) { + if ( i.score == 0.f ) + i.score = String::fuzzyMatch( i.name, query ); + } + } + mManager->sendResponse( this, PluginMessageType::WorkspaceSymbol, + PluginMessageFormat::SymbolInformation, &info, id ); + } ); + } } - return {}; + return hdl; } PluginRequestHandle LSPClientPlugin::processTextDocumentSymbol( const PluginMessage& msg ) { @@ -637,6 +653,38 @@ PluginRequestHandle LSPClientPlugin::processMessage( const PluginMessage& msg ) return ret; break; } + case PluginMessageType::QueryPluginCapability: { + if ( msg.isRequest() && msg.isJSON() ) { + const auto& data = msg.asJSON(); + if ( !data.contains( "capability" ) || !data["capability"].is_number_integer() || + data["capability"].get() < 0 || + data["capability"].get() >= static_cast( PluginCapability::Max ) ) + return {}; + PluginCapability capability = data["capability"].get(); + URI uri; + if ( data.contains( "uri" ) && data["uri"].is_string() ) + uri = data["uri"]; + PluginInmediateResponse rmsg; + rmsg.type = PluginMessageType::QueryPluginCapability; + auto servers = mClientManager.getFilteredServers( + [capability, uri]( LSPClientServer* server ) { + if ( !uri.empty() && !server->hasDocument( uri ) ) + return false; + switch ( capability ) { + case PluginCapability::WorkspaceSymbol: + return server->getCapabilities().workspaceSymbolProvider; + case PluginCapability::TextDocumentSymbol: + return server->getCapabilities().documentSymbolProvider; + default: { + } + } + return false; + } ); + rmsg.data = !servers.empty(); + return PluginRequestHandle( rmsg ); + } + break; + } case PluginMessageType::LanguageServerCapabilities: { if ( msg.isRequest() && msg.isJSON() ) { const auto& data = msg.asJSON(); diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index aba55875a..1838a281d 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -1507,15 +1507,35 @@ LSPClientServer::LSPRequestHandle LSPClientServer::documentSymbolsBroadcast( con } ); } -void LSPClientServer::workspaceSymbol( const std::string& querySymbol, const JsonReplyHandler& h, - const size_t& limit ) { +void LSPClientServer::workspaceSymbolAsync( const std::string& querySymbol, + const JsonReplyHandler& h, const size_t& limit ) { auto params = json{ { MEMBER_QUERY, querySymbol }, { MEMBER_LIMIT, limit } }; sendAsync( newRequest( "workspace/symbol", params ), h ); } -void LSPClientServer::workspaceSymbol( const std::string& querySymbol, - const SymbolInformationHandler& h, const size_t& limit ) { - workspaceSymbol( +void LSPClientServer::workspaceSymbolAsync( const std::string& querySymbol, + const SymbolInformationHandler& h, + const size_t& limit ) { + workspaceSymbolAsync( + querySymbol, + [h]( const IdType& id, const json& json ) { + if ( h ) + h( id, parseWorkspaceSymbols( json ) ); + }, + limit ); +} + +LSPClientServer::LSPRequestHandle LSPClientServer::workspaceSymbol( const std::string& querySymbol, + const JsonReplyHandler& h, + const size_t& limit ) { + auto params = json{ { MEMBER_QUERY, querySymbol }, { MEMBER_LIMIT, limit } }; + return send( newRequest( "workspace/symbol", params ), h ); +} + +LSPClientServer::LSPRequestHandle +LSPClientServer::workspaceSymbol( const std::string& querySymbol, const SymbolInformationHandler& h, + const size_t& limit ) { + return workspaceSymbol( querySymbol, [h]( const IdType& id, const json& json ) { if ( h ) @@ -1579,7 +1599,7 @@ void LSPClientServer::publishDiagnostics( const json& msg ) { PluginMessageFormat::Diagnostics, &res ); } Log::info( "LSPClientServer::publishDiagnostics: %s - returned %zu items", - res.uri.toString().c_str(), res.diagnostics.size() ); + res.uri.toString().c_str(), res.diagnostics.size() ); Log::debug( "LSPClientServer::publishDiagnostics: %s", msg.dump().c_str() ); } diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index 431bf5554..6c7fb9128 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -177,11 +177,18 @@ class LSPClientServer { LSPRequestHandle documentCompletion( const URI& document, const TextPosition& pos, const CompletionHandler& h ); - void workspaceSymbol( const std::string& querySymbol, const JsonReplyHandler& h, - const size_t& limit = 100 ); + void workspaceSymbolAsync( const std::string& querySymbol, const JsonReplyHandler& h, + const size_t& limit = 100 ); - void workspaceSymbol( const std::string& querySymbol, const SymbolInformationHandler& h, - const size_t& limit = 100 ); + void workspaceSymbolAsync( const std::string& querySymbol, const SymbolInformationHandler& h, + const size_t& limit = 100 ); + + LSPRequestHandle workspaceSymbol( const std::string& querySymbol, const JsonReplyHandler& h, + const size_t& limit = 100 ); + + LSPRequestHandle workspaceSymbol( const std::string& querySymbol, + const SymbolInformationHandler& h, + const size_t& limit = 100 ); LSPRequestHandle selectionRange( const URI& document, const std::vector& positions, diff --git a/src/tools/ecode/plugins/pluginmanager.hpp b/src/tools/ecode/plugins/pluginmanager.hpp index 35021f428..ac371738d 100644 --- a/src/tools/ecode/plugins/pluginmanager.hpp +++ b/src/tools/ecode/plugins/pluginmanager.hpp @@ -61,6 +61,12 @@ struct PluginDefinition { PluginCreatorFn creatorSyncFn{ nullptr }; }; +enum class PluginCapability { + WorkspaceSymbol, + TextDocumentSymbol, + Max +}; + enum class PluginMessageType { WorkspaceFolderChanged, // Broadcast the workspace folder from the application to the plugins Diagnostics, // Broadcast a document diagnostics from the LSP Client @@ -84,6 +90,7 @@ enum class PluginMessageType { GetErrorOrWarning, // Request a component to provide the information of an error or warning in a // particular document location GetDiagnostics, // Request the diagnostic information from a cursor position + QueryPluginCapability, // Requests / queries if a plugin providers a capability Undefined }; diff --git a/src/tools/ecode/universallocator.cpp b/src/tools/ecode/universallocator.cpp index 8a4483012..da618b27b 100644 --- a/src/tools/ecode/universallocator.cpp +++ b/src/tools/ecode/universallocator.cpp @@ -336,7 +336,8 @@ void UniversalLocator::initLocateBar( UILocateBar* locateBar, UITextInput* locat if ( String::startsWith( mLocateInput->getText(), "sb " ) ) { auto pbm = mApp->getProjectBuildManager(); - if ( nullptr == pbm ) return; + if ( nullptr == pbm ) + return; auto cfg = pbm->getConfig(); std::string buildName = vName.toString(); if ( pbm->hasBuild( buildName ) ) { @@ -347,7 +348,8 @@ void UniversalLocator::initLocateBar( UILocateBar* locateBar, UITextInput* locat return; } else if ( String::startsWith( mLocateInput->getText(), "sbt " ) ) { auto pbm = mApp->getProjectBuildManager(); - if ( nullptr == pbm ) return; + if ( nullptr == pbm ) + return; auto cfg = pbm->getConfig(); auto build = pbm->getBuild( cfg.buildName ); if ( build != nullptr ) { @@ -440,7 +442,9 @@ void UniversalLocator::showBar() { mLocateTable->setVisible( true ); const String& text = mLocateInput->getText(); - if ( !text.empty() && ( text[0] == '>' || text[0] == ':' || text[0] == '.' ) ) { + if ( !text.empty() && ( text[0] == '>' || text[0] == ':' || text[0] == '.' || + ( text[0] == 'o' && text.size() > 1 && text[1] == ' ' ) || + ( text[0] == 'l' && text.size() > 1 && text[1] == ' ' ) ) ) { Int64 selectFrom = 1; if ( text.size() >= 2 && text[1] == ' ' ) selectFrom = 2; @@ -462,6 +466,8 @@ void UniversalLocator::showLocateBar() { if ( !mLocateInput->getText().empty() && ( mLocateInput->getText()[0] == '>' || mLocateInput->getText()[0] == ':' || mLocateInput->getText()[0] == '.' || + String::startsWith( mLocateInput->getText(), "l " ) || + String::startsWith( mLocateInput->getText(), "o " ) || String::startsWith( mLocateInput->getText(), "sb " ) || String::startsWith( mLocateInput->getText(), "sbt " ) ) ) mLocateInput->setText( "" ); @@ -707,22 +713,60 @@ std::shared_ptr UniversalLocator::emptyModel( const String& return LSPSymbolInfoModel::create( mUISceneNode, query, { info } ); } +bool UniversalLocator::findCapability( PluginCapability capability ) { + json capa; + capa["capability"] = capability; + capa["uri"] = getCurDocURI(); + PluginRequestHandle resp = mApp->getPluginManager()->sendRequest( + PluginMessageType::QueryPluginCapability, PluginMessageFormat::JSON, &capa ); + if ( resp.isResponse() && resp.getResponse().data.is_boolean() ) + return resp.getResponse().data.get(); + return false; +} + +String UniversalLocator::getDefQueryText( PluginCapability capability ) { + bool hasCapability = findCapability( capability ); + if ( !hasCapability ) + return mUISceneNode->i18n( "no_running_lsp_server", "No running LSP server" ); + return mUISceneNode->i18n( "insert_search_query", "Insert search query" ); +} + +nlohmann::json UniversalLocator::pluginID( const PluginIDType& id ) { + json r; + r["uri"] = getCurDocURI(); + if ( id.isInteger() ) + r["id"] = id.asInt(); + else + r["id"] = id.asString(); + return r; +} + void UniversalLocator::requestWorkspaceSymbol() { if ( mLocateInput->getText().empty() ) return; auto txt( mLocateInput->getText().substr( 1 ).trim() ); if ( mWorkspaceSymbolQuery != txt.toUtf8() || mWorkspaceSymbolQuery.empty() ) { mWorkspaceSymbolQuery = txt.toUtf8(); + if ( !mWorkspaceSymbolModel ) { - auto defTxt = mUISceneNode->i18n( "no_running_lsp_server", "No running LSP server" ); + auto defTxt = getDefQueryText( PluginCapability::WorkspaceSymbol ); mWorkspaceSymbolModel = emptyModel( defTxt, mWorkspaceSymbolQuery ); } mLocateTable->setModel( mWorkspaceSymbolModel ); - json j; - j["query"] = mWorkspaceSymbolQuery; - mApp->getPluginManager()->sendRequest( PluginMessageType::WorkspaceSymbol, - PluginMessageFormat::JSON, &j ); + if ( mQueryWorkspaceLastId.isValid() ) { + json r( pluginID( mQueryWorkspaceLastId ) ); + mApp->getPluginManager()->sendBroadcast( PluginMessageType::CancelRequest, + PluginMessageFormat::JSON, &r ); + } + + mApp->getThreadPool()->run( [this] { + json j; + j["query"] = mWorkspaceSymbolQuery; + auto hdl = mApp->getPluginManager()->sendRequest( PluginMessageType::WorkspaceSymbol, + PluginMessageFormat::JSON, &j ); + mQueryWorkspaceLastId = hdl.id(); + } ); } else if ( mWorkspaceSymbolModel && mWorkspaceSymbolModel.get() != mLocateTable->getModel() ) { mLocateTable->setModel( mWorkspaceSymbolModel ); } @@ -758,16 +802,18 @@ void UniversalLocator::requestDocumentSymbol() { if ( mSplitter->curEditorIsNotNull() ) { if ( needsUpdate ) { - mTextDocumentSymbolModel = emptyModel( - mUISceneNode->i18n( "no_running_lsp_server", "No running LSP server" ) ); + mTextDocumentSymbolModel = + emptyModel( getDefQueryText( PluginCapability::TextDocumentSymbol ) ); + mLocateTable->setModel( mTextDocumentSymbolModel ); + json j; j["uri"] = mCurDocURI = getCurDocURI(); - mApp->getPluginManager()->sendRequest( PluginMessageType::TextDocumentFlattenSymbol, - PluginMessageFormat::JSON, &j ); + auto hdl = mApp->getPluginManager()->sendRequest( + PluginMessageType::TextDocumentFlattenSymbol, PluginMessageFormat::JSON, &j ); } else { if ( !mTextDocumentSymbolModel ) { - mTextDocumentSymbolModel = emptyModel( - mUISceneNode->i18n( "no_running_lsp_server", "No running LSP server" ) ); + mTextDocumentSymbolModel = + emptyModel( getDefQueryText( PluginCapability::TextDocumentSymbol ) ); } mLocateTable->setModel( mTextDocumentSymbolModel ); } @@ -822,6 +868,7 @@ std::string UniversalLocator::getCurDocURI() { PluginRequestHandle UniversalLocator::processResponse( const PluginMessage& msg ) { if ( msg.isResponse() && msg.type == PluginMessageType::WorkspaceSymbol && msg.format == PluginMessageFormat::SymbolInformation ) { + mQueryWorkspaceLastId = PluginIDType(); updateWorkspaceSymbol( msg.asSymbolInformation() ); } else if ( msg.isResponse() && msg.type == PluginMessageType::TextDocumentFlattenSymbol && msg.format == PluginMessageFormat::SymbolInformation ) { diff --git a/src/tools/ecode/universallocator.hpp b/src/tools/ecode/universallocator.hpp index e303a11e7..ea51c8231 100644 --- a/src/tools/ecode/universallocator.hpp +++ b/src/tools/ecode/universallocator.hpp @@ -61,6 +61,7 @@ class UniversalLocator { std::string mCurDocQuery; std::shared_ptr mTextDocumentSymbolModel{ nullptr }; std::shared_ptr mOpenDocumentsModel{ nullptr }; + PluginIDType mQueryWorkspaceLastId; void updateLocateBar(); @@ -102,6 +103,12 @@ class UniversalLocator { std::shared_ptr> openBuildModel( const std::string& match ); std::shared_ptr> openBuildTypeModel( const std::string& match ); + + bool findCapability( PluginCapability ); + + String getDefQueryText( PluginCapability ); + + nlohmann::json pluginID( const PluginIDType& id ); }; } // namespace ecode