diff --git a/ftplugin/java.lua b/ftplugin/java.lua new file mode 100644 index 0000000..fafb9ee --- /dev/null +++ b/ftplugin/java.lua @@ -0,0 +1,253 @@ +vim.opt_local.shiftwidth = 2 +vim.opt_local.tabstop = 2 +vim.opt_local.cmdheight = 2 -- more space in the neovim command line for displaying messages + +local status, jdtls = pcall(require, 'jdtls') +if not status then + return +end + +-- Determine OS +local home = os.getenv 'HOME' +if vim.fn.has 'mac' == 1 then + WORKSPACE_PATH = home .. '/workspace/' + CONFIG = 'mac' +elseif vim.fn.has 'unix' == 1 then + WORKSPACE_PATH = home .. '/workspace/' + CONFIG = 'linux' +else + print 'Unsupported system' +end + +-- Find root of project +local root_markers = { '.git', 'mvnw', 'gradlew', 'pom.xml', 'build.gradle' } +local root_dir = require('jdtls.setup').find_root(root_markers) +if root_dir == '' then + return +end + +local extendedClientCapabilities = jdtls.extendedClientCapabilities +extendedClientCapabilities.resolveAdditionalTextEditsSupport = true + +local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ':p:h:t') + +local workspace_dir = WORKSPACE_PATH .. project_name + +JAVA_DAP_ACTIVE = true + +local bundles = { + vim.fn.glob( + home .. '/.config/nvim/.java-debug/com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin-*.jar' + ), +} + +vim.list_extend(bundles, vim.split(vim.fn.glob(home .. '/.config/nvim/.vscode-java-test/server/*.jar'), '\n')) + +local function lsp_keymaps(bufnr) + local opt = { noremap = true, silent = true } + local keymap = vim.api.nvim_buf_set_keymap + keymap(bufnr, 'n', 'gD', 'lua vim.lsp.buf.declaration()', opt) + keymap(bufnr, 'n', 'gd', 'lua vim.lsp.buf.definition()', opt) + keymap(bufnr, 'n', 'K', 'lua vim.lsp.buf.hover()', opt) + keymap(bufnr, 'n', 'gI', 'lua vim.lsp.buf.implementation()', opt) + keymap(bufnr, 'n', 'gr', 'TroubleToggle lsp_references', opt) + keymap(bufnr, 'n', 'gl', 'lua vim.diagnostic.open_float()', opt) + keymap(bufnr, 'n', 'lf', 'lua vim.lsp.buf.format { async = true }', opt) + keymap(bufnr, 'n', 'li', 'LspInfo', opt) + keymap(bufnr, 'n', 'lI', 'LspInstallInfo', opt) + keymap(bufnr, 'n', 'la', 'lua vim.lsp.buf.code_action()', opt) + keymap(bufnr, 'n', 'lj', 'lua vim.diagnostic.goto_next({buffer=0})', opt) + keymap(bufnr, 'n', 'lk', 'lua vim.diagnostic.goto_prev({buffer=0})', opt) + keymap(bufnr, 'n', 'lr', 'lua vim.lsp.buf.rename()', opt) + keymap(bufnr, 'n', 'ls', 'lua vim.lsp.buf.signature_help()', opt) + keymap(bufnr, 'n', 'lq', 'lua vim.diagnostic.setloclist()', opt) + keymap(bufnr, 'n', 'e', 'lua vim.diagnostic.open_float()', opt) +end + +local on_attach = function(client, bufnr) + --[[ vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc') ]] + lsp_keymaps(bufnr) + if client.name == 'jdtls' then + client.server_capabilities.documentFormattingProvider = false + end + if client.name == 'jdt.ls' then + client.server_capabilities.documentFormattingProvider = false + vim.lsp.codelens.refresh() + if JAVA_DAP_ACTIVE then + require('jdtls').setup_dap { hotcodereplace = 'auto' } + require('jdtls.dap').setup_dap_main_class_configs() + end + end + local ft = vim.api.nvim_buf_get_option(bufnr, 'filetype') + local nls = require 'plugins.null-ls' + + local enable = false + if nls.has_formatter(ft) then + enable = client.name == 'null-ls' + else + enable = not (client.name == 'null-ls') + end + -- util.info(client.name .. " " .. (enable and "yes" or "no"), "format") + client.server_capabilities.documentFormattingProvider = enable +end + +local cmp_nvim_lsp = require 'cmp_nvim_lsp' +local capabilities = vim.lsp.protocol.make_client_capabilities() +capabilities.textDocument.completion.completionItem.snippetSupport = true +capabilities = cmp_nvim_lsp.default_capabilities(capabilities) +-- See `:help vim.lsp.start_client` for an overview of the supported `config` options. +local config = { + -- The command that starts the language server + -- See: https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line + cmd = { + + -- 💀 + 'java', -- or '/path/to/java11_or_newer/bin/java' + -- depends on if `java` is in your $PATH env variable and if it points to the right version. + + '-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:' .. home .. '/.local/share/nvim/mason/packages/jdtls/lombok.jar', + '-Xms1g', + '--add-modules=ALL-SYSTEM', + '--add-opens', + 'java.base/java.util=ALL-UNNAMED', + '--add-opens', + 'java.base/java.lang=ALL-UNNAMED', + + -- 💀 + '-jar', + vim.fn.glob(home .. '/.local/share/nvim/mason/packages/jdtls/plugins/org.eclipse.equinox.launcher_*.jar'), + -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^ + -- Must point to the Change this to + -- eclipse.jdt.ls installation the actual version + + -- 💀 + '-configuration', + home .. '/.local/share/nvim/mason/packages/jdtls/config_' .. CONFIG, + -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^ + -- Must point to the Change to one of `linux`, `win` or `mac` + -- eclipse.jdt.ls installation Depending on your system. + + -- 💀 + -- See `data directory configuration` section in the README + '-data', + workspace_dir, + }, + + on_attach = on_attach, + capabilities = capabilities, + + -- 💀 + -- This is the default if not provided, you can remove it. Or adjust as needed. + -- One dedicated LSP server & client will be started per unique root_dir + root_dir = root_dir, + + -- Here you can configure eclipse.jdt.ls specific settings + -- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request + -- or https://github.com/redhat-developer/vscode-java#supported-vs-code-settings + -- for a list of options + settings = { + java = { + eclipse = { + downloadSources = true, + }, + configuration = { + updateBuildConfiguration = 'interactive', + }, + maven = { + downloadSources = true, + }, + implementationsCodeLens = { + enabled = true, + }, + referencesCodeLens = { + enabled = true, + }, + references = { + includeDecompiledSources = true, + }, + -- Set this to true to use jdtls as your formatter + format = { + enabled = false, + }, + }, + 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 = extendedClientCapabilities, + sources = { + organizeImports = { + starThreshold = 9999, + staticStarThreshold = 9999, + }, + }, + codeGeneration = { + toString = { + template = '${object.className}{${member.name()}=${member.value}, ${otherMembers}}', + }, + useBlocks = true, + }, + }, + + flags = { + allow_incremental_sync = true, + }, + + -- Language server `initializationOptions` + -- You need to extend the `bundles` with paths to jar files + -- if you want to use additional eclipse.jdt.ls plugins. + -- + -- See https://github.com/mfussenegger/nvim-jdtls#java-debug-installation + -- + -- If you don't plan on using the debugger or other eclipse.jdt.ls plugins you can remove this + init_options = { + -- bundles = {}, + bundles = bundles, + }, +} + +vim.api.nvim_create_autocmd({ 'BufWritePost' }, { + pattern = { '*.java' }, + callback = function() + vim.lsp.codelens.refresh() + end, +}) + +-- This starts a new client & server, +-- or attaches to an existing client & server depending on the `root_dir`. +require('jdtls').start_or_attach(config) + +vim.cmd "command! -buffer -nargs=? -complete=custom,v:lua.require'jdtls'._complete_compile JdtCompile lua require('jdtls').compile()" +vim.cmd "command! -buffer -nargs=? -complete=custom,v:lua.require'jdtls'._complete_set_runtime JdtSetRuntime lua require('jdtls').set_runtime()" +vim.cmd "command! -buffer JdtUpdateConfig lua require('jdtls').update_project_config()" +vim.cmd "command! -buffer JdtBytecode lua require('jdtls').javap()" + +-- Shorten function name +local keymap = vim.keymap.set +-- Silent keymap option +local opts = { silent = true } + +keymap('n', 'jo', "lua require'jdtls'.organize_imports()", opts) +keymap('n', 'jv', "lua require('jdtls').extract_variable()", opts) +keymap('n', 'jc', "lua require('jdtls').extract_constant()", opts) +keymap('n', 'jt', "lua require'jdtls'.test_nearest_method()", opts) +keymap('n', 'jT', "lua require'jdtls'.test_class()", opts) +keymap('n', 'ju', 'JdtUpdateConfig', opts) + +keymap('v', 'jv', "lua require('jdtls').extract_variable(true)", opts) +keymap('v', 'jc', "lua require('jdtls').extract_constant(true)", opts) +keymap('v', 'jm', "lua require('jdtls').extract_method(true)", opts) diff --git a/lua/config/mappings.lua b/lua/config/mappings.lua index dd7d607..d9e9061 100644 --- a/lua/config/mappings.lua +++ b/lua/config/mappings.lua @@ -220,3 +220,45 @@ local mappings = { } -- }}} wk.register(mappings, opts) + +local optsj = { + mode = "n", -- NORMAL mode + prefix = "", + buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings + silent = true, -- use `silent` when creating keymaps + noremap = true, -- use `noremap` when creating keymaps + nowait = true, -- use `nowait` when creating keymaps +} + +local vopts = { + mode = "v", -- VISUAL mode + prefix = "", + buffer = nil, -- Global mappings. Specify a buffer number for buffer local mappings + silent = true, -- use `silent` when creating keymaps + noremap = true, -- use `noremap` when creating keymaps + nowait = true, -- use `nowait` when creating keymaps +} + +local mappingsj = { + L = { + name = "Java", + o = { "lua require'jdtls'.organize_imports()", "Organize Imports" }, + v = { "lua require('jdtls').extract_variable()", "Extract Variable" }, + c = { "lua require('jdtls').extract_constant()", "Extract Constant" }, + t = { "lua require'jdtls'.test_nearest_method()", "Test Method" }, + T = { "lua require'jdtls'.test_class()", "Test Class" }, + u = { "JdtUpdateConfig", "Update Config" }, + }, +} + +local vmappings = { + L = { + name = "Java", + v = { "lua require('jdtls').extract_variable(true)", "Extract Variable" }, + c = { "lua require('jdtls').extract_constant(true)", "Extract Constant" }, + m = { "lua require('jdtls').extract_method(true)", "Extract Method" }, + }, +} + +wk.register(mappingsj, optsj) +wk.register(vmappings, vopts) diff --git a/lua/plugins/init.lua b/lua/plugins/init.lua index f2e8847..72dbd8d 100644 --- a/lua/plugins/init.lua +++ b/lua/plugins/init.lua @@ -11,6 +11,7 @@ return { }, }, 'williamboman/mason-lspconfig.nvim', + 'mfussenegger/nvim-jdtls', { 'phaazon/hop.nvim', -- event = 'BufRead', diff --git a/lua/plugins/lsp/init.lua b/lua/plugins/lsp/init.lua index 65d3834..0e47a12 100644 --- a/lua/plugins/lsp/init.lua +++ b/lua/plugins/lsp/init.lua @@ -9,6 +9,7 @@ M.tools = { 'clangd', 'gopls', 'sumneko_lua', + 'jdtls', } function M.config() @@ -112,7 +113,17 @@ function M.config() } opts = vim.tbl_deep_extend('force', clang_opts, opts) end + if server == 'jdtls' then + vim.cmd "command! -buffer -nargs=? -complete=custom,v:lua.require'jdtls'._complete_compile JdtCompile lua require('jdtls').compile()" + vim.cmd "command! -buffer -nargs=? -complete=custom,v:lua.require'jdtls'._complete_set_runtime JdtSetRuntime lua require('jdtls').set_runtime()" + vim.cmd "command! -buffer JdtUpdateConfig lua require('jdtls').update_project_config()" + -- vim.cmd "command! -buffer JdtJol lua require('jdtls').jol()" + vim.cmd "command! -buffer JdtBytecode lua require('jdtls').javap()" + -- vim.cmd "command! -buffer JdtJshell lua require('jdtls').jshell()" + end + if server == 'jdtls' then goto continue end lspconfig[server].setup(opts) + ::continue:: end local config = {