From 4814569db1e014609cd7464f41d3c0c1ed7d1cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 30 Oct 2025 22:15:20 -0300 Subject: [PATCH] Implemented workspace/didChangeConfiguration command in LSP client. Now it's possible to set LSP settings for this command (SpartanJ/ecode#694). --- .../ecode/plugins/lsp/lspclientplugin.cpp | 34 ++++++------ .../ecode/plugins/lsp/lspclientserver.cpp | 54 ++++++++++++++++++- .../ecode/plugins/lsp/lspclientserver.hpp | 7 +++ .../plugins/lsp/lspclientservermanager.cpp | 10 ++++ src/tools/ecode/plugins/lsp/lspdefinition.hpp | 1 + 5 files changed, 89 insertions(+), 17 deletions(-) diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index deaf9b47c..8c18223d2 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -297,17 +297,6 @@ static LSPURIAndServer getServerURIFromTextDocumentURI( LSPClientServerManager& return { uri, manager.getOneLSPClientServer( uri ) }; } -static void sanitizeCommand( std::string& cmd, const std::string& workspaceFolder ) { - static std::string cpucount( String::toString( Sys::getCPUCount() ) ); - static std::string userdir = Sys::getUserDirectory(); - String::replaceAll( cmd, "$NPROC", cpucount ); - String::replaceAll( cmd, "${nproc}", cpucount ); - String::replaceAll( cmd, "$PROJECTPATH", workspaceFolder ); - String::replaceAll( cmd, "${project_root}", workspaceFolder ); - String::replaceAll( cmd, "$HOME", userdir ); - String::replaceAll( cmd, "${home}", userdir ); -} - LSPPositionAndServer getLSPLocationFromJSON( LSPClientServerManager& manager, const json& data ) { if ( !data.contains( "uri" ) || !data.contains( "position" ) ) return {}; @@ -972,7 +961,7 @@ static std::string parseCommand( nlohmann::json cmd, const std::string& workspac command = cmd[platform].get(); } } - sanitizeCommand( command, workspaceFolder ); + LSPClientServer::sanitizeCommand( command, workspaceFolder ); return command; } @@ -1144,7 +1133,8 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std ( obj.contains( "command_parameters" ) && obj.at( "command_parameters" ).is_string() ) || ( obj.contains( "host" ) && obj.at( "host" ).is_string() && - obj.contains( "port " ) && obj.at( "port" ).is_number_integer() ) ) ) { + obj.contains( "port " ) && obj.at( "port" ).is_number_integer() ) || + obj.contains( "settings" ) || obj.contains( "initializationOptions" ) ) ) { for ( auto& lspR : lsps ) { std::string name = obj.contains( "name" ) ? obj["name"] : obj["use"]; if ( lspR.name == name ) { @@ -1163,12 +1153,19 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std if ( !cmdParam.empty() && cmdParam.front() != ' ' ) cmdParam = " " + cmdParam; lspR.commandParameters += cmdParam; - sanitizeCommand( lspR.commandParameters, mManager->getWorkspaceFolder() ); + LSPClientServer::sanitizeCommand( lspR.commandParameters, + mManager->getWorkspaceFolder() ); } if ( obj.contains( "host" ) ) { lspR.host = obj.value( "host", "" ); lspR.port = obj.value( "port", 0 ); } + if ( obj.contains( "settings" ) ) { + lspR.settings = obj["settings"]; + } + if ( obj.contains( "initializationOptions" ) ) { + lspR.initializationOptions = obj["initializationOptions"]; + } tryAddEnv( obj, lspR ); } } @@ -1202,6 +1199,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std lsp.host = tlsp.host; lsp.port = tlsp.port; lsp.initializationOptions = tlsp.initializationOptions; + lsp.settings = tlsp.settings; lsp.extraTriggerChars = tlsp.extraTriggerChars; lsp.usesLSP = use; if ( obj.contains( "share_process" ) && obj["share_process"].is_boolean() ) { @@ -1233,9 +1231,13 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std } lsp.commandParameters = obj.value( "command_parameters", lsp.commandParameters ); + if ( obj.contains( "initializationOptions" ) ) lsp.initializationOptions = obj["initializationOptions"]; + if ( obj.contains( "settings" ) ) + lsp.settings = obj["settings"]; + auto& fp = obj["file_patterns"]; for ( auto& pattern : fp ) @@ -1257,8 +1259,8 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std } } - sanitizeCommand( lsp.command, mManager->getWorkspaceFolder() ); - sanitizeCommand( lsp.commandParameters, mManager->getWorkspaceFolder() ); + LSPClientServer::sanitizeCommand( lsp.command, mManager->getWorkspaceFolder() ); + LSPClientServer::sanitizeCommand( lsp.commandParameters, mManager->getWorkspaceFolder() ); tryAddEnv( obj, lsp ); diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.cpp b/src/tools/ecode/plugins/lsp/lspclientserver.cpp index 125c88469..0696e6c4f 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.cpp @@ -14,6 +14,35 @@ namespace ecode { +void LSPClientServer::sanitizeCommand( std::string& cmd, const std::string& workspaceFolder ) { + static std::string cpucount( String::toString( Sys::getCPUCount() ) ); + static std::string userdir = Sys::getUserDirectory(); + String::replaceAll( cmd, "$NPROC", cpucount ); + String::replaceAll( cmd, "${nproc}", cpucount ); + String::replaceAll( cmd, "$PROJECTPATH", workspaceFolder ); + String::replaceAll( cmd, "${project_root}", workspaceFolder ); + String::replaceAll( cmd, "${workspaceFolder}", workspaceFolder ); + String::replaceAll( cmd, "$HOME", userdir ); + String::replaceAll( cmd, "${home}", userdir ); +} + +void LSPClientServer::sanitizeCommand( nlohmann::json& jsonObj, + const std::string& workspaceFolder ) { + if ( jsonObj.is_string() ) { + std::string s = jsonObj.get(); + sanitizeCommand( s, workspaceFolder ); + jsonObj = s; // Assign the modified string back to the JSON object + } else if ( jsonObj.is_object() ) { + for ( auto& el : jsonObj.items() ) { + sanitizeCommand( el.value(), workspaceFolder ); + } + } else if ( jsonObj.is_array() ) { + for ( auto& el : jsonObj ) { + sanitizeCommand( el, workspaceFolder ); + } + } +} + #define CONTENT_LENGTH "Content-Length" #define CONTENT_LENGTH_HEADER "Content-Length:" @@ -204,6 +233,12 @@ static std::vector parseDocumentLocation( const json& result ) { return ret; } +static json changeConfigurationParams( const json& settings ) { + json ret; + ret["settings"] = settings; + return ret; +} + static json changeWorkspaceFoldersParams( const std::vector& added, const std::vector& removed ) { json event; @@ -1164,6 +1199,7 @@ void LSPClientServer::initialize() { { "formatting", json{ { "dynamicRegistration", true } } }, { "rangeFormatting", json{ { "dynamicRegistration", true } } }, { "codeLens", json{ { "dynamicRegistration", true } } }, + { "didChangeConfiguration", json{ { "dynamicRegistration", true } } }, } }, { "window", json{ { "workDoneProgress", true }, { "showMessage", showMessage }, @@ -1219,7 +1255,10 @@ void LSPClientServer::initialize() { #endif mCapabilities.languages = mLanguagesSupported; mReady = true; - write( newRequest( "initialized" ) ); + write( newRequest( "initialized" ), [this]( const IdType&, const json& ) { + if ( !mLSP.settings.empty() ) + didChangeConfiguration( mLSP.settings, mWorkspaceFolder.getFSPath(), true ); + } ); sendQueuedMessages(); notifyServerInitialized(); @@ -2230,6 +2269,19 @@ void LSPClientServer::switchSourceHeader( const URI& document ) { } ); } +LSPClientServer::LSPRequestHandle +LSPClientServer::didChangeConfiguration( const nlohmann::json& settings, + const std::string& workspaceFolder, bool async ) { + auto finalSettings = settings; + sanitizeCommand( finalSettings, workspaceFolder ); + auto params = changeConfigurationParams( finalSettings ); + if ( async && needsAsync() ) { + sendAsync( newRequest( "workspace/didChangeConfiguration", params ) ); + return {}; + } + return send( newRequest( "workspace/didChangeConfiguration", params ) ); +} + LSPClientServer::LSPRequestHandle LSPClientServer::didChangeWorkspaceFolders( const std::vector& added, const std::vector& removed, diff --git a/src/tools/ecode/plugins/lsp/lspclientserver.hpp b/src/tools/ecode/plugins/lsp/lspclientserver.hpp index f3767288d..16064d1b6 100644 --- a/src/tools/ecode/plugins/lsp/lspclientserver.hpp +++ b/src/tools/ecode/plugins/lsp/lspclientserver.hpp @@ -28,6 +28,10 @@ class LSPClientServerManager; class LSPClientServer { public: + static void sanitizeCommand( std::string& cmd, const std::string& workspaceFolder ); + + static void sanitizeCommand( nlohmann::json& jsonObj, const std::string& workspaceFolder ); + static PluginIDType getID( const json& json ); using IdType = PluginIDType; @@ -147,6 +151,9 @@ class LSPClientServer { bool hasDocuments() const; + LSPRequestHandle didChangeConfiguration( const nlohmann::json& settings, + const std::string& workspaceFolder, bool async ); + LSPRequestHandle didChangeWorkspaceFolders( const std::vector& added, const std::vector& removed, bool async ); diff --git a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp index 5b8a0a950..debefacfd 100644 --- a/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientservermanager.cpp @@ -117,6 +117,11 @@ void LSPClientServerManager::tryRunServer( const std::shared_ptr& if ( ( server = serverUP.get() ) ) { mClients[id] = std::move( serverUP ); if ( server->isRunning() ) { + if ( !server->getDefinition().settings.empty() ) { + server->didChangeConfiguration( server->getDefinition().settings, + rootPath, true ); + } + if ( !mLSPWorkspaceFolder.uri.empty() ) server->didChangeWorkspaceFolders( { mLSPWorkspaceFolder }, {}, true ); } else { @@ -488,6 +493,11 @@ void LSPClientServerManager::didChangeWorkspaceFolders( const std::string& folde std::vector newWorkspaceFolder = { mLSPWorkspaceFolder }; Lock l( mClientsMutex ); for ( auto& server : mClients ) { + if ( !server.second->getDefinition().settings.empty() ) { + server.second->didChangeConfiguration( server.second->getDefinition().settings, folder, + true ); + } + server.second->didChangeWorkspaceFolders( newWorkspaceFolder, oldLSPWorkspaceFolder, true ); if ( server.second->getCapabilities().diagnosticProvider.workspaceDiagnostics ) { mPlugin->getManager()->sendRequest( PluginMessageType::WorkspaceDiagnostic, diff --git a/src/tools/ecode/plugins/lsp/lspdefinition.hpp b/src/tools/ecode/plugins/lsp/lspdefinition.hpp index 727c53a75..2a6152f8c 100644 --- a/src/tools/ecode/plugins/lsp/lspdefinition.hpp +++ b/src/tools/ecode/plugins/lsp/lspdefinition.hpp @@ -26,6 +26,7 @@ struct LSPDefinition { std::unordered_map languageIdsForFilePatterns; int port{ 0 }; nlohmann::json initializationOptions; + nlohmann::json settings; std::string usesLSP; bool shareProcessWithOtherDefinition{ false };