From a45e5e40742f24e4896383d1554ea5b7a0524e60 Mon Sep 17 00:00:00 2001 From: Squibid Date: Thu, 6 Jul 2023 12:28:31 -0400 Subject: make jdtls work without the seperate plugin :) --- after/ftplugin/java.lua | 272 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 252 insertions(+), 20 deletions(-) (limited to 'after/ftplugin') diff --git a/after/ftplugin/java.lua b/after/ftplugin/java.lua index c2c22f2..960dcdf 100644 --- a/after/ftplugin/java.lua +++ b/after/ftplugin/java.lua @@ -1,22 +1,254 @@ -local status_ok, jdtls = pcall(require, "jdtls") -if not status_ok then - return -end +local java_cmds = vim.api.nvim_create_augroup('java_cmds', {clear = true}) +local cache_vars = {} -jdtls.start_or_attach { - --[[ - There are 3 ways to get the jdtls server (ranked from easiest to hardest): - 1. Install it from your distros repo - 2. Download a milestone from: https://download.eclipse.org/jdtls/milestones - then extract it. - 3. Compile it from source: https://github.com/eclipse/eclipse.jdt.ls - good luck :) - - For all 3 of these you need to put the path to the jdtls binary in the - "cmd" section below - ]] - cmd = {''}, - root_dir = vim.fs.dirname( - vim.fs.find({ 'gradlew', '.git', 'mvnw' }, { upward = true })[1] - ), +local root_files = { + '.git', + 'mvnw', + 'gradlew', + 'pom.xml', + 'build.gradle', } + +local features = { + -- change this to `true` to enable codelens + codelens = true, + + -- change this to `true` if you have `nvim-dap`, + -- `java-test` and `java-debug-adapter` installed + debugger = false, +} + +local function get_jdtls_paths() + if cache_vars.paths then + return cache_vars.paths + end + + local path = {} + path.data_dir = vim.fn.stdpath('cache') .. '/nvim-jdtls' + local jdtls_install = require('mason-registry').get_package('jdtls'):get_install_path() + path.java_agent = jdtls_install .. '/lombok.jar' + path.launcher_jar = vim.fn.glob(jdtls_install .. '/plugins/org.eclipse.equinox.launcher_*.jar') + path.platform_config = jdtls_install .. '/config_linux' + path.bundles = {} + + -- Include java-test bundle if present + local java_test_path = require('mason-registry') + .get_package('java-test') + :get_install_path() + + local java_test_bundle = vim.split( + vim.fn.glob(java_test_path .. '/extension/server/*.jar'), + '\n' + ) + + if java_test_bundle[1] ~= '' then + vim.list_extend(path.bundles, java_test_bundle) + end + + -- Include java-debug-adapter bundle if present + local java_debug_path = require('mason-registry') + .get_package('java-debug-adapter') + :get_install_path() + + local java_debug_bundle = vim.split( + vim.fn.glob(java_debug_path .. '/extension/server/com.microsoft.java.debug.plugin-*.jar'), + '\n' + ) + + if java_debug_bundle[1] ~= '' then + vim.list_extend(path.bundles, java_debug_bundle) + end + + -- Useful if you're starting jdtls with a Java version that's + -- different from the one the project uses. + path.runtimes = { + -- Note: the field `name` must be a valid `ExecutionEnvironment`, + -- you can find the list here: + -- https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request + -- + -- This example assume you are using sdkman: https://sdkman.io + -- { + -- name = 'JavaSE-17', + -- path = vim.fn.expand('~/.sdkman/candidates/java/17.0.6-tem'), + -- }, + -- { + -- name = 'JavaSE-18', + -- path = vim.fn.expand('~/.sdkman/candidates/java/18.0.2-amzn'), + -- }, + } + + cache_vars.paths = path + return path +end + +local function enable_codelens(bufnr) + pcall(vim.lsp.codelens.refresh) + + vim.api.nvim_create_autocmd('BufWritePost', { + buffer = bufnr, + group = java_cmds, + desc = 'refresh codelens', + callback = function() + pcall(vim.lsp.codelens.refresh) + end, + }) +end + +local function enable_debugger(bufnr) + require('jdtls').setup_dap({hotcodereplace = 'auto'}) + require('jdtls.dap').setup_dap_main_class_configs() + + local opts = {buffer = bufnr} + vim.keymap.set('n', 'df', "lua require('jdtls').test_class()", opts) + vim.keymap.set('n', 'dn', "lua require('jdtls').test_nearest_method()", opts) +end + +local function jdtls_on_attach(client, bufnr) + if features.debugger then + enable_debugger(bufnr) + end + + if features.codelens then + enable_codelens(bufnr) + end + + -- The following mappings are based on the suggested usage of nvim-jdtls + -- https://github.com/mfussenegger/nvim-jdtls#usage + local opts = {buffer = bufnr} + vim.keymap.set('n', 'cri', "lua require('jdtls').organize_imports()", opts) + vim.keymap.set('n', 'crv', "lua require('jdtls').extract_variable()", opts) + vim.keymap.set('x', 'crv', "lua require('jdtls').extract_variable(true)", opts) + vim.keymap.set('n', 'crc', "lua require('jdtls').extract_constant()", opts) + vim.keymap.set('x', 'crc', "lua require('jdtls').extract_constant(true)", opts) + vim.keymap.set('x', 'crm', "lua require('jdtls').extract_method(true)", opts) +end + +local function jdtls_setup(event) + local jdtls = require('jdtls') + + local path = get_jdtls_paths() + local data_dir = path.data_dir .. '/' .. vim.fn.fnamemodify(vim.fn.getcwd(), ':p:h:t') + + if cache_vars.capabilities == nil then + jdtls.extendedClientCapabilities.resolveAdditionalTextEditsSupport = true + + local ok_cmp, cmp_lsp = pcall(require, 'cmp_nvim_lsp') + cache_vars.capabilities = vim.tbl_deep_extend( + 'force', + vim.lsp.protocol.make_client_capabilities(), + ok_cmp and cmp_lsp.default_capabilities() or {} + ) + end + + -- The command that starts the language server + -- See: https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line + local cmd = { + 'java', + + '-Declipse.application=org.eclipse.jdt.ls.core.id1', + '-Dosgi.bundles.defaultStartLevel=4', + '-Declipse.product=org.eclipse.jdt.ls.core.product', + '-Dlog.protocol=true', + '-Dlog.level=ALL', + '-javaagent:' .. path.java_agent, + '-Xms1G', + '--add-modules=ALL-SYSTEM', + '--add-opens', + 'java.base/java.util=ALL-UNNAMED', + '--add-opens', + 'java.base/java.lang=ALL-UNNAMED', + '-jar', path.launcher_jar, + '-configuration', path.platform_config, + '-data', data_dir, + } + + local lsp_settings = { + java = { + -- jdt = { + -- ls = { + -- vmargs = "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m" + -- } + -- }, + eclipse = { + downloadSources = true, + }, + configuration = { + updateBuildConfiguration = 'interactive', + runtimes = path.runtimes, + }, + maven = { + downloadSources = true, + }, + implementationsCodeLens = { + enabled = true, + }, + referencesCodeLens = { + enabled = true, + }, + -- inlayHints = { + -- parameterNames = { + -- enabled = 'all' -- literals, all, none + -- } + -- }, + format = { + enabled = true, + -- settings = { + -- profile = 'asdf' + -- }, + } + }, + signatureHelp = { + enabled = true, + }, + completion = { + favoriteStaticMembers = { + 'org.hamcrest.MatcherAssert.assertThat', + 'org.hamcrest.Matchers.*', + 'org.hamcrest.CoreMatchers.*', + 'org.junit.jupiter.api.Assertions.*', + 'java.util.Objects.requireNonNull', + 'java.util.Objects.requireNonNullElse', + 'org.mockito.Mockito.*', + }, + }, + contentProvider = { + preferred = 'fernflower', + }, + extendedClientCapabilities = jdtls.extendedClientCapabilities, + sources = { + organizeImports = { + starThreshold = 9999, + staticStarThreshold = 9999, + } + }, + codeGeneration = { + toString = { + template = '${object.className}{${member.name()}=${member.value}, ${otherMembers}}', + }, + useBlocks = true, + }, + } + + -- This starts a new client & server, + -- or attaches to an existing client & server depending on the `root_dir`. + jdtls.start_or_attach({ + cmd = cmd, + settings = lsp_settings, + on_attach = jdtls_on_attach, + capabilities = cache_vars.capabilities, + root_dir = jdtls.setup.find_root(root_files), + flags = { + allow_incremental_sync = true, + }, + init_options = { + bundles = path.bundles, + }, + }) +end + +vim.api.nvim_create_autocmd('FileType', { + group = java_cmds, + pattern = {'java'}, + desc = 'Setup jdtls', + callback = jdtls_setup, +}) -- cgit v1.2.1