diff --git a/lua/auto-jdtls/init.lua b/lua/auto-jdtls/init.lua new file mode 100644 index 0000000..b8b0f1f --- /dev/null +++ b/lua/auto-jdtls/init.lua @@ -0,0 +1,7 @@ +local M = {} + +M.setup = function() + require("auto-jdtls.utils").attach_jdtls() +end + +return M diff --git a/lua/auto-jdtls/utils.lua b/lua/auto-jdtls/utils.lua new file mode 100644 index 0000000..c365e2d --- /dev/null +++ b/lua/auto-jdtls/utils.lua @@ -0,0 +1,201 @@ +local M = {} +M.java_filetypes = { "java" } +M.root_markers = { ".git", "mvnw", "gradlew", "pom.xml", "build.gradle" } + +-- Utility function to extend or override a config table, similar to the way +-- that Plugin.opts works. +---@param config table +---@param custom function | table | nil +M.extend_or_override = function(config, custom, ...) + if type(custom) == "function" then + config = custom(config, ...) or config + elseif custom then + config = vim.tbl_deep_extend("force", config, custom) --[[@as table]] + end + return config +end + +function M.capabilities() + local status_ok, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp") + if status_ok then + return cmp_nvim_lsp.default_capabilities() + end + + local CAPABILITIES = vim.lsp.protocol.make_client_capabilities() + CAPABILITIES.textDocument.completion.completionItem.snippetSupport = true + CAPABILITIES.textDocument.completion.completionItem.resolveSupport = { + properties = { + "documentation", + "detail", + "additionalTextEdits", + }, + } + + return CAPABILITIES +end +-- stylua: ignore +M.lsp_keymaps=function () + vim.keymap.set({ "n", "v" }, "l", "", { desc = "LSP" }) + -- Set vim motion for + l + h to show code documentation about the code the cursor is currently over if available + vim.keymap.set("n", "lh", vim.lsp.buf.hover, { desc = "Code Hover Documentation" }) + -- Set vim motion for + l + d to go where the code/variable under the cursor was defined + vim.keymap.set("n", "ld", vim.lsp.buf.definition, { desc = "Code Goto Definition" }) + -- Set vim motion for + l + a for display code action suggestions for code diagnostics in both normal and visual mode + vim.keymap.set({ "n", "v" }, "la", vim.lsp.buf.code_action, { desc = "Code Actions" }) + -- Set vim motion for + l + r to display references to the code under the cursor + vim.keymap.set("n", "lr", require("telescope.builtin").lsp_references, { desc = "Code Goto References" }) + -- Set vim motion for + l + i to display implementations to the code under the cursor + vim.keymap.set("n", "li", require("telescope.builtin").lsp_implementations, { desc = "Code Goto Implementations" }) + -- Set a vim motion for + l + R to smartly rename the code under the cursor + vim.keymap.set("n", "lR", vim.lsp.buf.rename, { desc = "Code Rename" }) + -- Set a vim motion for + l + D to go to where the code/object was declared in the project (class file) + vim.keymap.set("n", "lD", vim.lsp.buf.declaration, { desc = "Code Goto Declaration" }) +end + +-- stylua: ignore +M.jdtls_keymaps=function () + -- add keymaps + vim.keymap.set('n', 'J', "", { desc = "Java" }) + -- Set a Vim motion to + J + o to organize imports in normal mode + vim.keymap.set('n', 'Jo', " lua require('jdtls').organize_imports()", { desc = "Java Organize Imports" }) + -- Set a Vim motion to + J + v to extract the code under the cursor to a variable + vim.keymap.set('n', 'Jv', " lua require('jdtls').extract_variable()", { desc = "Java Extract Variable" }) + -- Set a Vim motion to + J + v to extract the code selected in visual mode to a variable + vim.keymap.set('v', 'Jv', " lua require('jdtls').extract_variable(true)", { desc = "Java Extract Variable" }) + -- Set a Vim motion to + J + C to extract the code under the cursor to a static variable + vim.keymap.set('n', 'JC', " lua require('jdtls').extract_constant()", { desc = "Java Extract Constant" }) + -- Set a Vim motion to + J + C to extract the code selected in visual mode to a static variable + vim.keymap.set('v', 'JC', " lua require('jdtls').extract_constant(true)", { desc = "Java Extract Constant" }) + -- Set a Vim motion to + J + t to run the test method currently under the cursor + vim.keymap.set('n', 'Jt', " lua require('jdtls').test_nearest_method()", { desc = "Java Test Method" }) + -- Set a Vim motion to + J + t to run the test method that is currently selected in visual mode + vim.keymap.set('v', 'Jt', " lua require('jdtls').test_nearest_method(true)", { desc = "Java Test Method" }) + -- Set a Vim motion to + J + T to run an entire test suite (class) + vim.keymap.set('n', 'JT', " lua require('jdtls').test_class()", { desc = "Java Test Class" }) + -- Set a Vim motion to + J + u to update the project configuration + vim.keymap.set('n', 'Ju', " JdtUpdateConfig", { desc = "Java Update Config" }) +end + +M.opts = { + root_dir = require("jdtls.setup").find_root(M.root_markers), + project_name = function() + return vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t") + end, + + -- Where are the config and workspace dirs for a project? + jdtls_config_dir = function(project_name) + return vim.fn.stdpath("cache") .. "/jdtls/" .. project_name .. "/config" + end, + jdtls_workspace_dir = function(project_name) + return vim.fn.stdpath("cache") .. "/jdtls/" .. project_name .. "/workspace" + end, + cmd = { vim.fn.exepath("jdtls") }, + full_cmd = function(opts) + local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t") + local cmd = vim.deepcopy(opts.cmd) + if project_name then + vim.list_extend(cmd, { + "-configuration", + opts.jdtls_config_dir(project_name), + "-data", + opts.jdtls_workspace_dir(project_name), + }) + end + return cmd + end, + + -- These depend on nvim-dap, but can additionally be disabled by setting false here. + dap = { hotcodereplace = "auto", config_overrides = {} }, + dap_main = {}, + test = true, + settings = { + java = { + inlayHints = { + parameterNames = { + enabled = "all", + }, + }, + }, + }, +} + +M.attach_jdtls = function() + local opt = vim.opt + opt.shiftwidth = 4 + opt.tabstop = 4 + opt.softtabstop = 4 + opt.ts = 4 + opt.expandtab = true + + local mason_registry = require("mason-registry") + local bundles = {} ---@type string[] + if M.opts.dap and mason_registry.is_installed("java-debug-adapter") then + local java_dbg_pkg = mason_registry.get_package("java-debug-adapter") + local java_dbg_path = java_dbg_pkg:get_install_path() + local jar_patterns = { + java_dbg_path .. "/extension/server/com.microsoft.java.debug.plugin-*.jar", + } + -- java-test also depends on java-debug-adapter. + if M.opts.test and mason_registry.is_installed("java-test") then + local java_test_pkg = mason_registry.get_package("java-test") + local java_test_path = java_test_pkg:get_install_path() + vim.list_extend(jar_patterns, { + java_test_path .. "/extension/server/*.jar", + }) + end + for _, jar_pattern in ipairs(jar_patterns) do + for _, bundle in ipairs(vim.split(vim.fn.glob(jar_pattern), "\n")) do + table.insert(bundles, bundle) + end + end + end + -- initialisasi config + local function attach_jdtls() + -- Configuration can be augmented and overridden by opts.jdtls + local config = M.extend_or_override({ + cmd = M.opts.full_cmd(M.opts), + root_dir = require("jdtls.setup").find_root(M.root_markers), + init_options = { + bundles = bundles, + }, + settings = M.opts.settings, + -- enable CMP capabilities + -- capabilities = require("user.lsp.handlers").capabilities or nil, + -- capabilities = require("auto-lsp.lsp.handlers").capabilities or nil, + capabilities = M.capabilities() or nil, + }, M.opts.jdtls) + + -- Existing server will be reused if the root_dir matches. + require("jdtls").start_or_attach(config) + end + + vim.api.nvim_create_autocmd("FileType", { + pattern = M.java_filetypes, + callback = attach_jdtls, + }) + + vim.api.nvim_create_autocmd("LspAttach", { + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + -- stylua: ignore + if client and client.name == "jdtls" then + M.jdtls_keymaps() + M.lsp_keymaps() + if M.opts.dap and mason_registry.is_installed("java-debug-adapter") then + -- custom init for Java debugger + require("jdtls").setup_dap(M.opts.dap) + require("jdtls.dap").setup_dap_main_class_configs(M.opts.dap_main) + end + + -- User can set additional keymaps in opts.on_attach + if M.opts.on_attach then + M.opts.on_attach(args) + end + end + end, + }) + + attach_jdtls() +end + +return M diff --git a/lua/pcode/plugins/lang/java3.lua b/lua/pcode/plugins/lang/java3.lua new file mode 100644 index 0000000..12e37e0 --- /dev/null +++ b/lua/pcode/plugins/lang/java3.lua @@ -0,0 +1,135 @@ +local M = {} +M = { + { + "williamboman/mason-lspconfig.nvim", + opts = function(_, opts) + opts.skip_config = opts.skip_config or {} + vim.list_extend(opts.skip_config, { "jdtls" }) + end, + keys = { + { "l", "", desc = "LSP", mode = { "n", "v" } }, + }, + }, + { + "mfussenegger/nvim-jdtls", + ft = { "java" }, + enabled = true, + config = function() + require("auto-jdtls").setup() + end, + }, + { + "nvim-treesitter/nvim-treesitter", + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { "java" }) + end, + }, + { + "williamboman/mason-lspconfig.nvim", + opts = function(_, opts) + opts.ensure_installed = opts.ensure_installed or {} + vim.list_extend(opts.ensure_installed, { "jdtls" }) + end, + }, + { + "pojokcodeid/auto-conform.nvim", + event = "VeryLazy", + opts = function(_, opts) + vim.list_extend(opts.ensure_installed, { "java-debug-adapter", "java-test" }) + opts.formatters_by_ft.java = { "lsp_fmt" } + end, + }, + { + "nvim-neotest/neotest", + dependencies = { + "nvim-neotest/nvim-nio", + "nvim-lua/plenary.nvim", + "antoinemadec/FixCursorHold.nvim", + "nvim-treesitter/nvim-treesitter", + "andy-bell101/neotest-java", + }, + config = function() + local project_type = "maven" + local gradle_file = vim.fn.findfile("build.gradle", vim.fn.getcwd()) + if gradle_file then + project_type = "gradle" + end + + require("neotest").setup({ + adapters = { + require("neotest-java")({ + -- function to determine which runner to use based on project path + determine_runner = function(project_root_path) + -- return should be "maven" or "gradle" + return project_type + end, + -- override the builtin runner discovery behaviour to always use given + -- tool. Default is "nil", so no override + force_runner = nil, + -- if the automatic runner discovery can't uniquely determine whether + -- to use Gradle or Maven, fallback to using this runner. Default is + -- "gradle or maven" + fallback_runner = project_type, + }), + }, + }) + end, + -- stylua: ignore + keys = { + { "T","",desc="  Test"}, + { "Tt", function() require("neotest").run.run(vim.fn.expand("%")) end, desc = "Run File" }, + { "Tr", function() require("neotest").run.run() end, desc = "Run Nearest" }, + { "TT", function() require("neotest").run.run(vim.loop.cwd()) end, desc = "Run All Test Files" }, + { "Tl", function() require("neotest").run.run_last() end, desc = "Run Last" }, + { "Ts", function() require("neotest").summary.toggle() end, desc = "Toggle Summary" }, + { "To", function() require("neotest").output.open({ enter = true, auto_close = true }) end, desc = "Show Output" }, + { "TO", function() require("neotest").output_panel.toggle() end, desc = "Toggle Output Panel" }, + { "TS", function() require("neotest").run.stop() end, desc = "Stop" }, + { "rg", "terminalgradle run", desc = "Run Gradle", mode = "n" }, + }, + }, + { + "rockerBOO/symbols-outline.nvim", + cmd = "SymbolsOutline", + config = function() + require("symbols-outline").setup({ + symbols = { + File = { icon = "󰈔", hl = "@text.uri" }, + Module = { icon = "", hl = "@namespace" }, + Namespace = { icon = "󰅪", hl = "@namespace" }, + Package = { icon = "", hl = "@namespace" }, + Class = { icon = "𝓒", hl = "@type" }, + Method = { icon = "ƒ", hl = "@method" }, + Property = { icon = "", hl = "@method" }, + Field = { icon = "", hl = "@field" }, + Constructor = { icon = "", hl = "@constructor" }, + Enum = { icon = "ℰ", hl = "@type" }, + Interface = { icon = "", hl = "@type" }, + Function = { icon = "", hl = "@function" }, + Variable = { icon = "", hl = "@constant" }, + Constant = { icon = "", hl = "@constant" }, + String = { icon = "𝓐", hl = "@string" }, + Number = { icon = "#", hl = "@number" }, + Boolean = { icon = "󰨙 ", hl = "@boolean" }, + Array = { icon = "", hl = "@constant" }, + Object = { icon = "⦿", hl = "@type" }, + Key = { icon = "🔐", hl = "@type" }, + Null = { icon = "NULL", hl = "@type" }, + EnumMember = { icon = "", hl = "@field" }, + Struct = { icon = "𝓢", hl = "@type" }, + Event = { icon = "🗲", hl = "@type" }, + Operator = { icon = "+", hl = "@operator" }, + TypeParameter = { icon = "𝙏", hl = "@parameter" }, + Component = { icon = "󰅴", hl = "@function" }, + Fragment = { icon = "󰅴", hl = "@constant" }, + }, + }) + end, + keys = { + { "S", "SymbolsOutline", desc = "Toggle Outline" }, + }, + }, +} + +return M diff --git a/lua/pcode/user/default.lua b/lua/pcode/user/default.lua index daf97c2..4512d0c 100644 --- a/lua/pcode/user/default.lua +++ b/lua/pcode/user/default.lua @@ -7,6 +7,7 @@ pcode.lang = { golang = false, java = false, java2 = false, + java3 = true, javascript = false, kotlin = false, markdown = false,