diff --git a/ftplugin/java.lua b/ftplugin/java.lua new file mode 100644 index 00000000000..aafb2b6fe6c --- /dev/null +++ b/ftplugin/java.lua @@ -0,0 +1,121 @@ +-- ftplugin/java.lua +-- This file is automatically loaded when a Java file is opened. +-- It configures jdtls (Eclipse JDT Language Server) for Java development. + +local jdtls = require 'jdtls' + +-- Mason install paths +local mason_path = vim.fn.stdpath 'data' .. '/mason/packages' +local jdtls_path = mason_path .. '/jdtls' +local java_debug_path = mason_path .. '/java-debug-adapter' +local java_test_path = mason_path .. '/java-test' + +-- Detect the OS for the jdtls config directory +local os_config = 'config_linux' +if vim.fn.has 'mac' == 1 then + os_config = 'config_mac' +elseif vim.fn.has 'win32' == 1 then + os_config = 'config_win' +end + +-- Find the launcher jar +local launcher_jar = vim.fn.glob(jdtls_path .. '/plugins/org.eclipse.equinox.launcher_*.jar') + +-- Determine the project name for workspace isolation +local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ':p:h:t') +local workspace_dir = vim.fn.stdpath 'data' .. '/jdtls-workspace/' .. project_name + +-- Find the root directory of the project +local root_dir = require('jdtls.setup').find_root { 'pom.xml', 'build.gradle', 'gradlew', '.git', 'mvnw' } + +-- Collect debug and test bundles +local bundles = {} + +-- java-debug-adapter +local debug_jar = vim.fn.glob(java_debug_path .. '/extension/server/com.microsoft.java.debug.plugin-*.jar', true) +if debug_jar ~= '' then + table.insert(bundles, debug_jar) +end + +-- java-test +local test_jars = vim.split(vim.fn.glob(java_test_path .. '/extension/server/*.jar', true), '\n') +for _, jar in ipairs(test_jars) do + if jar ~= '' then + table.insert(bundles, jar) + end +end + +-- jdtls configuration +local config = { + 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', + '-Xmx1g', + '--add-modules=ALL-SYSTEM', + '--add-opens', 'java.base/java.util=ALL-UNNAMED', + '--add-opens', 'java.base/java.lang=ALL-UNNAMED', + '-jar', launcher_jar, + '-configuration', jdtls_path .. '/' .. os_config, + '-data', workspace_dir, + }, + + root_dir = root_dir, + + settings = { + java = { + signatureHelp = { enabled = true }, + contentProvider = { preferred = 'fernflower' }, + completion = { + favoriteStaticMembers = { + 'org.junit.jupiter.api.Assertions.*', + 'org.mockito.Mockito.*', + 'org.mockito.ArgumentMatchers.*', + 'java.util.Objects.requireNonNull', + 'java.util.Objects.requireNonNullElse', + }, + }, + sources = { + organizeImports = { + starThreshold = 9999, + staticStarThreshold = 9999, + }, + }, + }, + }, + + init_options = { + bundles = bundles, + }, + + -- Called when jdtls attaches to a buffer + on_attach = function(_, bufnr) + -- Enable debug and test support after LSP is ready + jdtls.setup_dap { hotcodereplace = 'auto' } + require('jdtls.dap').setup_dap_main_class_configs() + + local opts = { buffer = bufnr } + + -- Java-specific keymaps under j + vim.keymap.set('n', 'ji', jdtls.organize_imports, vim.tbl_extend('force', opts, { desc = '[J]ava Organize [I]mports' })) + vim.keymap.set('n', 'jc', jdtls.extract_constant, vim.tbl_extend('force', opts, { desc = '[J]ava Extract [C]onstant' })) + vim.keymap.set('v', 'jm', function() jdtls.extract_method(true) end, vim.tbl_extend('force', opts, { desc = '[J]ava Extract [M]ethod' })) + vim.keymap.set('v', 'jv', jdtls.extract_variable, vim.tbl_extend('force', opts, { desc = '[J]ava Extract [V]ariable' })) + + -- Test keymaps using neotest + local neotest = require 'neotest' + vim.keymap.set('n', 'jt', function() neotest.run.run() end, vim.tbl_extend('force', opts, { desc = '[J]ava Run [T]est (cursor)' })) + vim.keymap.set('n', 'jf', function() neotest.run.run(vim.fn.expand '%') end, vim.tbl_extend('force', opts, { desc = '[J]ava Run Tests [F]ile' })) + vim.keymap.set('n', 'js', function() neotest.summary.toggle() end, vim.tbl_extend('force', opts, { desc = '[J]ava Test [S]ummary' })) + vim.keymap.set('n', 'jo', function() neotest.output.open { enter = true } end, vim.tbl_extend('force', opts, { desc = '[J]ava Test [O]utput' })) + + -- Debug test under cursor + vim.keymap.set('n', 'jd', function() neotest.run.run { strategy = 'dap' } end, vim.tbl_extend('force', opts, { desc = '[J]ava [D]ebug Test' })) + end, +} + +-- Start or attach jdtls +jdtls.start_or_attach(config) diff --git a/init.lua b/init.lua index d5ae6dc9b2a..c6142390067 100644 --- a/init.lua +++ b/init.lua @@ -100,9 +100,8 @@ vim.g.have_nerd_font = false -- Make line numbers default vim.o.number = true --- You can also add relative line numbers, to help with jumping. --- Experiment for yourself to see if you like it! --- vim.o.relativenumber = true +-- Relative line numbers, helps with jumping. +vim.o.relativenumber = true -- Enable mouse mode, can be useful for resizing splits for example! vim.o.mouse = 'a' @@ -286,6 +285,52 @@ require('lazy').setup({ }, }, + { -- Git integration with fugitive + 'tpope/vim-fugitive', + cmd = { 'Git', 'Gvdiffsplit', 'GBrowse', 'Gwrite', 'Gread', 'Gdiffsplit' }, + dependencies = { + 'tpope/vim-rhubarb', -- GitHub support for :GBrowse + 'shumphrey/fugitive-gitlab.vim', -- GitLab support for :GBrowse + }, + keys = { + { 'gs', 'Git', desc = '[G]it [S]tatus' }, + { 'gb', 'Git blame', desc = '[G]it [B]lame' }, + { 'gd', 'Gvdiffsplit', desc = '[G]it [D]iff' }, + { 'gl', 'Git log --oneline', desc = '[G]it [L]og' }, + { 'gp', 'Git push', desc = '[G]it [P]ush' }, + { 'gP', 'Git pull', desc = '[G]it [P]ull' }, + { 'gc', 'Git commit', desc = '[G]it [C]ommit' }, + { 'gB', 'GBrowse', desc = '[G]it [B]rowse' }, + }, + }, + + { -- Quick file navigation (like workspaces) + 'ThePrimeagen/harpoon', + branch = 'harpoon2', + dependencies = { 'nvim-lua/plenary.nvim' }, + config = function() + local harpoon = require 'harpoon' + harpoon:setup() + + -- Add current file to harpoon list + vim.keymap.set('n', 'a', function() harpoon:list():add() end, { desc = 'Harpoon: [A]dd file' }) + + -- Toggle harpoon quick menu + vim.keymap.set('n', 'e', function() harpoon.ui:toggle_quick_menu(harpoon:list()) end, { desc = 'Harpoon: Toggle m[E]nu' }) + + -- Navigate to files 1-5 + vim.keymap.set('n', '1', function() harpoon:list():select(1) end, { desc = 'Harpoon: File [1]' }) + vim.keymap.set('n', '2', function() harpoon:list():select(2) end, { desc = 'Harpoon: File [2]' }) + vim.keymap.set('n', '3', function() harpoon:list():select(3) end, { desc = 'Harpoon: File [3]' }) + vim.keymap.set('n', '4', function() harpoon:list():select(4) end, { desc = 'Harpoon: File [4]' }) + vim.keymap.set('n', '5', function() harpoon:list():select(5) end, { desc = 'Harpoon: File [5]' }) + + -- Navigate prev/next in harpoon list + vim.keymap.set('n', 'p', function() harpoon:list():prev() end, { desc = 'Harpoon: [P]revious file' }) + vim.keymap.set('n', 'n', function() harpoon:list():next() end, { desc = 'Harpoon: [N]ext file' }) + end, + }, + -- NOTE: Plugins can also be configured to run Lua code when they are loaded. -- -- This is often very useful to both group configuration, as well as handle @@ -313,6 +358,9 @@ require('lazy').setup({ { 's', group = '[S]earch', mode = { 'n', 'v' } }, { 't', group = '[T]oggle' }, { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, + { 'g', group = '[G]it' }, + { 'gf', group = '[F]ind' }, + { 'j', group = '[J]ava' }, }, }, }, @@ -469,6 +517,11 @@ require('lazy').setup({ -- Shortcut for searching your Neovim configuration files vim.keymap.set('n', 'sn', function() builtin.find_files { cwd = vim.fn.stdpath 'config' } end, { desc = '[S]earch [N]eovim files' }) + + -- Git Telescope integration + vim.keymap.set('n', 'gfb', builtin.git_branches, { desc = '[G]it [F]ind [B]ranches' }) + vim.keymap.set('n', 'gfc', builtin.git_commits, { desc = '[G]it [F]ind [C]ommits' }) + vim.keymap.set('n', 'gfs', builtin.git_stash, { desc = '[G]it [F]ind [S]tash' }) end, }, @@ -592,17 +645,44 @@ require('lazy').setup({ -- Enable the following language servers -- Feel free to add/remove any LSPs that you want here. They will automatically be installed. -- See `:help lsp-config` for information about keys and how to configure + -- Path to Vue language server for the TypeScript plugin + local vue_language_server_path = vim.fn.stdpath 'data' .. '/mason/packages/vue-language-server/node_modules/@vue/language-server' + local servers = { -- clangd = {}, -- gopls = {}, -- pyright = {}, -- rust_analyzer = {}, - -- - -- Some languages (like typescript) have entire language plugins that can be useful: - -- https://github.com/pmizio/typescript-tools.nvim - -- - -- But for many setups, the LSP (`ts_ls`) will work just fine - -- ts_ls = {}, + + vtsls = { + settings = { + vtsls = { + -- Enable auto-use of workspace TypeScript version + autoUseWorkspaceTsdk = true, + tsserver = { + globalPlugins = { + { + name = '@vue/typescript-plugin', + location = vue_language_server_path, + languages = { 'vue' }, + configNamespace = 'typescript', + }, + }, + }, + }, + }, + -- Include vue so vtsls attaches to .vue files for TypeScript support + filetypes = { 'javascript', 'javascriptreact', 'typescript', 'typescriptreact', 'vue' }, + }, + + vue_ls = {}, + + eslint = { + settings = { + -- Auto-fix on save + run = 'onSave', + }, + }, } -- Ensure the servers and tools above are installed @@ -612,12 +692,18 @@ require('lazy').setup({ -- :Mason -- -- You can press `g?` for help in this menu. - local ensure_installed = vim.tbl_keys(servers or {}) - vim.list_extend(ensure_installed, { - 'lua_ls', -- Lua Language server + local ensure_installed = { + 'lua-language-server', -- Lua Language server 'stylua', -- Used to format Lua code - -- You can add other tools here that you want Mason to install - }) + 'vtsls', -- TypeScript/JavaScript LSP + 'vue-language-server', -- Vue.js LSP (Volar) + 'eslint-lsp', -- ESLint LSP + 'prettierd', -- Prettier daemon (formatter) + 'jdtls', -- Java LSP (Eclipse JDT) + 'java-debug-adapter', -- Java debug adapter + 'java-test', -- Java test runner + 'google-java-format', -- Java formatter + } require('mason-tool-installer').setup { ensure_installed = ensure_installed } @@ -686,11 +772,15 @@ require('lazy').setup({ end, formatters_by_ft = { lua = { 'stylua' }, - -- Conform can also run multiple formatters sequentially - -- python = { "isort", "black" }, - -- - -- You can use 'stop_after_first' to run the first available formatter from the list - -- javascript = { "prettierd", "prettier", stop_after_first = true }, + javascript = { 'prettierd', 'prettier', stop_after_first = true }, + javascriptreact = { 'prettierd', 'prettier', stop_after_first = true }, + typescript = { 'prettierd', 'prettier', stop_after_first = true }, + typescriptreact = { 'prettierd', 'prettier', stop_after_first = true }, + vue = { 'prettierd', 'prettier', stop_after_first = true }, + css = { 'prettierd', 'prettier', stop_after_first = true }, + html = { 'prettierd', 'prettier', stop_after_first = true }, + json = { 'prettierd', 'prettier', stop_after_first = true }, + java = { 'google-java-format' }, }, }, }, @@ -849,18 +939,69 @@ require('lazy').setup({ end, }, - { -- Highlight, edit, and navigate code - 'nvim-treesitter/nvim-treesitter', + { -- Auto-close and auto-rename HTML/Vue tags + 'windwp/nvim-ts-autotag', + event = { 'BufReadPre', 'BufNewFile' }, + opts = {}, + }, + + { -- Java LSP support via jdtls + 'mfussenegger/nvim-jdtls', + ft = 'java', + }, + + { -- Test runner framework + 'nvim-neotest/neotest', + dependencies = { + 'nvim-neotest/nvim-nio', + 'nvim-lua/plenary.nvim', + 'nvim-treesitter/nvim-treesitter', + 'rcasia/neotest-java', -- JUnit 5 adapter + }, config = function() - local filetypes = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc' } - require('nvim-treesitter').install(filetypes) - vim.api.nvim_create_autocmd('FileType', { - pattern = filetypes, - callback = function() vim.treesitter.start() end, - }) + require('neotest').setup { + adapters = { + require 'neotest-java', + }, + } + + -- Command to download JUnit jar on machines without Neovim 0.12+ + vim.api.nvim_create_user_command('NeotestJavaDownload', function() + local version = '1.10.1' + local dir = vim.fn.stdpath 'data' .. '/neotest-java' + local jar = dir .. '/junit-platform-console-standalone-' .. version .. '.jar' + if vim.fn.filereadable(jar) == 1 then + vim.notify('JUnit jar already exists at ' .. jar, vim.log.levels.INFO) + return + end + vim.fn.mkdir(dir, 'p') + local url = 'https://repo1.maven.org/maven2/org/junit/platform/junit-platform-console-standalone/' + .. version + .. '/junit-platform-console-standalone-' + .. version + .. '.jar' + vim.notify('Downloading JUnit Platform Console Standalone ' .. version .. '...', vim.log.levels.INFO) + vim.fn.system { 'curl', '-L', '-o', jar, url } + if vim.v.shell_error == 0 then + vim.notify('JUnit jar downloaded successfully!', vim.log.levels.INFO) + else + vim.notify('Failed to download JUnit jar', vim.log.levels.ERROR) + end + end, { desc = 'Download JUnit Platform Console Standalone jar for neotest-java' }) end, }, + { -- Highlight, edit, and navigate code + 'nvim-treesitter/nvim-treesitter', + build = ':TSUpdate', + opts = { + ensure_installed = { 'bash', 'c', 'css', 'diff', 'html', 'java', 'javascript', 'json', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'tsx', 'typescript', 'vim', 'vimdoc', 'vue' }, + auto_install = true, + highlight = { enable = true }, + indent = { enable = true }, + }, + }, + -- The following comments only work if you have downloaded the kickstart repo, not just copy pasted the -- init.lua. If you want these files, they are in the repository, so you can just download them and -- place them in the correct locations. @@ -870,7 +1011,7 @@ require('lazy').setup({ -- Here are some example plugins that I've included in the Kickstart repository. -- Uncomment any of the lines below to enable them (you will need to restart nvim). -- - -- require 'kickstart.plugins.debug', + require 'kickstart.plugins.debug', -- require 'kickstart.plugins.indent_line', -- require 'kickstart.plugins.lint', -- require 'kickstart.plugins.autopairs', diff --git a/lua/kickstart/plugins/debug.lua b/lua/kickstart/plugins/debug.lua index 1e3570f9bf3..a79dfa9d939 100644 --- a/lua/kickstart/plugins/debug.lua +++ b/lua/kickstart/plugins/debug.lua @@ -81,6 +81,7 @@ return { ensure_installed = { -- Update this to ensure that you have the debuggers for the langs you want 'delve', + 'java-debug-adapter', }, }