diff --git a/bin/assets/plugins/lspclient.json b/bin/assets/plugins/lspclient.json index 197ea7462..deb538642 100644 --- a/bin/assets/plugins/lspclient.json +++ b/bin/assets/plugins/lspclient.json @@ -53,7 +53,7 @@ "language": "c", "name": "clangd", "url": "https://clang.llvm.org/extra/clangd/", - "command": "clangd -log=error --background-index --limit-results=500 --completion-style=bundled", + "command": "clangd -log=error --background-index --header-insertion=never --limit-results=0 --limit-references=0 --clang-tidy=0 --use-dirty-headers --completion-style=bundled -j=$NPROC", "file_patterns": ["%.c$", "%.h$", "%.C$", "%.H$", "%.objc$"] }, { diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index a6186b066..e311af0de 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -71,6 +71,10 @@ static LSPURIAndServer getServerURIFromTextDocumentURI( LSPClientServerManager& return { uri, manager.getOneLSPClientServer( uri ) }; } +static void sanitizeCommand( std::string& cmd ) { + String::replaceAll( cmd, "$NPROC", String::toString( Sys::getCPUCount() ) ); +} + LSPPositionAndServer getLSPLocationFromJSON( LSPClientServerManager& manager, const json& data ) { if ( !data.contains( "uri" ) || !data.contains( "position" ) ) return {}; @@ -307,7 +311,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std auto list = { "lsp-go-to-definition", "lsp-go-to-declaration", "lsp-go-to-implementation", "lsp-go-to-type-definition", "lsp-switch-header-source", "lsp-symbol-info", - "lsp-symbol-references" }; + "lsp-symbol-references", "lsp-memory-usage" }; for ( const auto& key : list ) { if ( kb.contains( key ) ) { if ( !kb[key].empty() ) @@ -326,34 +330,47 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std auto& servers = j["servers"]; for ( auto& obj : servers ) { + bool lspOverwritten = false; // Allow disabling an LSP by redeclaring it in the user configuration file. - if ( obj.contains( "name" ) && obj.contains( "disabled" ) && - obj.at( "disabled" ).is_boolean() ) { - for ( auto& lsp : lsps ) { - if ( lsp.name == obj["name"] ) { - lsp.disabled = obj["disabled"].get(); - break; + if ( updateConfigFile && ( obj.contains( "name" ) || obj.contains( "use" ) ) && + obj.contains( "disabled" ) && obj.at( "disabled" ).is_boolean() ) { + std::string name = obj.contains( "name" ) ? obj["name"] : obj["use"]; + for ( auto& lspD : lsps ) { + if ( lspD.name == name ) { + lspD.disabled = obj["disabled"].get(); + lspOverwritten = true; } } } // Allow overriding the command for already defined LSP // And allow adding parameters to the already defined LSP - if ( obj.contains( "name" ) && + if ( updateConfigFile && ( obj.contains( "name" ) || obj.contains( "use" ) ) && ( ( obj.contains( "command" ) && obj.at( "command" ).is_string() ) || ( obj.contains( "command_parameters" ) && obj.at( "command_parameters" ).is_string() ) ) ) { - for ( auto& lsp : lsps ) { - if ( lsp.name == obj["name"] ) { - if ( !obj.value( "command", "" ).empty() ) - lsp.command = obj.value( "command", "" ); - if ( !obj.value( "command_parameters", "" ).empty() ) - lsp.commandParameters = obj.value( "command_parameters", "" ); - break; + for ( auto& lspR : lsps ) { + std::string name = obj.contains( "name" ) ? obj["name"] : obj["use"]; + if ( lspR.name == name ) { + lspOverwritten = true; + if ( !obj.value( "command", "" ).empty() ) { + lspR.command = obj.value( "command", "" ); + sanitizeCommand( lspR.command ); + } + if ( !obj.value( "command_parameters", "" ).empty() ) { + std::string cmdParam( obj.value( "command_parameters", "" ) ); + if ( !cmdParam.empty() && cmdParam.front() != ' ' ) + cmdParam = " " + cmdParam; + lspR.commandParameters += cmdParam; + sanitizeCommand( lspR.commandParameters ); + } } } } + if ( lspOverwritten ) + continue; + if ( !obj.contains( "language" ) || !obj.contains( "file_patterns" ) ) { Log::warning( "LSP server without language or file_patterns, ignored..." ); continue; @@ -408,6 +425,9 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std lsp.rootIndicationFileNames.push_back( fn ); } + sanitizeCommand( lsp.command ); + sanitizeCommand( lsp.commandParameters ); + // If the file pattern is repeated, we will overwrite the previous LSP. // The previous LSP should be the "default" LSP that comes with ecode. size_t pos = lspFilePatternPosition( lsps, lsp.filePatterns ); @@ -473,6 +493,10 @@ void LSPClientPlugin::onRegister( UICodeEditor* editor ) { doc.setCommand( "lsp-symbol-references", [&, editor] { mClientManager.getSymbolReferences( editor->getDocumentRef() ); } ); + + doc.setCommand( "lsp-memory-usage", [&, editor] { + mClientManager.memoryUsage( editor->getDocumentRef() ); + } ); } std::vector listeners; @@ -585,6 +609,11 @@ bool LSPClientPlugin::onCreateContextMenu( UICodeEditor* editor, UIPopUpMenu* me if ( server->getDefinition().language == "cpp" || server->getDefinition().language == "c" ) addFn( "lsp-switch-header-source", "Switch Header/Source" ); +#ifdef EE_DEBUG + if ( server->getDefinition().name == "clangd" ) + addFn( "lsp-memory-usage", "LSP Memory Usage" ); +#endif + return false; } diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 9fe52d770..36831b641 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -892,8 +892,13 @@ LSPClientServer::~LSPClientServer() { } bool LSPClientServer::start() { - bool ret = mProcess.create( mLSP.command + mLSP.commandParameters, Process::getDefaultOptions(), - {}, mRootPath ); + std::string cmd( mLSP.command ); + if ( !mLSP.commandParameters.empty() ) { + if ( mLSP.commandParameters.front() != ' ' ) + mLSP.commandParameters = " " + mLSP.commandParameters; + cmd += mLSP.commandParameters; + } + bool ret = mProcess.create( cmd, Process::getDefaultOptions(), {}, mRootPath ); if ( ret && mProcess.isAlive() ) { mProcess.startAsyncRead( [this]( const char* bytes, size_t n ) { readStdOut( bytes, n ); }, @@ -1216,7 +1221,6 @@ void LSPClientServer::processNotification( const json& msg ) { } else if ( method == ( "$/progress" ) ) { workDoneProgress( parseWorkDone( msg[MEMBER_PARAMS] ) ); return; - } else { } Log::debug( "LSPClientServer::processNotification server %s: %s", mLSP.name.c_str(), msg.dump().c_str() ); @@ -1557,6 +1561,16 @@ LSPClientServer::documentFormatting( const URI& document, const json& options, } ); } +LSPClientServer::LSPRequestHandle LSPClientServer::memoryUsage( const JsonReplyHandler& h ) { + return send( newRequest( "$/memoryUsage" ), h ); +} + +LSPClientServer::LSPRequestHandle LSPClientServer::memoryUsage() { + return memoryUsage( []( const IdType&, const json& json ) { + Log::warning( "Received Memory Usage Information:\n%s", json.dump( 2 ).c_str() ); + } ); +} + LSPClientServer::LSPRequestHandle LSPClientServer::documentSemanticTokensFull( const URI& document, bool delta, const std::string& requestId, const TextRange& range, @@ -1575,4 +1589,5 @@ LSPClientServer::documentSemanticTokensFull( const URI& document, bool delta, return send( newRequest( "textDocument/semanticTokens/full", params ), h ); } + } // namespace ecode diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index 9b4f8a482..bee6811fe 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -188,6 +188,11 @@ class LSPClientServer { LSPRequestHandle documentFormatting( const URI& document, const json& options, const TextEditArrayHandler& h ); + + LSPRequestHandle memoryUsage( const JsonReplyHandler& h ); + + LSPRequestHandle memoryUsage(); + void removeDoc( TextDocument* doc ); protected: diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp index 5eb7f8ac6..5ec807f40 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp @@ -279,6 +279,13 @@ void LSPClientServerManager::getSymbolReferences( std::shared_ptr } ); } +void LSPClientServerManager::memoryUsage( std::shared_ptr doc ) { + auto* server = getOneLSPClientServer( doc ); + if ( !server ) + return; + server->memoryUsage(); +} + void LSPClientServerManager::didChangeWorkspaceFolders( const std::string& folder ) { mLSPWorkspaceFolder = { "file://" + folder, FileSystem::fileNameFromPath( folder ) }; Lock l( mClientsMutex ); diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp index 2b004c470..b0ee8823b 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.hpp @@ -66,6 +66,8 @@ class LSPClientServerManager { void getSymbolReferences( std::shared_ptr doc ); + void memoryUsage( std::shared_ptr doc ); + protected: friend class LSPClientServer; PluginManager* mPluginManager{ nullptr };