From 9afac33690df7a936e68aab503a40c80c40c7abc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 10 Mar 2023 00:04:10 -0300 Subject: [PATCH] ecode: LSP command can be customized per platform. Added LSP support for Objective-C, ELM, Crystal, V, Scala, Swift and R. Added formatter support for Objective-C and V. Improved --health output. --- bin/assets/plugins/formatters.json | 9 +++- bin/assets/plugins/lspclient.json | 52 +++++++++++++++++++ src/tools/ecode/featureshealth.cpp | 6 +-- .../ecode/plugins/lsp/lspclientplugin.cpp | 36 +++++++++---- 4 files changed, 89 insertions(+), 14 deletions(-) diff --git a/bin/assets/plugins/formatters.json b/bin/assets/plugins/formatters.json index a2c3b8439..3605d0d2f 100644 --- a/bin/assets/plugins/formatters.json +++ b/bin/assets/plugins/formatters.json @@ -12,8 +12,8 @@ "command": "prettier $FILENAME" }, { - "language": ["c", "cpp"], - "file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$"], + "language": ["c", "cpp", "objective-c"], + "file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$", "%.m$"], "command": "clang-format --style=file $FILENAME" }, { @@ -66,6 +66,11 @@ "language": "haskell", "file_patterns": ["%.hs$"], "command": "ormolu $FILENAME" + }, + { + "language": "v", + "file_patterns": ["%.v$"], + "command": "v fmt $FILENAME" } ] } diff --git a/bin/assets/plugins/lspclient.json b/bin/assets/plugins/lspclient.json index d48a3caaa..95771857c 100644 --- a/bin/assets/plugins/lspclient.json +++ b/bin/assets/plugins/lspclient.json @@ -61,6 +61,11 @@ "use": "clangd", "file_patterns": ["%.inl$", "%.cpp$", "%.hpp$", "%.cc$", "%.cxx$", "%.c++$", "%.hh$", "%.hxx$", "%.h++$", "%.objcpp$"] }, + { + "language": "objective-c", + "use": "clangd", + "file_patterns": ["%.m$"] + }, { "language": "d", "name": "serve-d", @@ -213,6 +218,53 @@ "command": "cmake-language-server", "file_patterns": ["%.cmake$", "CMakeLists.txt$"], "rootIndicationFileNames": ["build"] + }, + { + "language": "elm", + "name": "elm-language-server", + "url": "https://github.com/elm-tooling/elm-language-server", + "command": "elm-language-server", + "file_patterns": ["%.elm$"], + "rootIndicationFileNames": ["elm.json"] + }, + { + "language": "crystal", + "name": "crystalline", + "url": "https://github.com/elbywan/crystalline", + "command": "crystalline --stdio", + "file_patterns": ["%.cr$"], + "rootIndicationFileNames": ["shard.yml"] + }, + { + "language": "v", + "name": "vls", + "url": "https://github.com/vlang/vls", + "command": "v ls", + "file_patterns": ["%.v$"] + }, + { + "language": "scala", + "name": "metals", + "url": "https://github.com/scalameta/metals", + "command": "metals", + "file_patterns": ["%.sc$", "%.scala$"] + }, + { + "language": "swift", + "name": "sourcekit-lsp", + "url": "https://github.com/apple/sourcekit-lsp", + "command": { + "macos": "xcrun sourcekit-lsp", + "other": "sourcekit-lsp" + }, + "file_patterns": ["%.swift$"] + }, + { + "language": "r", + "name": "r languageserver", + "url": "https://github.com/REditorSupport/languageserver", + "command": "R --slave -e\"languageserver::run()\"", + "file_patterns": ["%.r$"] } ] } diff --git a/src/tools/ecode/featureshealth.cpp b/src/tools/ecode/featureshealth.cpp index 6aff1f4f8..bde58cd3f 100644 --- a/src/tools/ecode/featureshealth.cpp +++ b/src/tools/ecode/featureshealth.cpp @@ -85,8 +85,8 @@ std::vector FeaturesHealth::getHealth( PluginManager LSPDefinition found = lsp->getClientManager().getLSPForLang( def.getLSPName(), def.getFiles() ); if ( !found.command.empty() ) { - lang.lsp.name = String::split( found.command, ' ' )[0]; - lang.lsp.path = Sys::which( lang.lsp.name ); + lang.lsp.name = found.name; + lang.lsp.path = Sys::which( String::split( found.command, ' ' )[0] ); lang.lsp.found = !lang.lsp.path.empty(); } } @@ -128,7 +128,7 @@ std::string FeaturesHealth::generateHealthStatus( PluginManager* pluginManager, } for ( const auto& ht : status ) { - table.add_row( { ht.lang, "Found", ht.lsp.name.empty() ? "None" : ht.lsp.name, + table.add_row( { ht.lang, "✓", ht.lsp.name.empty() ? "None" : ht.lsp.name, ht.linter.name.empty() ? "None" : ht.linter.name, ht.formatter.name.empty() ? "None" : ht.formatter.name } ); diff --git a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp index 2bcb11361..bacdf3b4a 100644 --- a/src/tools/ecode/plugins/lsp/lspclientplugin.cpp +++ b/src/tools/ecode/plugins/lsp/lspclientplugin.cpp @@ -174,7 +174,7 @@ static LSPURIAndServer getServerURIFromTextDocumentURI( LSPClientServerManager& return { uri, manager.getOneLSPClientServer( uri ) }; } -static void sanitizeCommand( std::string& cmd ) { +static void sanitizeCommand( std::string cmd ) { String::replaceAll( cmd, "$NPROC", String::toString( Sys::getCPUCount() ) ); } @@ -649,6 +649,26 @@ void LSPClientPlugin::load( PluginManager* pluginManager ) { fireReadyCbs(); } +static std::string parseCommand( nlohmann::json cmd ) { + std::string command; + if ( cmd.is_string() ) { + command = cmd.get(); + } else if ( !cmd.is_object() ) { + return ""; + } else { + std::string platform( String::toLower( Sys::getPlatform() ) ); + if ( cmd.contains( platform ) && cmd[platform].is_string() ) { + command = cmd[platform].get(); + } else { + platform = "other"; + if ( cmd.contains( platform ) && cmd[platform].is_string() ) + command = cmd[platform].get(); + } + } + sanitizeCommand( command ); + return command; +} + void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std::string& path, bool updateConfigFile ) { std::string data; @@ -724,16 +744,14 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std // Allow overriding the command for already defined LSP // And allow adding parameters to the already defined LSP 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() ) ) ) { + ( obj.contains( "command" ) || ( obj.contains( "command_parameters" ) && + obj.at( "command_parameters" ).is_string() ) ) ) { 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.contains( "command" ) ) { + lspR.command = parseCommand( obj["command"] ); } if ( !obj.value( "command_parameters", "" ).empty() ) { std::string cmdParam( obj.value( "command_parameters", "" ) ); @@ -767,7 +785,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std if ( tlsp.name == use ) { lsp.language = obj["language"]; foundTlsp = true; - lsp.command = tlsp.command; + lsp.command = parseCommand( tlsp.command ); lsp.name = tlsp.name; lsp.rootIndicationFileNames = tlsp.rootIndicationFileNames; lsp.url = tlsp.url; @@ -782,7 +800,7 @@ void LSPClientPlugin::loadLSPConfig( std::vector& lsps, const std } } else { lsp.language = obj["language"]; - lsp.command = obj["command"]; + lsp.command = parseCommand( obj["command"] ); lsp.name = obj["name"]; }