diff --git a/modules/plugins/lsp/presets/default.nix b/modules/plugins/lsp/presets/default.nix index caae9347..a1d4c92a 100644 --- a/modules/plugins/lsp/presets/default.nix +++ b/modules/plugins/lsp/presets/default.nix @@ -8,6 +8,7 @@ ./deno.nix ./harper.nix ./intelephense.nix + ./jdt-language-server.nix ./jinja-lsp.nix ./jq-lsp.nix ./julia-languageserver.nix diff --git a/modules/plugins/lsp/presets/jdt-language-server.nix b/modules/plugins/lsp/presets/jdt-language-server.nix new file mode 100644 index 00000000..fc5e2900 --- /dev/null +++ b/modules/plugins/lsp/presets/jdt-language-server.nix @@ -0,0 +1,136 @@ +{ + config, + lib, + pkgs, + ... +}: let + inherit (lib.meta) getExe; + inherit (lib.modules) mkIf; + inherit (lib.options) mkEnableOption; + inherit (lib.generators) mkLuaInline; + inherit (lib.nvim.dag) entryBefore; + + cfg = config.vim.lsp.presets.jdt-language-server; +in { + options.vim.lsp.presets.jdt-language-server = { + enable = mkEnableOption "the Eclipse JDT Language Server"; + }; + + config = mkIf cfg.enable { + vim = { + lsp.servers.jdt-language-server = { + enable = true; + cmd = mkLuaInline '' + { + '${getExe pkgs.jdt-language-server}', + '-configuration', + get_jdtls_config_dir(), + '-data', + get_jdtls_workspace_dir(), + get_jdtls_jvm_args(), + } + ''; + root_markers = [ + # Multi-module projects + ".git" + "build.gradle" + "build.gradle.kts" + # Single-module projects + "build.xml" # Ant + "pom.xml" # Maven + "settings.gradle" # Gradle + "settings.gradle.kts" # Gradle + ]; + init_options = { + workspace = mkLuaInline "get_jdtls_workspace_dir()"; + jvm_args = {}; + os_config = mkLuaInline "nil"; + }; + handlers = { + "textDocument/codeAction" = mkLuaInline "jdtls_on_textdocument_codeaction"; + "textDocument/rename" = mkLuaInline "jdtls_on_textdocument_rename"; + "workspace/applyEdit" = mkLuaInline "jdtls_on_workspace_applyedit"; + "language/status" = mkLuaInline "vim.schedule_wrap(jdtls_on_language_status)"; + }; + }; + luaConfigRC.jdtls-util = entryBefore ["lsp-servers"] '' + local jdtls_handlers = require 'vim.lsp.handlers' + + local jdtls_env = { + HOME = vim.uv.os_homedir(), + XDG_CACHE_HOME = os.getenv 'XDG_CACHE_HOME', + JDTLS_JVM_ARGS = os.getenv 'JDTLS_JVM_ARGS', + } + + local function get_cache_dir() + return jdtls_env.XDG_CACHE_HOME and jdtls_env.XDG_CACHE_HOME or jdtls_env.HOME .. '/.cache' + end + + local function get_jdtls_cache_dir() + return get_cache_dir() .. '/jdtls' + end + + local function get_jdtls_config_dir() + return get_jdtls_cache_dir() .. '/config' + end + + local function get_jdtls_workspace_dir() + return get_jdtls_cache_dir() .. '/workspace' + end + + local function get_jdtls_jvm_args() + local args = {} + for a in string.gmatch((jdtls_env.JDTLS_JVM_ARGS or '''), '%S+') do + local arg = string.format('--jvm-arg=%s', a) + table.insert(args, arg) + end + return unpack(args) + end + + -- TextDocument version is reported as 0, override with nil so that + -- the client doesn't think the document is newer and refuses to update + -- See: https://github.com/eclipse/eclipse.jdt.ls/issues/1695 + local function jdtls_fix_zero_version(workspace_edit) + if workspace_edit and workspace_edit.documentChanges then + for _, change in pairs(workspace_edit.documentChanges) do + local text_document = change.textDocument + if text_document and text_document.version and text_document.version == 0 then + text_document.version = nil + end + end + end + return workspace_edit + end + + local function jdtls_on_textdocument_codeaction(err, actions, ctx) + for _, action in ipairs(actions) do + -- TODO: (steelsojka) Handle more than one edit? + if action.command == 'java.apply.workspaceEdit' then -- 'action' is Command in java format + action.edit = jdtls_fix_zero_version(action.edit or action.arguments[1]) + elseif type(action.command) == 'table' and action.command.command == 'java.apply.workspaceEdit' then -- 'action' is CodeAction in java format + action.edit = jdtls_fix_zero_version(action.edit or action.command.arguments[1]) + end + end + + jdtls_handlers[ctx.method](err, actions, ctx) + end + + local function jdtls_on_textdocument_rename(err, workspace_edit, ctx) + jdtls_handlers[ctx.method](err, jdtls_fix_zero_version(workspace_edit), ctx) + end + + local function jdtls_on_workspace_applyedit(err, workspace_edit, ctx) + jdtls_handlers[ctx.method](err, jdtls_fix_zero_version(workspace_edit), ctx) + end + + -- Non-standard notification that can be used to display progress + local function jdtls_on_language_status(_, result) + local command = vim.api.nvim_command + command 'echohl ModeMsg' + command(string.format('echo "%s"', result.message)) + command 'echohl None' + end + ''; + }; + }; +}