From fd5aad9663b3137f6c178dcd4f4fda43d83a33a0 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Tue, 25 Jun 2024 23:20:10 +0530 Subject: [PATCH 001/147] chore: code clean up related to runner (#208) --- lua/java.lua | 2 - lua/java/api/runner.lua | 341 +--------------- lua/java/runner/run-logger.lua | 57 +++ lua/java/runner/run.lua | 88 ++++ lua/java/runner/runner.lua | 138 +++++++ tests/java/api/runner_spec.lua | 713 --------------------------------- tests/java/java_spec.lua | 18 - 7 files changed, 295 insertions(+), 1062 deletions(-) create mode 100644 lua/java/runner/run-logger.lua create mode 100644 lua/java/runner/run.lua create mode 100644 lua/java/runner/runner.lua delete mode 100644 tests/java/api/runner_spec.lua delete mode 100644 tests/java/java_spec.lua diff --git a/lua/java.lua b/lua/java.lua index 6925e81..e378f87 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -64,8 +64,6 @@ M.refactor.extract_variable = refactor.extract_variable -- Runner APIs -- ---------------------------------------------------------------------- M.runner = {} -M.runner.run_app = runner.run_app - M.runner.built_in = {} M.runner.built_in.run_app = runner.built_in.run_app M.runner.built_in.toggle_logs = runner.built_in.toggle_logs diff --git a/lua/java/api/runner.lua b/lua/java/api/runner.lua index 84a909e..b060062 100644 --- a/lua/java/api/runner.lua +++ b/lua/java/api/runner.lua @@ -1,362 +1,45 @@ -local log = require('java.utils.log') local async = require('java-core.utils.async').sync local get_error_handler = require('java.handlers.error') -local jdtls = require('java.utils.jdtls') -local DapSetup = require('java-dap.api.setup') -local ui = require('java.utils.ui') -local profile_config = require('java.api.profile_config') -local class = require('java-core.utils.class') - -local group = vim.api.nvim_create_augroup('logger', { clear = true }) - ---- @class RunnerApi ---- @field client LspClient ---- @field private dap java.DapSetup -local RunnerApi = class() - -function RunnerApi:_init(args) - self.client = args.client - self.dap = DapSetup(args.client) -end - -function RunnerApi:get_config() - local configs = self.dap:get_dap_config() - return ui.select_from_dap_configs(configs) -end - ---- @param callback fun(cmd, config) ---- @param args string -function RunnerApi:run_app(callback, args) - local config = self:get_config() - if not config then - return - end - - config.request = 'launch' - local enrich_config = self.dap:enrich_config(config) - local class_paths = table.concat(enrich_config.classPaths, ':') - local main_class = enrich_config.mainClass - local java_exec = enrich_config.javaExec - - local active_profile = - profile_config.get_active_profile(enrich_config.mainClass) - - local vm_args = '' - local prog_args = '' - if active_profile then - prog_args = (active_profile.prog_args or '') .. ' ' .. (args or '') - vm_args = active_profile.vm_args or '' - end - - local cmd = { - java_exec, - vm_args, - '-cp', - class_paths, - main_class, - prog_args, - } - log.debug('run app cmd: ', cmd) - callback(cmd, config) -end - ---- @class RunningApp ---- @field win number ---- @field bufnr number ---- @field job_id number ---- @field chan number ---- @field dap_config table ---- @field is_open boolean ---- @field running_status string -local RunningApp = class() - ---- @param dap_config table -function RunningApp:_init(dap_config) - self.is_open = false - self.dap_config = dap_config - self.bufnr = vim.api.nvim_create_buf(false, true) -end - ---- @class BuiltInMainRunner ---- @field running_apps table ---- @field current_app RunningApp -local BuiltInMainRunner = class() - -function BuiltInMainRunner:_init() - self.running_apps = {} - self.current_app = nil -end - ---- @param running_app RunningApp -function BuiltInMainRunner.set_up_buffer_autocmd(running_app) - vim.api.nvim_create_autocmd({ 'BufHidden' }, { - group = group, - buffer = running_app.bufnr, - callback = function(_) - running_app.is_open = false - end, - }) -end - ---- @param running_app RunningApp -function BuiltInMainRunner.stop(running_app) - if running_app.job_id ~= nil then - vim.fn.jobstop(running_app.job_id) - vim.fn.jobwait({ running_app.job_id }, 1000) - running_app.job_id = nil - end -end - ---- @param running_app RunningApp -function BuiltInMainRunner.set_up_buffer(running_app) - vim.cmd('sp | winc J | res 15 | buffer ' .. running_app.bufnr) - running_app.win = vim.api.nvim_get_current_win() - - vim.wo[running_app.win].number = false - vim.wo[running_app.win].relativenumber = false - vim.wo[running_app.win].signcolumn = 'no' - - BuiltInMainRunner.set_up_buffer_autocmd(running_app) - running_app.is_open = true -end - ---- @param exit_code number ---- @param running_app RunningApp -function BuiltInMainRunner.on_exit(exit_code, running_app) - local exit_message = 'Process finished with exit code ' .. exit_code - vim.fn.chansend(running_app.chan, '\n' .. exit_message .. '\n') - local current_buf = vim.api.nvim_get_current_buf() - if current_buf == running_app.bufnr then - vim.cmd('stopinsert') - end - vim.notify(running_app.dap_config.name .. ' ' .. exit_message) - running_app.running_status = exit_message -end - ---- @param running_app RunningApp -function BuiltInMainRunner.scroll_down(running_app) - local last_line = vim.api.nvim_buf_line_count(running_app.bufnr) - vim.api.nvim_win_set_cursor(running_app.win, { last_line, 0 }) -end - ---- @param running_app RunningApp -function BuiltInMainRunner.hide_logs(running_app) - if not running_app or not running_app.is_open then - return - end - if running_app.bufnr then - vim.api.nvim_buf_call(running_app.bufnr, function() - vim.cmd('hide') - end) - end -end - ---- @param running_app RunningApp -function BuiltInMainRunner.toggle_logs(running_app) - if running_app and not running_app.is_open then - vim.api.nvim_buf_call(running_app.bufnr, function() - BuiltInMainRunner.set_up_buffer(running_app) - BuiltInMainRunner.scroll_down(running_app) - end) - else - BuiltInMainRunner.hide_logs(running_app) - end -end - -function BuiltInMainRunner:toggle_current_app_logs() - local running_app = self.current_app - if not running_app then - return - end - BuiltInMainRunner.toggle_logs(running_app) -end - ---- @param data string[] ---- @param running_app RunningApp -function BuiltInMainRunner.on_stdout(data, running_app) - vim.fn.chansend(running_app.chan, data) - if not running_app.is_open then - return - end - vim.api.nvim_buf_call(running_app.bufnr, function() - local current_buf = vim.api.nvim_get_current_buf() - local mode = vim.api.nvim_get_mode().mode - if current_buf ~= running_app.bufnr or mode ~= 'i' then - BuiltInMainRunner.scroll_down(running_app) - end - end) -end - ---- @param cmd string[] ---- @param config table -function BuiltInMainRunner:run_app(cmd, config) - local selected_app = self:select_app_with_dap_config(config) - if self.current_app then - if self.current_app.job_id == selected_app.job_id then - BuiltInMainRunner.stop(selected_app) - end - end - self:change_current_app(selected_app) - if not selected_app.is_open or not selected_app.chan then - vim.api.nvim_buf_call(selected_app.bufnr, function() - BuiltInMainRunner.set_up_buffer(selected_app) - BuiltInMainRunner.scroll_down(selected_app) - end) - vim.api.nvim_buf_set_name(selected_app.bufnr, selected_app.dap_config.name) - end - - selected_app.chan = vim.api.nvim_open_term(selected_app.bufnr, { - on_input = function(_, _, _, data) - if selected_app.job_id then - vim.fn.chansend(selected_app.job_id, data) - end - end, - }) - - selected_app.running_status = '(running)' - local command = table.concat(cmd, ' ') - vim.fn.chansend(selected_app.chan, command) - selected_app.job_id = vim.fn.jobstart(command, { - pty = true, - on_stdout = function(_, data) - BuiltInMainRunner.on_stdout(data, selected_app) - end, - on_exit = function(_, exit_code) - BuiltInMainRunner.on_exit(exit_code, selected_app) - end, - }) -end - ---- @return RunningApp|nil -function BuiltInMainRunner:select_app_with_ui() - --- @type RunningApp - local app = nil - local count = 0 - for _ in pairs(self.running_apps) do - count = count + 1 - end - if count == 0 then - error('No running apps') - elseif count == 1 then - for _, _app in pairs(self.running_apps) do - app = _app - end - else - local configs = {} - for _, _app in pairs(self.running_apps) do - _app.dap_config.extra_args = _app.running_status - table.insert(configs, _app.dap_config) - end - - local selected = ui.select_from_dap_configs(configs) - if not selected then - return nil - end - app = self.running_apps[selected.mainClass] - end - self:change_current_app(app) - return app -end - ---- @param app RunningApp -function BuiltInMainRunner:change_current_app(app) - if - self.current_app - and self.current_app.dap_config.name ~= app.dap_config.name - then - BuiltInMainRunner.hide_logs(self.current_app) - end - self.current_app = app -end - ---- @param config table|nil ---- @return RunningApp -function BuiltInMainRunner:select_app_with_dap_config(config) - --- @type RunningApp - if not config then - error('No config') - end - - if self.running_apps and self.running_apps[config.mainClass] then - return self.running_apps[config.mainClass] - end - local running_app = RunningApp(config) - self.running_apps[running_app.dap_config.mainClass] = running_app - return running_app -end - -function BuiltInMainRunner:switch_app() - local selected_app = self:select_app_with_ui() - if not selected_app then - return - end - if not selected_app.is_open then - BuiltInMainRunner.toggle_logs(selected_app) - end -end - -function BuiltInMainRunner:stop_app() - local selected_app = self:select_app_with_ui() - if not selected_app then - return - end - BuiltInMainRunner.stop(selected_app) - BuiltInMainRunner.toggle_logs(selected_app) -end +local Runner = require('java.runner.runner') local M = { built_in = {}, - --- @type BuiltInMainRunner - runner = BuiltInMainRunner(), -} ---- @param callback fun(cmd, config) ---- @param args string -function M.run_app(callback, args) - return async(function() - return RunnerApi(jdtls()):run_app(callback, args) - end) - .catch(get_error_handler('failed to run app')) - .run() -end + ---@type java.Runner + runner = Runner(), +} --- @param opts {} function M.built_in.run_app(opts) async(function() - M.run_app(function(cmd, config) - M.runner:run_app(cmd, config) - end, opts.args) + M.runner:start_run(opts.args) end) - .catch(get_error_handler('failed to run app')) + .catch(get_error_handler('Failed to run app')) .run() end function M.built_in.toggle_logs() async(function() - M.runner:toggle_current_app_logs() + M.runner:toggle_open_log() end) - .catch(get_error_handler('failed to toggle logs')) + .catch(get_error_handler('Failed to run app')) .run() end function M.built_in.switch_app() async(function() - M.runner:switch_app() + M.runner:switch_log() end) - .catch(get_error_handler('failed to switch app')) + .catch(get_error_handler('Failed to switch run')) .run() end function M.built_in.stop_app() async(function() - M.runner:stop_app() + M.runner:stop_run() end) - .catch(get_error_handler('failed to stop app')) + .catch(get_error_handler('Failed to switch run')) .run() end -M.RunnerApi = RunnerApi -M.BuiltInMainRunner = BuiltInMainRunner -M.RunningApp = RunningApp - return M diff --git a/lua/java/runner/run-logger.lua b/lua/java/runner/run-logger.lua new file mode 100644 index 0000000..96ed4cc --- /dev/null +++ b/lua/java/runner/run-logger.lua @@ -0,0 +1,57 @@ +local class = require('java-core.utils.class') + +---@class java.RunLogger +---@field window number +local RunLogger = class() + +function RunLogger:_init() + self.window = -1 +end + +---Opens the log window with the given run buffer +---@param buffer number +function RunLogger:create(buffer) + vim.cmd('sp | winc J | res 15 | buffer ' .. buffer) + self.window = vim.api.nvim_get_current_win() + + vim.wo[self.window].number = false + vim.wo[self.window].relativenumber = false + vim.wo[self.window].signcolumn = 'no' + + self:scroll_to_bottom() +end + +function RunLogger:set_buffer(buffer) + if self:is_opened() then + vim.api.nvim_win_set_buf(self.window, buffer) + else + self:create(buffer) + end + + self:scroll_to_bottom() +end + +function RunLogger:scroll_to_bottom() + local buffer = vim.api.nvim_win_get_buf(self.window) + local line_count = vim.api.nvim_buf_line_count(buffer) + vim.api.nvim_win_set_cursor(self.window, { line_count, 0 }) +end + +---Returns true if the log window is opened +---@return boolean +function RunLogger:is_opened() + if not self.window then + return false + end + + return vim.api.nvim_win_is_valid(self.window) +end + +---Closes the log window if opened +function RunLogger:close() + if self.window and vim.api.nvim_win_is_valid(self.window) then + vim.api.nvim_win_hide(self.window) + end +end + +return RunLogger diff --git a/lua/java/runner/run.lua b/lua/java/runner/run.lua new file mode 100644 index 0000000..4e677f6 --- /dev/null +++ b/lua/java/runner/run.lua @@ -0,0 +1,88 @@ +local class = require('java-core.utils.class') +local notify = require('java-core.utils.notify') + +---@class java.Run +---@field name string +---@field main_class string +---@field buffer number +---@field private cmd string +---@field private term_chan_id number +---@field private job_chan_id number | nil +---@field private is_running boolean +---@field private is_failure boolean +local Run = class() + +---@param dap_config java-dap.DapLauncherConfig +---@param cmd string[] +function Run:_init(dap_config, cmd) + self.name = dap_config.name + self.main_class = dap_config.mainClass + self.cmd = table.concat(cmd, ' ') + self.buffer = vim.api.nvim_create_buf(false, true) + self.term_chan_id = vim.api.nvim_open_term(self.buffer, { + on_input = function(_, _, _, data) + self:send_job(data) + end, + }) +end + +function Run:start() + self.is_running = true + self:send_term(self.cmd) + + self.job_chan_id = vim.fn.jobstart(self.cmd, { + pty = true, + on_stdout = function(_, data) + self:send_term(data) + end, + on_exit = function(_, exit_code) + self:on_job_exit(exit_code) + end, + }) +end + +function Run:stop() + if not self.job_chan_id then + return + end + + vim.fn.jobstop(self.job_chan_id) + vim.fn.jobwait({ self.job_chan_id }, 1000) + self.job_chan_id = nil +end + +---Send data to execution job channel +---@private +---@param data string +function Run:send_job(data) + if self.job_chan_id then + vim.fn.chansend(self.job_chan_id, data) + end +end + +---Send message to terminal channel +---@private +---@param data string +function Run:send_term(data) + vim.fn.chansend(self.term_chan_id, data) +end + +---Runs when the current job exists +---@private +---@param exit_code number +function Run:on_job_exit(exit_code) + local message = + string.format('Process finished with exit code::%s', exit_code) + self:send_term(message) + + self.is_running = false + + if exit_code == 0 then + self.is_failure = false + else + self.is_failure = true + notify.error(string.format('%s %s', self.name, message)) + end +end + +return Run diff --git a/lua/java/runner/runner.lua b/lua/java/runner/runner.lua new file mode 100644 index 0000000..3f7c2f3 --- /dev/null +++ b/lua/java/runner/runner.lua @@ -0,0 +1,138 @@ +local ui = require('java.utils.ui') +local class = require('java-core.utils.class') +local jdtls = require('java.utils.jdtls2') +local profile_config = require('java.api.profile_config') +local Run = require('java.runner.run') +local RunLogger = require('java.runner.run-logger') +local DapSetup = require('java-dap.api.setup') + +---@class java.Runner +---@field runs table +---@field logger java.RunLogger +local Runner = class() + +function Runner:_init() + self.runs = {} + self.curr_run = nil + self.logger = RunLogger() +end + +---Starts a new run +---@param args string +function Runner:start_run(args) + local cmd, dap_config = Runner.select_dap_config(args) + + if not cmd or not dap_config then + return + end + + local run ---@as java.Run + + -- get the default run if exist or create new run + if self.runs[dap_config.mainClass] then + run = self.runs[dap_config.mainClass] + else + run = Run(dap_config, cmd) + self.runs[dap_config.mainClass] = run + end + + self.curr_run = run + self.logger:set_buffer(run.buffer) + + run:start() +end + +---Stops the user selected run +function Runner:stop_run() + local run = self:select_run() + + if not run then + return + end + + run:stop() +end + +function Runner:toggle_open_log() + if self.logger:is_opened() then + self.logger:close() + else + if self.curr_run then + self.logger:create(self.curr_run.buffer) + end + end +end + +---Switches the log to selected run +function Runner:switch_log() + local selected_run = self:select_run() + + if not selected_run then + return + end + + self.curr_run = selected_run + self.logger:set_buffer(selected_run.buffer) +end + +---Prompt the user to select an active run and returns the selected run +---@private +---@return java.Run | nil +function Runner:select_run() + local active_main_classes = {} ---@type string[] + + for _, run in pairs(self.runs) do + table.insert(active_main_classes, run.main_class) + end + + local selected_main = ui.select('Select main class', active_main_classes) + + if not selected_main then + return + end + + return self.runs[selected_main] +end + +---Returns the dap config for user selected main +---@param args string additional program arguments to pass +---@return string[] | nil +---@return java-dap.DapLauncherConfig | nil +function Runner.select_dap_config(args) + local dap = DapSetup(jdtls()) + local dap_config_list = dap:get_dap_config() + local selected_dap_config = ui.select_from_dap_configs(dap_config_list) + + if not selected_dap_config then + return nil, nil + end + + local enriched_config = dap:enrich_config(selected_dap_config) + + local class_paths = table.concat(enriched_config.classPaths, ':') + local main_class = enriched_config.mainClass + local java_exec = enriched_config.javaExec + + local active_profile = profile_config.get_active_profile(enriched_config.name) + + local vm_args = '' + local prog_args = args + + if active_profile then + prog_args = (active_profile.prog_args or '') .. ' ' .. (args or '') + vm_args = active_profile.vm_args or '' + end + + local cmd = { + java_exec, + vm_args, + '-cp', + class_paths, + main_class, + prog_args, + } + + return cmd, selected_dap_config +end + +return Runner diff --git a/tests/java/api/runner_spec.lua b/tests/java/api/runner_spec.lua deleted file mode 100644 index 3707e8f..0000000 --- a/tests/java/api/runner_spec.lua +++ /dev/null @@ -1,713 +0,0 @@ -local spy = require('luassert.spy') -local mock = require('luassert.mock') -local notify = require('java-core.utils.notify') -local DapSetup = require('java-dap.api.setup') -local mock_client = { jdtls_args = {} } -local runner = require('java.api.runner') -local async = require('java-core.utils.async').sync -local profile_config = require('java.api.profile_config') -local ui = require('java.utils.ui') - -local RunnerApi = runner.RunnerApi({ client = mock_client }) - -describe('java-core.api.runner', function() - before_each(function() - package.loaded['java.api.runner'] = nil - package.loaded['java.utils.ui'] = nil - end) - - it('RunnerApi()', function() - local mock_dap = DapSetup(mock_client) - assert.same(RunnerApi.client, mock_client) - assert.same(RunnerApi.dap, mock_dap) - end) - - it('RunnerApi:get_config when no config found', function() - local dap_mock = mock(DapSetup, true) - dap_mock.get_dap_config.returns({}) - - local notify_spy = spy.on(notify, 'warn') - local config = RunnerApi:get_config() - - assert.equals(config, nil) - assert.spy(notify_spy).was_called_with('Config not found') - mock.revert() - end) - - it('RunnerApi:get_config when only one config found', function() - local dap_mock = mock(DapSetup, true) - dap_mock.get_dap_config.returns({ - { name = 'config1', projectName = 'projectName' }, - }) - - local config = RunnerApi:get_config() - assert.same(config, { name = 'config1', projectName = 'projectName' }) - mock.revert() - end) - - it('RunnerApi:get_config when multiple config found', function() - RunnerApi.dap.get_dap_config = function() - return { - { name = 'config1' }, - { name = 'config2', projectName = 'project2' }, - { name = 'config3', projectName = 'project3' }, - } - end - - local mock_ui = mock(vim.ui, true) - mock_ui.select.returns() - - local select_spy = spy.on(vim.ui, 'select') - - async(function() - local config = RunnerApi:get_config() - assert.same({ name = 'config2', projectName = 'project2' }, config) - mock.revert(mock_ui) - end).run() - - assert.same(select_spy.calls[1].vals[1], { 'config2', 'config3' }) - - assert.same( - select_spy.calls[1].vals[2], - { prompt = 'Select the main class (modul -> mainClass)' } - ) - - mock.revert(mock_ui) - end) - - it('RunnerApi:run_app when no config found', function() - RunnerApi.get_config = function() - return nil - end - - local callback_mock = function(_) end - local callback_spy = spy.new(callback_mock) - - RunnerApi:run_app(callback_spy) - assert.spy(callback_spy).was_not_called() - end) - - it('RunnerApi:run_app without active profile', function() - RunnerApi.get_config = function() - return { name = 'config1' } - end - - RunnerApi.dap.enrich_config = function() - return { - classPaths = { 'path1', 'path2' }, - mainClass = 'mainClass', - javaExec = 'javaExec', - } - end - - RunnerApi.profile_config = profile_config - RunnerApi.profile_config.get_active_profile = function(main_class) - assert.equals(main_class, 'mainClass') - return nil - end - - local callback_mock = function(_, _) end - local callback_spy = spy.new(callback_mock) - - RunnerApi:run_app(callback_spy) - assert.spy(callback_spy).was_called_with({ - 'javaExec', - '', -- vm_args - '-cp', - 'path1:path2', - 'mainClass', - '', -- prog_args - }, { name = 'config1', request = 'launch' }) - end) - - it('RunnerApi:run_app with active profile', function() - RunnerApi.get_config = function() - return { name = 'config1' } - end - - RunnerApi.dap.enrich_config = function() - return { - classPaths = { 'path1', 'path2' }, - mainClass = 'mainClass', - javaExec = 'javaExec', - } - end - - RunnerApi.profile_config = profile_config - RunnerApi.profile_config.get_active_profile = function(main_class) - assert.equals(main_class, 'mainClass') - return { - prog_args = 'profile_prog_args', - vm_args = 'vm_args', - } - end - - local callback_mock = function(_, _) end - local callback_spy = spy.new(callback_mock) - - RunnerApi:run_app(callback_spy, 'input_prog_args') - assert.spy(callback_spy).was_called_with({ - 'javaExec', - 'vm_args', - '-cp', - 'path1:path2', - 'mainClass', - 'profile_prog_args input_prog_args', - }, { name = 'config1', request = 'launch' }) - end) - - it('RunningApp:new', function() - local api = mock(vim.api, true) - api.nvim_create_buf.returns(1) - local running_app = runner.RunningApp({ projectName = 'projectName' }) - assert.equals(running_app.win, nil) - assert.equals(running_app.bufnr, 1) - assert.equals(running_app.job_id, nil) - assert.equals(running_app.chan, nil) - assert.same(running_app.dap_config, { projectName = 'projectName' }) - assert.equals(running_app.is_open, false) - assert.equals(running_app.running_status, nil) - mock.revert(api) - end) - - it('BuildInRunner:new', function() - local built_in_main_runner = runner.BuiltInMainRunner() - assert.equals(built_in_main_runner.current_app, nil) - assert.same(built_in_main_runner.running_apps, {}) - end) - - it('BuildInRunner:_set_up_buffer', function() - local vim = mock(vim, true) - vim.wo = {} - vim.wo[1] = {} - local api = mock(vim.api, true) - api.nvim_get_current_win.returns(1) - api.nvim_create_buf.returns(2) - local spy_cmd = spy.on(vim, 'cmd') - - local running_app = runner.RunningApp({ projectName = 'projectName' }) - - spy.on(runner.BuiltInMainRunner, 'set_up_buffer_autocmd') - runner.BuiltInMainRunner.set_up_buffer(running_app) - - assert.equals(running_app.is_open, true) - assert.equals(running_app.win, 1) - assert.spy(spy_cmd).was_called_with('sp | winc J | res 15 | buffer 2') - assert.spy(runner.BuiltInMainRunner.set_up_buffer_autocmd).was_called() - - mock.revert(api) - mock.revert(vim) - end) - - it('BuildInRunner:_set_up_buffer_autocmd', function() - local api = mock(vim.api, true) - api.nvim_create_buf.returns(1) - - local running_app = runner.RunningApp({ projectName = 'projectName' }) - running_app.is_open = true - runner.BuiltInMainRunner.set_up_buffer_autocmd(running_app) - - local call_info = api.nvim_create_autocmd.calls[1] - assert.same(call_info.vals[1], { 'BufHidden' }) - assert.equals(call_info.vals[2].buffer, 1) - - call_info.vals[2].callback() - assert.is_false(running_app.is_open) - - mock.revert(api) - end) - - it('BuiltInMainRunner.on_stdout when is_open=true', function() - local api = mock(vim.api, true) - local spy_chensend = spy.on(vim.fn, 'chansend') - - local running_app = runner.RunningApp({ projectName = 'projectName' }) - running_app.chan = 2 - - spy.on(vim.api, 'nvim_buf_call') - runner.BuiltInMainRunner.on_stdout({ 'data1', 'data2' }, running_app) - assert.spy(spy_chensend).was_called_with(2, { 'data1', 'data2' }) - - assert.spy(api.nvim_buf_call).was_not_called() - - mock.revert(api) - end) - - it( - 'BuiltInMainRunner.on_stdout when bufnr is equal to current bufnr and mode is "i" (skip scroll)', - function() - local mock_current_bufnr = 1 - local vim = mock(vim, true) - local api = mock(vim.api, true) - local spy_chensend = spy.on(vim.fn, 'chansend') - - api.nvim_get_current_buf.returns(mock_current_bufnr) - api.nvim_create_buf.returns(mock_current_bufnr) - api.nvim_get_mode.returns({ mode = 'i' }) - - local running_app = runner.RunningApp({ projectName = 'projectName' }) - running_app.chan = 2 - running_app.is_open = true - -- running_app.bufnr = mock_current_bufnr - spy.on(runner.BuiltInMainRunner, 'scroll_down') - - runner.BuiltInMainRunner.on_stdout({ 'data1', 'data2' }, running_app) - - assert.spy(spy_chensend).was_called_with(2, { 'data1', 'data2' }) - -- -- call nvim_create_buf - local call_info = api.nvim_buf_call.calls[1] - call_info.vals[2]() - - assert.spy(runner.BuiltInMainRunner.scroll_down).was_not_called() - -- - mock.revert(vim) - mock.revert(api) - end - ) - - it( - 'BuiltInMainRunner:_on_stdout when bufnr is not equal to current bufnr and mode is "i" (scroll)', - function() - local vim = mock(vim, true) - local api = mock(vim.api, true) - local spy_chensend = spy.on(vim.fn, 'chansend') - - api.nvim_get_current_buf.returns(1) - api.nvim_create_buf.returns(3) - api.nvim_get_mode.returns({ mode = 'i' }) - - local running_app = runner.RunningApp({ projectName = 'projectName' }) - running_app.chan = 2 - running_app.is_open = true - -- running_app.bufnr = mock_current_bufnr - spy.on(runner.BuiltInMainRunner, 'scroll_down') - - runner.BuiltInMainRunner.on_stdout({ 'data1', 'data2' }, running_app) - - assert.spy(spy_chensend).was_called_with(2, { 'data1', 'data2' }) - -- -- call nvim_create_buf - local call_info = api.nvim_buf_call.calls[1] - call_info.vals[2]() - - assert - .spy(runner.BuiltInMainRunner.scroll_down) - .was_called_with(running_app) - -- - mock.revert(vim) - mock.revert(api) - end - ) - - it( - 'BuiltInMainRunner:_on_stdout when bufnr is equal to current bufnr and mode is not "i" (scroll)', - function() - local vim = mock(vim, true) - local api = mock(vim.api, true) - local spy_chensend = spy.on(vim.fn, 'chansend') - - api.nvim_get_current_buf.returns(1) - api.nvim_create_buf.returns(1) - api.nvim_get_mode.returns({ mode = 'n' }) - - local running_app = runner.RunningApp({ projectName = 'projectName' }) - running_app.chan = 2 - running_app.is_open = true - -- running_app.bufnr = mock_current_bufnr - spy.on(runner.BuiltInMainRunner, 'scroll_down') - - runner.BuiltInMainRunner.on_stdout({ 'data1', 'data2' }, running_app) - - assert.spy(spy_chensend).was_called_with(2, { 'data1', 'data2' }) - -- -- call nvim_create_buf - local call_info = api.nvim_buf_call.calls[1] - call_info.vals[2]() - - assert - .spy(runner.BuiltInMainRunner.scroll_down) - .was_called_with(running_app) - -- - mock.revert(vim) - mock.revert(api) - end - ) - - it( - 'BuiltInMainRunner:_on_exit when bufnr is equal to current bufnr (stopinsert)', - function() - local mock_current_bufnr = 1 - local api = mock(vim.api, true) - local spy_chensend = spy.on(vim.fn, 'chansend') - local spy_cmd = spy.on(vim, 'cmd') - - api.nvim_get_current_buf.returns(mock_current_bufnr) - api.nvim_create_buf.returns(mock_current_bufnr) - - local running_app = - runner.RunningApp({ projectName = 'projectName', name = 'config1' }) - running_app.chan = 2 - running_app.is_open = true - running_app.job_id = 1 - - runner.BuiltInMainRunner.on_exit(0, running_app) - assert - .spy(spy_chensend) - .was_called_with(2, '\nProcess finished with exit code 0\n') - assert.spy(spy_cmd).was_called_with('stopinsert') - assert.equals( - running_app.running_status, - 'Process finished with exit code 0' - ) - - mock.revert(api) - end - ) - - it( - 'BuiltInMainRunner:_on_exit when bufnr is not equal to current bufnr (skip stopinsert)', - function() - local api = mock(vim.api, true) - local spy_chensend = spy.on(vim.fn, 'chansend') - local spy_cmd = spy.on(vim, 'cmd') - - api.nvim_get_current_buf.returns(3) - api.nvim_create_buf.returns(4) - - local running_app = - runner.RunningApp({ projectName = 'projectName', name = 'config1' }) - running_app.chan = 2 - running_app.is_open = true - running_app.job_id = 1 - - runner.BuiltInMainRunner.on_exit(0, running_app) - assert - .spy(spy_chensend) - .was_called_with(2, '\nProcess finished with exit code 0\n') - assert.spy(spy_cmd).was_not_called() - assert.equals( - running_app.running_status, - 'Process finished with exit code 0' - ) - - mock.revert(api) - end - ) - - it('BuiltInMainRunner:run_app when there is no running job', function() - local fn = mock(vim.fn, true) - local spy_jobstart = spy.on(fn, 'jobstart') - local spy_chansend = spy.on(fn, 'chansend') - local api = mock(vim.api, true) - - api.nvim_create_buf.returns(1) - api.nvim_open_term.returns(2) - - local running_app = runner.RunningApp() - running_app.is_open = false - running_app.dap_config = { name = 'config1' } - - local built_in_main_runner = runner.BuiltInMainRunner() - - runner.BuiltInMainRunner.set_up_buffer = function(selected_app) - assert.equals(selected_app, running_app) - end - runner.BuiltInMainRunner.scroll_down = function(selected_app) - assert.equals(selected_app, running_app) - end - - local spy_stop = spy.on(runner.BuiltInMainRunner, 'stop') - - built_in_main_runner.select_app_with_dap_config = function() - return running_app - end - - built_in_main_runner:run_app( - { 'java', '-cp', 'path1:path2', 'mainClass' }, - { name = 'config1' } - ) - - assert.equals(running_app.chan, 2) - assert.equals(running_app.running_status, '(running)') - - assert - .spy(spy_chansend) - .was_called_with(2, 'java -cp path1:path2 mainClass') - assert.stub(api.nvim_buf_call).was_called() - assert.spy(spy_jobstart).was_called() - - local call_info = fn.jobstart.calls[1] - assert.equals(call_info.vals[1], 'java -cp path1:path2 mainClass') - assert.not_nil(call_info.vals[2].on_exit) - assert.not_nil(call_info.vals[2].on_stdout) - assert.spy(spy_stop).was_not_called() - assert.spy(api.nvim_buf_set_name).was_called_with(1, 'config1') - - mock.revert(api) - mock.revert(fn) - end) - - it('BuiltInMainRunner:run_app when there is a running job', function() - local fn = mock(vim.fn, true) - local spy_chensend = spy.on(fn, 'chansend') - local spy_jobstart = spy.on(fn, 'jobstart') - local spy_jobwait = spy.on(fn, 'jobwait') - local spy_stop = spy.on(fn, 'jobstop') - local api = mock(vim.api, true) - - api.nvim_create_buf.returns(1) - api.nvim_open_term.returns(2) - - local running_app = runner.RunningApp() - running_app.is_open = false - running_app.job_id = 1 - running_app.dap_config = { name = 'config1' } - - local built_in_main_runner = runner.BuiltInMainRunner() - built_in_main_runner.current_app = running_app - - runner.BuiltInMainRunner.set_up_buffer = function(selected_app) - assert.equals(selected_app, running_app) - end - runner.BuiltInMainRunner.scroll_down = function(selected_app) - assert.equals(selected_app, running_app) - end - runner.BuiltInMainRunner.change_current_app = function(app) - assert.equals(app.job_id, nil) -- check if job_id is nil - end - - built_in_main_runner.select_app_with_dap_config = function() - return running_app - end - - built_in_main_runner:run_app( - { 'java', '-cp', 'path1:path2', 'mainClass' }, - { name = 'config1' } - ) - - assert.equals(running_app.running_status, '(running)') - assert.is_nil(running_app.job_id) - assert - .spy(spy_chensend) - .was_called_with(2, 'java -cp path1:path2 mainClass') - assert.spy(spy_stop).was_called_with(1) - assert.spy(spy_jobstart).was_called() - assert.spy(spy_jobwait).was_called_with({ 1 }, 1000) - assert.spy(api.nvim_buf_set_name).was_called_with(1, 'config1') - - mock.revert(api) - mock.revert(fn) - end) - - it('BuiltInMainRunner:toggle_logs when is_open=true', function() - local api = mock(vim.api, true) - api.nvim_create_buf.returns(11) - - local running_app = runner.RunningApp() - running_app.is_open = true - - runner.BuiltInMainRunner.hide_logs = function(selected_app) - assert.equals(selected_app, running_app) - end - - local spy_set_up_buffer = spy.on(runner.BuiltInMainRunner, 'set_up_buffer') - local spy_hide_logs = spy.on(runner.BuiltInMainRunner, 'hide_logs') - - runner.BuiltInMainRunner.toggle_logs(running_app) - - assert.spy(spy_hide_logs).was_called() - assert.spy(spy_set_up_buffer).was_not_called() - - mock.revert(api) - end) - - it('BuiltInMainRunner:toggle_logs when is_open=false', function() - local api = mock(vim.api, true) - api.nvim_create_buf.returns(1) - - local running_app = runner.RunningApp() - running_app.is_open = false - - runner.BuiltInMainRunner.set_up_buffer = function(selected_app) - assert.equals(selected_app, running_app) - end - - runner.BuiltInMainRunner.scroll_down = function(selected_app) - assert.equals(selected_app, running_app) - end - - local spy_set_up_buffer = spy.on(runner.BuiltInMainRunner, 'set_up_buffer') - local spy_hide_logs = spy.on(runner.BuiltInMainRunner, 'hide_logs') - - runner.BuiltInMainRunner.toggle_logs(running_app) - - local call_info = api.nvim_buf_call.calls[1] - assert.equals(call_info.vals[1], 1) - call_info.vals[2]() - - assert.spy(spy_hide_logs).was_not_called() - assert.spy(spy_set_up_buffer).was_called() - - mock.revert(api) - end) - - it('BuiltInMainRunner:stop when job_id is nil', function() - local running_app = runner.RunningApp() - running_app.job_id = nil - - local fn_job_stop_spy = spy.on(vim.fn, 'jobstop') - local fn_job_wait_spy = spy.on(vim.fn, 'jobwait') - runner.BuiltInMainRunner.stop(running_app) - - assert.spy(fn_job_stop_spy).was_not_called() - assert.spy(fn_job_wait_spy).was_not_called() - end) - - it('BuiltInMainRunner:stop when job_id is not nil', function() - local running_app = runner.RunningApp() - running_app.job_id = 1 - - local fn_job_stop_spy = spy.on(vim.fn, 'jobstop') - local fn_job_wait_spy = spy.on(vim.fn, 'jobwait') - - runner.BuiltInMainRunner.stop(running_app) - - assert.spy(fn_job_stop_spy).was_called_with(1) - assert.spy(fn_job_wait_spy).was_called_with({ 1 }, 1000) - end) - - it( - 'BuiltInMainRunner:select_from_avaible_apps when no running apps', - function() - local build_in_main_runner = runner.BuiltInMainRunner() - build_in_main_runner.running_apps = {} - - assert.error(function() - build_in_main_runner:select_app_with_ui() - end) - end - ) - - it( - 'BuildInRunner:select_from_running_apps when only one running app', - function() - local build_in_main_runner = runner.BuiltInMainRunner() - local running_app = runner.RunningApp({ projectName = 'projectName' }) - build_in_main_runner.running_apps = { running_app } - - local selected_app = build_in_main_runner:select_app_with_ui() - assert.equals(selected_app, running_app) - end - ) - - it( - 'BuildInRunner:select_from_running_apps when multiple running apps', - function() - local running_app1 = runner.RunningApp({ - projectName = 'projectName1', - mainClass = 'mainClass1', - }) - local running_app2 = runner.RunningApp({ - projectName = 'projectName2', - mainClass = 'mainClass2', - }) - - local build_in_main_runner = runner.BuiltInMainRunner() - build_in_main_runner.running_apps[running_app1.dap_config.mainClass] = - running_app1 - build_in_main_runner.running_apps[running_app2.dap_config.mainClass] = - running_app2 - - build_in_main_runner.change_current_app = function(_) end - - local spy_change_current_app = - spy.on(build_in_main_runner, 'change_current_app') - - ui.select_from_dap_configs = function(configs) - assert(#configs == 2) - return running_app2.dap_config - end - - local result = build_in_main_runner:select_app_with_ui() - assert.equals(result, running_app2) - - assert.spy(spy_change_current_app).was_called() - end - ) - - it('BuildInRunner:select_app_with_dap_config when no config', function() - local build_in_main_runner = runner.BuiltInMainRunner() - assert.error(function() - build_in_main_runner:select_app_with_dap_config() - end) - end) - - it( - 'BuildInRunner:select_app_with_dap_config when config is not found', - function() - local build_in_main_runner = runner.BuiltInMainRunner() - build_in_main_runner.running_apps = {} - - local config = { projectName = 'projectName', mainClass = 'mainClass' } - local selected_app = - build_in_main_runner:select_app_with_dap_config(config) - - assert.equals(selected_app.dap_config, config) - end - ) - - it('BuildInRunner:select_app_with_dap_config when config is found', function() - local build_in_main_runner = runner.BuiltInMainRunner() - build_in_main_runner.running_apps = {} - - local config1 = { projectName = 'projectName1', mainClass = 'mainClass1' } - local config2 = { projectName = 'projectName2', mainClass = 'mainClass2' } - - build_in_main_runner.running_apps[config1.mainClass] = - runner.RunningApp(config1) - build_in_main_runner.running_apps[config2.mainClass] = - runner.RunningApp(config2) - - local selected_app = - build_in_main_runner:select_app_with_dap_config(config2) - assert.equals(selected_app.dap_config, config2) - end) - - it('BuildInRunner:change_current_app', function() - local build_in_main_runner = runner.BuiltInMainRunner() - local running_app1 = runner.RunningApp({ projectName = 'projectName1' }) - local running_app2 = runner.RunningApp({ projectName = 'projectName2' }) - - runner.BuiltInMainRunner.hide_logs = function(selected_app) - assert.equals(selected_app, running_app2) - end - - local spy_hide_logs = spy.on(runner.BuiltInMainRunner, 'hide_logs') - - build_in_main_runner.current_app = running_app1 - - build_in_main_runner.hide_logs = function(selected_app) - assert.equals(selected_app, running_app1) - end - - assert.spy(spy_hide_logs).was_not_called() - build_in_main_runner.current_app = running_app2 - build_in_main_runner:change_current_app(running_app1) - end) - - it('BuildInRunner:change_current_app when dap config is equal', function() - local build_in_main_runner = runner.BuiltInMainRunner() - local running_app1 = runner.RunningApp({ projectName = 'projectName1' }) - - local spy_hide_logs = spy.on(runner.BuiltInMainRunner, 'hide_logs') - - build_in_main_runner.current_app = running_app1 - - build_in_main_runner.hide_logs = function(selected_app) - assert.equals(selected_app, running_app1) - end - - build_in_main_runner:change_current_app(running_app1) - - assert.spy(spy_hide_logs).was_not_called() - build_in_main_runner.current_app = running_app1 - end) -end) diff --git a/tests/java/java_spec.lua b/tests/java/java_spec.lua deleted file mode 100644 index 61a939e..0000000 --- a/tests/java/java_spec.lua +++ /dev/null @@ -1,18 +0,0 @@ -local plugin = require('java') -local mock = require('luassert.mock') - -describe('setup', function() - it('setup function', function() - assert('setup function is available', plugin.setup) - end) - - describe('check runner function available:', function() - local mock_runner = mock(plugin.runner, true) - - it('run_app', function() - mock_runner.run_app.returns({}) - - assert.same(plugin.runner.run_app(), {}) - end) - end) -end) From 7b82808493ec1c1e4c2b291f344417bd828e5afc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:22:52 +0530 Subject: [PATCH 002/147] chore(main): release 1.6.0 (#201) * chore(main): release 1.6.0 * Update CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb7ad98..5d755cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.6.0](https://github.com/nvim-java/nvim-java/compare/v1.5.1...v1.6.0) (2024-06-25) + + +### Features + +* handle multiple running app ([#182](https://github.com/nvim-java/nvim-java/issues/182)) ([#191](https://github.com/nvim-java/nvim-java/issues/191)) ([ca5cfdb](https://github.com/nvim-java/nvim-java/commit/ca5cfdba0d0629a829d16fa838808be0d5db8baa)) + ## [1.5.1](https://github.com/nvim-java/nvim-java/compare/v1.5.0...v1.5.1) (2024-05-29) From c106d0f90696f165c2e7755518d8b7aae368254a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 23:27:09 +0530 Subject: [PATCH 003/147] chore(doc): automatic vimdoc update (#178) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index c740642..d0d6a4c 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 May 04 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 June 25 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -24,6 +24,9 @@ Table of Contents *nvim-java-table-of-contents* Just install and start writing `public static void main(String[] args)`. + [!WARNING] You cannot use `nvim-java` alongside `nvim-jdtls`. So remove + `nvim-jdtls` before installing this + DEMO *nvim-java-demo* @@ -36,6 +39,7 @@ FEATURES *nvim-java-features* - Automatic DAP debug configuration - Running tests +- Run & Debug profiles WHY *nvim-java-why* @@ -55,6 +59,12 @@ HOW TO INSTALL *nvim-java-how-to-install* :small_orange_diamond:details ~ +Q & A ~ + +If you face any issues, check our Q & A + wiki to see if that helps + + DISTRIBUTIONS ~ - Lazyvim From 1fae8de1327167ac4b9f744970b2b6b7c7652874 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 27 Jun 2024 15:28:31 +0530 Subject: [PATCH 004/147] fix: when the same main class is ran again, first process is not stopped --- lua/java/runner/run.lua | 2 +- lua/java/runner/runner.lua | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lua/java/runner/run.lua b/lua/java/runner/run.lua index 4e677f6..cb5ab35 100644 --- a/lua/java/runner/run.lua +++ b/lua/java/runner/run.lua @@ -5,10 +5,10 @@ local notify = require('java-core.utils.notify') ---@field name string ---@field main_class string ---@field buffer number +---@field is_running boolean ---@field private cmd string ---@field private term_chan_id number ---@field private job_chan_id number | nil ----@field private is_running boolean ---@field private is_failure boolean local Run = class() diff --git a/lua/java/runner/runner.lua b/lua/java/runner/runner.lua index 3f7c2f3..6b0ddc4 100644 --- a/lua/java/runner/runner.lua +++ b/lua/java/runner/runner.lua @@ -26,11 +26,13 @@ function Runner:start_run(args) return end - local run ---@as java.Run + local run = self.runs[dap_config.mainClass] -- get the default run if exist or create new run - if self.runs[dap_config.mainClass] then - run = self.runs[dap_config.mainClass] + if run then + if run.is_running then + run:stop() + end else run = Run(dap_config, cmd) self.runs[dap_config.mainClass] = run From 41abed319134ab2262a8c571486d46cee7ba35d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 15:30:26 +0530 Subject: [PATCH 005/147] chore(main): release 1.6.1 (#214) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d755cf..2d170de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.6.1](https://github.com/nvim-java/nvim-java/compare/v1.6.0...v1.6.1) (2024-06-27) + + +### Bug Fixes + +* when the same main class is ran again, first process is not stopped ([1fae8de](https://github.com/nvim-java/nvim-java/commit/1fae8de1327167ac4b9f744970b2b6b7c7652874)) + ## [1.6.0](https://github.com/nvim-java/nvim-java/compare/v1.5.1...v1.6.0) (2024-06-25) From 1349ac545feb37459a04a0a37d41496463c63c87 Mon Sep 17 00:00:00 2001 From: Joao-Queiroga <72106566+Joao-Queiroga@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:20:52 -0300 Subject: [PATCH 006/147] feat: add lazy.lua file to declare dependencies on lazy.nvim (#215) --- README.md | 23 +---------------------- lazy.lua | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 22 deletions(-) create mode 100644 lazy.lua diff --git a/README.md b/README.md index c94b909..5f22dde 100644 --- a/README.md +++ b/README.md @@ -55,28 +55,7 @@ If you face any issues, check our [Q & A](https://github.com/nvim-java/nvim-java Using [lazy.nvim](https://github.com/folke/lazy.nvim) ```lua -return { - 'nvim-java/nvim-java', - dependencies = { - 'nvim-java/lua-async-await', - 'nvim-java/nvim-java-refactor', - 'nvim-java/nvim-java-core', - 'nvim-java/nvim-java-test', - 'nvim-java/nvim-java-dap', - 'MunifTanjim/nui.nvim', - 'neovim/nvim-lspconfig', - 'mfussenegger/nvim-dap', - { - 'williamboman/mason.nvim', - opts = { - registries = { - 'github:nvim-java/mason-registry', - 'github:mason-org/mason-registry', - }, - }, - } - }, -} +return {'nvim-java/nvim-java'} ``` - Setup nvim-java before `lspconfig` diff --git a/lazy.lua b/lazy.lua new file mode 100644 index 0000000..f46af6d --- /dev/null +++ b/lazy.lua @@ -0,0 +1,22 @@ +return { + 'nvim-java/nvim-java', + dependencies = { + 'nvim-java/lua-async-await', + 'nvim-java/nvim-java-refactor', + 'nvim-java/nvim-java-core', + 'nvim-java/nvim-java-test', + 'nvim-java/nvim-java-dap', + 'MunifTanjim/nui.nvim', + 'neovim/nvim-lspconfig', + 'mfussenegger/nvim-dap', + { + 'williamboman/mason.nvim', + opts = { + registries = { + 'github:nvim-java/mason-registry', + 'github:mason-org/mason-registry', + }, + }, + }, + }, +} From 046b492cf2509800d2f895a7f3a116083765f7aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:55:23 +0530 Subject: [PATCH 007/147] chore(doc): automatic vimdoc update (#213) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index d0d6a4c..b378398 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 June 25 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 June 28 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -77,28 +77,7 @@ CUSTOM ~ Using lazy.nvim >lua - return { - 'nvim-java/nvim-java', - dependencies = { - 'nvim-java/lua-async-await', - 'nvim-java/nvim-java-refactor', - 'nvim-java/nvim-java-core', - 'nvim-java/nvim-java-test', - 'nvim-java/nvim-java-dap', - 'MunifTanjim/nui.nvim', - 'neovim/nvim-lspconfig', - 'mfussenegger/nvim-dap', - { - 'williamboman/mason.nvim', - opts = { - registries = { - 'github:nvim-java/mason-registry', - 'github:mason-org/mason-registry', - }, - }, - } - }, - } + return {'nvim-java/nvim-java'} < - Setup nvim-java before `lspconfig` From 96472bf5b7a2fdbfa49c58b056bfc8606f4b7275 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 21:56:10 +0530 Subject: [PATCH 008/147] chore(main): release 1.7.0 (#216) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d170de..c414746 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.7.0](https://github.com/nvim-java/nvim-java/compare/v1.6.1...v1.7.0) (2024-06-28) + + +### Features + +* add lazy.lua file to declare dependencies on lazy.nvim ([#215](https://github.com/nvim-java/nvim-java/issues/215)) ([1349ac5](https://github.com/nvim-java/nvim-java/commit/1349ac545feb37459a04a0a37d41496463c63c87)) + ## [1.6.1](https://github.com/nvim-java/nvim-java/compare/v1.6.0...v1.6.1) (2024-06-27) From 15bc822acb1e11983bde70f436dd17d41ba76925 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Mon, 1 Jul 2024 21:33:03 +0530 Subject: [PATCH 009/147] feat: add validations for exec order, duplicate setup calls (#219) NOTE: Previous installation instruction we had are causing jdtls to setup multiple times and on some configuration jdtls setup is done before nvim-java setup (nvim-java should always should be setup before jdtls). - Now there are validations for execution order check. For example, if the jdtls setup is called before the nvim-java setup, user will see an error on startup (This is not perfect but it's something). You can either remove the check from the nvim-java configuration if everything works fine for you and you don't want to touch anything. But if you have time, check the latest installation instruction or try to figure out what's causing the issue in your config. - Additionally, we check is nvim-java setup is being called more than once. If you are passing some configuration to nvim-java if they are not working, this is probably why. First time it's setup with the config you are passing but using the default in consequent calls. Once again, you can check the instructions to fix the issue. --- README.md | 84 +++++++++++++--------- lua/java.lua | 7 +- lua/java/config.lua | 21 ++++++ lua/java/startup/duplicate-setup-check.lua | 27 +++++++ lua/java/startup/exec-order-check.lua | 46 ++++++++++++ lua/java/startup/nvim-dep.lua | 26 ++++--- lua/java/startup/startup-check.lua | 46 ++++++++++++ 7 files changed, 213 insertions(+), 44 deletions(-) create mode 100644 lua/java/startup/duplicate-setup-check.lua create mode 100644 lua/java/startup/exec-order-check.lua create mode 100644 lua/java/startup/startup-check.lua diff --git a/README.md b/README.md index 5f22dde..f035206 100644 --- a/README.md +++ b/README.md @@ -264,38 +264,58 @@ want, following options are available ```lua { - -- list of file that exists in root of the project - root_markers = { - 'settings.gradle', - 'settings.gradle.kts', - 'pom.xml', - 'build.gradle', - 'mvnw', - 'gradlew', - 'build.gradle', - 'build.gradle.kts', - '.git', - }, - - -- load java test plugins - java_test = { - enable = true, - }, - - -- load java debugger plugins - java_debug_adapter = { - enable = true, - }, - - jdk = { - -- install jdk using mason.nvim - auto_install = true, - }, - - notifications = { - -- enable 'Configuring DAP' & 'DAP configured' messages on start up - dap = true, - }, + -- list of file that exists in root of the project + root_markers = { + 'settings.gradle', + 'settings.gradle.kts', + 'pom.xml', + 'build.gradle', + 'mvnw', + 'gradlew', + 'build.gradle', + 'build.gradle.kts', + '.git', + }, + + -- load java test plugins + java_test = { + enable = true, + }, + + -- load java debugger plugins + java_debug_adapter = { + enable = true, + }, + + jdk = { + -- install jdk using mason.nvim + auto_install = true, + }, + + notifications = { + -- enable 'Configuring DAP' & 'DAP configured' messages on start up + dap = true, + }, + + -- We do multiple verifications to make sure things are in place to run this + -- plugin + verification = { + -- nvim-java checks for the order of execution of following + -- * require('java').setup() + -- * require('lspconfig').jdtls.setup() + -- IF they are not executed in the correct order, you will see a error + -- notification. + -- Set following to false to disable the notification if you know what you + -- are doing + invalid_order = true, + + -- nvim-java checks if the require('java').setup() is called multiple + -- times. + -- IF there are multiple setup calls are executed, an error will be shown + -- Set following property value to false to disable the notification if + -- you know what you are doing + duplicate_setup_calls = true, + }, } ``` diff --git a/lua/java.lua b/lua/java.lua index e378f87..d6cdd08 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -1,7 +1,7 @@ local decomple_watch = require('java.startup.decompile-watcher') local mason_dep = require('java.startup.mason-dep') -local nvim_dep = require('java.startup.nvim-dep') local setup_wrap = require('java.startup.lspconfig-setup-wrap') +local startup_check = require('java.startup.startup-check') local test = require('java.api.test') local dap = require('java.api.dap') @@ -16,9 +16,12 @@ local M = {} function M.setup(custom_config) local config = vim.tbl_deep_extend('force', global_config, custom_config or {}) + vim.g.nvim_java_config = config - nvim_dep.check() + if not startup_check() then + return + end local is_installing = mason_dep.install(config) diff --git a/lua/java/config.lua b/lua/java/config.lua index e013d1e..e80c342 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -4,6 +4,7 @@ ---@field java_debug_adapter { enable: boolean } ---@field jdk { auto_install: boolean } ---@field notifications { dap: boolean } +---@field verification { invalid_order: boolean, duplicate_setup_calls: boolean } local config = { -- list of file that exists in root of the project root_markers = { @@ -37,6 +38,26 @@ local config = { -- enable 'Configuring DAP' & 'DAP configured' messages on start up dap = true, }, + + -- We do multiple verifications to make sure things are in place to run this + -- plugin + verification = { + -- nvim-java checks for the order of execution of following + -- * require('java').setup() + -- * require('lspconfig').jdtls.setup() + -- IF they are not executed in the correct order, you will see a error + -- notification. + -- Set following to false to disable the notification if you know what you + -- are doing + invalid_order = true, + + -- nvim-java checks if the require('java').setup() is called multiple + -- times. + -- IF there are multiple setup calls are executed, an error will be shown + -- Set following property value to false to disable the notification if + -- you know what you are doing + duplicate_setup_calls = true, + }, } return config diff --git a/lua/java/startup/duplicate-setup-check.lua b/lua/java/startup/duplicate-setup-check.lua new file mode 100644 index 0000000..cbe38eb --- /dev/null +++ b/lua/java/startup/duplicate-setup-check.lua @@ -0,0 +1,27 @@ +local M = {} + +local message = 'require("java").setup() is called more than once' + .. '\nnvim-java will continue to setup but nvim-java configurations might not work as expected' + .. '\nThis might be due to old installation instructions.' + .. '\nPlease check the latest guide at https://github.com/nvim-java/nvim-java#hammer-how-to-install' + .. '\nIf you know what you are doing, you can disable the check from the config' + .. '\nhttps://github.com/nvim-java/nvim-java#wrench-configuration' + +function M.is_valid() + if vim.g.nvim_java_setup_is_called then + return { + success = false, + continue = true, + message = message, + } + end + + vim.g.nvim_java_setup_is_called = true + + return { + success = true, + continue = true, + } +end + +return M diff --git a/lua/java/startup/exec-order-check.lua b/lua/java/startup/exec-order-check.lua new file mode 100644 index 0000000..42e84d5 --- /dev/null +++ b/lua/java/startup/exec-order-check.lua @@ -0,0 +1,46 @@ +local lspconfig = require('lspconfig') + +local M = {} + +lspconfig.util.on_setup = lspconfig.util.add_hook_before( + lspconfig.util.on_setup, + function(config) + if config.name == 'jdtls' then + vim.g.nvim_java_jdtls_setup_is_called = true + end + end +) + +local message = 'Looks like require("lspconfig").jdtls.setup() is called before require("java").setup().' + .. '\nnvim-java will continue to setup but most features may not work as expected' + .. '\nThis might be due to old installation instructions.' + .. '\nPlease check the latest guide at https://github.com/nvim-java/nvim-java#hammer-how-to-install' + .. '\nIf you know what you are doing, you can disable the check from the config' + .. '\nhttps://github.com/nvim-java/nvim-java#wrench-configuration' + +function M.is_valid() + if vim.g.nvim_java_jdtls_setup_is_called then + return { + success = false, + continue = true, + message = message, + } + end + + local clients = vim.lsp.get_clients({ name = 'jdtls' }) + + if #clients > 0 then + return { + success = false, + continue = true, + message = message, + } + end + + return { + success = true, + continue = true, + } +end + +return M diff --git a/lua/java/startup/nvim-dep.lua b/lua/java/startup/nvim-dep.lua index e304190..10c485e 100644 --- a/lua/java/startup/nvim-dep.lua +++ b/lua/java/startup/nvim-dep.lua @@ -1,4 +1,3 @@ -local notify = require('java-core.utils.notify') local log = require('java.utils.log') local pkgs = { @@ -36,26 +35,33 @@ Please follow the install guide in https://github.com/nvim-java/nvim-java to ins local M = {} -function M.check() +function M.is_valid() log.info('check neovim plugin dependencies') - M.neovim_plugin_check() -end ----@private -function M.neovim_plugin_check() for _, pkg in ipairs(pkgs) do local ok, _ = pcall(require, pkg.name) if not ok then if pkg.warn then - log.warn(pkg.warn) - notify.warn(pkg.warn) + return { + success = false, + continue = true, + message = pkg.warn, + } else - log.error(pkg.err) - error(pkg.err) + return { + success = false, + continue = false, + message = pkg.err, + } end end end + + return { + success = true, + continue = true, + } end return M diff --git a/lua/java/startup/startup-check.lua b/lua/java/startup/startup-check.lua new file mode 100644 index 0000000..d33d887 --- /dev/null +++ b/lua/java/startup/startup-check.lua @@ -0,0 +1,46 @@ +local log = require('java.utils.log') +local notify = require('java-core.utils.notify') + +local function get_checkers() + local config = vim.g.nvim_java_config + local checks = {} + + if config.verification.invalid_order then + table.insert(checks, select(1, require('java.startup.exec-order-check'))) + end + + if config.verification.duplicate_setup_calls then + table.insert( + checks, + select(1, require('java.startup.duplicate-setup-check')) + ) + end + + table.insert(checks, select(1, require('java.startup.nvim-dep'))) + + return checks +end + +return function() + local checkers = get_checkers() + + for _, check in ipairs(checkers) do + local check_res = check.is_valid() + + if check_res.message then + if not check_res.success then + log.error(check_res.message) + notify.error(check_res.message) + else + log.warn(check_res.message) + notify.warn(check_res.message) + end + end + + if not check_res.continue then + return false + end + end + + return true +end From 20156319a931b39d6d14defb805d6d962f1763cf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:34:25 +0530 Subject: [PATCH 010/147] chore(doc): automatic vimdoc update (#222) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 78 +++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index b378398..7df8eff 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 June 28 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 01 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -286,38 +286,58 @@ want, following options are available >lua { - -- list of file that exists in root of the project - root_markers = { - 'settings.gradle', - 'settings.gradle.kts', - 'pom.xml', - 'build.gradle', - 'mvnw', - 'gradlew', - 'build.gradle', - 'build.gradle.kts', - '.git', - }, + -- list of file that exists in root of the project + root_markers = { + 'settings.gradle', + 'settings.gradle.kts', + 'pom.xml', + 'build.gradle', + 'mvnw', + 'gradlew', + 'build.gradle', + 'build.gradle.kts', + '.git', + }, - -- load java test plugins - java_test = { - enable = true, - }, + -- load java test plugins + java_test = { + enable = true, + }, - -- load java debugger plugins - java_debug_adapter = { - enable = true, - }, + -- load java debugger plugins + java_debug_adapter = { + enable = true, + }, - jdk = { - -- install jdk using mason.nvim - auto_install = true, - }, + jdk = { + -- install jdk using mason.nvim + auto_install = true, + }, - notifications = { - -- enable 'Configuring DAP' & 'DAP configured' messages on start up - dap = true, - }, + notifications = { + -- enable 'Configuring DAP' & 'DAP configured' messages on start up + dap = true, + }, + + -- We do multiple verifications to make sure things are in place to run this + -- plugin + verification = { + -- nvim-java checks for the order of execution of following + -- * require('java').setup() + -- * require('lspconfig').jdtls.setup() + -- IF they are not executed in the correct order, you will see a error + -- notification. + -- Set following to false to disable the notification if you know what you + -- are doing + invalid_order = true, + + -- nvim-java checks if the require('java').setup() is called multiple + -- times. + -- IF there are multiple setup calls are executed, an error will be shown + -- Set following property value to false to disable the notification if + -- you know what you are doing + duplicate_setup_calls = true, + }, } < From 26bfcbe636f830d70b17384ea314bdd0a532169d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Jul 2024 21:35:12 +0530 Subject: [PATCH 011/147] chore(main): release 1.8.0 (#221) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c414746..3ebd937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.8.0](https://github.com/nvim-java/nvim-java/compare/v1.7.0...v1.8.0) (2024-07-01) + + +### Features + +* add validations for exec order, duplicate setup calls ([#219](https://github.com/nvim-java/nvim-java/issues/219)) ([15bc822](https://github.com/nvim-java/nvim-java/commit/15bc822acb1e11983bde70f436dd17d41ba76925)) + ## [1.7.0](https://github.com/nvim-java/nvim-java/compare/v1.6.1...v1.7.0) (2024-06-28) From a0e58a9ba8134e4f896fd82b8e126156abb75bd5 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Tue, 2 Jul 2024 23:39:31 +0530 Subject: [PATCH 012/147] chore(doc): link kickstart config in readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f035206..d0c818f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Just install and start writing `public static void main(String[] args)`. ## :loudspeaker: Demo -https://github.com/nvim-java/nvim-java/assets/18459807/047c8c46-9a0a-4869-b342-d5c2e15647bc + ## :dizzy: Features @@ -44,9 +44,10 @@ https://github.com/nvim-java/nvim-java/assets/18459807/047c8c46-9a0a-4869-b342-d If you face any issues, check our [Q & A](https://github.com/nvim-java/nvim-java/wiki/Q-&-A) wiki to see if that helps -### Distributions +### Distributions & Templates - [Lazyvim](https://github.com/nvim-java/nvim-java/wiki/Lazyvim) +- [Kickstart](https://github.com/nvim-java/nvim-java/wiki/Kickstart) ### Custom From 26b3d3f38412b8548af40138c8d6ad7787b7ee81 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:41:01 +0530 Subject: [PATCH 013/147] chore(doc): automatic vimdoc update (#223) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 7df8eff..6c5302e 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 01 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 02 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -30,7 +30,7 @@ Just install and start writing `public static void main(String[] args)`. DEMO *nvim-java-demo* -https://github.com/nvim-java/nvim-java/assets/18459807/047c8c46-9a0a-4869-b342-d5c2e15647bc + FEATURES *nvim-java-features* @@ -65,9 +65,10 @@ If you face any issues, check our Q & A wiki to see if that helps -DISTRIBUTIONS ~ +DISTRIBUTIONS & TEMPLATES ~ - Lazyvim +- Kickstart CUSTOM ~ From ef7597d158f0687c8c0bd2c11e5907c32e52574f Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Wed, 3 Jul 2024 21:55:43 +0530 Subject: [PATCH 014/147] feat: add mason registry check (#225) --- README.md | 6 ++++ lua/java/config.lua | 8 ++++- lua/java/startup/mason-registry-check.lua | 36 +++++++++++++++++++++++ lua/java/startup/startup-check.lua | 7 +++++ 4 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 lua/java/startup/mason-registry-check.lua diff --git a/README.md b/README.md index d0c818f..f059ad8 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,12 @@ want, following options are available -- Set following property value to false to disable the notification if -- you know what you are doing duplicate_setup_calls = true, + + -- nvim-java checks if nvim-java/mason-registry is added correctly to + -- mason.nvim plugin. + -- IF it's not registered correctly, an error will be thrown and nvim-java + -- will stop setup + invalid_mason_registry = true, }, } ``` diff --git a/lua/java/config.lua b/lua/java/config.lua index e80c342..003d5c5 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -4,7 +4,7 @@ ---@field java_debug_adapter { enable: boolean } ---@field jdk { auto_install: boolean } ---@field notifications { dap: boolean } ----@field verification { invalid_order: boolean, duplicate_setup_calls: boolean } +---@field verification { invalid_order: boolean, duplicate_setup_calls: boolean, invalid_mason_registry: boolean } local config = { -- list of file that exists in root of the project root_markers = { @@ -57,6 +57,12 @@ local config = { -- Set following property value to false to disable the notification if -- you know what you are doing duplicate_setup_calls = true, + + -- nvim-java checks if nvim-java/mason-registry is added correctly to + -- mason.nvim plugin. + -- IF it's not registered correctly, an error will be thrown and nvim-java + -- will stop setup + invalid_mason_registry = true, }, } diff --git a/lua/java/startup/mason-registry-check.lua b/lua/java/startup/mason-registry-check.lua new file mode 100644 index 0000000..76e3e8d --- /dev/null +++ b/lua/java/startup/mason-registry-check.lua @@ -0,0 +1,36 @@ +local mason_source = require('mason-registry.sources') + +local M = { + JAVA_REG_ID = 'github:nvim-java/mason-registry', +} + +function M.is_valid() + local has_reg = false + + for reg in mason_source.iter() do + if reg.id == M.JAVA_REG_ID then + has_reg = true + goto continue + end + end + + ::continue:: + + if has_reg then + return { + success = true, + continue = true, + } + end + + return { + success = false, + continue = false, + message = 'nvim-java mason registry is not added correctly!' + .. '\nThis occurs when mason.nvim configured incorrectly' + .. '\nPlease refer the link below to fix the issue' + .. '\nhttps://github.com/nvim-java/nvim-java/wiki/Q-&-A#no_entry-cannot-find-package-xxxxx', + } +end + +return M diff --git a/lua/java/startup/startup-check.lua b/lua/java/startup/startup-check.lua index d33d887..a1bf5bd 100644 --- a/lua/java/startup/startup-check.lua +++ b/lua/java/startup/startup-check.lua @@ -5,6 +5,13 @@ local function get_checkers() local config = vim.g.nvim_java_config local checks = {} + if config.verification.invalid_mason_registry then + table.insert( + checks, + select(1, require('java.startup.mason-registry-check')) + ) + end + if config.verification.invalid_order then table.insert(checks, select(1, require('java.startup.exec-order-check'))) end From b8729f0b7fc65008b8363b0b1a764368dd4c7777 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:04:15 +0530 Subject: [PATCH 015/147] chore(doc): automatic vimdoc update (#226) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 6c5302e..7364b11 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 02 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 03 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -338,6 +338,12 @@ want, following options are available -- Set following property value to false to disable the notification if -- you know what you are doing duplicate_setup_calls = true, + + -- nvim-java checks if nvim-java/mason-registry is added correctly to + -- mason.nvim plugin. + -- IF it's not registered correctly, an error will be thrown and nvim-java + -- will stop setup + invalid_mason_registry = true, }, } < From 009f3eb2749b68bd0cd5f1366c69b3fabb485b21 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Jul 2024 22:04:59 +0530 Subject: [PATCH 016/147] chore(main): release 1.9.0 (#227) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ebd937..1dc7aa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.9.0](https://github.com/nvim-java/nvim-java/compare/v1.8.0...v1.9.0) (2024-07-03) + + +### Features + +* add mason registry check ([#225](https://github.com/nvim-java/nvim-java/issues/225)) ([ef7597d](https://github.com/nvim-java/nvim-java/commit/ef7597d158f0687c8c0bd2c11e5907c32e52574f)) + ## [1.8.0](https://github.com/nvim-java/nvim-java/compare/v1.7.0...v1.8.0) (2024-07-01) From bb7d586161bf3e10153dc6a1180984d310c025fe Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Fri, 5 Jul 2024 23:24:43 +0530 Subject: [PATCH 017/147] fix: get_client func is failing on older neovim --- lua/java/startup/exec-order-check.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/java/startup/exec-order-check.lua b/lua/java/startup/exec-order-check.lua index 42e84d5..259f9f6 100644 --- a/lua/java/startup/exec-order-check.lua +++ b/lua/java/startup/exec-order-check.lua @@ -27,7 +27,9 @@ function M.is_valid() } end - local clients = vim.lsp.get_clients({ name = 'jdtls' }) + local clients = vim.lsp.get_clients + and vim.lsp.get_clients({ name = 'jdtls' }) + or vim.lsp.get_active_clients({ name = 'jdtls' }) if #clients > 0 then return { From f44b682fa3b985a9d65d474ccc129673ce9074fc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 23:28:08 +0530 Subject: [PATCH 018/147] chore(main): release 1.9.1 (#230) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dc7aa5..532b1e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.9.1](https://github.com/nvim-java/nvim-java/compare/v1.9.0...v1.9.1) (2024-07-05) + + +### Bug Fixes + +* get_client func is failing on older neovim ([bb7d586](https://github.com/nvim-java/nvim-java/commit/bb7d586161bf3e10153dc6a1180984d310c025fe)) + ## [1.9.0](https://github.com/nvim-java/nvim-java/compare/v1.8.0...v1.9.0) (2024-07-03) From 028e870c5a69cf5ee68e4776e9614a664cc65871 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sat, 6 Jul 2024 01:32:53 +0530 Subject: [PATCH 019/147] feat: add spring boot tools support (#232) --- README.md | 4 ++++ lazy.lua | 1 + lua/java/config.lua | 5 +++++ lua/java/startup/lspconfig-setup-wrap.lua | 14 ++++++++++++++ lua/java/startup/mason-dep.lua | 17 +++++++++++++---- 5 files changed, 37 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f059ad8..6af665c 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,10 @@ want, following options are available enable = true, }, + spring_boot_tools = { + enable = true, + }, + jdk = { -- install jdk using mason.nvim auto_install = true, diff --git a/lazy.lua b/lazy.lua index f46af6d..a3ea62b 100644 --- a/lazy.lua +++ b/lazy.lua @@ -9,6 +9,7 @@ return { 'MunifTanjim/nui.nvim', 'neovim/nvim-lspconfig', 'mfussenegger/nvim-dap', + 'JavaHello/spring-boot.nvim', { 'williamboman/mason.nvim', opts = { diff --git a/lua/java/config.lua b/lua/java/config.lua index 003d5c5..8844429 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -2,6 +2,7 @@ ---@field root_markers string[] ---@field java_test { enable: boolean } ---@field java_debug_adapter { enable: boolean } +---@field spring_boot_tools { enable: boolean } ---@field jdk { auto_install: boolean } ---@field notifications { dap: boolean } ---@field verification { invalid_order: boolean, duplicate_setup_calls: boolean, invalid_mason_registry: boolean } @@ -29,6 +30,10 @@ local config = { enable = true, }, + spring_boot_tools = { + enable = true, + }, + jdk = { -- install jdk using mason.nvim auto_install = true, diff --git a/lua/java/startup/lspconfig-setup-wrap.lua b/lua/java/startup/lspconfig-setup-wrap.lua index f179d67..ab3eb75 100644 --- a/lua/java/startup/lspconfig-setup-wrap.lua +++ b/lua/java/startup/lspconfig-setup-wrap.lua @@ -1,5 +1,6 @@ local lspconfig = require('lspconfig') local log = require('java.utils.log') +local mason_util = require('java-core.utils.mason') local server = require('java-core.ls.servers.jdtls') @@ -23,12 +24,25 @@ function M.setup(config) table.insert(jdtls_plugins, 'java-debug-adapter') end + if config.spring_boot_tools.enable then + table.insert(jdtls_plugins, 'spring-boot-tools') + end + local default_config = server.get_config({ root_markers = config.root_markers, jdtls_plugins = jdtls_plugins, use_mason_jdk = config.jdk.auto_install, }) + if config.spring_boot_tools.enable then + require('spring_boot').setup({ + ls_path = mason_util.get_pkg_path('spring-boot-tools') + .. '/extension/language-server', + }) + + require('spring_boot').init_lsp_commands() + end + org_setup(vim.tbl_extend('force', default_config, user_config)) end end diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index 8621732..73353b8 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -6,6 +6,8 @@ local async = require('java-core.utils.async') local lazy = require('java.ui.lazy') local sync = async.sync +local List = require('java-core.utils.list') + local M = {} ---Install mason package dependencies for nvim-java @@ -42,19 +44,26 @@ function M.refresh_and_install(packages) mason_util.install_pkgs(packages) end +---Returns a list of dependency packages +---@param config java.Config +---@return table function M.get_pkg_list(config) - local dependecies = { + local deps = List:new({ { name = 'jdtls', version = 'v1.31.0' }, { name = 'lombok-nightly', version = 'nightly' }, { name = 'java-test', version = '0.40.1' }, { name = 'java-debug-adapter', version = '0.55.0' }, - } + }) if config.jdk.auto_install then - table.insert(dependecies, { name = 'openjdk-17', version = '17.0.2' }) + deps:push({ name = 'openjdk-17', version = '17.0.2' }) + end + + if config.spring_boot_tools.enable then + deps:push({ name = 'spring-boot-tools', version = '1.55.1' }) end - return dependecies + return deps end return M From 8348857a8151316db5d3e1dcfd73796bae561c58 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 01:42:50 +0530 Subject: [PATCH 020/147] chore(doc): automatic vimdoc update (#231) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 7364b11..bdb56bc 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 03 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 05 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -310,6 +310,10 @@ want, following options are available enable = true, }, + spring_boot_tools = { + enable = true, + }, + jdk = { -- install jdk using mason.nvim auto_install = true, From 7ade92c38a517e7fbfe038900617a57898feb257 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sat, 6 Jul 2024 01:52:04 +0530 Subject: [PATCH 021/147] chore(doc): update features in readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6af665c..bbd18ce 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Just install and start writing `public static void main(String[] args)`. ## :dizzy: Features +- :white_check_mark: Spring boot tools - :white_check_mark: Diagnostics & Auto Completion - :white_check_mark: Automatic [DAP](https://github.com/mfussenegger/nvim-dap) debug configuration @@ -30,6 +31,7 @@ Just install and start writing `public static void main(String[] args)`. - Realtime server settings updates is possible using [neoconf](https://github.com/folke/neoconf.nvim) - Auto loads necessary `jdtls` plugins - Supported plugins are, + - `spring-boot-tools` - `lombok` - `java-test` - `java-debug-adapter` From 64ffdf8e2ac7cde817ddcc7156bdc0ae173418c2 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sat, 6 Jul 2024 01:52:32 +0530 Subject: [PATCH 022/147] refactor: lock spring-boot dep to reviewed commit --- lazy.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lazy.lua b/lazy.lua index a3ea62b..90e58b3 100644 --- a/lazy.lua +++ b/lazy.lua @@ -9,7 +9,10 @@ return { 'MunifTanjim/nui.nvim', 'neovim/nvim-lspconfig', 'mfussenegger/nvim-dap', - 'JavaHello/spring-boot.nvim', + { + 'JavaHello/spring-boot.nvim', + commit = '218c0c26c14d99feca778e4d13f5ec3e8b1b60f0', + }, { 'williamboman/mason.nvim', opts = { From ccf38dbfe41eecd70edfc23cf6acf81ab5707e29 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 01:53:28 +0530 Subject: [PATCH 023/147] chore(doc): automatic vimdoc update (#234) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index bdb56bc..a0c7fc1 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -35,6 +35,7 @@ DEMO *nvim-java-demo* FEATURES *nvim-java-features* +- Spring boot tools - Diagnostics & Auto Completion - Automatic DAP debug configuration @@ -49,6 +50,7 @@ WHY *nvim-java-why* - Realtime server settings updates is possible using neoconf - Auto loads necessary `jdtls` plugins - Supported plugins are, + - `spring-boot-tools` - `lombok` - `java-test` - `java-debug-adapter` From 5b07857ce01df35fceb0b2fb3ebb718060658441 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 01:58:15 +0530 Subject: [PATCH 024/147] chore(main): release 1.10.0 (#233) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 532b1e2..5990832 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.10.0](https://github.com/nvim-java/nvim-java/compare/v1.9.1...v1.10.0) (2024-07-05) + + +### Features + +* add spring boot tools support ([#232](https://github.com/nvim-java/nvim-java/issues/232)) ([028e870](https://github.com/nvim-java/nvim-java/commit/028e870c5a69cf5ee68e4776e9614a664cc65871)) + ## [1.9.1](https://github.com/nvim-java/nvim-java/compare/v1.9.0...v1.9.1) (2024-07-05) From 4d92c3d8552aa3c80a3f4a98754e570a564addf5 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sat, 6 Jul 2024 14:27:06 +0530 Subject: [PATCH 025/147] feat: add build workspace command --- README.md | 27 +++++++++++++++++++++++---- lua/java.lua | 7 +++++++ lua/java/api/build.lua | 22 ++++++++++++++++++++++ plugin/java.lua | 2 ++ 4 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 lua/java/api/build.lua diff --git a/README.md b/README.md index bbd18ce..6a48d6e 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,10 @@ Yep! That's all :) :small_orange_diamond:details +### Build + +- `JavaBuildWorkspace` - Runs a full workspace build + ### Runner - `JavaRunnerRunMain` - Runs the application or selected main class (if there @@ -125,6 +129,14 @@ Yep! That's all :) :small_orange_diamond:details +### Build + +- `build.build_workspace` - Runs a full workspace build + +```lua +require('java').build.build_workspace() +``` + ### Runner - `built_in.run_app` - Runs the application or selected main class (if there @@ -397,7 +409,14 @@ For instance, to run the current test, ## :bookmark_tabs: Projects Acknowledgement -[nvim-jdtls](https://github.com/mfussenegger/nvim-jdtls) is a plugin that follows -"Keep it simple, stupid!" approach. If you love customizing things by yourself, -then give nvim-jdtls a try. I may or may not have copied some code :wink: -Beauty of Open source! +- [spring-boot.nvim](https://github.com/JavaHello/spring-boot.nvim) is the one + that starts sts4 & do other necessary `jdtls` `sts4` sync command registration + in `nvim-java`. + +- [nvim-jdtls](https://github.com/mfussenegger/nvim-jdtls) is a plugin that follows + "Keep it simple, stupid!" approach. If you love customizing things by yourself, + then give nvim-jdtls a try. + +> [!WARNING] +> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` +> before installing this diff --git a/lua/java.lua b/lua/java.lua index d6cdd08..718f883 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -8,6 +8,7 @@ local dap = require('java.api.dap') local runner = require('java.api.runner') local profile_ui = require('java.ui.profile') local refactor = require('java.api.refactor') +local build_api = require('java.api.build') local global_config = require('java.config') @@ -32,6 +33,12 @@ function M.setup(custom_config) end end +---------------------------------------------------------------------- +-- Experimental APIs -- +---------------------------------------------------------------------- +M.build = {} +M.build.build_workspace = build_api.full_build_workspace + ---------------------------------------------------------------------- -- DAP APIs -- ---------------------------------------------------------------------- diff --git a/lua/java/api/build.lua b/lua/java/api/build.lua new file mode 100644 index 0000000..ac54056 --- /dev/null +++ b/lua/java/api/build.lua @@ -0,0 +1,22 @@ +local async = require('java-core.utils.async').sync +local get_error_handler = require('java.handlers.error') + +local M = {} + +function M.full_build_workspace() + return async(function() + local JavaCoreJdtlsClient = require('java-core.ls.clients.jdtls-client') + local jdtls = require('java.utils.jdtls') + local buf_util = require('java.utils.buffer') + local notify = require('java-core.utils.notify') + + JavaCoreJdtlsClient:new(jdtls()) + :java_build_workspace(true, buf_util.get_curr_buf()) + + notify.info('Workspace build successful!') + end) + .catch(get_error_handler('Workspace build failed')) + .run() +end + +return M diff --git a/plugin/java.lua b/plugin/java.lua index 9c28519..39763a6 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -5,6 +5,8 @@ local function c(cmd, callback, opts) end local cmd_map = { + JavaBuildWorkspace = { java.build.build_workspace }, + JavaDapConfig = { java.dap.config_dap }, JavaTestRunCurrentClass = { java.test.run_current_class }, From 0ab04ddf16f945cee8c7b0a28691c36bd98a0e55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 14:32:55 +0530 Subject: [PATCH 026/147] chore(doc): automatic vimdoc update (#237) Co-authored-by: s1n7ax --- doc/nvim-java.txt | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index a0c7fc1..ec98759 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 05 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 06 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -103,6 +103,11 @@ COMMANDS *nvim-java-commands* :small_orange_diamond:details ~ +BUILD ~ + +- `JavaBuildWorkspace` - Runs a full workspace build + + RUNNER ~ - `JavaRunnerRunMain` - Runs the application or selected main class (if there @@ -147,6 +152,15 @@ APIS *nvim-java-apis* :small_orange_diamond:details ~ +BUILD ~ + +- `build.build_workspace` - Runs a full workspace build + +>lua + require('java').build.build_workspace() +< + + RUNNER ~ - `built_in.run_app` - Runs the application or selected main class (if there @@ -415,11 +429,16 @@ For instance, to run the current test, PROJECTS ACKNOWLEDGEMENT *nvim-java-projects-acknowledgement* -nvim-jdtls is a plugin that -follows "Keep it simple, stupid!" approach. If you love customizing things by -yourself, then give nvim-jdtls a try. I may or may not have copied some code -Beautyof Open source! +- spring-boot.nvim is the one + that starts sts4 & do other necessary `jdtls` `sts4` sync command registration + in `nvim-java`. +- nvim-jdtls is a plugin that + follows "Keep it simple, stupid!" approach. If you love customizing things by + yourself, then give nvim-jdtls a try. + + [!WARNING] You cannot use `nvim-java` alongside `nvim-jdtls`. So remove + `nvim-jdtls` before installing this ============================================================================== 2. Links *nvim-java-links* From 36b2d8dfcda8901df43eeb95508c4401230cb5d6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 6 Jul 2024 14:33:37 +0530 Subject: [PATCH 027/147] chore(main): release 1.11.0 (#236) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5990832..d899662 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.11.0](https://github.com/nvim-java/nvim-java/compare/v1.10.0...v1.11.0) (2024-07-06) + + +### Features + +* add build workspace command ([4d92c3d](https://github.com/nvim-java/nvim-java/commit/4d92c3d8552aa3c80a3f4a98754e570a564addf5)) + ## [1.10.0](https://github.com/nvim-java/nvim-java/compare/v1.9.1...v1.10.0) (2024-07-05) From 0b9fac9cae5ac13590d5e8201d9611aebbbece73 Mon Sep 17 00:00:00 2001 From: atm1020 <30997516+atm1020@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:57:01 +0200 Subject: [PATCH 028/147] fix: the manually stoped/restarted job show the error message (#242) (#243) --- lua/java/api/runner.lua | 2 +- lua/java/runner/run.lua | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lua/java/api/runner.lua b/lua/java/api/runner.lua index b060062..d5e34c5 100644 --- a/lua/java/api/runner.lua +++ b/lua/java/api/runner.lua @@ -38,7 +38,7 @@ function M.built_in.stop_app() async(function() M.runner:stop_run() end) - .catch(get_error_handler('Failed to switch run')) + .catch(get_error_handler('Failed to stop run')) .run() end diff --git a/lua/java/runner/run.lua b/lua/java/runner/run.lua index cb5ab35..d5214b2 100644 --- a/lua/java/runner/run.lua +++ b/lua/java/runner/run.lua @@ -6,6 +6,7 @@ local notify = require('java-core.utils.notify') ---@field main_class string ---@field buffer number ---@field is_running boolean +---@field is_manually_stoped boolean ---@field private cmd string ---@field private term_chan_id number ---@field private job_chan_id number | nil @@ -46,6 +47,7 @@ function Run:stop() return end + self.is_manually_stoped = true vim.fn.jobstop(self.job_chan_id) vim.fn.jobwait({ self.job_chan_id }, 1000) self.job_chan_id = nil @@ -77,8 +79,9 @@ function Run:on_job_exit(exit_code) self.is_running = false - if exit_code == 0 then + if exit_code == 0 or self.is_manually_stoped then self.is_failure = false + self.is_manually_stoped = false else self.is_failure = true notify.error(string.format('%s %s', self.name, message)) From a6b1c8b8a5569476c1a73bcb606ba2e33025d54e Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Tue, 9 Jul 2024 13:31:50 +0530 Subject: [PATCH 029/147] fix: remove github token from stylua workflow --- .github/workflows/stylua.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml index a7b609a..6c55707 100644 --- a/.github/workflows/stylua.yml +++ b/.github/workflows/stylua.yml @@ -11,6 +11,5 @@ jobs: - uses: actions/checkout@v4 - uses: JohnnyMorganz/stylua-action@v3 with: - token: ${{ secrets.GITHUB_TOKEN }} version: latest args: --color always --check lua From 81f849d03fe71c98debf39cb61634aac69968dfd Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Tue, 9 Jul 2024 13:57:03 +0530 Subject: [PATCH 030/147] chore: update github workflows (#248) --- .github/workflows/docs.yml | 5 ++++- .github/workflows/lint.yml | 2 ++ .github/workflows/release.yml | 2 +- .github/workflows/stylua.yml | 5 ++++- .github/workflows/test.yml | 2 ++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 96bd2bf..34df049 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,6 +4,9 @@ on: push: branches: - main + paths: + - README.md + - .github/workflows/panvimdoc.yml permissions: pull-requests: write @@ -24,7 +27,7 @@ jobs: demojify: true - name: create pull request - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: base: "main" commit-message: "chore(doc): automatic vimdoc update" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 11e3060..b29c1e1 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -2,6 +2,8 @@ name: Lint on: push: + branches: + - "main" pull_request: jobs: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ca11efb..5ee0126 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,6 @@ jobs: name: release runs-on: ubuntu-latest steps: - - uses: google-github-actions/release-please-action@v4 + - uses: googleapis/release-please-action@v4 with: release-type: simple diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml index 6c55707..79840b2 100644 --- a/.github/workflows/stylua.yml +++ b/.github/workflows/stylua.yml @@ -2,6 +2,8 @@ name: Stylua on: push: + branches: + - "main" pull_request: jobs: @@ -9,7 +11,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: JohnnyMorganz/stylua-action@v3 + - uses: JohnnyMorganz/stylua-action@v4 with: + token: ${{ secrets.GITHUB_TOKEN }} version: latest args: --color always --check lua diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index fb1e389..d46b2ff 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -2,6 +2,8 @@ name: Test on: push: + branches: + - "main" pull_request: jobs: From af9c8ff3c7cf313611daa194409cb65e7831e98a Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Wed, 10 Jul 2024 11:17:23 +0530 Subject: [PATCH 031/147] feat: add command to change the runtime (#244) --- .luacheckrc | 1 + README.md | 12 ++ lua/java.lua | 21 +++ lua/java/api/build.lua | 23 ++- lua/java/api/settings.lua | 57 ++++++ lua/java/commands/init.lua | 200 ++++++++++++++++++++++ lua/java/startup/decompile-watcher.lua | 11 +- lua/java/startup/lspconfig-setup-wrap.lua | 2 + lua/java/utils/config.lua | 17 ++ lua/java/utils/jdtls2.lua | 2 +- lua/java/utils/ui.lua | 9 +- plugin/java.lua | 2 + 12 files changed, 341 insertions(+), 16 deletions(-) create mode 100644 lua/java/api/settings.lua create mode 100644 lua/java/commands/init.lua create mode 100644 lua/java/utils/config.lua diff --git a/.luacheckrc b/.luacheckrc index c5cdeab..2998a81 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -4,6 +4,7 @@ globals = { 'vim.wo', 'vim.bo', 'vim.opt', + 'vim.lsp', } read_globals = { 'vim', diff --git a/README.md b/README.md index 6a48d6e..4f2ae29 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,10 @@ Yep! That's all :) - `JavaRefactorExtractVariable` - Create a variable from returned value at cursor +### Settings + +- `JavaSettingsChangeRuntime` - Change the JDK version to another + ## :computer: APIs @@ -214,6 +218,14 @@ require('java').profile.ui() require('java').refactor.extract_variable() ``` +### Settings + +- `change_runtime` - Change the JDK version to another + +```lua +require('java').settings.change_runtime() +``` + ## :clamp: How to Use JDK X.X Version? diff --git a/lua/java.lua b/lua/java.lua index 718f883..fa45643 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -1,3 +1,5 @@ +require('java.commands') + local decomple_watch = require('java.startup.decompile-watcher') local mason_dep = require('java.startup.mason-dep') local setup_wrap = require('java.startup.lspconfig-setup-wrap') @@ -9,17 +11,25 @@ local runner = require('java.api.runner') local profile_ui = require('java.ui.profile') local refactor = require('java.api.refactor') local build_api = require('java.api.build') +local settings_api = require('java.api.settings') local global_config = require('java.config') local M = {} function M.setup(custom_config) + vim.api.nvim_exec_autocmds('User', { pattern = 'JavaPreSetup' }) + local config = vim.tbl_deep_extend('force', global_config, custom_config or {}) vim.g.nvim_java_config = config + vim.api.nvim_exec_autocmds( + 'User', + { pattern = 'JavaSetup', data = { config = config } } + ) + if not startup_check() then return end @@ -31,6 +41,11 @@ function M.setup(custom_config) decomple_watch.setup() dap.setup_dap_on_lsp_attach() end + + vim.api.nvim_exec_autocmds( + 'User', + { pattern = 'JavaPostSetup', data = { config = config } } + ) end ---------------------------------------------------------------------- @@ -86,6 +101,12 @@ M.runner.built_in.switch_app = runner.built_in.switch_app M.profile = {} M.profile.ui = profile_ui.ui +---------------------------------------------------------------------- +-- Settings -- +---------------------------------------------------------------------- +M.settings = {} +M.settings.change_runtime = settings_api.change_runtime + function M.__run() test.debug_current_method() end diff --git a/lua/java/api/build.lua b/lua/java/api/build.lua index ac54056..292a153 100644 --- a/lua/java/api/build.lua +++ b/lua/java/api/build.lua @@ -3,15 +3,22 @@ local get_error_handler = require('java.handlers.error') local M = {} -function M.full_build_workspace() - return async(function() - local JavaCoreJdtlsClient = require('java-core.ls.clients.jdtls-client') - local jdtls = require('java.utils.jdtls') - local buf_util = require('java.utils.buffer') - local notify = require('java-core.utils.notify') +---Do a workspace build +---@param is_full_build? boolean +---@return number +function M.full_build_workspace(is_full_build) + local JavaCoreJdtlsClient = require('java-core.ls.clients.jdtls-client') + local jdtls = require('java.utils.jdtls2') + local buf_util = require('java.utils.buffer') + local notify = require('java-core.utils.notify') + + is_full_build = type(is_full_build) == 'boolean' and is_full_build or true - JavaCoreJdtlsClient:new(jdtls()) - :java_build_workspace(true, buf_util.get_curr_buf()) + return async(function() + JavaCoreJdtlsClient(jdtls()):java_build_workspace( + is_full_build, + buf_util.get_curr_buf() + ) notify.info('Workspace build successful!') end) diff --git a/lua/java/api/settings.lua b/lua/java/api/settings.lua new file mode 100644 index 0000000..8cc47a1 --- /dev/null +++ b/lua/java/api/settings.lua @@ -0,0 +1,57 @@ +local get_jdtls = require('java.utils.jdtls2') +local JdtlsClient = require('java-core.ls.clients.jdtls-client') +local conf_utils = require('java.utils.config') +local notify = require('java-core.utils.notify') +local ui = require('java.utils.ui') +local async = require('java-core.utils.async').sync +local get_error_handler = require('java.handlers.error') + +local M = {} + +function M.change_runtime() + local client = get_jdtls() + + ---@type RuntimeOption[] + local runtimes = conf_utils.get_property_from_conf( + client.config, + 'settings.java.configuration.runtimes', + {} + ) + + if #runtimes < 1 then + notify.error( + 'No configured runtimes available' + .. '\nRefer following link for instructions define available runtimes' + .. '\nhttps://github.com/nvim-java/nvim-java?tab=readme-ov-file#clamp-how-to-use-jdk-xx-version' + ) + return + end + + local jdtls = JdtlsClient(client) + + async(function() + local sel_runtime = ui.select( + 'Select Runtime', + runtimes, + function(runtime) + return runtime.name .. '::' .. runtime.path + end + ) + + for _, runtime in + ipairs(client.config.settings.java.configuration.runtimes) + do + if sel_runtime.path == runtime.path then + runtime.default = true + else + runtime.default = nil + end + end + + jdtls:workspace_did_change_configuration(client.config.settings) + end) + .catch(get_error_handler('Changing runtime failed')) + .run() +end + +return M diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua new file mode 100644 index 0000000..f260495 --- /dev/null +++ b/lua/java/commands/init.lua @@ -0,0 +1,200 @@ +local log = require('java.utils.log') + +local M = {} + +local id + +id = vim.api.nvim_create_autocmd('LspAttach', { + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + + if client and client.name == 'jdtls' then + log.debug('adding vim.lsp.commands for java') + + for key, handler in pairs(M.handlers) do + vim.lsp.commands[key] = handler + end + + vim.api.nvim_del_autocmd(id) + end + end, + + group = vim.api.nvim_create_augroup('JavaCommandReg', {}), +}) + +M.commands = { + + OPEN_BROWSER = 'vscode.open', + + OPEN_OUTPUT = 'java.open.output', + + SHOW_JAVA_REFERENCES = 'java.show.references', + + SHOW_JAVA_IMPLEMENTATIONS = 'java.show.implementations', + + SHOW_REFERENCES = 'editor.action.showReferences', + + GOTO_LOCATION = 'editor.action.goToLocations', + + MARKDOWN_API_RENDER = 'markdown.api.render', + + CONFIGURATION_UPDATE = 'java.projectConfiguration.update', + + IGNORE_INCOMPLETE_CLASSPATH = 'java.ignoreIncompleteClasspath', + + IGNORE_INCOMPLETE_CLASSPATH_HELP = 'java.ignoreIncompleteClasspath.help', + + RELOAD_WINDOW = 'workbench.action.reloadWindow', + + PROJECT_CONFIGURATION_STATUS = 'java.projectConfiguration.status', + + NULL_ANALYSIS_SET_MODE = 'java.compile.nullAnalysis.setMode', + + APPLY_WORKSPACE_EDIT = 'java.apply.workspaceEdit', + + EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand', + + COMPILE_WORKSPACE = 'java.workspace.compile', + + BUILD_PROJECT = 'java.project.build', + + OPEN_SERVER_LOG = 'java.open.serverLog', + + OPEN_SERVER_STDOUT_LOG = 'java.open.serverStdoutLog', + + OPEN_SERVER_STDERR_LOG = 'java.open.serverStderrLog', + + OPEN_CLIENT_LOG = 'java.open.clientLog', + + OPEN_LOGS = 'java.open.logs', + + OPEN_FORMATTER = 'java.open.formatter.settings', + + OPEN_FILE = 'java.open.file', + + CLEAN_WORKSPACE = 'java.clean.workspace', + + UPDATE_SOURCE_ATTACHMENT_CMD = 'java.project.updateSourceAttachment.command', + UPDATE_SOURCE_ATTACHMENT = 'java.project.updateSourceAttachment', + + RESOLVE_SOURCE_ATTACHMENT = 'java.project.resolveSourceAttachment', + + ADD_TO_SOURCEPATH_CMD = 'java.project.addToSourcePath.command', + ADD_TO_SOURCEPATH = 'java.project.addToSourcePath', + + REMOVE_FROM_SOURCEPATH_CMD = 'java.project.removeFromSourcePath.command', + REMOVE_FROM_SOURCEPATH = 'java.project.removeFromSourcePath', + + LIST_SOURCEPATHS_CMD = 'java.project.listSourcePaths.command', + LIST_SOURCEPATHS = 'java.project.listSourcePaths', + + IMPORT_PROJECTS_CMD = 'java.project.import.command', + IMPORT_PROJECTS = 'java.project.import', + CHANGE_IMPORTED_PROJECTS = 'java.project.changeImportedProjects', + + OVERRIDE_METHODS_PROMPT = 'java.action.overrideMethodsPrompt', + + HASHCODE_EQUALS_PROMPT = 'java.action.hashCodeEqualsPrompt', + + OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson', + + ORGANIZE_IMPORTS = 'java.action.organizeImports', + + ORGANIZE_IMPORTS_SILENTLY = 'java.edit.organizeImports', + MANUAL_CLEANUP = 'java.action.doCleanup', + + HANDLE_PASTE_EVENT = 'java.edit.handlePasteEvent', + + CLIPBOARD_ONPASTE = 'java.action.clipboardPasteAction', + + FILESEXPLORER_ONPASTE = 'java.action.filesExplorerPasteAction', + + CHOOSE_IMPORTS = 'java.action.organizeImports.chooseImports', + + GENERATE_TOSTRING_PROMPT = 'java.action.generateToStringPrompt', + + GENERATE_ACCESSORS_PROMPT = 'java.action.generateAccessorsPrompt', + + GENERATE_CONSTRUCTORS_PROMPT = 'java.action.generateConstructorsPrompt', + + GENERATE_DELEGATE_METHODS_PROMPT = 'java.action.generateDelegateMethodsPrompt', + + APPLY_REFACTORING_COMMAND = 'java.action.applyRefactoringCommand', + + RENAME_COMMAND = 'java.action.rename', + + NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND = 'java.action.navigateToSuperImplementation', + + SHOW_TYPE_HIERARCHY = 'java.action.showTypeHierarchy', + + SHOW_SUPERTYPE_HIERARCHY = 'java.action.showSupertypeHierarchy', + + SHOW_SUBTYPE_HIERARCHY = 'java.action.showSubtypeHierarchy', + + SHOW_CLASS_HIERARCHY = 'java.action.showClassHierarchy', + + CHANGE_BASE_TYPE = 'java.action.changeBaseType', + + OPEN_TYPE_HIERARCHY = 'java.navigate.openTypeHierarchy', + + RESOLVE_TYPE_HIERARCHY = 'java.navigate.resolveTypeHierarchy', + + SHOW_SERVER_TASK_STATUS = 'java.show.server.task.status', + + GET_PROJECT_SETTINGS = 'java.project.getSettings', + + GET_CLASSPATHS = 'java.project.getClasspaths', + + IS_TEST_FILE = 'java.project.isTestFile', + + GET_ALL_JAVA_PROJECTS = 'java.project.getAll', + + SWITCH_SERVER_MODE = 'java.server.mode.switch', + + RESTART_LANGUAGE_SERVER = 'java.server.restart', + + LEARN_MORE_ABOUT_REFACTORING = '_java.learnMoreAboutRefactorings', + + LEARN_MORE_ABOUT_CLEAN_UPS = '_java.learnMoreAboutCleanUps', + + TEMPLATE_VARIABLES = '_java.templateVariables', + + NOT_COVERED_EXECUTION = '_java.notCoveredExecution', + + METADATA_FILES_GENERATION = '_java.metadataFilesGeneration', + + RUNTIME_VALIDATION_OPEN = 'java.runtimeValidation.open', + + RESOLVE_WORKSPACE_SYMBOL = 'java.project.resolveWorkspaceSymbol', + + GET_WORKSPACE_PATH = '_java.workspace.path', + + UPGRADE_GRADLE_WRAPPER_CMD = 'java.project.upgradeGradle.command', + UPGRADE_GRADLE_WRAPPER = 'java.project.upgradeGradle', + + LOMBOK_CONFIGURE = 'java.lombokConfigure', + + CREATE_MODULE_INFO = 'java.project.createModuleInfo', + + CREATE_MODULE_INFO_COMMAND = 'java.project.createModuleInfo.command', + + REFRESH_BUNDLES = 'java.reloadBundles', + + REFRESH_BUNDLES_COMMAND = '_java.reloadBundles.command', + + CLEAN_SHARED_INDEXES = 'java.clean.sharedIndexes', + + GET_DECOMPILED_SOURCE = 'java.decompile', + + SMARTSEMICOLON_DETECTION = 'java.edit.smartSemicolonDetection', + + RESOLVE_PASTED_TEXT = 'java.project.resolveText', + + OPEN_STATUS_SHORTCUT = '_java.openShortcuts', +} + +M.handlers = { + [M.commands.COMPILE_WORKSPACE] = function(is_full_build) + require('java.api.build').full_build_workspace(is_full_build) + end, +} diff --git a/lua/java/startup/decompile-watcher.lua b/lua/java/startup/decompile-watcher.lua index 078cc44..9c3e9c1 100644 --- a/lua/java/startup/decompile-watcher.lua +++ b/lua/java/startup/decompile-watcher.lua @@ -1,4 +1,4 @@ -local jdtls = require('java.utils.jdtls') +local jdtls = require('java.utils.jdtls2') local get_error_handler = require('java.handlers.error') local async = require('java-core.utils.async').sync @@ -22,11 +22,10 @@ function M.setup() local done = false async(function() - local client_obj = jdtls() + local client = jdtls() local buffer = opts.buf - local text = JavaCoreJdtlsClient:new(client_obj) - :java_decompile(opts.file) + local text = JavaCoreJdtlsClient(client):java_decompile(opts.file) local lines = vim.split(text, '\n') @@ -38,8 +37,8 @@ function M.setup() vim.bo[buffer].filetype = 'java' vim.bo[buffer].modifiable = false - if not vim.lsp.buf_is_attached(buffer, client_obj.client.id) then - vim.lsp.buf_attach_client(buffer, client_obj.client.id) + if not vim.lsp.buf_is_attached(buffer, client.id) then + vim.lsp.buf_attach_client(buffer, client.id) end done = true diff --git a/lua/java/startup/lspconfig-setup-wrap.lua b/lua/java/startup/lspconfig-setup-wrap.lua index ab3eb75..2c92189 100644 --- a/lua/java/startup/lspconfig-setup-wrap.lua +++ b/lua/java/startup/lspconfig-setup-wrap.lua @@ -14,6 +14,8 @@ function M.setup(config) local org_setup = lspconfig.jdtls.setup lspconfig.jdtls.setup = function(user_config) + vim.api.nvim_exec_autocmds('User', { pattern = 'JavaJdtlsSetup' }) + local jdtls_plugins = {} if config.java_test.enable then diff --git a/lua/java/utils/config.lua b/lua/java/utils/config.lua new file mode 100644 index 0000000..eaf9f3b --- /dev/null +++ b/lua/java/utils/config.lua @@ -0,0 +1,17 @@ +local M = {} + +function M.get_property_from_conf(config, path, default) + local node = config + + for key in string.gmatch(path, '([^.]+)') do + if not node[key] then + return default + end + + node = node[key] + end + + return node +end + +return M diff --git a/lua/java/utils/jdtls2.lua b/lua/java/utils/jdtls2.lua index 5458847..2eb49c9 100644 --- a/lua/java/utils/jdtls2.lua +++ b/lua/java/utils/jdtls2.lua @@ -1,7 +1,7 @@ local get_error_handler = require('java.handlers.error') ---Returns an active jdtls client ----@return { client: LspClient } +---@return vim.lsp.Client local function get_jdtls() local clients diff --git a/lua/java/utils/ui.lua b/lua/java/utils/ui.lua index 214112d..23f2275 100644 --- a/lua/java/utils/ui.lua +++ b/lua/java/utils/ui.lua @@ -4,10 +4,17 @@ local notify = require('java-core.utils.notify') local M = {} -function M.select(prompt, values) +---Async vim.ui.select function +---@generic T +---@param prompt string +---@param values T[] +---@param format_item fun(item: T): string +---@return T +function M.select(prompt, values, format_item) return await(function(callback) vim.ui.select(values, { prompt = prompt, + format_item = format_item, }, callback) end) end diff --git a/plugin/java.lua b/plugin/java.lua index 39763a6..9f37cb8 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -5,6 +5,8 @@ local function c(cmd, callback, opts) end local cmd_map = { + JavaSettingsChangeRuntime = { java.settings.change_runtime }, + JavaBuildWorkspace = { java.build.build_workspace }, JavaDapConfig = { java.dap.config_dap }, From 72510a5628b01983ebe544447313ced7b47f94bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:45:43 +0530 Subject: [PATCH 032/147] chore(doc): automatic vimdoc update (#246) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index ec98759..2d8ba3b 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 06 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 10 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -147,6 +147,11 @@ REFACTOR ~ - `JavaRefactorExtractVariable` - Create a variable from returned value at cursor +SETTINGS ~ + +- `JavaSettingsChangeRuntime` - Change the JDK version to another + + APIS *nvim-java-apis* :small_orange_diamond:details ~ @@ -243,6 +248,15 @@ REFACTOR ~ < +SETTINGS ~ + +- `change_runtime` - Change the JDK version to another + +>lua + require('java').settings.change_runtime() +< + + HOW TO USE JDK X.X VERSION? *nvim-java-how-to-use-jdk-x.x-version?* :small_orange_diamond:details ~ From 3f807cd63e6869d88fef40dfca75b224edcd4ccf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 11:46:19 +0530 Subject: [PATCH 033/147] chore(main): release 1.12.0 (#245) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d899662..1c10b0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [1.12.0](https://github.com/nvim-java/nvim-java/compare/v1.11.0...v1.12.0) (2024-07-10) + + +### Features + +* add command to change the runtime ([#244](https://github.com/nvim-java/nvim-java/issues/244)) ([af9c8ff](https://github.com/nvim-java/nvim-java/commit/af9c8ff3c7cf313611daa194409cb65e7831e98a)) + + +### Bug Fixes + +* remove github token from stylua workflow ([a6b1c8b](https://github.com/nvim-java/nvim-java/commit/a6b1c8b8a5569476c1a73bcb606ba2e33025d54e)) +* the manually stoped/restarted job show the error message ([#242](https://github.com/nvim-java/nvim-java/issues/242)) ([#243](https://github.com/nvim-java/nvim-java/issues/243)) ([0b9fac9](https://github.com/nvim-java/nvim-java/commit/0b9fac9cae5ac13590d5e8201d9611aebbbece73)) + ## [1.11.0](https://github.com/nvim-java/nvim-java/compare/v1.10.0...v1.11.0) (2024-07-06) From 0ec0f463efa7b3cc77d30660ced357e295ab7cd7 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 11 Jul 2024 02:03:41 +0530 Subject: [PATCH 034/147] feat: add more extract commands --- README.md | 27 +++++++++++++++++++++++++-- lua/java.lua | 4 ++++ lua/java/api/refactor.lua | 20 +++++++++++++++++++- plugin/java.lua | 14 ++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 4f2ae29..0743230 100644 --- a/README.md +++ b/README.md @@ -119,7 +119,11 @@ Yep! That's all :) ### Refactor -- `JavaRefactorExtractVariable` - Create a variable from returned value at cursor +- `JavaRefactorExtractVariable` - Create a variable from value at cursor +- `JavaRefactorExtractVariableAllOccurrence` - Create a variable for all + occurrences from value at cursor +- `JavaRefactorExtractConstant` - Create a constant from the value at cursor +- `JavaRefactorExtractMethod` - Create method from the value at cursor ### Settings @@ -212,12 +216,31 @@ require('java').profile.ui() ### Refactor -- `extract_variable` - Create a variable from returned value at cursor +- `extract_variable` - Create a variable from value at cursor ```lua require('java').refactor.extract_variable() ``` +- `extract_variable_all_occurrence` - Create a variable for all occurrences from + value at cursor + +```lua +require('java').refactor.extract_variable_all_occurrence() +``` + +- `extract_constant` - Create a constant from the value at cursor + +```lua +require('java').refactor.extract_constant() +``` + +- `extract_method` - Create method from the value at cursor + +```lua +require('java').refactor.extract_method() +``` + ### Settings - `change_runtime` - Change the JDK version to another diff --git a/lua/java.lua b/lua/java.lua index fa45643..b94d76c 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -84,6 +84,10 @@ M.manipulate = {} ---------------------------------------------------------------------- M.refactor = {} M.refactor.extract_variable = refactor.extract_variable +M.refactor.extract_constant = refactor.extract_constant +M.refactor.extract_method = refactor.extract_method +M.refactor.extract_variable_all_occurrence = + refactor.extract_variable_all_occurrence ---------------------------------------------------------------------- -- Runner APIs -- diff --git a/lua/java/api/refactor.lua b/lua/java/api/refactor.lua index ec43fb1..dd68d97 100644 --- a/lua/java/api/refactor.lua +++ b/lua/java/api/refactor.lua @@ -6,10 +6,28 @@ local async = require('java-core.utils.async').sync local M = {} function M.extract_variable() + M.extract('extractVariable') +end + +function M.extract_variable_all_occurrence() + M.extract('extractVariableAllOccurrence') +end + +function M.extract_constant() + M.extract('extractConstant') +end + +function M.extract_method() + M.extract('extractMethod') +end + +--- +---@param refactor_command jdtls.CodeActionCommand +function M.extract(refactor_command) return async(function() local RefactorCommands = require('java-refactor.refactor-commands') local refactor_commands = RefactorCommands(jdtls()) - refactor_commands:extract_variable() + refactor_commands:refactor(refactor_command) end) .catch(get_error_handler('failed to refactor variable')) .run() diff --git a/plugin/java.lua b/plugin/java.lua index 9f37cb8..adfd1ea 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -30,6 +30,20 @@ local cmd_map = { java.refactor.extract_variable, { range = 2 }, }, + + JavaRefactorExtractMethod = { + java.refactor.extract_method, + }, + + JavaRefactorExtractConstant = { + java.refactor.extract_constant, + { range = 2 }, + }, + + JavaRefactorExtractVariableAllOccurrence = { + java.refactor.extract_variable_all_occurrence, + { range = 2 }, + }, } for cmd, details in pairs(cmd_map) do From 0c2a5d2a95dc1e367c6fc7da26e50ca4f0d3a04b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 02:07:10 +0530 Subject: [PATCH 035/147] chore(doc): automatic vimdoc update (#251) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 2d8ba3b..1896734 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -144,7 +144,11 @@ PROFILES ~ REFACTOR ~ -- `JavaRefactorExtractVariable` - Create a variable from returned value at cursor +- `JavaRefactorExtractVariable` - Create a variable from value at cursor +- `JavaRefactorExtractVariableAllOccurrence` - Create a variable for all + occurrences from value at cursor +- `JavaRefactorExtractConstant` - Create a constant from the value at cursor +- `JavaRefactorExtractMethod` - Create method from the value at cursor SETTINGS ~ @@ -241,12 +245,31 @@ PROFILES ~ REFACTOR ~ -- `extract_variable` - Create a variable from returned value at cursor +- `extract_variable` - Create a variable from value at cursor >lua require('java').refactor.extract_variable() < +- `extract_variable_all_occurrence` - Create a variable for all occurrences from + value at cursor + +>lua + require('java').refactor.extract_variable_all_occurrence() +< + +- `extract_constant` - Create a constant from the value at cursor + +>lua + require('java').refactor.extract_constant() +< + +- `extract_method` - Create method from the value at cursor + +>lua + require('java').refactor.extract_method() +< + SETTINGS ~ From 202a6b8d94429f666ab5ea83861542f3a89336f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 02:08:06 +0530 Subject: [PATCH 036/147] chore(main): release 1.13.0 (#252) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c10b0c..f9f7093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.13.0](https://github.com/nvim-java/nvim-java/compare/v1.12.0...v1.13.0) (2024-07-10) + + +### Features + +* add more extract commands ([0ec0f46](https://github.com/nvim-java/nvim-java/commit/0ec0f463efa7b3cc77d30660ced357e295ab7cd7)) + ## [1.12.0](https://github.com/nvim-java/nvim-java/compare/v1.11.0...v1.12.0) (2024-07-10) From 644c4cbe7cda5d7bac08f7ec293e08078d4afac0 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 11 Jul 2024 23:28:30 +0530 Subject: [PATCH 037/147] feat: upgrade java debug adapter --- lua/java/startup/mason-dep.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index 73353b8..b85a86b 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -52,7 +52,7 @@ function M.get_pkg_list(config) { name = 'jdtls', version = 'v1.31.0' }, { name = 'lombok-nightly', version = 'nightly' }, { name = 'java-test', version = '0.40.1' }, - { name = 'java-debug-adapter', version = '0.55.0' }, + { name = 'java-debug-adapter', version = '0.58.0' }, }) if config.jdk.auto_install then From aad6139d54f34807ecc66e76b16a24712e5d59b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 23:32:32 +0530 Subject: [PATCH 038/147] chore(main): release 1.14.0 (#256) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9f7093..e966419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.14.0](https://github.com/nvim-java/nvim-java/compare/v1.13.0...v1.14.0) (2024-07-11) + + +### Features + +* upgrade java debug adapter ([644c4cb](https://github.com/nvim-java/nvim-java/commit/644c4cbe7cda5d7bac08f7ec293e08078d4afac0)) + ## [1.13.0](https://github.com/nvim-java/nvim-java/compare/v1.12.0...v1.13.0) (2024-07-10) From 4208223ec69864a62eb7bd3873276665d088ec4f Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 11 Jul 2024 23:51:54 +0530 Subject: [PATCH 039/147] chore(doc): update doc with starter projects --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0743230..75ab827 100644 --- a/README.md +++ b/README.md @@ -42,16 +42,20 @@ Just install and start writing `public static void main(String[] args)`. :small_orange_diamond:details -### Q & A +### Pre-configured Starter Configurations (Recommend for newbies) -If you face any issues, check our [Q & A](https://github.com/nvim-java/nvim-java/wiki/Q-&-A) wiki to see if that helps +Following are forks of original repositories pre-configured for java. If you +don't know how to get started, use one of the following to get started. -### Distributions & Templates +- [LazyVim](https://github.com/nvim-java/starter-lazyvim) - [All changes](https://github.com/LazyVim/starter/compare/main...nvim-java:starter-lazyvim:main) +- [Kickstart](https://github.com/nvim-java/starter-kickstart) - [All changes](https://github.com/nvim-lua/kickstart.nvim/compare/master...nvim-java:starter-kickstart:master) -- [Lazyvim](https://github.com/nvim-java/nvim-java/wiki/Lazyvim) +### Distribution Specific Instructions + +- [LazyVim](https://github.com/nvim-java/nvim-java/wiki/Lazyvim) - [Kickstart](https://github.com/nvim-java/nvim-java/wiki/Kickstart) -### Custom +### Custom Configuration Instructions - Install the plugin From f722d0d7ecd29c62383afe293e10d2453ac61019 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 11 Jul 2024 23:55:12 +0530 Subject: [PATCH 040/147] chore(doc): update doc --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 75ab827..eeba843 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,13 @@ Just install and start writing `public static void main(String[] args)`. :small_orange_diamond:details -### Pre-configured Starter Configurations (Recommend for newbies) +### Starter Configs (Recommend for newbies) Following are forks of original repositories pre-configured for java. If you don't know how to get started, use one of the following to get started. -- [LazyVim](https://github.com/nvim-java/starter-lazyvim) - [All changes](https://github.com/LazyVim/starter/compare/main...nvim-java:starter-lazyvim:main) -- [Kickstart](https://github.com/nvim-java/starter-kickstart) - [All changes](https://github.com/nvim-lua/kickstart.nvim/compare/master...nvim-java:starter-kickstart:master) +- [LazyVim](https://github.com/nvim-java/starter-lazyvim) +- [Kickstart](https://github.com/nvim-java/starter-kickstart) ### Distribution Specific Instructions From aabca011b56b605f7bf12df0534f7d5b60b16fff Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Fri, 12 Jul 2024 09:32:30 +0530 Subject: [PATCH 041/147] feat: add extract_field command --- README.md | 23 +++++++++++++++-------- lua/java.lua | 1 + lua/java/api/refactor.lua | 4 ++++ lua/java/utils/ui.lua | 2 +- plugin/java.lua | 6 ++++++ 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index eeba843..5c3d712 100644 --- a/README.md +++ b/README.md @@ -123,11 +123,12 @@ Yep! That's all :) ### Refactor -- `JavaRefactorExtractVariable` - Create a variable from value at cursor +- `JavaRefactorExtractVariable` - Create a variable from value at cursor/selection - `JavaRefactorExtractVariableAllOccurrence` - Create a variable for all - occurrences from value at cursor -- `JavaRefactorExtractConstant` - Create a constant from the value at cursor -- `JavaRefactorExtractMethod` - Create method from the value at cursor + occurrences from value at cursor/selection +- `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection +- `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection +- `JavaRefactorExtractField` - Create a field from the value at cursor/selection ### Settings @@ -220,31 +221,37 @@ require('java').profile.ui() ### Refactor -- `extract_variable` - Create a variable from value at cursor +- `extract_variable` - Create a variable from value at cursor/selection ```lua require('java').refactor.extract_variable() ``` - `extract_variable_all_occurrence` - Create a variable for all occurrences from - value at cursor + value at cursor/selection ```lua require('java').refactor.extract_variable_all_occurrence() ``` -- `extract_constant` - Create a constant from the value at cursor +- `extract_constant` - Create a constant from the value at cursor/selection ```lua require('java').refactor.extract_constant() ``` -- `extract_method` - Create method from the value at cursor +- `extract_method` - Create method from the value at cursor/selection ```lua require('java').refactor.extract_method() ``` +- `extract_field` - Create a field from the value at cursor/selection + +```lua +require('java').refactor.extract_field() +``` + ### Settings - `change_runtime` - Change the JDK version to another diff --git a/lua/java.lua b/lua/java.lua index b94d76c..a1ac187 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -86,6 +86,7 @@ M.refactor = {} M.refactor.extract_variable = refactor.extract_variable M.refactor.extract_constant = refactor.extract_constant M.refactor.extract_method = refactor.extract_method +M.refactor.extract_field = refactor.extract_field M.refactor.extract_variable_all_occurrence = refactor.extract_variable_all_occurrence diff --git a/lua/java/api/refactor.lua b/lua/java/api/refactor.lua index dd68d97..a5a61ed 100644 --- a/lua/java/api/refactor.lua +++ b/lua/java/api/refactor.lua @@ -21,6 +21,10 @@ function M.extract_method() M.extract('extractMethod') end +function M.extract_field() + M.extract('extractField') +end + --- ---@param refactor_command jdtls.CodeActionCommand function M.extract(refactor_command) diff --git a/lua/java/utils/ui.lua b/lua/java/utils/ui.lua index 23f2275..cd86123 100644 --- a/lua/java/utils/ui.lua +++ b/lua/java/utils/ui.lua @@ -8,7 +8,7 @@ local M = {} ---@generic T ---@param prompt string ---@param values T[] ----@param format_item fun(item: T): string +---@param format_item? fun(item: T): string ---@return T function M.select(prompt, values, format_item) return await(function(callback) diff --git a/plugin/java.lua b/plugin/java.lua index adfd1ea..1357c32 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -33,6 +33,7 @@ local cmd_map = { JavaRefactorExtractMethod = { java.refactor.extract_method, + { range = 2 }, }, JavaRefactorExtractConstant = { @@ -44,6 +45,11 @@ local cmd_map = { java.refactor.extract_variable_all_occurrence, { range = 2 }, }, + + JavaRefactorExtractField = { + java.refactor.extract_field, + { range = 2 }, + }, } for cmd, details in pairs(cmd_map) do From 61b1226e1c1dcee880f968d5ab894599a65993c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:35:02 +0530 Subject: [PATCH 042/147] chore(doc): automatic vimdoc update (#257) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 1896734..d816db6 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 10 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 12 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -61,19 +61,22 @@ HOW TO INSTALL *nvim-java-how-to-install* :small_orange_diamond:details ~ -Q & A ~ +STARTER CONFIGS (RECOMMEND FOR NEWBIES) ~ -If you face any issues, check our Q & A - wiki to see if that helps +Following are forks of original repositories pre-configured for java. If you +don’t know how to get started, use one of the following to get started. +- LazyVim +- Kickstart -DISTRIBUTIONS & TEMPLATES ~ -- Lazyvim +DISTRIBUTION SPECIFIC INSTRUCTIONS ~ + +- LazyVim - Kickstart -CUSTOM ~ +CUSTOM CONFIGURATION INSTRUCTIONS ~ - Install the plugin @@ -144,11 +147,12 @@ PROFILES ~ REFACTOR ~ -- `JavaRefactorExtractVariable` - Create a variable from value at cursor +- `JavaRefactorExtractVariable` - Create a variable from value at cursor/selection - `JavaRefactorExtractVariableAllOccurrence` - Create a variable for all - occurrences from value at cursor -- `JavaRefactorExtractConstant` - Create a constant from the value at cursor -- `JavaRefactorExtractMethod` - Create method from the value at cursor + occurrences from value at cursor/selection +- `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection +- `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection +- `JavaRefactorExtractField` - Create a field from the value at cursor/selection SETTINGS ~ @@ -245,31 +249,37 @@ PROFILES ~ REFACTOR ~ -- `extract_variable` - Create a variable from value at cursor +- `extract_variable` - Create a variable from value at cursor/selection >lua require('java').refactor.extract_variable() < - `extract_variable_all_occurrence` - Create a variable for all occurrences from - value at cursor + value at cursor/selection >lua require('java').refactor.extract_variable_all_occurrence() < -- `extract_constant` - Create a constant from the value at cursor +- `extract_constant` - Create a constant from the value at cursor/selection >lua require('java').refactor.extract_constant() < -- `extract_method` - Create method from the value at cursor +- `extract_method` - Create method from the value at cursor/selection >lua require('java').refactor.extract_method() < +- `extract_field` - Create a field from the value at cursor/selection + +>lua + require('java').refactor.extract_field() +< + SETTINGS ~ From c6798cd486e8fdf43294617f3ebd416a10912a86 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:36:03 +0530 Subject: [PATCH 043/147] chore(main): release 1.15.0 (#259) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e966419..86ae163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.15.0](https://github.com/nvim-java/nvim-java/compare/v1.14.0...v1.15.0) (2024-07-12) + + +### Features + +* add extract_field command ([aabca01](https://github.com/nvim-java/nvim-java/commit/aabca011b56b605f7bf12df0534f7d5b60b16fff)) + ## [1.14.0](https://github.com/nvim-java/nvim-java/compare/v1.13.0...v1.14.0) (2024-07-11) From 2635a640aebd007b52a3d288b1318cacca7dc44c Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sat, 13 Jul 2024 14:55:14 +0530 Subject: [PATCH 044/147] feat: add convert to variable refactor command --- README.md | 7 +++++++ lua/java.lua | 1 + lua/java/api/refactor.lua | 4 ++++ plugin/java.lua | 5 +++++ 4 files changed, 17 insertions(+) diff --git a/README.md b/README.md index 5c3d712..b36acb7 100644 --- a/README.md +++ b/README.md @@ -129,6 +129,7 @@ Yep! That's all :) - `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection - `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection - `JavaRefactorExtractField` - Create a field from the value at cursor/selection +- `JavaRefactorConvertVariableToField` - Change a local variable to field ### Settings @@ -252,6 +253,12 @@ require('java').refactor.extract_method() require('java').refactor.extract_field() ``` +- `convert_variable_to_field` - Change a local variable to field + +```lua +require('java').refactor.convert_variable_to_field() +``` + ### Settings - `change_runtime` - Change the JDK version to another diff --git a/lua/java.lua b/lua/java.lua index a1ac187..30fb85d 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -87,6 +87,7 @@ M.refactor.extract_variable = refactor.extract_variable M.refactor.extract_constant = refactor.extract_constant M.refactor.extract_method = refactor.extract_method M.refactor.extract_field = refactor.extract_field +M.refactor.convert_variable_to_field = refactor.convert_variable_to_field M.refactor.extract_variable_all_occurrence = refactor.extract_variable_all_occurrence diff --git a/lua/java/api/refactor.lua b/lua/java/api/refactor.lua index a5a61ed..35bb211 100644 --- a/lua/java/api/refactor.lua +++ b/lua/java/api/refactor.lua @@ -25,6 +25,10 @@ function M.extract_field() M.extract('extractField') end +function M.convert_variable_to_field() + M.extract('convertVariableToField') +end + --- ---@param refactor_command jdtls.CodeActionCommand function M.extract(refactor_command) diff --git a/plugin/java.lua b/plugin/java.lua index 1357c32..b6ac3f7 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -50,6 +50,11 @@ local cmd_map = { java.refactor.extract_field, { range = 2 }, }, + + JavaRefactorConvertVariableToField = { + java.refactor.convert_variable_to_field, + { range = 2 }, + }, } for cmd, details in pairs(cmd_map) do From 470fa4f96d65c24b347665ab4e8f0f106e7fecf5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:11:49 +0530 Subject: [PATCH 045/147] chore(doc): automatic vimdoc update (#263) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index d816db6..77231e2 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 12 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 13 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -153,6 +153,7 @@ REFACTOR ~ - `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection - `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection - `JavaRefactorExtractField` - Create a field from the value at cursor/selection +- `JavaRefactorConvertVariableToField` - Change a local variable to field SETTINGS ~ @@ -280,6 +281,12 @@ REFACTOR ~ require('java').refactor.extract_field() < +- `convert_variable_to_field` - Change a local variable to field + +>lua + require('java').refactor.convert_variable_to_field() +< + SETTINGS ~ From c3996dfbc67c3a028f3453d9d1303af72043faf7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:19:21 +0530 Subject: [PATCH 046/147] chore(main): release 1.16.0 (#262) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86ae163..e709018 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.16.0](https://github.com/nvim-java/nvim-java/compare/v1.15.0...v1.16.0) (2024-07-13) + + +### Features + +* add convert to variable refactor command ([2635a64](https://github.com/nvim-java/nvim-java/commit/2635a640aebd007b52a3d288b1318cacca7dc44c)) + ## [1.15.0](https://github.com/nvim-java/nvim-java/compare/v1.14.0...v1.15.0) (2024-07-12) From ea5371bf0de96dd6856ae623455376d6e2062045 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 14 Jul 2024 01:22:24 +0530 Subject: [PATCH 047/147] feat: add generate constructor code action --- lua/java/api/build.lua | 4 +-- lua/java/api/generate.lua | 55 ++++++++++++++++++++++++++++++++++++++ lua/java/api/refactor.lua | 1 - lua/java/commands/init.lua | 7 +++++ lua/java/utils/ui.lua | 53 +++++++++++++++++++++++++++++++++--- 5 files changed, 113 insertions(+), 7 deletions(-) create mode 100644 lua/java/api/generate.lua diff --git a/lua/java/api/build.lua b/lua/java/api/build.lua index 292a153..73de525 100644 --- a/lua/java/api/build.lua +++ b/lua/java/api/build.lua @@ -7,7 +7,7 @@ local M = {} ---@param is_full_build? boolean ---@return number function M.full_build_workspace(is_full_build) - local JavaCoreJdtlsClient = require('java-core.ls.clients.jdtls-client') + local JdtlsClient = require('java-core.ls.clients.jdtls-client') local jdtls = require('java.utils.jdtls2') local buf_util = require('java.utils.buffer') local notify = require('java-core.utils.notify') @@ -15,7 +15,7 @@ function M.full_build_workspace(is_full_build) is_full_build = type(is_full_build) == 'boolean' and is_full_build or true return async(function() - JavaCoreJdtlsClient(jdtls()):java_build_workspace( + JdtlsClient(jdtls()):java_build_workspace( is_full_build, buf_util.get_curr_buf() ) diff --git a/lua/java/api/generate.lua b/lua/java/api/generate.lua new file mode 100644 index 0000000..244725e --- /dev/null +++ b/lua/java/api/generate.lua @@ -0,0 +1,55 @@ +local M = {} + +---@param params nvim.CodeActionParamsResponse +function M.generate_constructor(params) + local JdtlsClient = require('java-core.ls.clients.jdtls-client') + local async = require('java-core.utils.async').sync + local get_client = require('java.utils.jdtls2') + local get_error_handler = require('java.handlers.error') + local ui = require('java.utils.ui') + + return async(function() + local jdtls = JdtlsClient(get_client()) + local status = jdtls:java_check_constructors_status(params.params) + + if not status or not status.constructors then + return + end + + local selected_constructor = ui.select( + 'Select super class constructor(s).', + status.constructors, + function(constructor) + return string.format( + '%s %s', + constructor.name, + table.concat(constructor.parameters, ', ') + ) + end + ) + + if not selected_constructor then + return + end + + local selected_fields = ui.multi_select( + 'Select Fields:', + status.fields, + function(field) + return field.name + end + ) + + local edit = jdtls:java_generate_constructor({ + context = params.params, + constructors = { selected_constructor }, + fields = selected_fields or {}, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') + end) + .catch(get_error_handler('Generating constructor failed')) + .run() +end + +return M diff --git a/lua/java/api/refactor.lua b/lua/java/api/refactor.lua index 35bb211..8ab3b95 100644 --- a/lua/java/api/refactor.lua +++ b/lua/java/api/refactor.lua @@ -29,7 +29,6 @@ function M.convert_variable_to_field() M.extract('convertVariableToField') end ---- ---@param refactor_command jdtls.CodeActionCommand function M.extract(refactor_command) return async(function() diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua index f260495..a5ad7fa 100644 --- a/lua/java/commands/init.lua +++ b/lua/java/commands/init.lua @@ -194,6 +194,13 @@ M.commands = { } M.handlers = { + ---@param _ lsp.Command + ---@param params nvim.CodeActionParamsResponse + [M.commands.GENERATE_CONSTRUCTORS_PROMPT] = function(_, params) + require('java.api.generate').generate_constructor(params) + end, + + ---@param is_full_build boolean [M.commands.COMPILE_WORKSPACE] = function(is_full_build) require('java.api.build').full_build_workspace(is_full_build) end, diff --git a/lua/java/utils/ui.lua b/lua/java/utils/ui.lua index cd86123..ece856e 100644 --- a/lua/java/utils/ui.lua +++ b/lua/java/utils/ui.lua @@ -1,6 +1,6 @@ -local async = require('java-core.utils.async') -local await = async.wait +local wait = require('async.waits.wait') local notify = require('java-core.utils.notify') +local List = require('java-core.utils.list') local M = {} @@ -11,7 +11,7 @@ local M = {} ---@param format_item? fun(item: T): string ---@return T function M.select(prompt, values, format_item) - return await(function(callback) + return wait(function(callback) vim.ui.select(values, { prompt = prompt, format_item = format_item, @@ -19,8 +19,53 @@ function M.select(prompt, values, format_item) end) end +function M.multi_select(prompt, values, format_item) + return wait(function(callback) + local wrapped_items = List:new(values):map(function(item, index) + return { + index = index, + is_selected = false, + value = item, + } + end) + + local open_select + + open_select = function() + vim.ui.select(wrapped_items, { + prompt = prompt, + format_item = function(item) + local prefix = item.is_selected and '* ' or '' + return prefix + .. (format_item and format_item(item.value) or item.value) + end, + }, function(selected) + if not selected then + local selected_items = wrapped_items + :filter(function(item) + return item.is_selected + end) + :map(function(item) + return item.value + end) + + callback(#selected_items > 0 and selected_items or nil) + return + end + + wrapped_items[selected.index].is_selected = + not wrapped_items[selected.index].is_selected + + open_select() + end) + end + + open_select() + end) +end + function M.input(prompt) - return await(function(callback) + return wait(function(callback) vim.ui.input({ prompt = prompt, }, callback) From 81f63c99ecd63e3d6cff9e0da6f03259b61c2c3f Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sun, 14 Jul 2024 01:23:13 +0530 Subject: [PATCH 048/147] chore(doc): update readme (#264) --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b36acb7..f856563 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # :coffee: nvim-java +![Spring](https://img.shields.io/badge/Spring-6DB33F?style=for-the-badge&logo=spring&logoColor=white) ![Java](https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white) ![Gradle](https://img.shields.io/badge/Gradle-02303A.svg?style=for-the-badge&logo=Gradle&logoColor=white) ![Apache Maven](https://img.shields.io/badge/Apache%20Maven-C71A36?style=for-the-badge&logo=Apache%20Maven&logoColor=white) @@ -17,12 +18,14 @@ Just install and start writing `public static void main(String[] args)`. ## :dizzy: Features -- :white_check_mark: Spring boot tools +- :white_check_mark: Spring Boot Tools - :white_check_mark: Diagnostics & Auto Completion - :white_check_mark: Automatic [DAP](https://github.com/mfussenegger/nvim-dap) debug configuration -- :white_check_mark: Running tests +- :white_check_mark: Organize Imports & Code Formatting +- :white_check_mark: Running Tests - :white_check_mark: Run & Debug profiles +- :white_check_mark: Code Actions ## :bulb: Why From 8452a5fca9a0442e8074c7499a02a1a4523df271 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 14 Jul 2024 01:25:46 +0530 Subject: [PATCH 049/147] chore(test): remove tests --- tests/java/api/profile_config_spec.lua | 422 ------------------------- tests/java/ui/profile_spec.lua | 405 ------------------------ 2 files changed, 827 deletions(-) delete mode 100644 tests/java/api/profile_config_spec.lua delete mode 100644 tests/java/ui/profile_spec.lua diff --git a/tests/java/api/profile_config_spec.lua b/tests/java/api/profile_config_spec.lua deleted file mode 100644 index e926651..0000000 --- a/tests/java/api/profile_config_spec.lua +++ /dev/null @@ -1,422 +0,0 @@ -local mock = require('luassert.mock') -local Profile = require('java.api.profile_config').Profile -local spy = require('luassert.spy') - -describe('java.api.profile_config', function() - local project_path = 'project_path' - local mock_fn = {} - local mock_io = {} - local file_mock = { - read = function(_, readmode) - assert(readmode == '*a') - return '{ "test": "test" }' - end, - close = function() end, - write = function() end, - } - - local json_encode = vim.fn.json_encode - local json_decode = vim.fn.json_decode - - before_each(function() - package.loaded['java.api.profile_config'] = nil - mock_io = mock(io, true) - mock_fn = mock(vim.fn, true) - - mock_fn.json_decode = function(data) - return json_decode(data) - end - - mock_fn.json_encode = function(data) - return json_encode(data) - end - - mock_fn.getcwd = function() - return project_path - end - - mock_fn.stdpath = function() - return 'config_path' - end - - mock_fn.expand = function() - return project_path - end - end) - - after_each(function() - mock.revert(mock_fn) - mock.revert(mock_io) - end) - - describe('read_config', function() - it('when no config file', function() - mock_io.open = function() - return nil - end - local profile_config = require('java.api.profile_config') - profile_config.setup() - assert.same(profile_config.get_all_profiles('module1 -> main_class'), {}) - end) - - it('when config cannot be decoded', function() - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{' - end - mock_io.open = function() - return file_mock - end - - local profile_config = require('java.api.profile_config') - profile_config.setup(project_path) - assert.same(profile_config.get_all_profiles('module1 -> main_class'), {}) - end) - - it('successfully', function() - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": \ - { \ - "module1 -> main_class": {\ - "profile_name1":{ \ - "vm_args": "vm_args",\ - "prog_args": "prog_args",\ - "name": "profile_name1",\ - "is_active": true }\ - } \ - }\ - }' - end - - mock_io.open = function(config_path, mode) - assert(config_path == 'config_path/nvim-java-profiles.json') - assert(mode == 'r') - return file_mock - end - - local profile_config = require('java.api.profile_config') - profile_config.setup(project_path) - assert.same(profile_config.get_all_profiles('module1 -> main_class'), { - profile_name1 = Profile('vm_args', 'prog_args', 'profile_name1', true), - }) - end) - end) - - describe('add_or_update_profile', function() - it('when updating profile', function() - local input_profile = - Profile('vm_args_updated', 'prog_args_updated', 'name', true) - - mock_io.open = function() - return file_mock - end - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": {\ - "module1 -> main_class": {\ - "profile_name1":{\ - "vm_args": "vm_args",\ - "prog_args": "prog_args",\ - "name": "name",\ - "is_active": true }\ - }\ - }\ - }' - end - - local spy_close = spy.on(file_mock, 'close') - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - profile_config.add_or_update_profile( - input_profile, - 'name', - 'module1 -> main_class' - ) - --- verify - assert.same( - { name = input_profile }, - profile_config.get_all_profiles('module1 -> main_class') - ) - assert.spy(spy_close).was_called(3) -- init, full_config, save - end) - end) - - it('when add new profile (set activate automatically)', function() - local input_profile = Profile('vm_args2', 'prog_args2', 'name2') - - mock_io.open = function() - return file_mock - end - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": { \ - "module1 -> main_class": [\ - { \ - "vm_args": "vm_args1",\ - "prog_args": "prog_args1",\ - "name": "name1",\ - "is_active": true }\ - ]\ - }\ - }' - end - - mock_fn.json_encode = function(data) - local expected1_id = 1 - local expected2_id = 2 - - if data.project_path['module1 -> main_class'][1].name == 'name2' then - expected1_id = 2 - expected2_id = 1 - end - - assert.same(data.project_path['module1 -> main_class'][expected1_id], { - vm_args = 'vm_args1', - prog_args = 'prog_args1', - name = 'name1', - is_active = false, - }) - assert.same(data.project_path['module1 -> main_class'][expected2_id], { - vm_args = 'vm_args2', - prog_args = 'prog_args2', - name = 'name2', - is_active = true, - }) - return json_encode(data) - end - - local spy_close = spy.on(file_mock, 'close') - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - profile_config.add_or_update_profile( - input_profile, - nil, - 'module1 -> main_class' - ) - - local expected_app_profiles_output = { - name1 = Profile('vm_args1', 'prog_args1', 'name1', false), - name2 = Profile('vm_args2', 'prog_args2', 'name2', true), - } - assert.same( - profile_config.get_all_profiles('module1 -> main_class'), - expected_app_profiles_output - ) - assert.spy(spy_close).was_called(3) -- init, full_config, save - end) - - it('when profile already exists', function() - -- setup - local input_profile = Profile('vm_args1', 'prog_args1', 'name1') - - -- mocks/verify mocks calls with expected values - mock_io.open = function() - return file_mock - end - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": { \ - "module1 -> main_class": {\ - "profile_name1":{\ - "vm_args": "vm_args1",\ - "prog_args": "prog_args1",\ - "name": "name1",\ - "is_active": true }\ - }\ - }\ - }' - end - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - assert.has.error(function() - profile_config.add_or_update_profile( - input_profile, - nil, - 'module1 -> main_class' - ) - end, "Profile with name 'name1' already exists") - end) - - it('when profile name is required', function() - -- setup - local input_profile = Profile('vm_args', 'prog_args', nil) - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - assert.has.error(function() - profile_config.add_or_update_profile( - input_profile, - nil, - 'module1 -> main_class' - ) - end, 'Profile name is required') - end) - - it('set_active_profile', function() - -- mocks/verify mocks calls with expected values - mock_io.open = function() - return file_mock - end - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": \ - { \ - "module1 -> main_class": [{ \ - "vm_args": "vm_args",\ - "prog_args": "prog_args",\ - "name": "name1",\ - "is_active": true \ - },\ - { \ - "vm_args": "vm_args",\ - "prog_args": "prog_args",\ - "name": "name2",\ - "is_active": false \ - }] \ - }\ - }' - end - - local spy_close = spy.on(file_mock, 'close') - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - profile_config.set_active_profile('name2', 'module1 -> main_class') - - --- verify - assert.same( - profile_config.get_active_profile('module1 -> main_class'), - Profile('vm_args', 'prog_args', 'name2', true) - ) - assert.spy(spy_close).was_called(3) -- init, full_config, save - end) - - it('get_profile_by_name', function() - -- setup - - -- mocks/verify mocks calls with expected values - mock_io.open = function() - return file_mock - end - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": \ - { \ - "module1 -> main_class": [{ \ - "vm_args": "vm_args1",\ - "prog_args": "prog_args1",\ - "name": "name1",\ - "is_active": true \ - },\ - { \ - "vm_args": "vm_args2",\ - "prog_args": "prog_args2",\ - "name": "name2",\ - "is_active": false \ - }] \ - }\ - }' - end - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - local profile = profile_config.get_profile('name1', 'module1 -> main_class') - --- verify - assert.same(profile, Profile('vm_args1', 'prog_args1', 'name1', true)) - end) - - describe('get_active_profile', function() - -- mocks/verify mocks calls with expected values - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": \ - { \ - "module1 -> main_class": [{ \ - "vm_args": "vm_args1",\ - "prog_args": "prog_args1",\ - "name": "name1",\ - "is_active": true \ - },\ - { \ - "vm_args": "vm_args2",\ - "prog_args": "prog_args2",\ - "name": "name2",\ - "is_active": false \ - }] \ - }\ - }' - end - - it('succesfully', function() - -- setup - mock_io.open = function() - return file_mock - end - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - local profile = profile_config.get_active_profile('module1 -> main_class') - - --- verify - assert.same(profile, Profile('vm_args1', 'prog_args1', 'name1', true)) - end) - - it('when number_of_profiles == 0', function() - -- setup - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": { "module1 -> main_class": [] } }' - end - - mock_io.open = function() - return file_mock - end - - -- call the function - local profile_config = require('java.api.profile_config') - profile_config.setup() - local profile = profile_config.get_active_profile('module1 -> main_class') - -- verify - assert(profile == nil) - end) - - it('when number_of_profiles > 0', function() - mock_io.open = function() - return file_mock - end - - file_mock.read = function(_, readmode) - assert(readmode == '*a') - return '{ "project_path": { "module1 -> main_class": [{ \ - "vm_args": "vm_args1",\ - "prog_args": "prog_args1",\ - "name": "name1",\ - "is_active": false \ - }] } }' - end - - local profile_config = require('java.api.profile_config') - profile_config.setup() - assert.has.error(function() - profile_config.get_active_profile('module1 -> main_class') - end, 'No active profile') - end) - end) -end) diff --git a/tests/java/ui/profile_spec.lua b/tests/java/ui/profile_spec.lua deleted file mode 100644 index 2c7b700..0000000 --- a/tests/java/ui/profile_spec.lua +++ /dev/null @@ -1,405 +0,0 @@ -local spy = require('luassert.spy') -local Menu = require('nui.menu') -local class = require('java-core.utils.class') - -describe('java.ui.profile', function() - local default_win_options = { - winhighlight = 'Normal:Normal,FloatBorder:Normal', - } - local default_style = 'single' - - -- profile_config mock - local profile_config = { - Profile = function(vm_args, prog_args, name, is_active) - return { - vm_args = vm_args, - prog_args = prog_args, - name = name, - is_active = is_active, - } - end, - get_profile = function() - return {} - end, - get_all_profiles = function() - return {} - end, - } - - -- notify mock - local notify = { - warn = function() end, - error = function() end, - } - - -- dap mock - local dap = { - config_dap = function() end, - } - - -- NuiMenu mock - local MockMenu = class() - function MockMenu:_init(table1, table2) - self.table1 = table1 - self.table2 = table2 - end - function MockMenu.on() end - function MockMenu.unmount() end - function MockMenu.map() end - function MockMenu.mount() end - - before_each(function() - package.loaded['java.api.profile_config'] = nil - package.loaded['java.ui.profile'] = nil - package.loaded['java-core.utils.notify'] = notify - end) - - it('get_tree_node_list_for_menu', function() - local expected_menu_items = { - Menu.item('name2 (active)'), - Menu.item('name'), - Menu.separator(), - Menu.item('New Profile'), - } - - profile_config.get_all_profiles = function() - return { - name = profile_config.Profile('vm_args', 'prog_args', 'name', false), - name2 = profile_config.Profile('vm_args2', 'prog_args2', 'name2', true), - } - end - package.loaded['java.api.profile_config'] = profile_config - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI('main_class') - local items = ui:get_tree_node_list_for_menu() - - for key, value in pairs(items) do - assert.same(expected_menu_items[key].text, value.text) - assert.same(expected_menu_items[key]._type, value._type) - end - end) - - it('get_menu', function() - package.loaded['nui.menu'] = MockMenu - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - ui.get_tree_node_list_for_menu = function() - return { menu_list = 'mock_menu_list' } - end - - local menu = ui:get_menu() - - assert.same(menu.table2.lines, { menu_list = 'mock_menu_list' }) - assert.same(menu.table1.border.text.top, '[Profiles]') - assert.same( - menu.table1.border.text.bottom, - '[a]ctivate [d]elete [b]ack [q]uit' - ) - assert(menu.table2.on_submit ~= nil) - end) - - describe('_get_and_fill_popup', function() - it('successfully', function() - local spy_nvim_api = spy.on(vim.api, 'nvim_buf_set_lines') - - profile_config.get_profile = function(name, main_class) - assert.same(name, 'target_profile') - assert.same(main_class, 'main_class') - return profile_config.Profile('vm_args', 'prog_args', 'name', false) - end - package.loaded['java.api.profile_config'] = profile_config - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI('main_class') - local popup = ui:_get_and_fill_popup('Title', 'name', 'target_profile') - - assert - .spy(spy_nvim_api) - .was_called_with(popup.bufnr, 0, -1, false, { 'name' }) - spy_nvim_api:revert() - - assert.same(popup.border._.text.top._content, '[Title]') - assert.same(popup.border._.text.top_align, 'center') - assert.same(popup.border._.winhighlight, default_win_options.winhighlight) - assert.same(popup.border._.style, default_style) - end) - - it('when target_profile is nil', function() - local spy_nvim_api = spy.on(vim.api, 'nvim_buf_set_lines') - profile_config.get_profile = function(_, _) - return profile_config.Profile('vm_args', 'prog_args', 'name', false) - end - package.loaded['java.api.profile_config'] = profile_config - - local profile_ui = require('java.ui.profile') - profile_ui.ProfileUI() - assert.spy(spy_nvim_api).was_not_called() - spy_nvim_api:revert() - end) - end) - - it('_open_profile_editor', function() - package.loaded['java.api.profile_config'] = profile_config - - -- mock popup - local MockPopup = class() - function MockPopup:_init(options) - self.border = options.border - self.enter = options.enter - self.win_options = options.win_options - self.bufnr = 1 - self.map_list = {} - end - - function MockPopup:map(mode, key, callback) - table.insert( - self.map_list, - { mode = mode, key = key, callback = callback } - ) - end - package.loaded['nui.popup'] = MockPopup - - -- mock layout - local MockLayout = class() - function MockLayout:_init(layout_settings, layout_box_list) - self.layout_settings = layout_settings - self.layout_box_list = layout_box_list - end - - local boxes = {} - function MockLayout.Box(box, options) - table.insert(boxes, { box = box, options = options }) - return {} - end - - function MockLayout.mount() end - local spy_mockLayout_mount = spy.on(MockLayout, 'mount') - - package.loaded['nui.layout'] = MockLayout - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - ui:_open_profile_editor('target_profile') - - -- verify Layout mount call - assert.spy(spy_mockLayout_mount).was_called() - - -- verify Layout.Box calls - assert.same(boxes[1].box.border.text.top, '[Name]') - assert.same(boxes[2].box.border.text.top, '[VM arguments]') - assert.same(boxes[3].box.border.text.top, '[Program arguments]') - assert.same(boxes[3].box.border.text.bottom, '[s]ave [b]ack [q]uit') - assert.same(boxes[3].box.border.text.bottom_align, 'center') - - assert.same(boxes[1].options, { grow = 0.2 }) - assert.same(boxes[2].options, { grow = 0.4 }) - assert.same(boxes[3].options, { grow = 0.4 }) - - -- loop in popup.map calls - for i = 1, 3 do - local list = boxes[i].box.map_list - -- verify keybindings - - -- actions - -- back - assert.same(list[1].mode, 'n') - assert.same(list[1].key, 'b') - assert(list[1].callback ~= nil) - - assert.same(list[2].mode, 'n') - assert.same(list[2].key, 'q') - assert(list[2].callback ~= nil) - - assert.same(list[3].mode, 'n') - assert.same(list[3].key, 's') - assert(list[3].callback ~= nil) - - -- navigation - assert.same(list[4].mode, 'n') - assert.same(list[4].key, '') - assert(list[4].callback ~= nil) - - assert.same(list[5].mode, 'n') - assert.same(list[5].key, 'k') - assert(list[5].callback ~= nil) - - assert.same(list[6].mode, 'n') - assert.same(list[6].key, 'j') - assert(list[6].callback ~= nil) - end - end) - - describe('_set_active_profile', function() - it('successfully', function() - -- mock profile_config - local new_profile = 'mock_new_profile' - profile_config.set_active_profile = function(name) - assert.same(name, new_profile) - end - package.loaded['java.api.profile_config'] = profile_config - package.loaded['java.api.dap'] = dap - - local spy_dap = spy.on(dap, 'config_dap') - local spy_mockMenu = spy.on(MockMenu, 'unmount') - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - -- set up mock in ui - ui.menu = MockMenu() - local openMenu_call = 0 - ui.openMenu = function() - openMenu_call = openMenu_call + 1 - end - ui.focus_item = { text = new_profile } - - -- call function - ui:_set_active_profile() - - --verify - assert.spy(spy_mockMenu).was_called() - assert.spy(spy_dap).was_called() - end) - - it('when selected profile is not modifiable', function() - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - ui._is_selected_profile_modifiable = function() - return false - end - ui:_set_active_profile() - end) - - it('when selected profile active', function() - local spy_notify = spy.on(notify, 'error') - local spy_profile_config = spy.on(profile_config, 'set_active_profile') - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - ui._is_selected_profile_modifiable = function() - return true - end - - ui.focus_item = { text = 'mock (active)' } - ui:_set_active_profile() - assert.spy(spy_notify).was_not_called() - assert.spy(spy_profile_config).was_not_called() - end) - end) - - describe('_delete_profile', function() - it('successfully', function() - -- mock profile_config - local new_profile = 'mock_new_profile' - profile_config.delete_profile = function(name) - assert.same(name, new_profile) - end - package.loaded['java.api.profile_config'] = profile_config - - local spy_mockMenu = spy.on(MockMenu, 'unmount') - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - -- set up mock in ui - ui.menu = MockMenu() - local openMenu_call = 0 - ui.openMenu = function() - openMenu_call = openMenu_call + 1 - end - ui.focus_item = { text = new_profile } - - -- call function - ui:_delete_profile() - - --verify - assert.spy(spy_mockMenu).was_called() - end) - - it('when selected profile is not modifiable', function() - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - ui._is_selected_profile_modifiable = function() - return false - end - ui:_delete_profile() - end) - - it('when selected profile active', function() - local spy_notify = spy.on(notify, 'warn') - local spy_profile_config = spy.on(profile_config, 'delete_profile') - - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - ui.focus_item = { text = 'mock (active)' } - ui:_delete_profile() - assert.spy(spy_notify).was_called_with('Cannot delete active profile') - assert.spy(spy_profile_config).was_not_called() - end) - end) - - describe('_is_selected_profile_modifiable when ', function() - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - - it('focus_item is nil', function() - ui:_is_selected_profile_modifiable() - assert.same(ui:_is_selected_profile_modifiable(), false) - end) - - it('focus_item.text is nil', function() - ui.focus_item = { text = nil } - ui:_is_selected_profile_modifiable() - assert.same(ui:_is_selected_profile_modifiable(), false) - end) - - it('focus_item.text is new_profile', function() - ui.focus_item = { text = 'New Profile' } - ui:_is_selected_profile_modifiable() - assert.same(ui:_is_selected_profile_modifiable(), false) - end) - end) - - it('openMenu', function() - -- mock Menu - local spy_on_mount = spy.on(MockMenu, 'mount') - local spy_on_map = spy.on(MockMenu, 'map') - -- mock profile_ui - local profile_ui = require('java.ui.profile') - local ui = profile_ui.ProfileUI() - ui.get_menu = function() - return MockMenu() - end - - ui:openMenu() - - assert.spy(spy_on_mount).was_called(1) - assert.spy(spy_on_map).was_called(4) - - -- verify keybindings - -- quit - assert.same(spy_on_map.calls[1].refs[2], 'n') - assert.same(spy_on_map.calls[1].refs[3], 'q') - assert(spy_on_map.calls[1].refs[4] ~= nil) - - -- back - assert.same(spy_on_map.calls[2].refs[2], 'n') - assert.same(spy_on_map.calls[2].refs[3], 'b') - assert(spy_on_map.calls[2].refs[4] ~= nil) - - -- set active profile - assert.same(spy_on_map.calls[3].refs[2], 'n') - assert.same(spy_on_map.calls[3].refs[3], 'a') - assert(spy_on_map.calls[3].refs[4] ~= nil) - - -- delete profile - assert.same(spy_on_map.calls[4].refs[2], 'n') - assert.same(spy_on_map.calls[4].refs[3], 'd') - assert(spy_on_map.calls[4].refs[4] ~= nil) - end) -end) From 48240e1c2937ea4e239ce5a88793c9980ec1900c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 01:27:01 +0530 Subject: [PATCH 050/147] chore(doc): automatic vimdoc update (#266) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 77231e2..93ea986 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -35,12 +35,14 @@ DEMO *nvim-java-demo* FEATURES *nvim-java-features* -- Spring boot tools +- Spring Boot Tools - Diagnostics & Auto Completion - Automatic DAP debug configuration -- Running tests +- Organize Imports & Code Formatting +- Running Tests - Run & Debug profiles +- Code Actions WHY *nvim-java-why* @@ -496,11 +498,12 @@ PROJECTS ACKNOWLEDGEMENT *nvim-java-projects-acknowledgement* ============================================================================== 2. Links *nvim-java-links* -1. *Java*: https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white -2. *Gradle*: https://img.shields.io/badge/Gradle-02303A.svg?style=for-the-badge&logo=Gradle&logoColor=white -3. *Apache Maven*: https://img.shields.io/badge/Apache%20Maven-C71A36?style=for-the-badge&logo=Apache%20Maven&logoColor=white -4. *Neovim*: https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white -5. *Lua*: https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white +1. *Spring*: https://img.shields.io/badge/Spring-6DB33F?style=for-the-badge&logo=spring&logoColor=white +2. *Java*: https://img.shields.io/badge/java-%23ED8B00.svg?style=for-the-badge&logo=openjdk&logoColor=white +3. *Gradle*: https://img.shields.io/badge/Gradle-02303A.svg?style=for-the-badge&logo=Gradle&logoColor=white +4. *Apache Maven*: https://img.shields.io/badge/Apache%20Maven-C71A36?style=for-the-badge&logo=Apache%20Maven&logoColor=white +5. *Neovim*: https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white +6. *Lua*: https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white Generated by panvimdoc From e3ed314ca4076d70c05d996614c0ed243a6c64bd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 01:28:46 +0530 Subject: [PATCH 051/147] chore(main): release 1.17.0 (#265) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e709018..8445c10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.17.0](https://github.com/nvim-java/nvim-java/compare/v1.16.0...v1.17.0) (2024-07-13) + + +### Features + +* add generate constructor code action ([ea5371b](https://github.com/nvim-java/nvim-java/commit/ea5371bf0de96dd6856ae623455376d6e2062045)) + ## [1.16.0](https://github.com/nvim-java/nvim-java/compare/v1.15.0...v1.16.0) (2024-07-13) From 0930c3b658c274413bb898ce8a38c42b751e1898 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 14 Jul 2024 12:10:13 +0530 Subject: [PATCH 052/147] chore: code clean up --- lua/java/runner/runner.lua | 9 ++++++- lua/java/ui/profile.lua | 18 ++++++++++++-- lua/java/utils/ui.lua | 48 +++++++++++++------------------------- 3 files changed, 40 insertions(+), 35 deletions(-) diff --git a/lua/java/runner/runner.lua b/lua/java/runner/runner.lua index 6b0ddc4..8705ed2 100644 --- a/lua/java/runner/runner.lua +++ b/lua/java/runner/runner.lua @@ -103,7 +103,14 @@ end function Runner.select_dap_config(args) local dap = DapSetup(jdtls()) local dap_config_list = dap:get_dap_config() - local selected_dap_config = ui.select_from_dap_configs(dap_config_list) + + local selected_dap_config = ui.select( + 'Select the main class (module -> mainClass)', + dap_config_list, + function(config) + return config.name + end + ) if not selected_dap_config then return nil, nil diff --git a/lua/java/ui/profile.lua b/lua/java/ui/profile.lua index 023a3a9..54bb1ac 100644 --- a/lua/java/ui/profile.lua +++ b/lua/java/ui/profile.lua @@ -357,11 +357,25 @@ M.ProfileUI = ProfileUI function M.ui() return async(function() - local dap_config = DapSetup(jdtls().client):get_dap_config() - local selected_config = ui.select_from_dap_configs(dap_config) + local configs = DapSetup(jdtls().client):get_dap_config() + + if not configs or #configs == 0 then + notify.error('No classes with main methods are found') + return + end + + local selected_config = ui.select( + 'Select the main class (module -> mainClass)', + configs, + function(config) + return config.name + end + ) + if not selected_config then return end + M.profile_ui = ProfileUI(selected_config.name) return M.profile_ui:openMenu() end) diff --git a/lua/java/utils/ui.lua b/lua/java/utils/ui.lua index ece856e..d487775 100644 --- a/lua/java/utils/ui.lua +++ b/lua/java/utils/ui.lua @@ -1,5 +1,4 @@ local wait = require('async.waits.wait') -local notify = require('java-core.utils.notify') local List = require('java-core.utils.list') local M = {} @@ -9,9 +8,17 @@ local M = {} ---@param prompt string ---@param values T[] ---@param format_item? fun(item: T): string ----@return T -function M.select(prompt, values, format_item) +---@param opts? { return_one: boolean } +---@return T | nil +function M.select(prompt, values, format_item, opts) + opts = opts or { prompt_single = false } + return wait(function(callback) + if not opts.prompt_single and #values == 1 then + callback(values[1]) + return + end + vim.ui.select(values, { prompt = prompt, format_item = format_item, @@ -19,6 +26,12 @@ function M.select(prompt, values, format_item) end) end +---Async vim.ui.select function +---@generic T +---@param prompt string +---@param values T[] +---@param format_item? fun(item: T): string +---@return T[] | nil function M.multi_select(prompt, values, format_item) return wait(function(callback) local wrapped_items = List:new(values):map(function(item, index) @@ -72,33 +85,4 @@ function M.input(prompt) end) end ----@param configs table -function M.select_from_dap_configs(configs) - local config_names = {} - local config_lookup = {} - for _, config in ipairs(configs) do - if config.projectName then - local key = config.name - if config.extra_args then - key = key .. ' | ' .. config.extra_args - end - table.insert(config_names, key) - config_lookup[key] = config - end - end - - if #config_names == 0 then - notify.warn('Config not found') - return - end - - if #config_names == 1 then - return config_lookup[config_names[1]] - end - - local selected_config = - M.select('Select the main class (modul -> mainClass)', config_names) - return config_lookup[selected_config] -end - return M From b43e30567275ee5be7e833686afe1c6855b0dc38 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sun, 14 Jul 2024 13:36:53 +0530 Subject: [PATCH 053/147] chore(doc): add code action tips link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f856563..6abf710 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Just install and start writing `public static void main(String[] args)`. - :white_check_mark: Organize Imports & Code Formatting - :white_check_mark: Running Tests - :white_check_mark: Run & Debug profiles -- :white_check_mark: Code Actions +- :white_check_mark: [Code Actions](https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks#running-code-actions) ## :bulb: Why From a2b645dd3d5cc1bc87820a86f6e3f3a185253939 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:49:29 +0530 Subject: [PATCH 054/147] chore(doc): automatic vimdoc update (#267) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 93ea986..4b2e71e 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 13 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 14 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -42,7 +42,7 @@ FEATURES *nvim-java-features* - Organize Imports & Code Formatting - Running Tests - Run & Debug profiles -- Code Actions +- Code Actions WHY *nvim-java-why* From 5d3a1d5f92e6f2fca243892ee8dfcfd7a4ed7e87 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 14 Jul 2024 14:52:20 +0530 Subject: [PATCH 055/147] refactor: use code_action native function for refactor commands --- README.md | 7 ------- lua/java/api/refactor.lua | 42 ++++++++++++++++++--------------------- plugin/java.lua | 5 ----- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 6abf710..7a47211 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,6 @@ Yep! That's all :) - `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection - `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection - `JavaRefactorExtractField` - Create a field from the value at cursor/selection -- `JavaRefactorConvertVariableToField` - Change a local variable to field ### Settings @@ -256,12 +255,6 @@ require('java').refactor.extract_method() require('java').refactor.extract_field() ``` -- `convert_variable_to_field` - Change a local variable to field - -```lua -require('java').refactor.convert_variable_to_field() -``` - ### Settings - `change_runtime` - Change the JDK version to another diff --git a/lua/java/api/refactor.lua b/lua/java/api/refactor.lua index 8ab3b95..87803c8 100644 --- a/lua/java/api/refactor.lua +++ b/lua/java/api/refactor.lua @@ -1,43 +1,39 @@ -local jdtls = require('java.utils.jdtls2') -local get_error_handler = require('java.handlers.error') - -local async = require('java-core.utils.async').sync - local M = {} function M.extract_variable() - M.extract('extractVariable') + M.run_code_action('refactor.extract.variable', 'extractVariable') end function M.extract_variable_all_occurrence() - M.extract('extractVariableAllOccurrence') + M.run_code_action('refactor.extract.variable', 'extractVariableAllOccurrence') end function M.extract_constant() - M.extract('extractConstant') + M.run_code_action('refactor.extract.constant') end function M.extract_method() - M.extract('extractMethod') + M.run_code_action('refactor.extract.function') end function M.extract_field() - M.extract('extractField') -end - -function M.convert_variable_to_field() - M.extract('convertVariableToField') + M.run_code_action('refactor.extract.field') end ----@param refactor_command jdtls.CodeActionCommand -function M.extract(refactor_command) - return async(function() - local RefactorCommands = require('java-refactor.refactor-commands') - local refactor_commands = RefactorCommands(jdtls()) - refactor_commands:refactor(refactor_command) - end) - .catch(get_error_handler('failed to refactor variable')) - .run() +---@private +---@param action_type string +---@param filter? string +function M.run_code_action(action_type, filter) + vim.lsp.buf.code_action({ + apply = true, + context = { + diagnostics = vim.lsp.diagnostic.get_line_diagnostics(0), + only = { action_type }, + }, + filter = filter and function(refactor) + return refactor.command.arguments[1] == filter + end or nil, + }) end return M diff --git a/plugin/java.lua b/plugin/java.lua index b6ac3f7..1357c32 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -50,11 +50,6 @@ local cmd_map = { java.refactor.extract_field, { range = 2 }, }, - - JavaRefactorConvertVariableToField = { - java.refactor.convert_variable_to_field, - { range = 2 }, - }, } for cmd, details in pairs(cmd_map) do From 8ab396a39384db633a3d4fd19d319362c7b9a0e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:54:33 +0530 Subject: [PATCH 056/147] chore(doc): automatic vimdoc update (#268) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 4b2e71e..e47b886 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -155,7 +155,6 @@ REFACTOR ~ - `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection - `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection - `JavaRefactorExtractField` - Create a field from the value at cursor/selection -- `JavaRefactorConvertVariableToField` - Change a local variable to field SETTINGS ~ @@ -283,12 +282,6 @@ REFACTOR ~ require('java').refactor.extract_field() < -- `convert_variable_to_field` - Change a local variable to field - ->lua - require('java').refactor.convert_variable_to_field() -< - SETTINGS ~ From a889ff4ab49774849bd647d136370fa1b69c70f8 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 14 Jul 2024 15:45:39 +0530 Subject: [PATCH 057/147] feat: add warning on not yet implemented client commands --- lua/java/commands/init.lua | 251 +++++++++++++++---------------------- 1 file changed, 98 insertions(+), 153 deletions(-) diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua index a5ad7fa..33485bf 100644 --- a/lua/java/commands/init.lua +++ b/lua/java/commands/init.lua @@ -1,4 +1,5 @@ local log = require('java.utils.log') +local notify = require('java-core.utils.notify') local M = {} @@ -23,174 +24,94 @@ id = vim.api.nvim_create_autocmd('LspAttach', { }) M.commands = { - - OPEN_BROWSER = 'vscode.open', - - OPEN_OUTPUT = 'java.open.output', - - SHOW_JAVA_REFERENCES = 'java.show.references', - - SHOW_JAVA_IMPLEMENTATIONS = 'java.show.implementations', - - SHOW_REFERENCES = 'editor.action.showReferences', - - GOTO_LOCATION = 'editor.action.goToLocations', - - MARKDOWN_API_RENDER = 'markdown.api.render', - - CONFIGURATION_UPDATE = 'java.projectConfiguration.update', - - IGNORE_INCOMPLETE_CLASSPATH = 'java.ignoreIncompleteClasspath', - - IGNORE_INCOMPLETE_CLASSPATH_HELP = 'java.ignoreIncompleteClasspath.help', - - RELOAD_WINDOW = 'workbench.action.reloadWindow', - - PROJECT_CONFIGURATION_STATUS = 'java.projectConfiguration.status', - - NULL_ANALYSIS_SET_MODE = 'java.compile.nullAnalysis.setMode', - + ADD_TO_SOURCEPATH = 'java.project.addToSourcePath', + ADD_TO_SOURCEPATH_CMD = 'java.project.addToSourcePath.command', + APPLY_REFACTORING_COMMAND = 'java.action.applyRefactoringCommand', APPLY_WORKSPACE_EDIT = 'java.apply.workspaceEdit', - - EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand', - - COMPILE_WORKSPACE = 'java.workspace.compile', - BUILD_PROJECT = 'java.project.build', - - OPEN_SERVER_LOG = 'java.open.serverLog', - - OPEN_SERVER_STDOUT_LOG = 'java.open.serverStdoutLog', - - OPEN_SERVER_STDERR_LOG = 'java.open.serverStderrLog', - - OPEN_CLIENT_LOG = 'java.open.clientLog', - - OPEN_LOGS = 'java.open.logs', - - OPEN_FORMATTER = 'java.open.formatter.settings', - - OPEN_FILE = 'java.open.file', - - CLEAN_WORKSPACE = 'java.clean.workspace', - - UPDATE_SOURCE_ATTACHMENT_CMD = 'java.project.updateSourceAttachment.command', - UPDATE_SOURCE_ATTACHMENT = 'java.project.updateSourceAttachment', - - RESOLVE_SOURCE_ATTACHMENT = 'java.project.resolveSourceAttachment', - - ADD_TO_SOURCEPATH_CMD = 'java.project.addToSourcePath.command', - ADD_TO_SOURCEPATH = 'java.project.addToSourcePath', - - REMOVE_FROM_SOURCEPATH_CMD = 'java.project.removeFromSourcePath.command', - REMOVE_FROM_SOURCEPATH = 'java.project.removeFromSourcePath', - - LIST_SOURCEPATHS_CMD = 'java.project.listSourcePaths.command', - LIST_SOURCEPATHS = 'java.project.listSourcePaths', - - IMPORT_PROJECTS_CMD = 'java.project.import.command', - IMPORT_PROJECTS = 'java.project.import', + CHANGE_BASE_TYPE = 'java.action.changeBaseType', CHANGE_IMPORTED_PROJECTS = 'java.project.changeImportedProjects', - - OVERRIDE_METHODS_PROMPT = 'java.action.overrideMethodsPrompt', - - HASHCODE_EQUALS_PROMPT = 'java.action.hashCodeEqualsPrompt', - - OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson', - - ORGANIZE_IMPORTS = 'java.action.organizeImports', - - ORGANIZE_IMPORTS_SILENTLY = 'java.edit.organizeImports', - MANUAL_CLEANUP = 'java.action.doCleanup', - - HANDLE_PASTE_EVENT = 'java.edit.handlePasteEvent', - + CHOOSE_IMPORTS = 'java.action.organizeImports.chooseImports', + CLEAN_SHARED_INDEXES = 'java.clean.sharedIndexes', + CLEAN_WORKSPACE = 'java.clean.workspace', CLIPBOARD_ONPASTE = 'java.action.clipboardPasteAction', - + COMPILE_WORKSPACE = 'java.workspace.compile', + CONFIGURATION_UPDATE = 'java.projectConfiguration.update', + CREATE_MODULE_INFO = 'java.project.createModuleInfo', + CREATE_MODULE_INFO_COMMAND = 'java.project.createModuleInfo.command', + EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand', FILESEXPLORER_ONPASTE = 'java.action.filesExplorerPasteAction', - - CHOOSE_IMPORTS = 'java.action.organizeImports.chooseImports', - - GENERATE_TOSTRING_PROMPT = 'java.action.generateToStringPrompt', - GENERATE_ACCESSORS_PROMPT = 'java.action.generateAccessorsPrompt', - GENERATE_CONSTRUCTORS_PROMPT = 'java.action.generateConstructorsPrompt', - GENERATE_DELEGATE_METHODS_PROMPT = 'java.action.generateDelegateMethodsPrompt', - - APPLY_REFACTORING_COMMAND = 'java.action.applyRefactoringCommand', - - RENAME_COMMAND = 'java.action.rename', - - NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND = 'java.action.navigateToSuperImplementation', - - SHOW_TYPE_HIERARCHY = 'java.action.showTypeHierarchy', - - SHOW_SUPERTYPE_HIERARCHY = 'java.action.showSupertypeHierarchy', - - SHOW_SUBTYPE_HIERARCHY = 'java.action.showSubtypeHierarchy', - - SHOW_CLASS_HIERARCHY = 'java.action.showClassHierarchy', - - CHANGE_BASE_TYPE = 'java.action.changeBaseType', - - OPEN_TYPE_HIERARCHY = 'java.navigate.openTypeHierarchy', - - RESOLVE_TYPE_HIERARCHY = 'java.navigate.resolveTypeHierarchy', - - SHOW_SERVER_TASK_STATUS = 'java.show.server.task.status', - - GET_PROJECT_SETTINGS = 'java.project.getSettings', - + GENERATE_TOSTRING_PROMPT = 'java.action.generateToStringPrompt', + GET_ALL_JAVA_PROJECTS = 'java.project.getAll', GET_CLASSPATHS = 'java.project.getClasspaths', - + GET_DECOMPILED_SOURCE = 'java.decompile', + GET_PROJECT_SETTINGS = 'java.project.getSettings', + GET_WORKSPACE_PATH = '_java.workspace.path', + GOTO_LOCATION = 'editor.action.goToLocations', + HANDLE_PASTE_EVENT = 'java.edit.handlePasteEvent', + HASHCODE_EQUALS_PROMPT = 'java.action.hashCodeEqualsPrompt', + IGNORE_INCOMPLETE_CLASSPATH = 'java.ignoreIncompleteClasspath', + IGNORE_INCOMPLETE_CLASSPATH_HELP = 'java.ignoreIncompleteClasspath.help', + IMPORT_PROJECTS = 'java.project.import', + IMPORT_PROJECTS_CMD = 'java.project.import.command', IS_TEST_FILE = 'java.project.isTestFile', - - GET_ALL_JAVA_PROJECTS = 'java.project.getAll', - - SWITCH_SERVER_MODE = 'java.server.mode.switch', - - RESTART_LANGUAGE_SERVER = 'java.server.restart', - - LEARN_MORE_ABOUT_REFACTORING = '_java.learnMoreAboutRefactorings', - LEARN_MORE_ABOUT_CLEAN_UPS = '_java.learnMoreAboutCleanUps', - - TEMPLATE_VARIABLES = '_java.templateVariables', - - NOT_COVERED_EXECUTION = '_java.notCoveredExecution', - - METADATA_FILES_GENERATION = '_java.metadataFilesGeneration', - - RUNTIME_VALIDATION_OPEN = 'java.runtimeValidation.open', - - RESOLVE_WORKSPACE_SYMBOL = 'java.project.resolveWorkspaceSymbol', - - GET_WORKSPACE_PATH = '_java.workspace.path', - - UPGRADE_GRADLE_WRAPPER_CMD = 'java.project.upgradeGradle.command', - UPGRADE_GRADLE_WRAPPER = 'java.project.upgradeGradle', - + LEARN_MORE_ABOUT_REFACTORING = '_java.learnMoreAboutRefactorings', + LIST_SOURCEPATHS = 'java.project.listSourcePaths', + LIST_SOURCEPATHS_CMD = 'java.project.listSourcePaths.command', LOMBOK_CONFIGURE = 'java.lombokConfigure', - - CREATE_MODULE_INFO = 'java.project.createModuleInfo', - - CREATE_MODULE_INFO_COMMAND = 'java.project.createModuleInfo.command', - + MANUAL_CLEANUP = 'java.action.doCleanup', + MARKDOWN_API_RENDER = 'markdown.api.render', + METADATA_FILES_GENERATION = '_java.metadataFilesGeneration', + NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND = 'java.action.navigateToSuperImplementation', + NOT_COVERED_EXECUTION = '_java.notCoveredExecution', + NULL_ANALYSIS_SET_MODE = 'java.compile.nullAnalysis.setMode', + OPEN_BROWSER = 'vscode.open', + OPEN_CLIENT_LOG = 'java.open.clientLog', + OPEN_FILE = 'java.open.file', + OPEN_FORMATTER = 'java.open.formatter.settings', + OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson', + OPEN_LOGS = 'java.open.logs', + OPEN_OUTPUT = 'java.open.output', + OPEN_SERVER_LOG = 'java.open.serverLog', + OPEN_SERVER_STDERR_LOG = 'java.open.serverStderrLog', + OPEN_SERVER_STDOUT_LOG = 'java.open.serverStdoutLog', + OPEN_STATUS_SHORTCUT = '_java.openShortcuts', + OPEN_TYPE_HIERARCHY = 'java.navigate.openTypeHierarchy', + ORGANIZE_IMPORTS = 'java.action.organizeImports', + ORGANIZE_IMPORTS_SILENTLY = 'java.edit.organizeImports', + OVERRIDE_METHODS_PROMPT = 'java.action.overrideMethodsPrompt', + PROJECT_CONFIGURATION_STATUS = 'java.projectConfiguration.status', REFRESH_BUNDLES = 'java.reloadBundles', - REFRESH_BUNDLES_COMMAND = '_java.reloadBundles.command', - - CLEAN_SHARED_INDEXES = 'java.clean.sharedIndexes', - - GET_DECOMPILED_SOURCE = 'java.decompile', - - SMARTSEMICOLON_DETECTION = 'java.edit.smartSemicolonDetection', - + RELOAD_WINDOW = 'workbench.action.reloadWindow', + REMOVE_FROM_SOURCEPATH = 'java.project.removeFromSourcePath', + REMOVE_FROM_SOURCEPATH_CMD = 'java.project.removeFromSourcePath.command', + RENAME_COMMAND = 'java.action.rename', RESOLVE_PASTED_TEXT = 'java.project.resolveText', - - OPEN_STATUS_SHORTCUT = '_java.openShortcuts', + RESOLVE_SOURCE_ATTACHMENT = 'java.project.resolveSourceAttachment', + RESOLVE_TYPE_HIERARCHY = 'java.navigate.resolveTypeHierarchy', + RESOLVE_WORKSPACE_SYMBOL = 'java.project.resolveWorkspaceSymbol', + RESTART_LANGUAGE_SERVER = 'java.server.restart', + RUNTIME_VALIDATION_OPEN = 'java.runtimeValidation.open', + SHOW_CLASS_HIERARCHY = 'java.action.showClassHierarchy', + SHOW_JAVA_IMPLEMENTATIONS = 'java.show.implementations', + SHOW_JAVA_REFERENCES = 'java.show.references', + SHOW_REFERENCES = 'editor.action.showReferences', + SHOW_SERVER_TASK_STATUS = 'java.show.server.task.status', + SHOW_SUBTYPE_HIERARCHY = 'java.action.showSubtypeHierarchy', + SHOW_SUPERTYPE_HIERARCHY = 'java.action.showSupertypeHierarchy', + SHOW_TYPE_HIERARCHY = 'java.action.showTypeHierarchy', + SMARTSEMICOLON_DETECTION = 'java.edit.smartSemicolonDetection', + SWITCH_SERVER_MODE = 'java.server.mode.switch', + TEMPLATE_VARIABLES = '_java.templateVariables', + UPDATE_SOURCE_ATTACHMENT = 'java.project.updateSourceAttachment', + UPDATE_SOURCE_ATTACHMENT_CMD = 'java.project.updateSourceAttachment.command', + UPGRADE_GRADLE_WRAPPER = 'java.project.upgradeGradle', + UPGRADE_GRADLE_WRAPPER_CMD = 'java.project.upgradeGradle.command', } M.handlers = { @@ -205,3 +126,27 @@ M.handlers = { require('java.api.build').full_build_workspace(is_full_build) end, } + +local ignored_commands = { M.commands.REFRESH_BUNDLES_COMMAND } + +for _, command in pairs(M.commands) do + if + not M.handlers[command] and not vim.tbl_contains(ignored_commands, command) + then + local message = string.format( + '"%s" is not supported yet!' + .. '\nPlease request the feature using below link' + .. '\nhttps://github.com/nvim-java/nvim-java/issues/new?assignees=' + .. '&labels=enhancement&projects=&template=feature_request.yml&title=feature%%3A+', + command + ) + + M.handlers[command] = function() + notify.warn(message) + return vim.lsp.rpc_response_error( + vim.lsp.protocol.ErrorCodes.MethodNotFound, + 'Not implemented yes' + ) + end + end +end From 8a2a8b8243056e835356df52e482cd7981c3137a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:48:03 +0530 Subject: [PATCH 058/147] chore(main): release 1.18.0 (#269) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8445c10..994b462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.18.0](https://github.com/nvim-java/nvim-java/compare/v1.17.0...v1.18.0) (2024-07-14) + + +### Features + +* add warning on not yet implemented client commands ([a889ff4](https://github.com/nvim-java/nvim-java/commit/a889ff4ab49774849bd647d136370fa1b69c70f8)) + ## [1.17.0](https://github.com/nvim-java/nvim-java/compare/v1.16.0...v1.17.0) (2024-07-13) From ff1a33ca0a88ae978b419e1649c57321bad936c8 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sun, 14 Jul 2024 15:49:11 +0530 Subject: [PATCH 059/147] chore(doc): update readme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7a47211..cb83315 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,10 @@ Just install and start writing `public static void main(String[] args)`. - :white_check_mark: Spring Boot Tools - :white_check_mark: Diagnostics & Auto Completion -- :white_check_mark: Automatic [DAP](https://github.com/mfussenegger/nvim-dap) - debug configuration +- :white_check_mark: Automatic Debug Configuration - :white_check_mark: Organize Imports & Code Formatting - :white_check_mark: Running Tests -- :white_check_mark: Run & Debug profiles +- :white_check_mark: Run & Debug Profiles - :white_check_mark: [Code Actions](https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks#running-code-actions) ## :bulb: Why From 71b7c910747c68118d8cf8418ed866ab87a831c2 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sun, 14 Jul 2024 16:28:28 +0530 Subject: [PATCH 060/147] chore(doc): add astronvim starter config --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cb83315..b15d417 100644 --- a/README.md +++ b/README.md @@ -47,15 +47,12 @@ Just install and start writing `public static void main(String[] args)`. ### Starter Configs (Recommend for newbies) Following are forks of original repositories pre-configured for java. If you -don't know how to get started, use one of the following to get started. +don't know how to get started, use one of the following to get started. +You can click on **n commits ahead of** link to see the changes made on top of the original project - [LazyVim](https://github.com/nvim-java/starter-lazyvim) - [Kickstart](https://github.com/nvim-java/starter-kickstart) - -### Distribution Specific Instructions - -- [LazyVim](https://github.com/nvim-java/nvim-java/wiki/Lazyvim) -- [Kickstart](https://github.com/nvim-java/nvim-java/wiki/Kickstart) +- [AstroNvim](https://github.com/nvim-java/starter-astronvim) ### Custom Configuration Instructions From 3282cd852e4eb896f0a5da9366a146e757c15e1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 14 Jul 2024 16:30:14 +0530 Subject: [PATCH 061/147] chore(doc): automatic vimdoc update (#270) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index e47b886..545faef 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -37,11 +37,10 @@ FEATURES *nvim-java-features* - Spring Boot Tools - Diagnostics & Auto Completion -- Automatic DAP - debug configuration +- Automatic Debug Configuration - Organize Imports & Code Formatting - Running Tests -- Run & Debug profiles +- Run & Debug Profiles - Code Actions @@ -66,16 +65,13 @@ HOW TO INSTALL *nvim-java-how-to-install* STARTER CONFIGS (RECOMMEND FOR NEWBIES) ~ Following are forks of original repositories pre-configured for java. If you -don’t know how to get started, use one of the following to get started. +don’t know how to get started, use one of the following to get started. You +can click on **n commits ahead of** link to see the changes made on top of the +original project - LazyVim - Kickstart - - -DISTRIBUTION SPECIFIC INSTRUCTIONS ~ - -- LazyVim -- Kickstart +- AstroNvim CUSTOM CONFIGURATION INSTRUCTIONS ~ From 8a1171cc21aaae58f8a08759562814aea87e694d Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 14 Jul 2024 23:28:43 +0530 Subject: [PATCH 062/147] feat: add clean workspace command --- lua/java/api/build.lua | 27 +++++++++++++++++++++++++-- lua/java/commands/init.lua | 7 +++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/lua/java/api/build.lua b/lua/java/api/build.lua index 73de525..a32b4c9 100644 --- a/lua/java/api/build.lua +++ b/lua/java/api/build.lua @@ -1,5 +1,7 @@ -local async = require('java-core.utils.async').sync +local runner = require('async.runner') local get_error_handler = require('java.handlers.error') +local ui = require('java.utils.ui') +local jdtls = require('java.utils.jdtls2') local M = {} @@ -14,7 +16,7 @@ function M.full_build_workspace(is_full_build) is_full_build = type(is_full_build) == 'boolean' and is_full_build or true - return async(function() + return runner(function() JdtlsClient(jdtls()):java_build_workspace( is_full_build, buf_util.get_curr_buf() @@ -26,4 +28,25 @@ function M.full_build_workspace(is_full_build) .run() end +function M.clean_workspace() + runner(function() + local client = jdtls() + + local workpace_path = + vim.tbl_get(client, 'config', 'init_options', 'workspace') + + local prompt = string.format('Do you want to delete "%s"', workpace_path) + + local choice = ui.select(prompt, { 'Yes', 'No' }) + + if choice ~= 'Yes' then + return + end + + vim.fn.delete(workpace_path, 'rf') + end) + .catch(get_error_handler('Failed to clean up the workspace')) + .run() +end + return M diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua index 33485bf..d761ecc 100644 --- a/lua/java/commands/init.lua +++ b/lua/java/commands/init.lua @@ -1,5 +1,8 @@ local log = require('java.utils.log') local notify = require('java-core.utils.notify') +local ui = require('java.utils.ui') +local get_error_handler = require('java.handlers.error') +local runner = require('async.runner') local M = {} @@ -125,6 +128,10 @@ M.handlers = { [M.commands.COMPILE_WORKSPACE] = function(is_full_build) require('java.api.build').full_build_workspace(is_full_build) end, + + [M.commands.CLEAN_WORKSPACE] = function() + require('java.api.build').clean_workspace() + end, } local ignored_commands = { M.commands.REFRESH_BUNDLES_COMMAND } From 4b1e1bd5206174b8914858d085fe6809482b5575 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 15 Jul 2024 00:32:28 +0530 Subject: [PATCH 063/147] feat: add toString code action --- lua/java/api/generate.lua | 55 ++++++++++++++++++++++++++--- lua/java/commands/init.lua | 7 ++-- lua/java/utils/instance_factory.lua | 25 +++++++++++++ 3 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 lua/java/utils/instance_factory.lua diff --git a/lua/java/api/generate.lua b/lua/java/api/generate.lua index 244725e..1008857 100644 --- a/lua/java/api/generate.lua +++ b/lua/java/api/generate.lua @@ -1,15 +1,15 @@ +local runner = require('async.runner') + local M = {} ---@param params nvim.CodeActionParamsResponse function M.generate_constructor(params) - local JdtlsClient = require('java-core.ls.clients.jdtls-client') - local async = require('java-core.utils.async').sync - local get_client = require('java.utils.jdtls2') + local instance = require('java.utils.instance_factory') local get_error_handler = require('java.handlers.error') local ui = require('java.utils.ui') - return async(function() - local jdtls = JdtlsClient(get_client()) + return runner(function() + local jdtls = instance.jdtls_client() local status = jdtls:java_check_constructors_status(params.params) if not status or not status.constructors then @@ -52,4 +52,49 @@ function M.generate_constructor(params) .run() end +---@param params nvim.CodeActionParamsResponse +function M.generate_to_string(params) + local instance = require('java.utils.instance_factory') + local get_error_handler = require('java.handlers.error') + local ui = require('java.utils.ui') + + runner(function() + local jdtls = instance.jdtls_client() + local status = jdtls:java_check_to_string_status(params.params) + + if status.exists then + local prompt = string.format( + 'Method "toString()" already exists in the Class %s. Do you want to replace the implementation?', + status.type + ) + local choice = ui.select(prompt, { 'Replace', 'Cancel' }) + + if choice ~= 'Replace' then + return + end + end + + local fields = ui.multi_select( + 'Select the fields to include in the toString() method.', + status.fields, + function(field) + return field.name + end + ) + + if not fields then + return + end + + local edit = jdtls:java_generate_to_string({ + context = params.params, + fields = fields, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') + end) + .catch(get_error_handler('Generating to string failed')) + .run() +end + return M diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua index d761ecc..df7b6e7 100644 --- a/lua/java/commands/init.lua +++ b/lua/java/commands/init.lua @@ -1,8 +1,5 @@ local log = require('java.utils.log') local notify = require('java-core.utils.notify') -local ui = require('java.utils.ui') -local get_error_handler = require('java.handlers.error') -local runner = require('async.runner') local M = {} @@ -124,6 +121,10 @@ M.handlers = { require('java.api.generate').generate_constructor(params) end, + [M.commands.GENERATE_TOSTRING_PROMPT] = function(_, params) + require('java.api.generate').generate_to_string(params) + end, + ---@param is_full_build boolean [M.commands.COMPILE_WORKSPACE] = function(is_full_build) require('java.api.build').full_build_workspace(is_full_build) diff --git a/lua/java/utils/instance_factory.lua b/lua/java/utils/instance_factory.lua new file mode 100644 index 0000000..5bbfb75 --- /dev/null +++ b/lua/java/utils/instance_factory.lua @@ -0,0 +1,25 @@ +local class = require('java-core.utils.class') + +local M = class() + +---@private +---@return vim.lsp.Client +local function get_client() + local clients = vim.lsp.get_clients({ name = 'jdtls' }) + + if #clients < 1 then + local message = string.format('No jdtls client found to instantiate class') + require('java-core.utils.notify').error(message) + require('java.utils.log').error(message) + error(message) + end + + return clients[1] +end + +---@return java-core.JdtlsClient +function M.jdtls_client() + return require('java-core.ls.clients.jdtls-client')(get_client()) +end + +return M From 112bf1e8d4243a486539e308907d836fd6f826dd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 00:50:03 +0530 Subject: [PATCH 064/147] chore(main): release 1.19.0 (#271) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 994b462..6e4f441 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [1.19.0](https://github.com/nvim-java/nvim-java/compare/v1.18.0...v1.19.0) (2024-07-14) + + +### Features + +* add clean workspace command ([8a1171c](https://github.com/nvim-java/nvim-java/commit/8a1171cc21aaae58f8a08759562814aea87e694d)) +* add toString code action ([4b1e1bd](https://github.com/nvim-java/nvim-java/commit/4b1e1bd5206174b8914858d085fe6809482b5575)) + ## [1.18.0](https://github.com/nvim-java/nvim-java/compare/v1.17.0...v1.18.0) (2024-07-14) From 6a714fedc4a8a327d2acebe34d673b768dcbb0d8 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 15 Jul 2024 01:20:22 +0530 Subject: [PATCH 065/147] feat: add generate hash code and equals code action --- lua/java/api/generate.lua | 59 ++++++++++++++++++++++++++++++++++++++ lua/java/commands/init.lua | 4 +++ 2 files changed, 63 insertions(+) diff --git a/lua/java/api/generate.lua b/lua/java/api/generate.lua index 1008857..4e3f94f 100644 --- a/lua/java/api/generate.lua +++ b/lua/java/api/generate.lua @@ -97,4 +97,63 @@ function M.generate_to_string(params) .run() end +---@param params nvim.CodeActionParamsResponse +function M.generate_hash_code_and_equals(params) + local instance = require('java.utils.instance_factory') + local get_error_handler = require('java.handlers.error') + local ui = require('java.utils.ui') + + runner(function() + local jdtls = instance.jdtls_client() + local status = jdtls:java_check_hash_code_equals_status(params.params) + + if not status or not status.fields or #status.fields < 1 then + local message = string.format( + 'The operation is not applicable to the type %s.', + status.type + ) + require('java-core.utils.notify').warn(message) + return + end + + local regenerate = false + + if status.existingMethods and #status.existingMethods > 0 then + local prompt = string.format( + 'Methods %s already exists in the Class %s. Do you want to regenerate the implementation?', + 'Regenerate', + 'Cancel' + ) + + local choice = ui.select(prompt, { 'Regenerate', 'Cancel' }) + + if choice == 'Regenerate' then + regenerate = true + end + end + + local fields = ui.multi_select( + 'Select the fields to include in the hashCode() and equals() methods.', + status.fields, + function(field) + return field.name + end + ) + + if not fields or #fields < 1 then + return + end + + local edit = jdtls:java_generate_hash_code_equals({ + context = params.params, + fields = fields, + regenerate = regenerate, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') + end) + .catch(get_error_handler('Generating hash code failed')) + .run() +end + return M diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua index df7b6e7..8480039 100644 --- a/lua/java/commands/init.lua +++ b/lua/java/commands/init.lua @@ -125,6 +125,10 @@ M.handlers = { require('java.api.generate').generate_to_string(params) end, + [M.commands.HASHCODE_EQUALS_PROMPT] = function(_, params) + require('java.api.generate').generate_hash_code_and_equals(params) + end, + ---@param is_full_build boolean [M.commands.COMPILE_WORKSPACE] = function(is_full_build) require('java.api.build').full_build_workspace(is_full_build) From 22ae0841e4d70ae9b30f5da25db0d1d70775af72 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 01:22:38 +0530 Subject: [PATCH 066/147] chore(main): release 1.20.0 (#272) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e4f441..b794dbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.20.0](https://github.com/nvim-java/nvim-java/compare/v1.19.0...v1.20.0) (2024-07-14) + + +### Features + +* add generate hash code and equals code action ([6a714fe](https://github.com/nvim-java/nvim-java/commit/6a714fedc4a8a327d2acebe34d673b768dcbb0d8)) + ## [1.19.0](https://github.com/nvim-java/nvim-java/compare/v1.18.0...v1.19.0) (2024-07-14) From 94625466f5023719c3625438fcf95f75f7d0c02d Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 15 Jul 2024 21:39:08 +0530 Subject: [PATCH 067/147] feat: adding delegate method generate code action --- lua/java/api/generate.lua | 79 ++++++++++++++++++++++++++++++++++++++ lua/java/commands/init.lua | 4 ++ 2 files changed, 83 insertions(+) diff --git a/lua/java/api/generate.lua b/lua/java/api/generate.lua index 4e3f94f..1712215 100644 --- a/lua/java/api/generate.lua +++ b/lua/java/api/generate.lua @@ -156,4 +156,83 @@ function M.generate_hash_code_and_equals(params) .run() end +---@param params nvim.CodeActionParamsResponse +function M.generate_delegate_mothods_prompt(params) + local instance = require('java.utils.instance_factory') + local get_error_handler = require('java.handlers.error') + local ui = require('java.utils.ui') + local List = require('java-core.utils.list') + + runner(function() + local jdtls = instance.jdtls_client() + local status = jdtls:java_check_delegate_methods_status(params.params) + + if + not status + or not status.delegateFields + or #status.delegateFields < 1 + then + require('notify').warn( + 'All delegatable methods are already implemented.' + ) + return + end + + local selected_delegate_field = ui.select( + 'Select target to generate delegates for.', + status.delegateFields, + function(field) + return field.field.name .. ': ' .. field.field.type + end + ) + + if not selected_delegate_field then + return + end + + if #selected_delegate_field.delegateMethods < 1 then + require('notify').warn( + 'All delegatable methods are already implemented.' + ) + return + end + + local selected_delegate_methods = ui.multi_select( + 'Select methods to generate delegates for.', + selected_delegate_field.delegateMethods, + function(method) + return string.format( + '%s.%s(%s)', + selected_delegate_field.field.name, + method.name, + table.concat(method.parameters, ', ') + ) + end + ) + + if not selected_delegate_methods or #selected_delegate_methods < 1 then + return + end + + local delegate_entries = List:new(selected_delegate_methods):map( + ---@param method jdtls.MethodBinding + function(method) + return { + field = selected_delegate_field.field, + delegateMethod = method, + } + end + ) + + local edit = jdtls:java_generate_delegate_methods({ + context = params.params, + delegateEntries = delegate_entries, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') + end) + .catch(get_error_handler('Generating delegate mothods failed')) + .run() +end + return M diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua index 8480039..e6c1923 100644 --- a/lua/java/commands/init.lua +++ b/lua/java/commands/init.lua @@ -129,6 +129,10 @@ M.handlers = { require('java.api.generate').generate_hash_code_and_equals(params) end, + [M.commands.GENERATE_DELEGATE_METHODS_PROMPT] = function(_, params) + require('java.api.generate').generate_delegate_mothods_prompt(params) + end, + ---@param is_full_build boolean [M.commands.COMPILE_WORKSPACE] = function(is_full_build) require('java.api.build').full_build_workspace(is_full_build) From c4cdc090243f028976b48f20d09816d0031674f8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 21:40:24 +0530 Subject: [PATCH 068/147] chore(main): release 1.21.0 (#273) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b794dbe..7beae60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.21.0](https://github.com/nvim-java/nvim-java/compare/v1.20.0...v1.21.0) (2024-07-15) + + +### Features + +* adding delegate method generate code action ([9462546](https://github.com/nvim-java/nvim-java/commit/94625466f5023719c3625438fcf95f75f7d0c02d)) + ## [1.20.0](https://github.com/nvim-java/nvim-java/compare/v1.19.0...v1.20.0) (2024-07-14) From 5498e76364def449533199ea5bdbdde9a7eeb83c Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 15 Jul 2024 21:42:33 +0530 Subject: [PATCH 069/147] fix(lint): lint error --- lua/java/api/build.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/lua/java/api/build.lua b/lua/java/api/build.lua index a32b4c9..92ef04b 100644 --- a/lua/java/api/build.lua +++ b/lua/java/api/build.lua @@ -10,7 +10,6 @@ local M = {} ---@return number function M.full_build_workspace(is_full_build) local JdtlsClient = require('java-core.ls.clients.jdtls-client') - local jdtls = require('java.utils.jdtls2') local buf_util = require('java.utils.buffer') local notify = require('java-core.utils.notify') From 1c04d72d10a4807583096848dc6ad92192a94ee1 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Thu, 25 Jul 2024 10:22:53 +0530 Subject: [PATCH 070/147] refactor!: move all the client commands to nvim-refactor repo (#278) --- lua/java.lua | 97 ++++++------ lua/java/api/build.lua | 51 ------ lua/java/api/generate.lua | 238 ---------------------------- lua/java/api/refactor.lua | 39 ----- lua/java/commands/init.lua | 168 -------------------- lua/java/config.lua | 2 +- lua/java/utils/command.lua | 25 +++ lua/java/utils/instance_factory.lua | 1 - plugin/java.lua | 27 ---- 9 files changed, 73 insertions(+), 575 deletions(-) delete mode 100644 lua/java/api/build.lua delete mode 100644 lua/java/api/generate.lua delete mode 100644 lua/java/api/refactor.lua delete mode 100644 lua/java/commands/init.lua create mode 100644 lua/java/utils/command.lua diff --git a/lua/java.lua b/lua/java.lua index 30fb85d..658e033 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -1,17 +1,15 @@ -require('java.commands') - local decomple_watch = require('java.startup.decompile-watcher') local mason_dep = require('java.startup.mason-dep') local setup_wrap = require('java.startup.lspconfig-setup-wrap') local startup_check = require('java.startup.startup-check') -local test = require('java.api.test') -local dap = require('java.api.dap') -local runner = require('java.api.runner') -local profile_ui = require('java.ui.profile') -local refactor = require('java.api.refactor') -local build_api = require('java.api.build') +local command_util = require('java.utils.command') + +local test_api = require('java.api.test') +local dap_api = require('java.api.dap') +local runner_api = require('java.api.runner') local settings_api = require('java.api.settings') +local profile_ui = require('java.ui.profile') local global_config = require('java.config') @@ -36,70 +34,73 @@ function M.setup(custom_config) local is_installing = mason_dep.install(config) - if not is_installing then - setup_wrap.setup(config) - decomple_watch.setup() - dap.setup_dap_on_lsp_attach() + if is_installing then + return end + setup_wrap.setup(config) + decomple_watch.setup() + dap_api.setup_dap_on_lsp_attach() + vim.api.nvim_exec_autocmds( 'User', { pattern = 'JavaPostSetup', data = { config = config } } ) end ----------------------------------------------------------------------- --- Experimental APIs -- ----------------------------------------------------------------------- -M.build = {} -M.build.build_workspace = build_api.full_build_workspace +---@param path string[] +---@param command fun() +---@param opts vim.api.keyset.user_command +function M.register_api(path, command, opts) + local name = command_util.path_to_command_name(path) + + vim.api.nvim_create_user_command(name, command, opts or {}) + + local last_index = #path - 1 + local func_name = path[last_index] + + table.remove(path, last_index) + + local node = M + + for _, v in ipairs(path) do + if not node[v] then + node[v] = {} + end + + node = node[v] + end + + node[func_name] = command +end ---------------------------------------------------------------------- -- DAP APIs -- ---------------------------------------------------------------------- M.dap = {} -M.dap.config_dap = dap.config_dap +M.dap.config_dap = dap_api.config_dap ---------------------------------------------------------------------- -- Test APIs -- ---------------------------------------------------------------------- M.test = {} -M.test.run_current_class = test.run_current_class -M.test.debug_current_class = test.debug_current_class +M.test.run_current_class = test_api.run_current_class +M.test.debug_current_class = test_api.debug_current_class -M.test.run_current_method = test.run_current_method -M.test.debug_current_method = test.debug_current_method +M.test.run_current_method = test_api.run_current_method +M.test.debug_current_method = test_api.debug_current_method -M.test.view_last_report = test.view_last_report - ----------------------------------------------------------------------- --- Manipulate -- ----------------------------------------------------------------------- - -M.manipulate = {} --- M.manipulate.organize_imports = {} - ----------------------------------------------------------------------- --- Refactor -- ----------------------------------------------------------------------- -M.refactor = {} -M.refactor.extract_variable = refactor.extract_variable -M.refactor.extract_constant = refactor.extract_constant -M.refactor.extract_method = refactor.extract_method -M.refactor.extract_field = refactor.extract_field -M.refactor.convert_variable_to_field = refactor.convert_variable_to_field -M.refactor.extract_variable_all_occurrence = - refactor.extract_variable_all_occurrence +M.test.view_last_report = test_api.view_last_report ---------------------------------------------------------------------- -- Runner APIs -- ---------------------------------------------------------------------- M.runner = {} M.runner.built_in = {} -M.runner.built_in.run_app = runner.built_in.run_app -M.runner.built_in.toggle_logs = runner.built_in.toggle_logs -M.runner.built_in.stop_app = runner.built_in.stop_app -M.runner.built_in.switch_app = runner.built_in.switch_app +M.runner.built_in.run_app = runner_api.built_in.run_app +M.runner.built_in.toggle_logs = runner_api.built_in.toggle_logs +M.runner.built_in.stop_app = runner_api.built_in.stop_app +M.runner.built_in.switch_app = runner_api.built_in.switch_app ---------------------------------------------------------------------- -- Profile UI -- @@ -113,8 +114,4 @@ M.profile.ui = profile_ui.ui M.settings = {} M.settings.change_runtime = settings_api.change_runtime -function M.__run() - test.debug_current_method() -end - return M diff --git a/lua/java/api/build.lua b/lua/java/api/build.lua deleted file mode 100644 index 92ef04b..0000000 --- a/lua/java/api/build.lua +++ /dev/null @@ -1,51 +0,0 @@ -local runner = require('async.runner') -local get_error_handler = require('java.handlers.error') -local ui = require('java.utils.ui') -local jdtls = require('java.utils.jdtls2') - -local M = {} - ----Do a workspace build ----@param is_full_build? boolean ----@return number -function M.full_build_workspace(is_full_build) - local JdtlsClient = require('java-core.ls.clients.jdtls-client') - local buf_util = require('java.utils.buffer') - local notify = require('java-core.utils.notify') - - is_full_build = type(is_full_build) == 'boolean' and is_full_build or true - - return runner(function() - JdtlsClient(jdtls()):java_build_workspace( - is_full_build, - buf_util.get_curr_buf() - ) - - notify.info('Workspace build successful!') - end) - .catch(get_error_handler('Workspace build failed')) - .run() -end - -function M.clean_workspace() - runner(function() - local client = jdtls() - - local workpace_path = - vim.tbl_get(client, 'config', 'init_options', 'workspace') - - local prompt = string.format('Do you want to delete "%s"', workpace_path) - - local choice = ui.select(prompt, { 'Yes', 'No' }) - - if choice ~= 'Yes' then - return - end - - vim.fn.delete(workpace_path, 'rf') - end) - .catch(get_error_handler('Failed to clean up the workspace')) - .run() -end - -return M diff --git a/lua/java/api/generate.lua b/lua/java/api/generate.lua deleted file mode 100644 index 1712215..0000000 --- a/lua/java/api/generate.lua +++ /dev/null @@ -1,238 +0,0 @@ -local runner = require('async.runner') - -local M = {} - ----@param params nvim.CodeActionParamsResponse -function M.generate_constructor(params) - local instance = require('java.utils.instance_factory') - local get_error_handler = require('java.handlers.error') - local ui = require('java.utils.ui') - - return runner(function() - local jdtls = instance.jdtls_client() - local status = jdtls:java_check_constructors_status(params.params) - - if not status or not status.constructors then - return - end - - local selected_constructor = ui.select( - 'Select super class constructor(s).', - status.constructors, - function(constructor) - return string.format( - '%s %s', - constructor.name, - table.concat(constructor.parameters, ', ') - ) - end - ) - - if not selected_constructor then - return - end - - local selected_fields = ui.multi_select( - 'Select Fields:', - status.fields, - function(field) - return field.name - end - ) - - local edit = jdtls:java_generate_constructor({ - context = params.params, - constructors = { selected_constructor }, - fields = selected_fields or {}, - }) - - vim.lsp.util.apply_workspace_edit(edit, 'utf-8') - end) - .catch(get_error_handler('Generating constructor failed')) - .run() -end - ----@param params nvim.CodeActionParamsResponse -function M.generate_to_string(params) - local instance = require('java.utils.instance_factory') - local get_error_handler = require('java.handlers.error') - local ui = require('java.utils.ui') - - runner(function() - local jdtls = instance.jdtls_client() - local status = jdtls:java_check_to_string_status(params.params) - - if status.exists then - local prompt = string.format( - 'Method "toString()" already exists in the Class %s. Do you want to replace the implementation?', - status.type - ) - local choice = ui.select(prompt, { 'Replace', 'Cancel' }) - - if choice ~= 'Replace' then - return - end - end - - local fields = ui.multi_select( - 'Select the fields to include in the toString() method.', - status.fields, - function(field) - return field.name - end - ) - - if not fields then - return - end - - local edit = jdtls:java_generate_to_string({ - context = params.params, - fields = fields, - }) - - vim.lsp.util.apply_workspace_edit(edit, 'utf-8') - end) - .catch(get_error_handler('Generating to string failed')) - .run() -end - ----@param params nvim.CodeActionParamsResponse -function M.generate_hash_code_and_equals(params) - local instance = require('java.utils.instance_factory') - local get_error_handler = require('java.handlers.error') - local ui = require('java.utils.ui') - - runner(function() - local jdtls = instance.jdtls_client() - local status = jdtls:java_check_hash_code_equals_status(params.params) - - if not status or not status.fields or #status.fields < 1 then - local message = string.format( - 'The operation is not applicable to the type %s.', - status.type - ) - require('java-core.utils.notify').warn(message) - return - end - - local regenerate = false - - if status.existingMethods and #status.existingMethods > 0 then - local prompt = string.format( - 'Methods %s already exists in the Class %s. Do you want to regenerate the implementation?', - 'Regenerate', - 'Cancel' - ) - - local choice = ui.select(prompt, { 'Regenerate', 'Cancel' }) - - if choice == 'Regenerate' then - regenerate = true - end - end - - local fields = ui.multi_select( - 'Select the fields to include in the hashCode() and equals() methods.', - status.fields, - function(field) - return field.name - end - ) - - if not fields or #fields < 1 then - return - end - - local edit = jdtls:java_generate_hash_code_equals({ - context = params.params, - fields = fields, - regenerate = regenerate, - }) - - vim.lsp.util.apply_workspace_edit(edit, 'utf-8') - end) - .catch(get_error_handler('Generating hash code failed')) - .run() -end - ----@param params nvim.CodeActionParamsResponse -function M.generate_delegate_mothods_prompt(params) - local instance = require('java.utils.instance_factory') - local get_error_handler = require('java.handlers.error') - local ui = require('java.utils.ui') - local List = require('java-core.utils.list') - - runner(function() - local jdtls = instance.jdtls_client() - local status = jdtls:java_check_delegate_methods_status(params.params) - - if - not status - or not status.delegateFields - or #status.delegateFields < 1 - then - require('notify').warn( - 'All delegatable methods are already implemented.' - ) - return - end - - local selected_delegate_field = ui.select( - 'Select target to generate delegates for.', - status.delegateFields, - function(field) - return field.field.name .. ': ' .. field.field.type - end - ) - - if not selected_delegate_field then - return - end - - if #selected_delegate_field.delegateMethods < 1 then - require('notify').warn( - 'All delegatable methods are already implemented.' - ) - return - end - - local selected_delegate_methods = ui.multi_select( - 'Select methods to generate delegates for.', - selected_delegate_field.delegateMethods, - function(method) - return string.format( - '%s.%s(%s)', - selected_delegate_field.field.name, - method.name, - table.concat(method.parameters, ', ') - ) - end - ) - - if not selected_delegate_methods or #selected_delegate_methods < 1 then - return - end - - local delegate_entries = List:new(selected_delegate_methods):map( - ---@param method jdtls.MethodBinding - function(method) - return { - field = selected_delegate_field.field, - delegateMethod = method, - } - end - ) - - local edit = jdtls:java_generate_delegate_methods({ - context = params.params, - delegateEntries = delegate_entries, - }) - - vim.lsp.util.apply_workspace_edit(edit, 'utf-8') - end) - .catch(get_error_handler('Generating delegate mothods failed')) - .run() -end - -return M diff --git a/lua/java/api/refactor.lua b/lua/java/api/refactor.lua deleted file mode 100644 index 87803c8..0000000 --- a/lua/java/api/refactor.lua +++ /dev/null @@ -1,39 +0,0 @@ -local M = {} - -function M.extract_variable() - M.run_code_action('refactor.extract.variable', 'extractVariable') -end - -function M.extract_variable_all_occurrence() - M.run_code_action('refactor.extract.variable', 'extractVariableAllOccurrence') -end - -function M.extract_constant() - M.run_code_action('refactor.extract.constant') -end - -function M.extract_method() - M.run_code_action('refactor.extract.function') -end - -function M.extract_field() - M.run_code_action('refactor.extract.field') -end - ----@private ----@param action_type string ----@param filter? string -function M.run_code_action(action_type, filter) - vim.lsp.buf.code_action({ - apply = true, - context = { - diagnostics = vim.lsp.diagnostic.get_line_diagnostics(0), - only = { action_type }, - }, - filter = filter and function(refactor) - return refactor.command.arguments[1] == filter - end or nil, - }) -end - -return M diff --git a/lua/java/commands/init.lua b/lua/java/commands/init.lua deleted file mode 100644 index e6c1923..0000000 --- a/lua/java/commands/init.lua +++ /dev/null @@ -1,168 +0,0 @@ -local log = require('java.utils.log') -local notify = require('java-core.utils.notify') - -local M = {} - -local id - -id = vim.api.nvim_create_autocmd('LspAttach', { - callback = function(args) - local client = vim.lsp.get_client_by_id(args.data.client_id) - - if client and client.name == 'jdtls' then - log.debug('adding vim.lsp.commands for java') - - for key, handler in pairs(M.handlers) do - vim.lsp.commands[key] = handler - end - - vim.api.nvim_del_autocmd(id) - end - end, - - group = vim.api.nvim_create_augroup('JavaCommandReg', {}), -}) - -M.commands = { - ADD_TO_SOURCEPATH = 'java.project.addToSourcePath', - ADD_TO_SOURCEPATH_CMD = 'java.project.addToSourcePath.command', - APPLY_REFACTORING_COMMAND = 'java.action.applyRefactoringCommand', - APPLY_WORKSPACE_EDIT = 'java.apply.workspaceEdit', - BUILD_PROJECT = 'java.project.build', - CHANGE_BASE_TYPE = 'java.action.changeBaseType', - CHANGE_IMPORTED_PROJECTS = 'java.project.changeImportedProjects', - CHOOSE_IMPORTS = 'java.action.organizeImports.chooseImports', - CLEAN_SHARED_INDEXES = 'java.clean.sharedIndexes', - CLEAN_WORKSPACE = 'java.clean.workspace', - CLIPBOARD_ONPASTE = 'java.action.clipboardPasteAction', - COMPILE_WORKSPACE = 'java.workspace.compile', - CONFIGURATION_UPDATE = 'java.projectConfiguration.update', - CREATE_MODULE_INFO = 'java.project.createModuleInfo', - CREATE_MODULE_INFO_COMMAND = 'java.project.createModuleInfo.command', - EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand', - FILESEXPLORER_ONPASTE = 'java.action.filesExplorerPasteAction', - GENERATE_ACCESSORS_PROMPT = 'java.action.generateAccessorsPrompt', - GENERATE_CONSTRUCTORS_PROMPT = 'java.action.generateConstructorsPrompt', - GENERATE_DELEGATE_METHODS_PROMPT = 'java.action.generateDelegateMethodsPrompt', - GENERATE_TOSTRING_PROMPT = 'java.action.generateToStringPrompt', - GET_ALL_JAVA_PROJECTS = 'java.project.getAll', - GET_CLASSPATHS = 'java.project.getClasspaths', - GET_DECOMPILED_SOURCE = 'java.decompile', - GET_PROJECT_SETTINGS = 'java.project.getSettings', - GET_WORKSPACE_PATH = '_java.workspace.path', - GOTO_LOCATION = 'editor.action.goToLocations', - HANDLE_PASTE_EVENT = 'java.edit.handlePasteEvent', - HASHCODE_EQUALS_PROMPT = 'java.action.hashCodeEqualsPrompt', - IGNORE_INCOMPLETE_CLASSPATH = 'java.ignoreIncompleteClasspath', - IGNORE_INCOMPLETE_CLASSPATH_HELP = 'java.ignoreIncompleteClasspath.help', - IMPORT_PROJECTS = 'java.project.import', - IMPORT_PROJECTS_CMD = 'java.project.import.command', - IS_TEST_FILE = 'java.project.isTestFile', - LEARN_MORE_ABOUT_CLEAN_UPS = '_java.learnMoreAboutCleanUps', - LEARN_MORE_ABOUT_REFACTORING = '_java.learnMoreAboutRefactorings', - LIST_SOURCEPATHS = 'java.project.listSourcePaths', - LIST_SOURCEPATHS_CMD = 'java.project.listSourcePaths.command', - LOMBOK_CONFIGURE = 'java.lombokConfigure', - MANUAL_CLEANUP = 'java.action.doCleanup', - MARKDOWN_API_RENDER = 'markdown.api.render', - METADATA_FILES_GENERATION = '_java.metadataFilesGeneration', - NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND = 'java.action.navigateToSuperImplementation', - NOT_COVERED_EXECUTION = '_java.notCoveredExecution', - NULL_ANALYSIS_SET_MODE = 'java.compile.nullAnalysis.setMode', - OPEN_BROWSER = 'vscode.open', - OPEN_CLIENT_LOG = 'java.open.clientLog', - OPEN_FILE = 'java.open.file', - OPEN_FORMATTER = 'java.open.formatter.settings', - OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson', - OPEN_LOGS = 'java.open.logs', - OPEN_OUTPUT = 'java.open.output', - OPEN_SERVER_LOG = 'java.open.serverLog', - OPEN_SERVER_STDERR_LOG = 'java.open.serverStderrLog', - OPEN_SERVER_STDOUT_LOG = 'java.open.serverStdoutLog', - OPEN_STATUS_SHORTCUT = '_java.openShortcuts', - OPEN_TYPE_HIERARCHY = 'java.navigate.openTypeHierarchy', - ORGANIZE_IMPORTS = 'java.action.organizeImports', - ORGANIZE_IMPORTS_SILENTLY = 'java.edit.organizeImports', - OVERRIDE_METHODS_PROMPT = 'java.action.overrideMethodsPrompt', - PROJECT_CONFIGURATION_STATUS = 'java.projectConfiguration.status', - REFRESH_BUNDLES = 'java.reloadBundles', - REFRESH_BUNDLES_COMMAND = '_java.reloadBundles.command', - RELOAD_WINDOW = 'workbench.action.reloadWindow', - REMOVE_FROM_SOURCEPATH = 'java.project.removeFromSourcePath', - REMOVE_FROM_SOURCEPATH_CMD = 'java.project.removeFromSourcePath.command', - RENAME_COMMAND = 'java.action.rename', - RESOLVE_PASTED_TEXT = 'java.project.resolveText', - RESOLVE_SOURCE_ATTACHMENT = 'java.project.resolveSourceAttachment', - RESOLVE_TYPE_HIERARCHY = 'java.navigate.resolveTypeHierarchy', - RESOLVE_WORKSPACE_SYMBOL = 'java.project.resolveWorkspaceSymbol', - RESTART_LANGUAGE_SERVER = 'java.server.restart', - RUNTIME_VALIDATION_OPEN = 'java.runtimeValidation.open', - SHOW_CLASS_HIERARCHY = 'java.action.showClassHierarchy', - SHOW_JAVA_IMPLEMENTATIONS = 'java.show.implementations', - SHOW_JAVA_REFERENCES = 'java.show.references', - SHOW_REFERENCES = 'editor.action.showReferences', - SHOW_SERVER_TASK_STATUS = 'java.show.server.task.status', - SHOW_SUBTYPE_HIERARCHY = 'java.action.showSubtypeHierarchy', - SHOW_SUPERTYPE_HIERARCHY = 'java.action.showSupertypeHierarchy', - SHOW_TYPE_HIERARCHY = 'java.action.showTypeHierarchy', - SMARTSEMICOLON_DETECTION = 'java.edit.smartSemicolonDetection', - SWITCH_SERVER_MODE = 'java.server.mode.switch', - TEMPLATE_VARIABLES = '_java.templateVariables', - UPDATE_SOURCE_ATTACHMENT = 'java.project.updateSourceAttachment', - UPDATE_SOURCE_ATTACHMENT_CMD = 'java.project.updateSourceAttachment.command', - UPGRADE_GRADLE_WRAPPER = 'java.project.upgradeGradle', - UPGRADE_GRADLE_WRAPPER_CMD = 'java.project.upgradeGradle.command', -} - -M.handlers = { - ---@param _ lsp.Command - ---@param params nvim.CodeActionParamsResponse - [M.commands.GENERATE_CONSTRUCTORS_PROMPT] = function(_, params) - require('java.api.generate').generate_constructor(params) - end, - - [M.commands.GENERATE_TOSTRING_PROMPT] = function(_, params) - require('java.api.generate').generate_to_string(params) - end, - - [M.commands.HASHCODE_EQUALS_PROMPT] = function(_, params) - require('java.api.generate').generate_hash_code_and_equals(params) - end, - - [M.commands.GENERATE_DELEGATE_METHODS_PROMPT] = function(_, params) - require('java.api.generate').generate_delegate_mothods_prompt(params) - end, - - ---@param is_full_build boolean - [M.commands.COMPILE_WORKSPACE] = function(is_full_build) - require('java.api.build').full_build_workspace(is_full_build) - end, - - [M.commands.CLEAN_WORKSPACE] = function() - require('java.api.build').clean_workspace() - end, -} - -local ignored_commands = { M.commands.REFRESH_BUNDLES_COMMAND } - -for _, command in pairs(M.commands) do - if - not M.handlers[command] and not vim.tbl_contains(ignored_commands, command) - then - local message = string.format( - '"%s" is not supported yet!' - .. '\nPlease request the feature using below link' - .. '\nhttps://github.com/nvim-java/nvim-java/issues/new?assignees=' - .. '&labels=enhancement&projects=&template=feature_request.yml&title=feature%%3A+', - command - ) - - M.handlers[command] = function() - notify.warn(message) - return vim.lsp.rpc_response_error( - vim.lsp.protocol.ErrorCodes.MethodNotFound, - 'Not implemented yes' - ) - end - end -end diff --git a/lua/java/config.lua b/lua/java/config.lua index 8844429..5cd2d29 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -67,7 +67,7 @@ local config = { -- mason.nvim plugin. -- IF it's not registered correctly, an error will be thrown and nvim-java -- will stop setup - invalid_mason_registry = true, + invalid_mason_registry = false, }, } diff --git a/lua/java/utils/command.lua b/lua/java/utils/command.lua new file mode 100644 index 0000000..4170b33 --- /dev/null +++ b/lua/java/utils/command.lua @@ -0,0 +1,25 @@ +local M = {} + +---Converts a path array to command name +---@param path string[] +---@return string +function M.path_to_command_name(path) + local name = 'Java' + + for _, word in ipairs(path) do + local sub_words = vim.split(word, '_') + local changed_word = '' + + for _, sub_word in ipairs(sub_words) do + local first_char = sub_word:sub(1, 1):upper() + local rest = sub_word:sub(2) + changed_word = changed_word .. first_char .. rest + end + + name = name .. changed_word + end + + return name +end + +return M diff --git a/lua/java/utils/instance_factory.lua b/lua/java/utils/instance_factory.lua index 5bbfb75..56787be 100644 --- a/lua/java/utils/instance_factory.lua +++ b/lua/java/utils/instance_factory.lua @@ -2,7 +2,6 @@ local class = require('java-core.utils.class') local M = class() ----@private ---@return vim.lsp.Client local function get_client() local clients = vim.lsp.get_clients({ name = 'jdtls' }) diff --git a/plugin/java.lua b/plugin/java.lua index 1357c32..a2d4a83 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -7,8 +7,6 @@ end local cmd_map = { JavaSettingsChangeRuntime = { java.settings.change_runtime }, - JavaBuildWorkspace = { java.build.build_workspace }, - JavaDapConfig = { java.dap.config_dap }, JavaTestRunCurrentClass = { java.test.run_current_class }, @@ -25,31 +23,6 @@ local cmd_map = { JavaRunnerSwitchLogs = { java.runner.built_in.switch_app }, JavaProfile = { java.profile.ui }, - - JavaRefactorExtractVariable = { - java.refactor.extract_variable, - { range = 2 }, - }, - - JavaRefactorExtractMethod = { - java.refactor.extract_method, - { range = 2 }, - }, - - JavaRefactorExtractConstant = { - java.refactor.extract_constant, - { range = 2 }, - }, - - JavaRefactorExtractVariableAllOccurrence = { - java.refactor.extract_variable_all_occurrence, - { range = 2 }, - }, - - JavaRefactorExtractField = { - java.refactor.extract_field, - { range = 2 }, - }, } for cmd, details in pairs(cmd_map) do From a02379fda538343f5a590fbffd4eb6e5a2ec9202 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:41:34 +0530 Subject: [PATCH 071/147] chore(main): release 2.0.0 (#274) * chore(main): release 2.0.0 * Update CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7beae60..abe1fde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [2.0.0](https://github.com/nvim-java/nvim-java/compare/v1.21.0...v2.0.0) (2024-07-25) + + +### ⚠ BREAKING CHANGES + +* move all the client commands to nvim-refactor repo ([#278](https://github.com/nvim-java/nvim-java/issues/278)) + +### Code Refactoring + +* move all the client commands to nvim-refactor repo ([#278](https://github.com/nvim-java/nvim-java/issues/278)) ([1c04d72](https://github.com/nvim-java/nvim-java/commit/1c04d72d10a4807583096848dc6ad92192a94ee1)) + ## [1.21.0](https://github.com/nvim-java/nvim-java/compare/v1.20.0...v1.21.0) (2024-07-15) From 4d07a4ec8c27158463cf539262477bac7917baf2 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 1 Aug 2024 08:04:09 +0530 Subject: [PATCH 072/147] chore: bump up the jdtls version --- lua/java/startup/mason-dep.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index b85a86b..8aed3fd 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -49,7 +49,7 @@ end ---@return table function M.get_pkg_list(config) local deps = List:new({ - { name = 'jdtls', version = 'v1.31.0' }, + { name = 'jdtls', version = 'v1.37.0' }, { name = 'lombok-nightly', version = 'nightly' }, { name = 'java-test', version = '0.40.1' }, { name = 'java-debug-adapter', version = '0.58.0' }, From b9e6b71c8cbb3f6db8ce7d3a9bd3f3cb805156f1 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Thu, 1 Aug 2024 15:04:20 +0530 Subject: [PATCH 073/147] fix: refactor and build lua API being registered incorrectly (#284) --- README.md | 16 ++++++++++++++-- lua/java.lua | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b15d417..ee42173 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Just install and start writing `public static void main(String[] args)`. ### Starter Configs (Recommend for newbies) Following are forks of original repositories pre-configured for java. If you -don't know how to get started, use one of the following to get started. +don't know how to get started, use one of the following to get started. You can click on **n commits ahead of** link to see the changes made on top of the original project - [LazyVim](https://github.com/nvim-java/starter-lazyvim) @@ -88,7 +88,11 @@ Yep! That's all :) ### Build -- `JavaBuildWorkspace` - Runs a full workspace build +- `JavaBuildBuildWorkspace` - Runs a full workspace build + +- `JavaBuildCleanWorkspace` - Clear the workspace cache + (for now you have to close and reopen to restart the language server after + the deletion) ### Runner @@ -149,6 +153,14 @@ Yep! That's all :) require('java').build.build_workspace() ``` +- `build.clean_workspace` - Clear the workspace cache + (for now you have to close and reopen to restart the language server after + the deletion) + +```lua +require('java').build.clean_workspace() +``` + ### Runner - `built_in.run_app` - Runs the application or selected main class (if there diff --git a/lua/java.lua b/lua/java.lua index 658e033..13c8b25 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -56,7 +56,7 @@ function M.register_api(path, command, opts) vim.api.nvim_create_user_command(name, command, opts or {}) - local last_index = #path - 1 + local last_index = #path local func_name = path[last_index] table.remove(path, last_index) From b3174e41ab51867123d8663eced53b33f1548522 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Aug 2024 12:52:29 +0530 Subject: [PATCH 074/147] chore(main): release 2.0.1 (#286) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index abe1fde..9feae68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.0.1](https://github.com/nvim-java/nvim-java/compare/v2.0.0...v2.0.1) (2024-08-01) + + +### Bug Fixes + +* refactor and build lua API being registered incorrectly ([#284](https://github.com/nvim-java/nvim-java/issues/284)) ([b9e6b71](https://github.com/nvim-java/nvim-java/commit/b9e6b71c8cbb3f6db8ce7d3a9bd3f3cb805156f1)) + ## [2.0.0](https://github.com/nvim-java/nvim-java/compare/v1.21.0...v2.0.0) (2024-07-25) From 905013eb83c58bda992724b3ecbe20f60b58513f Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Tue, 22 Oct 2024 07:50:41 +0530 Subject: [PATCH 075/147] chore(doc): add wiki links (#317) --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ee42173..ae7ccdd 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,15 @@ Just install and start writing `public static void main(String[] args)`. -> [!WARNING] +> [!CAUTION] > You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` before installing this +> [!TIP] +> You can find cool tips & tricks here https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks + +> [!NOTE] +> If you are facing errors while using, please check troubleshoot wiki https://github.com/nvim-java/nvim-java/wiki/Troubleshooting + ## :loudspeaker: Demo From a36f50c82f922f352d4ce7ac6a3c6b238b3e0a36 Mon Sep 17 00:00:00 2001 From: atm1020 <30997516+atm1020@users.noreply.github.com> Date: Tue, 3 Dec 2024 04:51:46 +0100 Subject: [PATCH 076/147] fix: runner cmd #241 (#329) The `Run` class always uses the command passed during the initialization, so the profile config changes don't apply. --- lua/java/runner/run.lua | 12 ++++++------ lua/java/runner/runner.lua | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lua/java/runner/run.lua b/lua/java/runner/run.lua index d5214b2..1691823 100644 --- a/lua/java/runner/run.lua +++ b/lua/java/runner/run.lua @@ -7,7 +7,6 @@ local notify = require('java-core.utils.notify') ---@field buffer number ---@field is_running boolean ---@field is_manually_stoped boolean ----@field private cmd string ---@field private term_chan_id number ---@field private job_chan_id number | nil ---@field private is_failure boolean @@ -15,10 +14,9 @@ local Run = class() ---@param dap_config java-dap.DapLauncherConfig ---@param cmd string[] -function Run:_init(dap_config, cmd) +function Run:_init(dap_config) self.name = dap_config.name self.main_class = dap_config.mainClass - self.cmd = table.concat(cmd, ' ') self.buffer = vim.api.nvim_create_buf(false, true) self.term_chan_id = vim.api.nvim_open_term(self.buffer, { on_input = function(_, _, _, data) @@ -27,11 +25,13 @@ function Run:_init(dap_config, cmd) }) end -function Run:start() +---@param cmd string[] +function Run:start(cmd) + local merged_cmd = table.concat(cmd, ' ') self.is_running = true - self:send_term(self.cmd) + self:send_term(merged_cmd) - self.job_chan_id = vim.fn.jobstart(self.cmd, { + self.job_chan_id = vim.fn.jobstart(merged_cmd, { pty = true, on_stdout = function(_, data) self:send_term(data) diff --git a/lua/java/runner/runner.lua b/lua/java/runner/runner.lua index 8705ed2..1120224 100644 --- a/lua/java/runner/runner.lua +++ b/lua/java/runner/runner.lua @@ -41,7 +41,7 @@ function Runner:start_run(args) self.curr_run = run self.logger:set_buffer(run.buffer) - run:start() + run:start(cmd) end ---Stops the user selected run From 60e5d968c17d77dcc499e5def752af35626663a2 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Tue, 24 Dec 2024 09:19:08 +0530 Subject: [PATCH 077/147] chore: add versions to config --- lua/java/config.lua | 16 ++++++++++++++-- lua/java/startup/mason-dep.lua | 11 +++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/lua/java/config.lua b/lua/java/config.lua index 5cd2d29..b560545 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -1,7 +1,9 @@ ---@class java.Config ---@field root_markers string[] ----@field java_test { enable: boolean } ----@field java_debug_adapter { enable: boolean } +---@field jdtls { version: string } +---@field lombok { version: string } +---@field java_test { enable: boolean, version: string } +---@field java_debug_adapter { enable: boolean, version: string } ---@field spring_boot_tools { enable: boolean } ---@field jdk { auto_install: boolean } ---@field notifications { dap: boolean } @@ -20,14 +22,24 @@ local config = { '.git', }, + jdtls = { + version = 'v1.37.0', + }, + + lombok = { + version = 'nightly', + }, + -- load java test plugins java_test = { enable = true, + version = '0.40.1', }, -- load java debugger plugins java_debug_adapter = { enable = true, + version = '0.58.1', }, spring_boot_tools = { diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index 8aed3fd..b239381 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -49,10 +49,13 @@ end ---@return table function M.get_pkg_list(config) local deps = List:new({ - { name = 'jdtls', version = 'v1.37.0' }, - { name = 'lombok-nightly', version = 'nightly' }, - { name = 'java-test', version = '0.40.1' }, - { name = 'java-debug-adapter', version = '0.58.0' }, + { name = 'jdtls', version = config.jdtls.version }, + { name = 'lombok-nightly', version = config.lombok.version }, + { name = 'java-test', version = config.java_test.version }, + { + name = 'java-debug-adapter', + version = config.java_debug_adapter.version, + }, }) if config.jdk.auto_install then From 50c8ce09755fc72d1f839a2b93b154b661984881 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Tue, 24 Dec 2024 09:22:01 +0530 Subject: [PATCH 078/147] chore: document update --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ae7ccdd..6d4108b 100644 --- a/README.md +++ b/README.md @@ -355,14 +355,24 @@ want, following options are available '.git', }, + jdtls = { + version = 'v1.37.0', + }, + + lombok = { + version = 'nightly', + }, + -- load java test plugins java_test = { enable = true, + version = '0.40.1', }, -- load java debugger plugins java_debug_adapter = { enable = true, + version = '0.58.1', }, spring_boot_tools = { @@ -402,7 +412,7 @@ want, following options are available -- mason.nvim plugin. -- IF it's not registered correctly, an error will be thrown and nvim-java -- will stop setup - invalid_mason_registry = true, + invalid_mason_registry = false, }, } ``` From 5d8d87ccb42fd699f41656c3a6428aa4fc1ded09 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:26:48 +0530 Subject: [PATCH 079/147] chore(main): release 2.0.2 (#330) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9feae68..0a6c617 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.0.2](https://github.com/nvim-java/nvim-java/compare/v2.0.1...v2.0.2) (2024-12-24) + + +### Bug Fixes + +* runner cmd [#241](https://github.com/nvim-java/nvim-java/issues/241) ([#329](https://github.com/nvim-java/nvim-java/issues/329)) ([a36f50c](https://github.com/nvim-java/nvim-java/commit/a36f50c82f922f352d4ce7ac6a3c6b238b3e0a36)) + ## [2.0.1](https://github.com/nvim-java/nvim-java/compare/v2.0.0...v2.0.1) (2024-08-01) From 1bf76d5c1721100f2e6a12d872fb1f0f284788e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:28:41 +0530 Subject: [PATCH 080/147] chore(doc): automatic vimdoc update (#285) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 545faef..c8dc219 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 July 14 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 December 24 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -24,9 +24,15 @@ Table of Contents *nvim-java-table-of-contents* Just install and start writing `public static void main(String[] args)`. - [!WARNING] You cannot use `nvim-java` alongside `nvim-jdtls`. So remove + [!CAUTION] You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` before installing this + [!TIP] You can find cool tips & tricks here + https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks + + [!NOTE] If you are facing errors while using, please check troubleshoot wiki + https://github.com/nvim-java/nvim-java/wiki/Troubleshooting + DEMO *nvim-java-demo* @@ -106,7 +112,9 @@ COMMANDS *nvim-java-commands* BUILD ~ -- `JavaBuildWorkspace` - Runs a full workspace build +- `JavaBuildBuildWorkspace` - Runs a full workspace build +- `JavaBuildCleanWorkspace` - Clear the workspace cache (for now you have to + close and reopen to restart the language server after the deletion) RUNNER ~ @@ -171,6 +179,14 @@ BUILD ~ require('java').build.build_workspace() < +- `build.clean_workspace` - Clear the workspace cache + (for now you have to close and reopen to restart the language server after + the deletion) + +>lua + require('java').build.clean_workspace() +< + RUNNER ~ @@ -361,14 +377,24 @@ want, following options are available '.git', }, + jdtls = { + version = 'v1.37.0', + }, + + lombok = { + version = 'nightly', + }, + -- load java test plugins java_test = { enable = true, + version = '0.40.1', }, -- load java debugger plugins java_debug_adapter = { enable = true, + version = '0.58.1', }, spring_boot_tools = { @@ -408,7 +434,7 @@ want, following options are available -- mason.nvim plugin. -- IF it's not registered correctly, an error will be thrown and nvim-java -- will stop setup - invalid_mason_registry = true, + invalid_mason_registry = false, }, } < From c5b4329d116b68eeaf751e57b4ec159c55f8c4b8 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Tue, 24 Dec 2024 10:37:07 +0530 Subject: [PATCH 081/147] chore: add sts & jdk versions to config (#334) --- README.md | 6 ++++-- lua/java/config.lua | 10 ++++++---- lua/java/startup/mason-dep.lua | 7 +++++-- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6d4108b..b2bce7f 100644 --- a/README.md +++ b/README.md @@ -356,7 +356,7 @@ want, following options are available }, jdtls = { - version = 'v1.37.0', + version = 'v1.43.0', }, lombok = { @@ -366,7 +366,7 @@ want, following options are available -- load java test plugins java_test = { enable = true, - version = '0.40.1', + version = '0.43.0', }, -- load java debugger plugins @@ -377,11 +377,13 @@ want, following options are available spring_boot_tools = { enable = true, + version = '1.59.0', }, jdk = { -- install jdk using mason.nvim auto_install = true, + version = '17.0.2', }, notifications = { diff --git a/lua/java/config.lua b/lua/java/config.lua index b560545..838a5c2 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -4,8 +4,8 @@ ---@field lombok { version: string } ---@field java_test { enable: boolean, version: string } ---@field java_debug_adapter { enable: boolean, version: string } ----@field spring_boot_tools { enable: boolean } ----@field jdk { auto_install: boolean } +---@field spring_boot_tools { enable: boolean, version: string } +---@field jdk { auto_install: boolean, version: string } ---@field notifications { dap: boolean } ---@field verification { invalid_order: boolean, duplicate_setup_calls: boolean, invalid_mason_registry: boolean } local config = { @@ -23,7 +23,7 @@ local config = { }, jdtls = { - version = 'v1.37.0', + version = 'v1.43.0', }, lombok = { @@ -33,7 +33,7 @@ local config = { -- load java test plugins java_test = { enable = true, - version = '0.40.1', + version = '0.43.0', }, -- load java debugger plugins @@ -44,11 +44,13 @@ local config = { spring_boot_tools = { enable = true, + version = '1.59.0', }, jdk = { -- install jdk using mason.nvim auto_install = true, + version = '17.0.2', }, notifications = { diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index b239381..f05e3a5 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -59,11 +59,14 @@ function M.get_pkg_list(config) }) if config.jdk.auto_install then - deps:push({ name = 'openjdk-17', version = '17.0.2' }) + deps:push({ name = 'openjdk-17', version = config.jdk.version }) end if config.spring_boot_tools.enable then - deps:push({ name = 'spring-boot-tools', version = '1.55.1' }) + deps:push({ + name = 'spring-boot-tools', + version = config.spring_boot_tools.version, + }) end return deps From 724a8ce2ea464d168117d4e24a5c42679b9123ae Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Tue, 24 Dec 2024 10:58:26 +0530 Subject: [PATCH 082/147] refactor: switch back to 1.55.1 sts version due to error in latest --- lua/java/config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/java/config.lua b/lua/java/config.lua index 838a5c2..7a1b8bb 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -44,7 +44,7 @@ local config = { spring_boot_tools = { enable = true, - version = '1.59.0', + version = '1.55.1', }, jdk = { From cb6ba6ce46fce7474171d41e8cd921c0e0dab1db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 12:01:34 +0530 Subject: [PATCH 083/147] chore(doc): automatic vimdoc update (#335) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index c8dc219..6d24539 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -378,7 +378,7 @@ want, following options are available }, jdtls = { - version = 'v1.37.0', + version = 'v1.43.0', }, lombok = { @@ -388,7 +388,7 @@ want, following options are available -- load java test plugins java_test = { enable = true, - version = '0.40.1', + version = '0.43.0', }, -- load java debugger plugins @@ -399,11 +399,13 @@ want, following options are available spring_boot_tools = { enable = true, + version = '1.59.0', }, jdk = { -- install jdk using mason.nvim auto_install = true, + version = '17.0.2', }, notifications = { From e8e84413ca03e7d0541921e795b2e6bf8801f186 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Tue, 24 Dec 2024 12:03:28 +0530 Subject: [PATCH 084/147] chore: downgrade java-test version --- lua/java/config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/java/config.lua b/lua/java/config.lua index 7a1b8bb..beb552e 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -33,7 +33,7 @@ local config = { -- load java test plugins java_test = { enable = true, - version = '0.43.0', + version = '0.40.1', }, -- load java debugger plugins From 4d810a546c262ca8f60228dc98ba51f81f5649c6 Mon Sep 17 00:00:00 2001 From: Luis Calle <53507599+TheLeoP@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:25:15 -0500 Subject: [PATCH 085/147] fix(dap): do not override previously defined user config (#342) --- lua/java/dap/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lua/java/dap/init.lua b/lua/java/dap/init.lua index 42d78f7..15804a9 100644 --- a/lua/java/dap/init.lua +++ b/lua/java/dap/init.lua @@ -105,7 +105,8 @@ function M:config_dap() end end -- end - nvim_dap.configurations.java = dap_config + nvim_dap.configurations.java = nvim_dap.configurations.java or {} + vim.list_extend(nvim_dap.configurations.java, dap_config) end return M From c616f72fa2ea0ad9d7798e1d7cab56aa6de56108 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sun, 26 Jan 2025 15:41:48 +0530 Subject: [PATCH 086/147] feat: add sync ui select util (#345) --- lua/java/utils/ui.lua | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lua/java/utils/ui.lua b/lua/java/utils/ui.lua index d487775..91860d4 100644 --- a/lua/java/utils/ui.lua +++ b/lua/java/utils/ui.lua @@ -26,6 +26,30 @@ function M.select(prompt, values, format_item, opts) end) end +--Sync vim.ui.select function +---@generic T +---@param prompt string +---@param values T[] +---@param format_item? fun(item: T, index: number): string +---@param opts? { return_one: boolean } +---@return T | nil +function M.select_sync(prompt, values, format_item, opts) + opts = opts or { prompt_single = false } + + if not opts.prompt_single and #values == 1 then + return values[1] + end + + local labels = { prompt } + for index, value in ipairs(values) do + table.insert(labels, format_item and format_item(value, index) or value) + end + + local selected_index = vim.fn.inputlist(labels) + + return values[selected_index] +end + ---Async vim.ui.select function ---@generic T ---@param prompt string From 04e3a41afce7357ad7c8d9c6676f0f3b5f5634e6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 26 Jan 2025 15:42:41 +0530 Subject: [PATCH 087/147] chore(main): release 2.1.0 (#343) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a6c617..48715b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [2.1.0](https://github.com/nvim-java/nvim-java/compare/v2.0.2...v2.1.0) (2025-01-26) + + +### Features + +* add sync ui select util ([#345](https://github.com/nvim-java/nvim-java/issues/345)) ([c616f72](https://github.com/nvim-java/nvim-java/commit/c616f72fa2ea0ad9d7798e1d7cab56aa6de56108)) + + +### Bug Fixes + +* **dap:** do not override previously defined user config ([#342](https://github.com/nvim-java/nvim-java/issues/342)) ([4d810a5](https://github.com/nvim-java/nvim-java/commit/4d810a546c262ca8f60228dc98ba51f81f5649c6)) + ## [2.0.2](https://github.com/nvim-java/nvim-java/compare/v2.0.1...v2.0.2) (2024-12-24) From db54fbf6a022f2720f86c6b6d7383ba501211b80 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sun, 16 Feb 2025 17:18:48 +0530 Subject: [PATCH 088/147] =?UTF-8?q?fix:=20nvim-java=20mason=20reg=20is=20a?= =?UTF-8?q?dded=20if=20the=20mason=20config=20does=20not=20consid=E2=80=A6?= =?UTF-8?q?=20(#355)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: nvim-java mason reg is added if the mason config does not consider parent conf * chore: format the config example --- README.md | 13 +++++++++++-- lazy.lua | 12 ++++++------ lua/java/config.lua | 9 +++++++++ lua/java/startup/mason-dep.lua | 10 ++++++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b2bce7f..3e2328c 100644 --- a/README.md +++ b/README.md @@ -366,7 +366,7 @@ want, following options are available -- load java test plugins java_test = { enable = true, - version = '0.43.0', + version = '0.40.1', }, -- load java debugger plugins @@ -377,7 +377,7 @@ want, following options are available spring_boot_tools = { enable = true, - version = '1.59.0', + version = '1.55.1', }, jdk = { @@ -416,7 +416,16 @@ want, following options are available -- will stop setup invalid_mason_registry = false, }, + + mason = { + -- These mason registries will be prepended to the existing mason + -- configuration + registries = { + 'github:nvim-java/mason-registry', + }, + }, } + ``` diff --git a/lazy.lua b/lazy.lua index 90e58b3..a352b8d 100644 --- a/lazy.lua +++ b/lazy.lua @@ -15,12 +15,12 @@ return { }, { 'williamboman/mason.nvim', - opts = { - registries = { - 'github:nvim-java/mason-registry', - 'github:mason-org/mason-registry', - }, - }, + -- opts = { + -- registries = { + -- 'github:nvim-java/mason-registry', + -- 'github:mason-org/mason-registry', + -- }, + -- }, }, }, } diff --git a/lua/java/config.lua b/lua/java/config.lua index beb552e..7383646 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -8,6 +8,7 @@ ---@field jdk { auto_install: boolean, version: string } ---@field notifications { dap: boolean } ---@field verification { invalid_order: boolean, duplicate_setup_calls: boolean, invalid_mason_registry: boolean } +---@field mason { registries: string[] } local config = { -- list of file that exists in root of the project root_markers = { @@ -83,6 +84,14 @@ local config = { -- will stop setup invalid_mason_registry = false, }, + + mason = { + -- These mason registries will be prepended to the existing mason + -- configuration + registries = { + 'github:nvim-java/mason-registry', + }, + }, } return config diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index f05e3a5..2e00762 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -1,6 +1,7 @@ local log = require('java.utils.log') local mason_ui = require('mason.ui') local mason_util = require('java.utils.mason') +local list_util = require('java-core.utils.list') local notify = require('java-core.utils.notify') local async = require('java-core.utils.async') local lazy = require('java.ui.lazy') @@ -13,6 +14,15 @@ local M = {} ---Install mason package dependencies for nvim-java ---@param config java.Config function M.install(config) + local mason_default_config = require('mason.settings').current + + local registries = list_util + :new(config.mason.registries) + :concat(mason_default_config.registries) + + require('mason').setup({ + registries = registries, + }) local packages = M.get_pkg_list(config) local is_outdated = mason_util.is_outdated(packages) From a43809938c756688c153edfec392dcf7b3af76bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 07:38:07 +0530 Subject: [PATCH 089/147] chore(main): release 2.1.1 (#357) * chore(main): release 2.1.1 * Update CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48715b3..6c78ea7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [2.1.1](https://github.com/nvim-java/nvim-java/compare/v2.1.0...v2.1.1) (2025-02-16) + + +### Bug Fixes + +* nvim-java mason reg is not added if the mason is not merging parent config in user end ([#355](https://github.com/nvim-java/nvim-java/issues/355)) ([db54fbf](https://github.com/nvim-java/nvim-java/commit/db54fbf6a022f2720f86c6b6d7383ba501211b80)) + ## [2.1.0](https://github.com/nvim-java/nvim-java/compare/v2.0.2...v2.1.0) (2025-01-26) From e552f06d86752edd77aedf50b9b49e59f6de1b65 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 07:38:52 +0530 Subject: [PATCH 090/147] chore(doc): automatic vimdoc update (#356) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 6d24539..31ad3be 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2024 December 24 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2025 February 16 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -388,7 +388,7 @@ want, following options are available -- load java test plugins java_test = { enable = true, - version = '0.43.0', + version = '0.40.1', }, -- load java debugger plugins @@ -399,7 +399,7 @@ want, following options are available spring_boot_tools = { enable = true, - version = '1.59.0', + version = '1.55.1', }, jdk = { @@ -438,6 +438,14 @@ want, following options are available -- will stop setup invalid_mason_registry = false, }, + + mason = { + -- These mason registries will be prepended to the existing mason + -- configuration + registries = { + 'github:nvim-java/mason-registry', + }, + }, } < From d25bc1c55b4cea53f6174b2e2171ed8519113bc5 Mon Sep 17 00:00:00 2001 From: Zeioth Date: Fri, 9 May 2025 06:32:26 +0200 Subject: [PATCH 091/147] =?UTF-8?q?=F0=9F=90=9Bfix(`mason=20v2.0`):=20Comp?= =?UTF-8?q?atibility=20added=20(#376)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🐛fix(`mason v2.0`): Compatibility added * format: stylua --- lua/java/utils/mason.lua | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/lua/java/utils/mason.lua b/lua/java/utils/mason.lua index 6e239a2..74ffdec 100644 --- a/lua/java/utils/mason.lua +++ b/lua/java/utils/mason.lua @@ -6,42 +6,33 @@ local await = async.wait_handle_ok local M = {} function M.is_available(package_name, package_version) + -- guard clause local has_pkg = mason_reg.has_package(package_name) - if not has_pkg then return false end - local has_version = false - + -- check local pkg = mason_reg.get_package(package_name) - pkg:get_installed_version(function(success, version) - if success and version == package_version then - has_version = true - end - end) + local version = pkg:get_installed_version() + local has_version = version == package_version return has_version end function M.is_installed(package_name, package_version) + -- guard clause local pkg = mason_reg.get_package(package_name) local is_installed = pkg:is_installed() - if not is_installed then return false end - local installed_version - pkg:get_installed_version(function(ok, version) - if not ok then - return - end - - installed_version = version - end) + -- check + local installed_version = pkg:get_installed_version() + is_installed = installed_version == package_version - return installed_version == package_version + return is_installed end function M.is_outdated(packages) From f92cadf7bdd01f5cfe383c8e725a283ffcc3685d Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 18 May 2025 00:35:02 +0530 Subject: [PATCH 092/147] =?UTF-8?q?Revert=20"=F0=9F=90=9Bfix(`mason=20v2.0?= =?UTF-8?q?`):=20Compatibility=20added=20(#376)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d25bc1c55b4cea53f6174b2e2171ed8519113bc5. --- lua/java/utils/mason.lua | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/lua/java/utils/mason.lua b/lua/java/utils/mason.lua index 74ffdec..6e239a2 100644 --- a/lua/java/utils/mason.lua +++ b/lua/java/utils/mason.lua @@ -6,33 +6,42 @@ local await = async.wait_handle_ok local M = {} function M.is_available(package_name, package_version) - -- guard clause local has_pkg = mason_reg.has_package(package_name) + if not has_pkg then return false end - -- check + local has_version = false + local pkg = mason_reg.get_package(package_name) - local version = pkg:get_installed_version() - local has_version = version == package_version + pkg:get_installed_version(function(success, version) + if success and version == package_version then + has_version = true + end + end) return has_version end function M.is_installed(package_name, package_version) - -- guard clause local pkg = mason_reg.get_package(package_name) local is_installed = pkg:is_installed() + if not is_installed then return false end - -- check - local installed_version = pkg:get_installed_version() - is_installed = installed_version == package_version + local installed_version + pkg:get_installed_version(function(ok, version) + if not ok then + return + end + + installed_version = version + end) - return is_installed + return installed_version == package_version end function M.is_outdated(packages) From 7c2e81caa301b0d1bc7992b88981af883b3b5d6b Mon Sep 17 00:00:00 2001 From: Jx <103049321+JxJxxJxJ@users.noreply.github.com> Date: Sat, 24 May 2025 08:57:31 -0300 Subject: [PATCH 093/147] fix(typo): project vise -> project-wise (#390) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e2328c..c540b14 100644 --- a/README.md +++ b/README.md @@ -288,8 +288,8 @@ require('java').settings.change_runtime() ### Method 1 [Neoconf](https://github.com/folke/neoconf.nvim) can be used to manage LSP -setting including jdtls. Neoconf allows global configuration as well as project -vice configurations. Here is how you can set Jdtls setting on `neoconf.json` +setting including jdtls. Neoconf allows global configuration as well as project-wise +configurations. Here is how you can set Jdtls setting on `neoconf.json` ```json { From 4fd68c4025acaafb9efbf2e0cf69e017bcc4476c Mon Sep 17 00:00:00 2001 From: logrusx <45463882+logrusx@users.noreply.github.com> Date: Mon, 4 Aug 2025 08:33:41 +0300 Subject: [PATCH 094/147] fix: mason registry verification fails (#405) * fix: mason registry verification fails * chore: code formatting --------- Co-authored-by: s1n7ax --- lua/java.lua | 2 ++ lua/java/startup/mason-dep.lua | 18 +++++++++++------- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lua/java.lua b/lua/java.lua index 13c8b25..3e3515d 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -28,6 +28,8 @@ function M.setup(custom_config) { pattern = 'JavaSetup', data = { config = config } } ) + mason_dep.add_custom_registries(config.mason.registries) + if not startup_check() then return end diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index 2e00762..c424b04 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -11,18 +11,22 @@ local List = require('java-core.utils.list') local M = {} ----Install mason package dependencies for nvim-java ----@param config java.Config -function M.install(config) +---Add custom registries to mason +---@param registries java.Config +function M.add_custom_registries(registries) local mason_default_config = require('mason.settings').current - local registries = list_util - :new(config.mason.registries) - :concat(mason_default_config.registries) + local new_registries = + list_util:new(registries):concat(mason_default_config.registries) require('mason').setup({ - registries = registries, + registries = new_registries, }) +end + +---Install mason package dependencies for nvim-java +---@param config java.Config +function M.install(config) local packages = M.get_pkg_list(config) local is_outdated = mason_util.is_outdated(packages) From 2776094c745af0d99b5acb24a4594d85b4f99545 Mon Sep 17 00:00:00 2001 From: logrusx <45463882+logrusx@users.noreply.github.com> Date: Mon, 4 Aug 2025 08:40:27 +0300 Subject: [PATCH 095/147] fix: java-debug-adapter doean't install (#407) --- lua/java/config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/java/config.lua b/lua/java/config.lua index 7383646..47467ee 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -40,7 +40,7 @@ local config = { -- load java debugger plugins java_debug_adapter = { enable = true, - version = '0.58.1', + version = '0.58.2', }, spring_boot_tools = { From 083114902630de67dd82fda73892966748a1eb7c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 5 Aug 2025 10:39:03 +0530 Subject: [PATCH 096/147] chore(main): release 2.1.2 (#392) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c78ea7..7cda89f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [2.1.2](https://github.com/nvim-java/nvim-java/compare/v2.1.1...v2.1.2) (2025-08-04) + + +### Bug Fixes + +* java-debug-adapter doean't install ([#407](https://github.com/nvim-java/nvim-java/issues/407)) ([2776094](https://github.com/nvim-java/nvim-java/commit/2776094c745af0d99b5acb24a4594d85b4f99545)) +* mason registry verification fails ([#405](https://github.com/nvim-java/nvim-java/issues/405)) ([4fd68c4](https://github.com/nvim-java/nvim-java/commit/4fd68c4025acaafb9efbf2e0cf69e017bcc4476c)) +* **typo:** project vise -> project-wise ([#390](https://github.com/nvim-java/nvim-java/issues/390)) ([7c2e81c](https://github.com/nvim-java/nvim-java/commit/7c2e81caa301b0d1bc7992b88981af883b3b5d6b)) + ## [2.1.1](https://github.com/nvim-java/nvim-java/compare/v2.1.0...v2.1.1) (2025-02-16) From 58c25cd45d867fc512af48a457c71bb26d9d778d Mon Sep 17 00:00:00 2001 From: logrusx <45463882+logrusx@users.noreply.github.com> Date: Wed, 6 Aug 2025 09:38:48 +0300 Subject: [PATCH 097/147] feat: adds Mason 2.0 support (#402) * feat: Mason 2.0 migration * feat: Mason 2.0 migration Pt. 2: backward compatibility with Mason 1.x * feat: Mason 2.0 migration Pt. 3: fixed formatting and typos, added missing diagnostic disable's * feat: Mason 2.0 migration Pt. 4: fixed formatting * chore: release 3.0.0 Release-As: 3.0.0 --------- Co-authored-by: s1n7ax --- lazy.lua | 2 +- lua/java/startup/mason-dep.lua | 22 ++++++- lua/java/startup/mason-registry-check.lua | 50 ++++++++++----- lua/java/utils/mason.lua | 76 ++++++++++++++++------- tests/prepare-config.lua | 2 +- 5 files changed, 106 insertions(+), 46 deletions(-) diff --git a/lazy.lua b/lazy.lua index a352b8d..03ef45a 100644 --- a/lazy.lua +++ b/lazy.lua @@ -14,7 +14,7 @@ return { commit = '218c0c26c14d99feca778e4d13f5ec3e8b1b60f0', }, { - 'williamboman/mason.nvim', + 'mason-org/mason.nvim', -- opts = { -- registries = { -- 'github:nvim-java/mason-registry', diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua index c424b04..129c777 100644 --- a/lua/java/startup/mason-dep.lua +++ b/lua/java/startup/mason-dep.lua @@ -6,14 +6,15 @@ local notify = require('java-core.utils.notify') local async = require('java-core.utils.async') local lazy = require('java.ui.lazy') local sync = async.sync +local mason_v2 = require('mason.version').MAJOR_VERSION == 2 local List = require('java-core.utils.list') local M = {} ----Add custom registries to mason +---Add custom registries to Mason 1.x ---@param registries java.Config -function M.add_custom_registries(registries) +local function add_custom_registries_v1(registries) local mason_default_config = require('mason.settings').current local new_registries = @@ -24,6 +25,21 @@ function M.add_custom_registries(registries) }) end +---Add custom registries to Mason 2.x +---@param registries java.Config +local function add_custom_registries_v2(registries) + for _, reg in ipairs(registries) do + ---@diagnostic disable-next-line: undefined-field + require('mason-registry').sources:prepend(reg) + end +end + +if mason_v2 then + M.add_custom_registries = add_custom_registries_v2 +else + M.add_custom_registries = add_custom_registries_v1 +end + ---Install mason package dependencies for nvim-java ---@param config java.Config function M.install(config) @@ -51,7 +67,7 @@ function M.refresh_and_install(packages) lazy.close_lazy_if_opened() mason_ui.open() - notify.warn('Please close and re-open after dependecies are installed') + notify.warn('Please close and re-open after dependencies are installed') end) mason_util.refresh_registry() diff --git a/lua/java/startup/mason-registry-check.lua b/lua/java/startup/mason-registry-check.lua index 76e3e8d..ce8db28 100644 --- a/lua/java/startup/mason-registry-check.lua +++ b/lua/java/startup/mason-registry-check.lua @@ -1,26 +1,42 @@ -local mason_source = require('mason-registry.sources') +local mason_v2 = require('mason.version').MAJOR_VERSION == 2 -local M = { - JAVA_REG_ID = 'github:nvim-java/mason-registry', -} +local mason_sources + +if mason_v2 then + -- compiler will complain when Mason 1.x is used + ---@diagnostic disable-next-line: undefined-field + mason_sources = require('mason-registry').sources +else + mason_sources = require('mason-registry.sources') +end + +local M = {} +if mason_v2 then + M.JAVA_REG_ID = 'nvim-java/mason-registry' +else + M.JAVA_REG_ID = 'github:nvim-java/mason-registry' +end function M.is_valid() - local has_reg = false + local iterator - for reg in mason_source.iter() do - if reg.id == M.JAVA_REG_ID then - has_reg = true - goto continue - end + if mason_v2 then + -- the compiler will complain when Mason 1.x is in use + ---@diagnostic disable-next-line: undefined-field + iterator = mason_sources.iterate + else + -- the compiler will complain when Mason 2.x is in use + ---@diagnostic disable-next-line: undefined-field + iterator = mason_sources.iter end - ::continue:: - - if has_reg then - return { - success = true, - continue = true, - } + for reg in iterator(mason_sources) do + if reg.id == M.JAVA_REG_ID then + return { + success = true, + continue = true, + } + end end return { diff --git a/lua/java/utils/mason.lua b/lua/java/utils/mason.lua index 6e239a2..824d425 100644 --- a/lua/java/utils/mason.lua +++ b/lua/java/utils/mason.lua @@ -2,44 +2,60 @@ local log = require('java.utils.log') local mason_reg = require('mason-registry') local async = require('java-core.utils.async') local await = async.wait_handle_ok +local mason_v2 = require('mason.version').MAJOR_VERSION == 2 local M = {} function M.is_available(package_name, package_version) - local has_pkg = mason_reg.has_package(package_name) + -- get_package errors if the package is not available in Mason 2.x + -- it works fine in Mason 1.x this way too. + local has_pkg, pkg = pcall(mason_reg.get_package, package_name) if not has_pkg then return false end - local has_version = false - - local pkg = mason_reg.get_package(package_name) - pkg:get_installed_version(function(success, version) - if success and version == package_version then - has_version = true - end - end) + local installed_version + if mason_v2 then + -- the compiler will complain when Mason 1.x is in use + ---@diagnostic disable-next-line: missing-parameter + installed_version = pkg:get_installed_version() + else + -- the compiler will complain when mason 2.x is in use + ---@diagnostic disable-next-line: param-type-mismatch + pkg:get_installed_version(function(success, version) + if success then + installed_version = version + end + end) + end - return has_version + return installed_version == package_version end function M.is_installed(package_name, package_version) - local pkg = mason_reg.get_package(package_name) - local is_installed = pkg:is_installed() + -- get_package errors if the package is not available in Mason 2.x + -- it works fine in Mason 1.x this way too. + local found, pkg = pcall(mason_reg.get_package, package_name) - if not is_installed then + if not found or not pkg:is_installed() then return false end local installed_version - pkg:get_installed_version(function(ok, version) - if not ok then - return - end - - installed_version = version - end) + if mason_v2 then + -- the compiler will complain when Mason 1.x is in use + ---@diagnostic disable-next-line: missing-parameter + installed_version = pkg:get_installed_version() + else + -- the compiler will complain when Mason 2.x is in use + ---@diagnostic disable-next-line: param-type-mismatch + pkg:get_installed_version(function(success, version) + if success then + installed_version = version + end + end) + end return installed_version == package_version end @@ -69,10 +85,22 @@ function M.install_pkgs(packages) if not M.is_installed(dep.name, dep.version) then local pkg = mason_reg.get_package(dep.name) - pkg:install({ - version = dep.version, - force = true, - }) + -- install errors if installation is already running in Mason 2.x + local guard + if mason_v2 then + -- guard if the package is already installing in Mason 2.x + -- the compiler will complain about the following line with Mason 1.x + ---@diagnostic disable-next-line: undefined-field + guard = pkg:is_installing() + else + guard = false + end + if not guard then + pkg:install({ + version = dep.version, + force = true, + }) + end end end end diff --git a/tests/prepare-config.lua b/tests/prepare-config.lua index f2dafac..cf2d89e 100644 --- a/tests/prepare-config.lua +++ b/tests/prepare-config.lua @@ -50,7 +50,7 @@ require('lazy').setup({ lazy = false, }, { - 'williamboman/mason.nvim', + 'mason-org/mason.nvim', lazy = false, }, { From 3cecf7362e5f83e4a34e84e4f14c65bad81968f1 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Wed, 6 Aug 2025 12:12:31 +0530 Subject: [PATCH 098/147] chore: release 3.0.0 Release-As: 3.0.0 From 3d56b7461facb42f135e25b2636bf220a7f0ed42 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 Aug 2025 12:31:31 +0530 Subject: [PATCH 099/147] chore(main): release 3.0.0 (#408) * chore(main): release 3.0.0 * Update CHANGELOG.md --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Srinesh Nisala --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cda89f..7b11fa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [3.0.0](https://github.com/nvim-java/nvim-java/compare/v2.1.2...v3.0.0) (2025-08-06) + + +### Features + +* [@logrusx](https://github.com/logrusx) adds Mason 2.0 support ([#402](https://github.com/nvim-java/nvim-java/issues/402)) ([58c25cd](https://github.com/nvim-java/nvim-java/commit/58c25cd45d867fc512af48a457c71bb26d9d778d)) + + +### Miscellaneous Chores + +* release 3.0.0 ([3cecf73](https://github.com/nvim-java/nvim-java/commit/3cecf7362e5f83e4a34e84e4f14c65bad81968f1)) + ## [2.1.2](https://github.com/nvim-java/nvim-java/compare/v2.1.1...v2.1.2) (2025-08-04) From 84b92531b1de55ee9bd1d0614a05e9965481d386 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 14:31:42 +0530 Subject: [PATCH 100/147] feat: v4.0.0 --- .devcontainer/config/nvim/init.lua | 126 +++++ .devcontainer/config/nvim/lazy-lock.json | 6 + .devcontainer/devcontainer.json | 21 + .devcontainer/setup.sh | 5 + .github/workflows/test.yml | 7 +- .gitignore | 2 +- .luacheckrc | 3 + .luarc.json | 4 + .stylua.toml | 2 +- .vscode/tasks.json | 15 +- CLAUDE.md | 153 ++++++ Makefile | 21 +- README.md | 498 +----------------- doc/development.md | 51 ++ doc/server-capabilities.md | 164 ++++++ doc/ts-to-lua-guide.md | 35 ++ lazy.lua | 15 - lua/async/runner.lua | 63 +++ lua/async/waits/wait.lua | 12 + lua/async/waits/wait_all.lua | 35 ++ lua/async/waits/wait_with_error_handler.lua | 20 + lua/async/wrap.lua | 20 + lua/java-core/constants/java_version.lua | 14 + .../ls/clients/java-debug-client.lua | 88 ++++ lua/java-core/ls/clients/java-test-client.lua | 112 ++++ lua/java-core/ls/clients/jdtls-client.lua | 385 ++++++++++++++ lua/java-core/ls/servers/jdtls/cmd.lua | 133 +++++ lua/java-core/ls/servers/jdtls/conf.lua | 33 ++ lua/java-core/ls/servers/jdtls/env.lua | 32 ++ lua/java-core/ls/servers/jdtls/init.lua | 26 + lua/java-core/ls/servers/jdtls/plugins.lua | 66 +++ lua/java-core/ls/servers/jdtls/root.lua | 29 + lua/java-core/types/jdtls-types.lua | 11 + lua/java-core/types/nvim-types.lua | 6 + lua/{java => java-core}/utils/buffer.lua | 0 lua/java-core/utils/class.lua | 280 ++++++++++ lua/java-core/utils/command.lua | 53 ++ .../utils/error_handler.lua} | 8 +- lua/java-core/utils/errors.lua | 14 + lua/java-core/utils/event.lua | 23 + lua/java-core/utils/file.lua | 15 + lua/java-core/utils/list.lua | 185 +++++++ lua/{java => java-core}/utils/log.lua | 33 +- lua/java-core/utils/log2.lua | 147 ++++++ lua/java-core/utils/lsp.lua | 66 +++ lua/java-core/utils/notify.lua | 22 + lua/java-core/utils/path.lua | 16 + lua/java-core/utils/set.lua | 22 + lua/java-core/utils/system.lua | 45 ++ lua/java-dap/data-adapters.lua | 44 ++ lua/java-dap/init.lua | 74 +++ lua/java-dap/runner.lua | 72 +++ lua/java-dap/setup.lua | 125 +++++ lua/java-refactor/action.lua | 303 +++++++++++ lua/java-refactor/api/build.lua | 19 + lua/java-refactor/api/refactor.lua | 38 ++ lua/java-refactor/client-command-handlers.lua | 129 +++++ lua/java-refactor/client-command.lua | 93 ++++ lua/java-refactor/init.lua | 46 ++ lua/java-refactor/refactor.lua | 359 +++++++++++++ lua/java-refactor/utils/error_handler.lua | 9 + lua/java-refactor/utils/instance-factory.lua | 12 + .../runner => java-runner}/run-logger.lua | 0 lua/{java/runner => java-runner}/run.lua | 10 +- lua/{java/runner => java-runner}/runner.lua | 26 +- lua/java-test/adapters.lua | 42 ++ lua/java-test/api.lua | 142 +++++ lua/java-test/init.lua | 87 +++ lua/java-test/reports/junit.lua | 73 +++ lua/java-test/results/execution-status.lua | 7 + lua/java-test/results/message-id.lua | 30 ++ .../results/result-parser-factory.lua | 13 + lua/java-test/results/result-parser.lua | 196 +++++++ lua/java-test/results/result-status.lua | 7 + lua/java-test/ui/floating-report-viewer.lua | 88 ++++ lua/java-test/ui/report-viewer.lua | 12 + lua/java-test/utils/string-builder.lua | 30 ++ lua/java.lua | 138 ++--- lua/java/api/dap.lua | 58 -- lua/java/api/profile_config.lua | 29 +- lua/java/api/runner.lua | 14 +- lua/java/api/settings.lua | 32 +- lua/java/api/test.lua | 86 --- lua/java/config.lua | 100 ++-- lua/java/dap/init.lua | 112 ---- lua/java/startup/decompile-watcher.lua | 12 +- lua/java/startup/duplicate-setup-check.lua | 27 - lua/java/startup/exec-order-check.lua | 48 -- lua/java/startup/lsp_setup.lua | 43 ++ lua/java/startup/lspconfig-setup-wrap.lua | 52 -- lua/java/startup/mason-dep.lua | 105 ---- lua/java/startup/mason-registry-check.lua | 52 -- lua/java/startup/nvim-dep.lua | 67 --- lua/java/startup/startup-check.lua | 53 -- lua/java/treesitter/init.lua | 26 - lua/java/treesitter/queries.lua | 15 - lua/java/ui/profile.lua | 98 ++-- lua/java/{utils/ui.lua => ui/utils.lua} | 6 +- lua/java/utils/command.lua | 25 - lua/java/utils/instance_factory.lua | 24 - lua/java/utils/jdtls.lua | 17 - lua/java/utils/jdtls2.lua | 21 - lua/java/utils/mason.lua | 108 ---- lua/pkgm/downloaders/factory.lua | 40 ++ lua/pkgm/downloaders/powershell.lua | 69 +++ lua/pkgm/downloaders/wget.lua | 63 +++ lua/pkgm/extractors/factory.lua | 77 +++ lua/pkgm/extractors/powershell.lua | 65 +++ lua/pkgm/extractors/tar.lua | 57 ++ lua/pkgm/extractors/uncompressed.lua | 47 ++ lua/pkgm/extractors/unzip.lua | 39 ++ lua/pkgm/manager.lua | 183 +++++++ lua/pkgm/pkgs/jdtls.lua | 0 lua/pkgm/specs/base-spec.lua | 220 ++++++++ lua/pkgm/specs/init.lua | 78 +++ lua/pkgm/specs/jdtls-spec/init.lua | 42 ++ lua/pkgm/specs/jdtls-spec/version-map.lua | 22 + plugin/java.lua | 42 +- tests/constants/capabilities.lua | 79 +++ tests/prepare-config.lua | 66 --- tests/specs/capabilities_spec.lua | 28 + tests/specs/jdtls_extensions_spec.lua | 34 ++ tests/specs/lsp_spec.lua | 14 + tests/test-config.lua | 26 - tests/utils/lsp-utils.lua | 25 + tests/utils/prepare-config.lua | 56 ++ tests/utils/test-config.lua | 28 + 127 files changed, 6180 insertions(+), 1816 deletions(-) create mode 100644 .devcontainer/config/nvim/init.lua create mode 100644 .devcontainer/config/nvim/lazy-lock.json create mode 100644 .devcontainer/devcontainer.json create mode 100755 .devcontainer/setup.sh create mode 100644 .luarc.json create mode 100644 CLAUDE.md create mode 100644 doc/development.md create mode 100644 doc/server-capabilities.md create mode 100644 doc/ts-to-lua-guide.md create mode 100644 lua/async/runner.lua create mode 100644 lua/async/waits/wait.lua create mode 100644 lua/async/waits/wait_all.lua create mode 100644 lua/async/waits/wait_with_error_handler.lua create mode 100644 lua/async/wrap.lua create mode 100644 lua/java-core/constants/java_version.lua create mode 100644 lua/java-core/ls/clients/java-debug-client.lua create mode 100644 lua/java-core/ls/clients/java-test-client.lua create mode 100644 lua/java-core/ls/clients/jdtls-client.lua create mode 100644 lua/java-core/ls/servers/jdtls/cmd.lua create mode 100644 lua/java-core/ls/servers/jdtls/conf.lua create mode 100644 lua/java-core/ls/servers/jdtls/env.lua create mode 100644 lua/java-core/ls/servers/jdtls/init.lua create mode 100644 lua/java-core/ls/servers/jdtls/plugins.lua create mode 100644 lua/java-core/ls/servers/jdtls/root.lua create mode 100644 lua/java-core/types/jdtls-types.lua create mode 100644 lua/java-core/types/nvim-types.lua rename lua/{java => java-core}/utils/buffer.lua (100%) create mode 100644 lua/java-core/utils/class.lua create mode 100644 lua/java-core/utils/command.lua rename lua/{java/handlers/error.lua => java-core/utils/error_handler.lua} (78%) create mode 100644 lua/java-core/utils/errors.lua create mode 100644 lua/java-core/utils/event.lua create mode 100644 lua/java-core/utils/file.lua create mode 100644 lua/java-core/utils/list.lua rename lua/{java => java-core}/utils/log.lua (86%) create mode 100644 lua/java-core/utils/log2.lua create mode 100644 lua/java-core/utils/lsp.lua create mode 100644 lua/java-core/utils/notify.lua create mode 100644 lua/java-core/utils/path.lua create mode 100644 lua/java-core/utils/set.lua create mode 100644 lua/java-core/utils/system.lua create mode 100644 lua/java-dap/data-adapters.lua create mode 100644 lua/java-dap/init.lua create mode 100644 lua/java-dap/runner.lua create mode 100644 lua/java-dap/setup.lua create mode 100644 lua/java-refactor/action.lua create mode 100644 lua/java-refactor/api/build.lua create mode 100644 lua/java-refactor/api/refactor.lua create mode 100644 lua/java-refactor/client-command-handlers.lua create mode 100644 lua/java-refactor/client-command.lua create mode 100644 lua/java-refactor/init.lua create mode 100644 lua/java-refactor/refactor.lua create mode 100644 lua/java-refactor/utils/error_handler.lua create mode 100644 lua/java-refactor/utils/instance-factory.lua rename lua/{java/runner => java-runner}/run-logger.lua (100%) rename lua/{java/runner => java-runner}/run.lua (94%) rename lua/{java/runner => java-runner}/runner.lua (84%) create mode 100644 lua/java-test/adapters.lua create mode 100644 lua/java-test/api.lua create mode 100644 lua/java-test/init.lua create mode 100644 lua/java-test/reports/junit.lua create mode 100644 lua/java-test/results/execution-status.lua create mode 100644 lua/java-test/results/message-id.lua create mode 100644 lua/java-test/results/result-parser-factory.lua create mode 100644 lua/java-test/results/result-parser.lua create mode 100644 lua/java-test/results/result-status.lua create mode 100644 lua/java-test/ui/floating-report-viewer.lua create mode 100644 lua/java-test/ui/report-viewer.lua create mode 100644 lua/java-test/utils/string-builder.lua delete mode 100644 lua/java/api/dap.lua delete mode 100644 lua/java/api/test.lua delete mode 100644 lua/java/dap/init.lua delete mode 100644 lua/java/startup/duplicate-setup-check.lua delete mode 100644 lua/java/startup/exec-order-check.lua create mode 100644 lua/java/startup/lsp_setup.lua delete mode 100644 lua/java/startup/lspconfig-setup-wrap.lua delete mode 100644 lua/java/startup/mason-dep.lua delete mode 100644 lua/java/startup/mason-registry-check.lua delete mode 100644 lua/java/startup/nvim-dep.lua delete mode 100644 lua/java/startup/startup-check.lua delete mode 100644 lua/java/treesitter/init.lua delete mode 100644 lua/java/treesitter/queries.lua rename lua/java/{utils/ui.lua => ui/utils.lua} (92%) delete mode 100644 lua/java/utils/command.lua delete mode 100644 lua/java/utils/instance_factory.lua delete mode 100644 lua/java/utils/jdtls.lua delete mode 100644 lua/java/utils/jdtls2.lua delete mode 100644 lua/java/utils/mason.lua create mode 100644 lua/pkgm/downloaders/factory.lua create mode 100644 lua/pkgm/downloaders/powershell.lua create mode 100644 lua/pkgm/downloaders/wget.lua create mode 100644 lua/pkgm/extractors/factory.lua create mode 100644 lua/pkgm/extractors/powershell.lua create mode 100644 lua/pkgm/extractors/tar.lua create mode 100644 lua/pkgm/extractors/uncompressed.lua create mode 100644 lua/pkgm/extractors/unzip.lua create mode 100644 lua/pkgm/manager.lua create mode 100644 lua/pkgm/pkgs/jdtls.lua create mode 100644 lua/pkgm/specs/base-spec.lua create mode 100644 lua/pkgm/specs/init.lua create mode 100644 lua/pkgm/specs/jdtls-spec/init.lua create mode 100644 lua/pkgm/specs/jdtls-spec/version-map.lua create mode 100644 tests/constants/capabilities.lua delete mode 100644 tests/prepare-config.lua create mode 100644 tests/specs/capabilities_spec.lua create mode 100644 tests/specs/jdtls_extensions_spec.lua create mode 100644 tests/specs/lsp_spec.lua delete mode 100644 tests/test-config.lua create mode 100644 tests/utils/lsp-utils.lua create mode 100644 tests/utils/prepare-config.lua create mode 100644 tests/utils/test-config.lua diff --git a/.devcontainer/config/nvim/init.lua b/.devcontainer/config/nvim/init.lua new file mode 100644 index 0000000..56e8214 --- /dev/null +++ b/.devcontainer/config/nvim/init.lua @@ -0,0 +1,126 @@ +local colemak = true + +local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' + +if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ + 'git', + 'clone', + '--filter=blob:none', + 'https://github.com/folke/lazy.nvim.git', + '--branch=stable', + lazypath, + }) +end + +vim.opt.rtp:prepend(lazypath) + +-- Setup lazy.nvim +require('lazy').setup({ + { + 'nvim-java/nvim-java', + dir = '/workspaces/nvim-java', + config = function() + require('java').setup() + vim.lsp.config('jdtls', { + handlers = { + ['language/status'] = function(_, data) + vim.notify(data.message, vim.log.levels.INFO) + end, + + ['$/progress'] = function(_, data) + vim.notify(data.value.message, vim.log.levels.INFO) + end, + }, + }) + vim.lsp.enable('jdtls') + end, + }, +}) + +-- Basic settings +vim.g.mapleader = ' ' +vim.opt.number = true +vim.opt.relativenumber = true +vim.opt.tabstop = 2 +vim.opt.shiftwidth = 2 +vim.opt.expandtab = false +vim.opt.completeopt = { 'menu', 'menuone', 'noselect' } + +vim.keymap.set('n', '', 'q') + +if colemak then + vim.keymap.set('n', '', '') + vim.keymap.set('n', 'E', 'K') + vim.keymap.set('n', 'H', 'I') + vim.keymap.set('n', 'K', 'N') + vim.keymap.set('n', 'L', 'E') + vim.keymap.set('n', 'N', 'J') + vim.keymap.set('n', 'e', '') + vim.keymap.set('n', 'h', 'i') + vim.keymap.set('n', 'i', '') + vim.keymap.set('n', 'j', 'm') + vim.keymap.set('n', 'k', 'n') + vim.keymap.set('n', 'l', 'e') + vim.keymap.set('n', 'm', '') + vim.keymap.set('n', 'n', '') +end + +vim.api.nvim_create_autocmd('LspAttach', { + callback = function(args) + vim.lsp.completion.enable(true, args.data.client_id, args.buf, { autotrigger = true }) + vim.keymap.set('i', '', function() + vim.lsp.completion.get() + end, { buffer = args.buf }) + + if colemak then + vim.keymap.set('i', '', '', { buffer = args.buf }) + vim.keymap.set('i', '', '', { buffer = args.buf }) + end + end, +}) + +vim.keymap.set('n', ']d', function() + vim.diagnostic.jump({ count = 1, float = true }) +end, { desc = 'Jump to next diagnostic' }) + +vim.keymap.set('n', '[d', function() + vim.diagnostic.jump({ count = -1, float = true }) +end, { desc = 'Jump to previous diagnostic' }) + +vim.keymap.set('n', 'ta', vim.lsp.buf.code_action, {}) + +-- DAP keymaps +vim.keymap.set('n', 'dd', function() + require('dap').toggle_breakpoint() +end, { desc = 'Toggle breakpoint' }) + +vim.keymap.set('n', 'dc', function() + require('dap').continue() +end, { desc = 'Continue' }) + +vim.keymap.set('n', 'dn', function() + require('dap').step_over() +end, { desc = 'Step over' }) + +vim.keymap.set('n', 'di', function() + require('dap').step_into() +end, { desc = 'Step into' }) + +vim.keymap.set('n', 'do', function() + require('dap').step_out() +end, { desc = 'Step out' }) + +vim.keymap.set('n', 'dr', function() + require('dap').repl.open() +end, { desc = 'Open REPL' }) + +vim.keymap.set('n', 'dl', function() + require('dap').run_last() +end, { desc = 'Run last' }) + +vim.keymap.set('n', 'dt', function() + require('dap').terminate() +end, { desc = 'Terminate' }) + +vim.keymap.set('n', 'm', "vnewput = execute('messages')") diff --git a/.devcontainer/config/nvim/lazy-lock.json b/.devcontainer/config/nvim/lazy-lock.json new file mode 100644 index 0000000..c076196 --- /dev/null +++ b/.devcontainer/config/nvim/lazy-lock.json @@ -0,0 +1,6 @@ +{ + "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, + "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, + "nvim-dap": { "branch": "master", "commit": "b38f7d30366d9169d0a623c4c85fbcf99d8d58bb" }, + "spring-boot.nvim": { "branch": "main", "commit": "218c0c26c14d99feca778e4d13f5ec3e8b1b60f0" } +} diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..8fe9ee8 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://raw.githubusercontent.com/devcontainers/spec/refs/heads/main/schemas/devContainer.base.schema.json", + "name": "nvim-java", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu", + "features": { + "ghcr.io/devcontainers/features/git:1": {}, + "ghcr.io/devcontainers/features/java:1": {}, + "ghcr.io/devcontainers/features/python:1": {}, + "ghcr.io/devcontainers-extra/features/wget-apt-get:1": {}, + "ghcr.io/duduribeiro/devcontainer-features/neovim:1": { + "version": "nightly" + }, + "ghcr.io/devcontainers-extra/features/springboot-sdkman:2": {} + }, + "postCreateCommand": "bash .devcontainer/setup.sh", + "customizations": { + "vscode": { + "extensions": ["sumneko.lua"] + } + } +} diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100755 index 0000000..843e7c7 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -euxo pipefail + +mkdir -p ~/.config +ln -sf /workspaces/nvim-java/.devcontainer/config/nvim ~/.config/nvim diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d46b2ff..9875bb4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,12 +8,13 @@ on: jobs: test: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: matrix: + os: [ubuntu-latest, macos-latest, windows-latest] nvim-versions: ["stable", "nightly"] - name: test + name: test (${{ matrix.os }}, nvim-${{ matrix.nvim-versions }}) steps: - name: checkout uses: actions/checkout@v3 @@ -24,4 +25,4 @@ jobs: version: ${{ matrix.nvim-versions }} - name: run tests - run: make test + run: make tests diff --git a/.gitignore b/.gitignore index f2a462f..f8b34a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ vendor/plenary.nvim .test_plugins -.luarc.json +proj diff --git a/.luacheckrc b/.luacheckrc index 2998a81..33c63e5 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -12,3 +12,6 @@ read_globals = { 'it', 'assert', } +ignore = { + '212/self', +} diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..acd7d9d --- /dev/null +++ b/.luarc.json @@ -0,0 +1,4 @@ +{ + "diagnostics.globals": ["describe"] +} + diff --git a/.stylua.toml b/.stylua.toml index 31321df..dc96236 100644 --- a/.stylua.toml +++ b/.stylua.toml @@ -1,4 +1,4 @@ -column_width = 80 +column_width = 120 line_endings = "Unix" indent_type = "Tabs" indent_width = 2 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index de6d140..e951a20 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,9 +1,10 @@ { - "tasks": [ - { - "label": "Run Tests", - "type": "shell", - "command": "make test" - } - ] + "tasks": [ + { + "label": "run current test file", + "type": "shell", + "command": "make test FILE=${file}", + "problemMatcher": [] + } + ] } diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d716f17 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,153 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## General Guidelines + +- Be extremely concise in all interactions and commit messages +- Sacrifice grammar for sake of concision +- Keep CLAUDE.md updated when changes make it outdated; add noteworthy patterns/conventions after implementing changes + +## Documentation + +Additional documentation available in `/doc`: +- `development.md` - Development environment setup (devcontainer, Spring Boot test projects) +- `nvim-java.txt` - Plugin documentation +- `server-capabilities.md` - Server capabilities reference +- `ts-to-lua-guide.md` - TypeScript to Lua translation guide + +## Build Commands + +```bash +make tests # Run Plenary/Busted tests (headless Neovim) +make test FILE=path # Run specific test file +make lint # Run luacheck linter +make format # Format with stylua +make all # lint -> format -> tests +``` + +## Architecture + +nvim-java is a Neovim plugin providing Java IDE features via JDTLS wrapper. Monorepo structure with bundled submodules: + +``` +lua/ +├── java.lua # Main API entry point +├── java/ # Core plugin (startup, config, api/, runner/, ui/, utils/) +├── java-core/ # LSP integration (ls/servers/jdtls/, clients/, adapters/, utils/) +├── java-test/ # Test runner (ui/, results/, reports/) +├── java-dap/ # Debug adapter (api/, data/) +├── java-refactor/ # Refactoring tools (api/, client-commands/) +├── pkgm/ # Package management (downloaders/, extractors/, version control) +└── async/ # Custom async/await wrapper +plugin/java.lua # User command registration +``` + +**java-core Details:** +- Core JDTLS features implementation +- `ls/servers/jdtls/` - JDTLS config generator, loads extensions (java-test, java-debug, spring-boot, lombok). Uses mason.nvim APIs for extension paths +- `ls/clients/` - LSP request wrappers: + - `jdtls-client.lua` - Core JDTLS LSP calls + - `java-test-client.lua`, `java-debug-client.lua` - Extension-specific calls + - Purpose: wrap async APIs with coroutines for sync-style calls, add typing + - 1:1 mappings of VSCode projects for Neovim: + - vscode-java, vscode-java-test, vscode-java-debug + +**pkgm Details:** +- Package management utilities +- `downloaders/` - Download implementations (wget, etc.) +- `extractors/` - Archive extraction utilities +- Version control and package lifecycle management +- `specs/jdtls-spec/version-map.lua` - JDTLS version to timestamp mapping + +**Updating JDTLS Versions:** +1. Visit https://download.eclipse.org/jdtls/milestones/ +2. Click version link in 'Directory Contents' section +3. Find file: `jdt-language-server-X.Y.Z-YYYYMMDDHHSS.tar.gz` +4. Extract version (X.Y.Z) and timestamp (YYYYMMDDHHSS) +5. Add to `version-map.lua`: `['X.Y.Z'] = 'YYYYMMDDHHSS'` + +**Key Files:** +- `lua/java/config.lua` - Default configuration (JDTLS version, plugins, JDK) +- `lua/java-core/ls/servers/jdtls/config.lua` - JDTLS server configuration +- `lazy.lua` - lazy.nvim plugin spec with dependencies + +## Test Structure + +``` +tests/ +├── assets/ # Test fixtures and assets (e.g., HelloWorld.java) +├── constants/ # Test constants (e.g., capabilities.lua) +├── utils/ # Test utilities and config files +│ ├── lsp-utils.lua # LSP test helpers +│ ├── prepare-config.lua # Lazy.nvim test setup +│ └── test-config.lua # Manual test setup +└── specs/ # Test specifications + ├── lsp_spec.lua # All LSP-related tests + └── pkgm_spec.lua # All pkgm-related tests +``` + +**Test Guidelines:** +- Group related tests in single spec file (e.g., all pkgm tests in `pkgm_spec.lua`) +- Extract reusable logic to `utils/` to keep test steps clean +- Store test data/fixtures in `assets/` +- Store constants (capabilities, expected values) in `constants/` + +## Code Patterns + +**Event-driven registration:** Modules register on JDTLS attach via `event.on_jdtls_attach()` + +**Config merging:** `vim.tbl_deep_extend('force', global_config, user_config or {})` + +**Config type sync:** When modifying `lua/java/config.lua` (add/update/delete properties), update both `java.Config` type and `java.PartialConfig` in `lua/java.lua` to keep types in sync + +**Complex types:** If type contains complex object, create class type instead of inlining type everywhere + +**Async operations:** Uses custom async/await in `lua/async/` instead of raw coroutines + +**User commands:** Registered in `plugin/java.lua`, map to nested API in `lua/java/api/` + +**Class creation:** Use `java-core/utils/class` (Penlight class system): +```lua +local class = require('java-core.utils.class') + +local Base = class() +function Base:_init(name) + self.name = name +end + +local Child = class(Base) +function Child:_init(name, age) + self:super(name) + self.age = age +end +``` + +**Logging:** Use `java-core/utils/log2` for all logging: +```lua +local log = require('java-core.utils.log2') +log.trace('trace message') +log.debug('debug message') +log.info('info message') +log.warn('warning message') +log.error('error message') +log.fatal('fatal message') +``` + +## Code Style + +- Tabs for indentation (tab width: 2) +- Line width: 80 chars +- Single quotes preferred +- LuaDoc annotations (`---@`) for types +- Type naming: Prefix types with package name (e.g., `java.Config`, `java-debug.DebugConfig`) +- Neovim globals allowed: vim.o, vim.g, vim.wo, vim.bo, vim.opt, vim.lsp +- Private methods: Use `@private` annotation, NOT `_` prefix (except `_init` constructor) +- Method syntax: Always use `:` for class methods regardless of `self` usage + - All class methods: `function ClassName:method_name()` (with or without `self`) + +## Git Guidelines + +- Use conventional commit messages per [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +- Never append "generated by AI" message +- Split unrelated changes into separate commits diff --git a/Makefile b/Makefile index 2a8f9d4..d64073d 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,26 @@ SRC_DIR=lua -TESTS_DIR=tests -PREPARE_CONFIG=${TESTS_DIR}/prepare-config.lua -TEST_CONFIG=${TESTS_DIR}/test-config.lua +TESTS_ROOT=tests +TESTS_DIR?=${TESTS_ROOT}/specs +PREPARE_CONFIG=${TESTS_ROOT}/utils/prepare-config.lua +TEST_CONFIG=${TESTS_ROOT}/utils/test-config.lua +TEST_TIMEOUT?=60000 -.PHONY: test lint format all +.PHONY: test tests lint format all -all: lint format test +all: lint format tests + +tests: + @nvim \ + --headless \ + -u ${PREPARE_CONFIG} \ + "+PlenaryBustedDirectory ${TESTS_DIR} { minimal_init = '${TEST_CONFIG}', timeout = ${TEST_TIMEOUT}, sequential = true }" test: @nvim \ --headless \ -u ${PREPARE_CONFIG} \ - "+PlenaryBustedDirectory ${TESTS_DIR} { minimal_init = '${TEST_CONFIG}' }" + "+PlenaryBustedDirectory ${FILE} { minimal_init = '${TEST_CONFIG}', timeout = ${TEST_TIMEOUT} }" + lint: luacheck ${SRC_DIR} ${TESTS_DIR} diff --git a/README.md b/README.md index c540b14..16d1022 100644 --- a/README.md +++ b/README.md @@ -7,500 +7,4 @@ ![Neovim](https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white) ![Lua](https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white) -Just install and start writing `public static void main(String[] args)`. - -> [!CAUTION] -> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` before installing this - -> [!TIP] -> You can find cool tips & tricks here https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks - -> [!NOTE] -> If you are facing errors while using, please check troubleshoot wiki https://github.com/nvim-java/nvim-java/wiki/Troubleshooting - -## :loudspeaker: Demo - - - -## :dizzy: Features - -- :white_check_mark: Spring Boot Tools -- :white_check_mark: Diagnostics & Auto Completion -- :white_check_mark: Automatic Debug Configuration -- :white_check_mark: Organize Imports & Code Formatting -- :white_check_mark: Running Tests -- :white_check_mark: Run & Debug Profiles -- :white_check_mark: [Code Actions](https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks#running-code-actions) - -## :bulb: Why - -- Everything necessary will be installed automatically -- Uses [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) to setup `jdtls` -- Realtime server settings updates is possible using [neoconf](https://github.com/folke/neoconf.nvim) -- Auto loads necessary `jdtls` plugins - - Supported plugins are, - - `spring-boot-tools` - - `lombok` - - `java-test` - - `java-debug-adapter` - -## :hammer: How to Install - -
- -:small_orange_diamond:details - -### Starter Configs (Recommend for newbies) - -Following are forks of original repositories pre-configured for java. If you -don't know how to get started, use one of the following to get started. -You can click on **n commits ahead of** link to see the changes made on top of the original project - -- [LazyVim](https://github.com/nvim-java/starter-lazyvim) -- [Kickstart](https://github.com/nvim-java/starter-kickstart) -- [AstroNvim](https://github.com/nvim-java/starter-astronvim) - -### Custom Configuration Instructions - -- Install the plugin - -Using [lazy.nvim](https://github.com/folke/lazy.nvim) - -```lua -return {'nvim-java/nvim-java'} -``` - -- Setup nvim-java before `lspconfig` - -```lua -require('java').setup() -``` - -- Setup jdtls like you would usually do - -```lua -require('lspconfig').jdtls.setup({}) -``` - -Yep! That's all :) - -
- -## :keyboard: Commands - -
- -:small_orange_diamond:details - -### Build - -- `JavaBuildBuildWorkspace` - Runs a full workspace build - -- `JavaBuildCleanWorkspace` - Clear the workspace cache - (for now you have to close and reopen to restart the language server after - the deletion) - -### Runner - -- `JavaRunnerRunMain` - Runs the application or selected main class (if there - are multiple main classes) - -```vim -:JavaRunnerRunMain -:JavaRunnerRunMain -``` - -- `JavaRunnerStopMain` - Stops the running application -- `JavaRunnerToggleLogs` - Toggle between show & hide runner log window - -### DAP - -- `JavaDapConfig` - DAP is autoconfigured on start up, but in case you want to - force configure it again, you can use this API - -### Test - -- `JavaTestRunCurrentClass` - Run the test class in the active buffer -- `JavaTestDebugCurrentClass` - Debug the test class in the active buffer -- `JavaTestRunCurrentMethod` - Run the test method on the cursor -- `JavaTestDebugCurrentMethod` - Debug the test method on the cursor -- `JavaTestViewLastReport` - Open the last test report in a popup window - -### Profiles - -- `JavaProfile` - Opens the profiles UI - -### Refactor - -- `JavaRefactorExtractVariable` - Create a variable from value at cursor/selection -- `JavaRefactorExtractVariableAllOccurrence` - Create a variable for all - occurrences from value at cursor/selection -- `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection -- `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection -- `JavaRefactorExtractField` - Create a field from the value at cursor/selection - -### Settings - -- `JavaSettingsChangeRuntime` - Change the JDK version to another - -
- -## :computer: APIs - -
- -:small_orange_diamond:details - -### Build - -- `build.build_workspace` - Runs a full workspace build - -```lua -require('java').build.build_workspace() -``` - -- `build.clean_workspace` - Clear the workspace cache - (for now you have to close and reopen to restart the language server after - the deletion) - -```lua -require('java').build.clean_workspace() -``` - -### Runner - -- `built_in.run_app` - Runs the application or selected main class (if there - are multiple main classes) - -```lua -require('java').runner.built_in.run_app({}) -require('java').runner.built_in.run_app({'arguments', 'to', 'pass', 'to', 'main'}) -``` - -- `built_in.stop_app` - Stops the running application - -```lua -require('java').runner.built_in.stop_app() -``` - -- `built_in.toggle_logs` - Toggle between show & hide runner log window - -```lua -require('java').runner.built_in.toggle_logs() -``` - -### DAP - -- `config_dap` - DAP is autoconfigured on start up, but in case you want to force - configure it again, you can use this API - -```lua -require('java').dap.config_dap() -``` - -### Test - -- `run_current_class` - Run the test class in the active buffer - -```lua -require('java').test.run_current_class() -``` - -- `debug_current_class` - Debug the test class in the active buffer - -```lua -require('java').test.debug_current_class() -``` - -- `run_current_method` - Run the test method on the cursor - -```lua -require('java').test.run_current_method() -``` - -- `debug_current_method` - Debug the test method on the cursor - -```lua -require('java').test.debug_current_method() -``` - -- `view_report` - Open the last test report in a popup window - -```lua -require('java').test.view_last_report() -``` - -### Profiles - -```lua -require('java').profile.ui() -``` - -### Refactor - -- `extract_variable` - Create a variable from value at cursor/selection - -```lua -require('java').refactor.extract_variable() -``` - -- `extract_variable_all_occurrence` - Create a variable for all occurrences from - value at cursor/selection - -```lua -require('java').refactor.extract_variable_all_occurrence() -``` - -- `extract_constant` - Create a constant from the value at cursor/selection - -```lua -require('java').refactor.extract_constant() -``` - -- `extract_method` - Create method from the value at cursor/selection - -```lua -require('java').refactor.extract_method() -``` - -- `extract_field` - Create a field from the value at cursor/selection - -```lua -require('java').refactor.extract_field() -``` - -### Settings - -- `change_runtime` - Change the JDK version to another - -```lua -require('java').settings.change_runtime() -``` - -
- -## :clamp: How to Use JDK X.X Version? - -
- -:small_orange_diamond:details - -### Method 1 - -[Neoconf](https://github.com/folke/neoconf.nvim) can be used to manage LSP -setting including jdtls. Neoconf allows global configuration as well as project-wise -configurations. Here is how you can set Jdtls setting on `neoconf.json` - -```json -{ - "lspconfig": { - "jdtls": { - "java.configuration.runtimes": [ - { - "name": "JavaSE-21", - "path": "/opt/jdk-21", - "default": true - } - ] - } - } -} -``` - -### Method 2 - -Pass the settings to Jdtls setup. - -```lua -require('lspconfig').jdtls.setup({ - settings = { - java = { - configuration = { - runtimes = { - { - name = "JavaSE-21", - path = "/opt/jdk-21", - default = true, - } - } - } - } - } -}) -``` - -
- -## :wrench: Configuration - -
- -:small_orange_diamond:details - -For most users changing the default configuration is not necessary. But if you -want, following options are available - -```lua -{ - -- list of file that exists in root of the project - root_markers = { - 'settings.gradle', - 'settings.gradle.kts', - 'pom.xml', - 'build.gradle', - 'mvnw', - 'gradlew', - 'build.gradle', - 'build.gradle.kts', - '.git', - }, - - jdtls = { - version = 'v1.43.0', - }, - - lombok = { - version = 'nightly', - }, - - -- load java test plugins - java_test = { - enable = true, - version = '0.40.1', - }, - - -- load java debugger plugins - java_debug_adapter = { - enable = true, - version = '0.58.1', - }, - - spring_boot_tools = { - enable = true, - version = '1.55.1', - }, - - jdk = { - -- install jdk using mason.nvim - auto_install = true, - version = '17.0.2', - }, - - notifications = { - -- enable 'Configuring DAP' & 'DAP configured' messages on start up - dap = true, - }, - - -- We do multiple verifications to make sure things are in place to run this - -- plugin - verification = { - -- nvim-java checks for the order of execution of following - -- * require('java').setup() - -- * require('lspconfig').jdtls.setup() - -- IF they are not executed in the correct order, you will see a error - -- notification. - -- Set following to false to disable the notification if you know what you - -- are doing - invalid_order = true, - - -- nvim-java checks if the require('java').setup() is called multiple - -- times. - -- IF there are multiple setup calls are executed, an error will be shown - -- Set following property value to false to disable the notification if - -- you know what you are doing - duplicate_setup_calls = true, - - -- nvim-java checks if nvim-java/mason-registry is added correctly to - -- mason.nvim plugin. - -- IF it's not registered correctly, an error will be thrown and nvim-java - -- will stop setup - invalid_mason_registry = false, - }, - - mason = { - -- These mason registries will be prepended to the existing mason - -- configuration - registries = { - 'github:nvim-java/mason-registry', - }, - }, -} - -``` - -
- -## :golf: Architecture - -
- -:small_orange_diamond:details - -Following is the high level idea. Jdtls is the language server nvim-java -communicates with. However, we don't have all the features we need just in -Jdtls. So, we are loading java-test & java-debug-adapter extensions when we -launch Jdtls. Once the language server is started, we communicate with the -language server to do stuff. - -For instance, to run the current test, - -- Request Jdtls for test classes -- Request Jdtls for class paths, module paths, java executable -- Request Jdtls to start a debug session and send the port of the session back -- Prepare TCP connections to listen to the test results -- Start nvim-dap and let user interactions to be handled by nvim-dap -- Parse the test results as they come in -- Once the execution is done, open a window show the test results - -```text - ┌────────────┐ ┌────────────┐ - │ │ │ │ - │ Neovim │ │ VSCode │ - │ │ │ │ - └─────▲──────┘ └──────▲─────┘ - │ │ - │ │ - │ │ - │ │ -┌───────▼───────┐ ┌──────────────▼──────────────┐ -│ │ │ │ -│ nvim-java │ │ Extension Pack for Java │ -│ │ │ │ -└───────▲───────┘ └──────────────▲──────────────┘ - │ │ - │ │ - │ │ - │ │ - │ │ - │ ┌───────────┐ │ - │ │ │ │ - └──────────────► JDTLS ◄────────────┘ - │ │ - └───▲───▲───┘ - │ │ - │ │ - │ │ - │ │ - │ │ - ┌───────────────┐ │ │ ┌────────────────────────┐ - │ │ │ │ │ │ - │ java-test ◄────────┘ └─────────► java-debug-adapter │ - │ │ │ │ - └───────────────┘ └────────────────────────┘ -``` - -
- -## :bookmark_tabs: Projects Acknowledgement - -- [spring-boot.nvim](https://github.com/JavaHello/spring-boot.nvim) is the one - that starts sts4 & do other necessary `jdtls` `sts4` sync command registration - in `nvim-java`. - -- [nvim-jdtls](https://github.com/mfussenegger/nvim-jdtls) is a plugin that follows - "Keep it simple, stupid!" approach. If you love customizing things by yourself, - then give nvim-jdtls a try. - -> [!WARNING] -> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` -> before installing this +monorepo is a complete rewrite. Do not use this plugin. This will be released as nvim-java 4.0.0 in the original repo soon. diff --git a/doc/development.md b/doc/development.md new file mode 100644 index 0000000..60ac94b --- /dev/null +++ b/doc/development.md @@ -0,0 +1,51 @@ +# Development Environment Setup + +## Prerequisites + +- Docker +- devcontainer CLI (`npm install -g @devcontainers/cli`) + +## Getting Started + +Build and start devcontainer: + +```bash +devcontainer up --workspace-folder . +devcontainer exec --workspace-folder . bash +``` + +The devcontainer includes: +- Java (via devcontainer feature) +- Neovim nightly +- Python +- Spring Boot CLI (via SDKMAN) +- wget + +Neovim config auto-links from `.devcontainer/config/nvim` to `~/.config/nvim` + +## Build Commands + +```bash +make tests # Run Plenary/Busted tests (headless Neovim) +make test FILE=path # Run specific test file +make lint # Run luacheck linter +make format # Format with stylua +make all # lint -> format -> tests +``` + +## Creating Test Projects + +### Spring Boot Project + +Create Spring Boot project inside devcontainer: + +```bash +spring init -d web,lombok --extract demo +``` + +This creates `demo/` with Web and Lombok dependencies. + +Options: +- `-d` dependencies (comma-separated) +- `--extract` extract to directory (default: creates zip) +- See `spring help init` for more options diff --git a/doc/server-capabilities.md b/doc/server-capabilities.md new file mode 100644 index 0000000..688e699 --- /dev/null +++ b/doc/server-capabilities.md @@ -0,0 +1,164 @@ +```lua +{ + callHierarchyProvider = true, + codeActionProvider = { + resolveProvider = true + }, + codeLensProvider = { + resolveProvider = true + }, + completionProvider = { + completionItem = { + labelDetailsSupport = true + }, + resolveProvider = true, + triggerCharacters = { ".", "@", "#", "*", " " } + }, + definitionProvider = true, + documentFormattingProvider = true, + documentHighlightProvider = true, + documentOnTypeFormattingProvider = { + firstTriggerCharacter = ";", + moreTriggerCharacter = { "\n", "}" } + }, + documentRangeFormattingProvider = true, + documentSymbolProvider = true, + executeCommandProvider = { + commands = { + "java.completion.onDidSelect", + "java.decompile", + "java.edit.handlePasteEvent", + "java.edit.organizeImports", + "java.edit.smartSemicolonDetection", + "java.edit.stringFormatting", + "java.navigate.openTypeHierarchy", + "java.navigate.resolveTypeHierarchy", + "java.project.addToSourcePath", + "java.project.createModuleInfo", + "java.project.getAll", + "java.project.getClasspaths", + "java.project.getSettings", + "java.project.import", + "java.project.isTestFile", + "java.project.listSourcePaths", + "java.project.refreshDiagnostics", + "java.project.removeFromSourcePath", + "java.project.resolveSourceAttachment", + "java.project.resolveStackTraceLocation", + "java.project.resolveWorkspaceSymbol", + "java.project.updateSourceAttachment", + "java.project.upgradeGradle", + "java.protobuf.generateSources", + "java.reloadBundles", + "vscode.java.buildWorkspace", + "vscode.java.checkProjectSettings", + "vscode.java.fetchPlatformSettings", + "vscode.java.fetchUsageData", + "vscode.java.inferLaunchCommandLength", + "vscode.java.isOnClasspath", + "vscode.java.resolveBuildFiles", + "vscode.java.resolveClassFilters", + "vscode.java.resolveClasspath", + "vscode.java.resolveElementAtSelection", + "vscode.java.resolveInlineVariables", + "vscode.java.resolveJavaExecutable", + "vscode.java.resolveMainClass", + "vscode.java.resolveMainMethod", + "vscode.java.resolveSourceUri", + "vscode.java.startDebugSession", + "vscode.java.test.findDirectTestChildrenForClass", + "vscode.java.test.findJavaProjects", + "vscode.java.test.findTestLocation", + "vscode.java.test.findTestPackagesAndTypes", + "vscode.java.test.findTestTypesAndMethods", + "vscode.java.test.generateTests", + "vscode.java.test.get.testpath", + "vscode.java.test.junit.argument", + "vscode.java.test.navigateToTestOrTarget", + "vscode.java.test.resolvePath", + "vscode.java.updateDebugSettings", + "vscode.java.validateLaunchConfig", + } + }, + foldingRangeProvider = true, + hoverProvider = true, + implementationProvider = true, + inlayHintProvider = true, + referencesProvider = true, + renameProvider = { + prepareProvider = true + }, + selectionRangeProvider = true, + semanticTokensProvider = { + documentSelector = { { + language = "java", + scheme = "file" + }, { + language = "java", + scheme = "jdt" + } }, + full = { + delta = false + }, + legend = { + tokenModifiers = { + "abstract", + "constructor", + "declaration", + "deprecated", + "documentation", + "generic", + "importDeclaration", + "native", + "private", + "protected", + "public", + "readonly", + "static", + "typeArgument", + }, + tokenTypes = { + "annotation", + "annotationMember", + "class", + "enum", + "enumMember", + "interface", + "keyword", + "method", + "modifier", + "namespace", + "parameter", + "property", + "record", + "recordComponent", + "type", + "typeParameter", + "variable", + } + }, + range = false + }, + signatureHelpProvider = { + triggerCharacters = { "(", "," } + }, + textDocumentSync = { + change = 2, + openClose = true, + save = { + includeText = true + }, + willSave = true, + willSaveWaitUntil = true + }, + typeDefinitionProvider = true, + typeHierarchyProvider = true, + workspace = { + workspaceFolders = { + changeNotifications = true, + supported = true + } + }, + workspaceSymbolProvider = true +} +``` diff --git a/doc/ts-to-lua-guide.md b/doc/ts-to-lua-guide.md new file mode 100644 index 0000000..cf46257 --- /dev/null +++ b/doc/ts-to-lua-guide.md @@ -0,0 +1,35 @@ +# Personal Notes + +## Communication + +We are using `request` function of `vim.lsp.Client` function to communicate with +the `jdtls`. + +```lua +fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) +``` + +This has almost 1 to 1 mapping with `vscode` APIs most of the time. + +```typescript +await this.languageClient.sendRequest( + method: string, + params: any, + // handler is not passed since there is async / await + // buffer I'm guessing is set to current buffer by default??? +); +``` + +However, some APIs sends more arguments, to which we don't have a Neovim lua +equivalent I'm guessing. Following is an example. + +```typescript +await this.languageClient.sendRequest( + CompileWorkspaceRequest.type, + isFullCompile, + token, +); +``` + +To make this request, probably `client.rpc.request` should be used without +`request()` wrapper. diff --git a/lazy.lua b/lazy.lua index 03ef45a..5e21aec 100644 --- a/lazy.lua +++ b/lazy.lua @@ -1,26 +1,11 @@ return { 'nvim-java/nvim-java', dependencies = { - 'nvim-java/lua-async-await', - 'nvim-java/nvim-java-refactor', - 'nvim-java/nvim-java-core', - 'nvim-java/nvim-java-test', - 'nvim-java/nvim-java-dap', 'MunifTanjim/nui.nvim', - 'neovim/nvim-lspconfig', 'mfussenegger/nvim-dap', { 'JavaHello/spring-boot.nvim', commit = '218c0c26c14d99feca778e4d13f5ec3e8b1b60f0', }, - { - 'mason-org/mason.nvim', - -- opts = { - -- registries = { - -- 'github:nvim-java/mason-registry', - -- 'github:mason-org/mason-registry', - -- }, - -- }, - }, }, } diff --git a/lua/async/runner.lua b/lua/async/runner.lua new file mode 100644 index 0000000..042e1ee --- /dev/null +++ b/lua/async/runner.lua @@ -0,0 +1,63 @@ +local wrap = require('async.wrap') +local co = coroutine + +---Runs the given function +---@param func fun(): any +---@return { run: function, catch: fun(error_handler: fun(error: any)) } +local function runner(func) + local m = { + error_handler = nil, + } + + local async_thunk_factory = wrap(function(handler, parent_handler_callback) + assert(type(handler) == 'function', 'type error :: expected func') + local thread = co.create(handler) + local step = nil + + step = function(...) + local ok, thunk = co.resume(thread, ...) + + -- when an error() is thrown after co-routine is resumed, obviously further + -- processing stops, and resume returns ok(false) and thunk(error) returns + -- the error message + if not ok then + if m.error_handler then + m.error_handler(thunk) + return + end + + if parent_handler_callback then + parent_handler_callback(thunk) + return + end + + error('unhandled error ' .. thunk) + end + + assert(ok, thunk) + if co.status(thread) == 'dead' then + if parent_handler_callback then + parent_handler_callback(thunk) + end + else + assert(type(thunk) == 'function', 'type error :: expected func') + thunk(step) + end + end + + step() + + return m + end) + + m.run = async_thunk_factory(func) + + m.catch = function(error_handler) + m.error_handler = error_handler + return m + end + + return m +end + +return runner diff --git a/lua/async/waits/wait.lua b/lua/async/waits/wait.lua new file mode 100644 index 0000000..b3a4e88 --- /dev/null +++ b/lua/async/waits/wait.lua @@ -0,0 +1,12 @@ +local co = coroutine + +---Waits for async function to be completed +---@generic T +---@param defer fun(callback: fun(result: T)) +---@return T +local function wait(defer) + assert(type(defer) == 'function', 'type error :: expected func') + return co.yield(defer) +end + +return wait diff --git a/lua/async/waits/wait_all.lua b/lua/async/waits/wait_all.lua new file mode 100644 index 0000000..780ce06 --- /dev/null +++ b/lua/async/waits/wait_all.lua @@ -0,0 +1,35 @@ +local co = coroutine + +local function __join(thunks) + local len = #thunks + local done = 0 + local acc = {} + + local thunk = function(step) + if len == 0 then + return step() + end + for i, tk in ipairs(thunks) do + assert(type(tk) == 'function', 'thunk must be function') + local callback = function(...) + acc[i] = { ... } + done = done + 1 + if done == len then + step(unpack(acc)) + end + end + tk(callback) + end + end + return thunk +end + +---Waits for list of async calls to be completed +---@param defer fun(callback: fun(result: any)) +---@return any[] +local function wait_all(defer) + assert(type(defer) == 'table', 'type error :: expected table') + return co.yield(__join(defer)) +end + +return wait_all diff --git a/lua/async/waits/wait_with_error_handler.lua b/lua/async/waits/wait_with_error_handler.lua new file mode 100644 index 0000000..b6e8e43 --- /dev/null +++ b/lua/async/waits/wait_with_error_handler.lua @@ -0,0 +1,20 @@ +local co = coroutine + +---Waits for async function to be completed but considers first parameter as +---error +---@generic T +---@param defer fun(callback: fun(result: T)) +---@return T +local function wait(defer) + assert(type(defer) == 'function', 'type error :: expected func') + + local err, value = co.yield(defer) + + if err then + error(err) + end + + return value +end + +return wait diff --git a/lua/async/wrap.lua b/lua/async/wrap.lua new file mode 100644 index 0000000..f083db3 --- /dev/null +++ b/lua/async/wrap.lua @@ -0,0 +1,20 @@ +---Make the given function a promise automatically assuming the callback +---function is the last argument +---@generic T +---@param func fun(..., callback: fun(result: T)): any +---@return fun(...): T +local function wrap(func) + assert(type(func) == 'function', 'type error :: expected func') + + local factory = function(...) + local params = { ... } + local thunk = function(step) + table.insert(params, step) + return func(unpack(params)) + end + return thunk + end + return factory +end + +return wrap diff --git a/lua/java-core/constants/java_version.lua b/lua/java-core/constants/java_version.lua new file mode 100644 index 0000000..4dc8c05 --- /dev/null +++ b/lua/java-core/constants/java_version.lua @@ -0,0 +1,14 @@ +return { + ['1.43.0'] = { from = 17, to = 21 }, + ['1.44.0'] = { from = 17, to = 21 }, + ['1.45.0'] = { from = 21, to = 21 }, + ['1.46.0'] = { from = 21, to = 21 }, + ['1.46.1'] = { from = 21, to = 21 }, + ['1.47.0'] = { from = 21, to = 21 }, + ['1.48.0'] = { from = 21, to = 21 }, + ['1.49.0'] = { from = 21, to = 21 }, + ['1.50.0'] = { from = 21, to = 21 }, + ['1.51.0'] = { from = 21, to = 21 }, + ['1.52.0'] = { from = 21, to = 21 }, + ['1.53.0'] = { from = 21, to = 21 }, +} diff --git a/lua/java-core/ls/clients/java-debug-client.lua b/lua/java-core/ls/clients/java-debug-client.lua new file mode 100644 index 0000000..d3248ff --- /dev/null +++ b/lua/java-core/ls/clients/java-debug-client.lua @@ -0,0 +1,88 @@ +local class = require('java-core.utils.class') + +local JdtlsClient = require('java-core.ls.clients.jdtls-client') + +---@class java-core.DebugClient: java-core.JdtlsClient +local DebugClient = class(JdtlsClient) + +---@class java-dap.JavaDebugResolveMainClassRecord +---@field mainClass string +---@field projectName string +---@field fileName string + +---Returns a list of main classes in the current workspace +---@return java-dap.JavaDebugResolveMainClassRecord[] # resolved main class +function DebugClient:resolve_main_class() + --- @type java-dap.JavaDebugResolveMainClassRecord[] + return self:workspace_execute_command('vscode.java.resolveMainClass') +end + +---Returns module paths and class paths of a given main class +---@param project_name string +---@param main_class string +---@return string[][] # resolved class and module paths +function DebugClient:resolve_classpath(main_class, project_name) + ---@type string[][] + return self:workspace_execute_command('vscode.java.resolveClasspath', { main_class, project_name }) +end + +---Returns the path to java executable for a given main class +---@param project_name string +---@param main_class string +---@return string # path to java executable +function DebugClient:resolve_java_executable(main_class, project_name) + ---@type string + return self:workspace_execute_command('vscode.java.resolveJavaExecutable', { + main_class, + project_name, + }) +end + +---Returns true if the project settings is the expected +---@param project_name string +---@param main_class string +---@param inheritedOptions boolean +---@param expectedOptions { [string]: any } +---@return boolean # true if the setting is the expected setting +function DebugClient:check_project_settings(main_class, project_name, inheritedOptions, expectedOptions) + ---@type boolean + return self:workspace_execute_command( + 'vscode.java.checkProjectSettings', + ---@diagnostic disable-next-line: param-type-mismatch + vim.fn.json_encode({ + className = main_class, + projectName = project_name, + inheritedOptions = inheritedOptions, + expectedOptions = expectedOptions, + }) + ) +end + +---Starts a debug session and returns the port number +---@return integer # port number of the debug session +function DebugClient:start_debug_session() + ---@type integer + return self:workspace_execute_command('vscode.java.startDebugSession') +end + +---Build the workspace +---@param main_class string +---@param project_name? string +---@param file_path? string +---@param is_full_build boolean +---@return java-core.CompileWorkspaceStatus # compiled status +function DebugClient:build_workspace(main_class, project_name, file_path, is_full_build) + ---@type java-core.CompileWorkspaceStatus + return self:workspace_execute_command( + 'vscode.java.buildWorkspace', + ---@diagnostic disable-next-line: param-type-mismatch + vim.fn.json_encode({ + mainClass = main_class, + projectName = project_name, + filePath = file_path, + isFullBuild = is_full_build, + }) + ) +end + +return DebugClient diff --git a/lua/java-core/ls/clients/java-test-client.lua b/lua/java-core/ls/clients/java-test-client.lua new file mode 100644 index 0000000..ceefee8 --- /dev/null +++ b/lua/java-core/ls/clients/java-test-client.lua @@ -0,0 +1,112 @@ +local log = require('java-core.utils.log2') +local class = require('java-core.utils.class') + +local JdtlsClient = require('java-core.ls.clients.jdtls-client') + +---@class java-core.TestDetails +---@field fullName string +---@field id string +---@field jdtHandler string +---@field label string +---@field projectName string +---@field testKind integer +---@field testLevel integer +---@field uri string + +---@class java-core.TestDetailsWithRange: java-core.TestDetails +---@field range lsp.Range + +---@class java-core.TestDetailsWithChildren: java-core.TestDetails +---@field children java-core.TestDetailsWithRange[] + +---@class java-core.TestDetailsWithChildrenAndRange: java-core.TestDetails +---@field range lsp.Range +---@field children java-core.TestDetailsWithRange[] + +---@class java-core.TestClient: java-core.JdtlsClient +local TestClient = class(JdtlsClient) + +---Returns a list of project details in the current root +---@return java-core.TestDetails[] # test details of the projects +function TestClient:find_java_projects() + ---@type java-core.TestDetails[] + return self:workspace_execute_command( + 'vscode.java.test.findJavaProjects', + { vim.uri_from_fname(self.client.config.root_dir) } + ) +end + +---Returns a list of test package details +---@param handler string +---@param token? string +---@return java-core.TestDetailsWithChildren[] # test package details +function TestClient:find_test_packages_and_types(handler, token) + ---@type java-core.TestDetailsWithChildren[] + return self:workspace_execute_command('vscode.java.test.findTestPackagesAndTypes', { handler, token }) +end + +---Returns test informations in a given file +---@param file_uri string +---@param token? string +---@return java-core.TestDetailsWithChildrenAndRange[] # test details +function TestClient:find_test_types_and_methods(file_uri, token) + ---@type java-core.TestDetailsWithChildrenAndRange[] + return self:workspace_execute_command('vscode.java.test.findTestTypesAndMethods', { file_uri, token }) +end + +---@class java-core.JavaCoreTestJunitLaunchArguments +---@field classpath string[] +---@field mainClass string +---@field modulepath string[] +---@field programArguments string[] +---@field projectName string +---@field vmArguments string[] +---@field workingDirectory string + +---@class java-core.JavaCoreTestResolveJUnitLaunchArgumentsParams +---@field project_name string +---@field test_names string[] +---@field test_level java-core.TestLevel +---@field test_kind java-core.TestKind + +---Returns junit launch arguments +---@param args java-core.JavaCoreTestResolveJUnitLaunchArgumentsParams +---@return java-core.JavaCoreTestJunitLaunchArguments # junit launch arguments +function TestClient:resolve_junit_launch_arguments(args) + local launch_args = self:workspace_execute_command( + 'vscode.java.test.junit.argument', + ---@diagnostic disable-next-line: param-type-mismatch + vim.fn.json_encode(args) + ) + + if not launch_args or not launch_args.body then + local msg = 'Failed to retrieve JUnit launch arguments' + + log.error(msg, launch_args) + error(msg) + end + + return launch_args.body +end + +---@enum java-core.TestKind +TestClient.TestKind = { + JUnit5 = 0, + JUnit = 1, + TestNG = 2, + None = 100, +} + +---@enum java-core.TestLevel +TestClient.TestLevel = { + Root = 0, + Workspace = 1, + WorkspaceFolder = 2, + Project = 3, + Package = 4, + Class = 5, + Method = 6, + Invocation = 7, +} + +return TestClient diff --git a/lua/java-core/ls/clients/jdtls-client.lua b/lua/java-core/ls/clients/jdtls-client.lua new file mode 100644 index 0000000..6a33eac --- /dev/null +++ b/lua/java-core/ls/clients/jdtls-client.lua @@ -0,0 +1,385 @@ +local log = require('java-core.utils.log2') +local class = require('java-core.utils.class') +local await = require('async.waits.wait_with_error_handler') + +---@alias java-core.JdtlsRequestMethod +---| 'workspace/executeCommand' +---| 'java/inferSelection' +---| 'java/getRefactorEdit' +---| 'java/buildWorkspace' +---| 'java/checkConstructorsStatus' +---| 'java/generateConstructors' +---| 'java/checkToStringStatus' +---| 'java/generateToString' +---| 'java/checkHashCodeEqualsStatus' +---| 'java/generateHashCodeEquals' +---| 'java/checkDelegateMethodsStatus' +---| 'java/generateDelegateMethods' +---| 'java/move' +---| 'java/searchSymbols' +---| 'java/getMoveDestinations' +---| 'java/listOverridableMethods' +---| 'java/addOverridableMethods' + +---@alias jdtls.CodeActionCommand +---| 'extractVariable' +---| 'assignVariable' +---| 'extractVariableAllOccurrence' +---| 'extractConstant' +---| 'extractMethod' +---| 'extractField' +---| 'extractInterface' +---| 'changeSignature' +---| 'assignField' +---| 'convertVariableToField' +---| 'invertVariable' +---| 'introduceParameter' +---| 'convertAnonymousClassToNestedCommand' + +---@class jdtls.RefactorWorkspaceEdit +---@field edit lsp.WorkspaceEdit +---@field command? lsp.Command +---@field errorMessage? string + +---@class jdtls.SelectionInfo +---@field name string +---@field length number +---@field offset number +---@field params? string[] + +---@class java-core.JdtlsClient +---@field client vim.lsp.Client +local JdtlsClient = class() + +function JdtlsClient:_init(client) + self.client = client +end + +---Sends a LSP request +---@param method java-core.JdtlsRequestMethod +---@param params table +---@param buffer? number +function JdtlsClient:request(method, params, buffer) + log.debug('sending LSP request: ' .. method) + + return await(function(callback) + local on_response = function(err, result) + if err then + log.error(method .. ' failed! arguments: ', params, ' error: ', err) + else + log.debug(method .. ' success! response: ', result) + end + + callback(err, result) + end + + ---@diagnostic disable-next-line: param-type-mismatch + return self.client:request(method, params, on_response, buffer) + end) +end + +--- Sends a notification to an LSP server. +--- +--- @param method vim.lsp.protocol.Method.ClientToServer.Notification LSP method name. +--- @param params table? LSP request params. +--- @return boolean status indicating if the notification was successful. +--- If it is false, then the client has shutdown. +function JdtlsClient:notify(method, params) + log.debug('sending LSP notify: ' .. method) + return self.client:notify(method, params) +end + +---Executes a workspace/executeCommand and returns the result +---@param command string workspace command to execute +---@param arguments? lsp.LSPAny[] +---@param buffer? integer +---@return lsp.LSPAny +function JdtlsClient:workspace_execute_command(command, arguments, buffer) + return await(function(callback) + self.client:exec_cmd( + ---@diagnostic disable-next-line: missing-fields + { command = command, arguments = arguments }, + { bufnr = buffer }, + callback + ) + end) +end + +---@class jdtls.ResourceMoveDestination +---@field displayName string +---@field isDefaultPackage boolean +---@field isParentOfSelectedFile boolean +---@field path string +---@field project string +---@field uri string + +---@class jdtls.InstanceMethodMoveDestination +---@field bindingKey string +---@field isField boolean +---@field isSelected boolean +---@field name string +---@field type string + +---@class jdtls.listOverridableMethodsResponse +---@field methods jdtls.OverridableMethod[] +---@field type string + +---@class jdtls.OverridableMethod +---@field key string +---@field bindingKey string +---@field declaringClass string +---@field declaringClassType string +---@field name string +---@field parameters string[] +---@field unimplemented boolean + +---@class jdtls.MoveDestinationsResponse +---@field errorMessage? string +---@field destinations jdtls.InstanceMethodMoveDestination[]|jdtls.ResourceMoveDestination[] + +---@class jdtls.ImportCandidate +---@field fullyQualifiedName string +---@field id string + +---@class jdtls.ImportSelection +---@field candidates jdtls.ImportCandidate[] +---@field range Range + +---@param params jdtls.MoveParams +---@return jdtls.MoveDestinationsResponse +function JdtlsClient:get_move_destination(params) + return self:request('java/getMoveDestinations', params) +end + +---@class jdtls.MoveParams +---@field moveKind string +---@field sourceUris string[] +---@field params lsp.CodeActionParams | nil +---@field destination? any +---@field updateReferences? boolean + +---@param params jdtls.MoveParams +---@return jdtls.RefactorWorkspaceEdit +function JdtlsClient:java_move(params) + return self:request('java/move', params) +end + +---@class jdtls.SearchSymbolParams: lsp.WorkspaceSymbolParams +---@field projectName string +---@field maxResults? number +---@field sourceOnly? boolean + +---@param params jdtls.SearchSymbolParams +---@return lsp.SymbolInformation +function JdtlsClient:java_search_symbols(params) + return self:request('java/searchSymbols', params) +end + +---Returns more information about the object the cursor is on +---@param command jdtls.CodeActionCommand +---@param params lsp.CodeActionParams +---@param buffer? number +---@return jdtls.SelectionInfo[] +function JdtlsClient:java_infer_selection(command, params, buffer) + return self:request('java/inferSelection', { + command = command, + context = params, + }, buffer) +end + +--- @class jdtls.VariableBinding +--- @field bindingKey string +--- @field name string +--- @field type string +--- @field isField boolean +--- @field isSelected? boolean + +---@class jdtls.MethodBinding +---@field bindingKey string; +---@field name string; +---@field parameters string[]; + +---@class jdtls.JavaCheckConstructorsStatusResponse +---@field constructors jdtls.MethodBinding +---@field fields jdtls.MethodBinding + +---@param params lsp.CodeActionParams +---@return jdtls.JavaCheckConstructorsStatusResponse +function JdtlsClient:java_check_constructors_status(params) + return self:request('java/checkConstructorsStatus', params) +end + +---@param params jdtls.GenerateConstructorsParams +---@return lsp.WorkspaceEdit +function JdtlsClient:java_generate_constructor(params) + return self:request('java/generateConstructors', params) +end + +---@class jdtls.CheckToStringResponse +---@field type string +---@field fields jdtls.VariableBinding[] +---@field exists boolean + +---@param params lsp.CodeActionParams +---@return jdtls.CheckToStringResponse +function JdtlsClient:java_check_to_string_status(params) + return self:request('java/checkToStringStatus', params) +end + +---@class jdtls.GenerateToStringParams +---@field context lsp.CodeActionParams +---@field fields jdtls.VariableBinding[] + +---@param params jdtls.GenerateToStringParams +---@return lsp.WorkspaceEdit +function JdtlsClient:java_generate_to_string(params) + return self:request('java/generateToString', params) +end + +---@class jdtls.CheckHashCodeEqualsResponse +---@field type string +---@field fields jdtls.VariableBinding[] +---@field existingMethods string[] + +---@param params lsp.CodeActionParams +---@return jdtls.CheckHashCodeEqualsResponse +function JdtlsClient:java_check_hash_code_equals_status(params) + return self:request('java/checkHashCodeEqualsStatus', params) +end + +---@class jdtls.GenerateHashCodeEqualsParams +---@field context lsp.CodeActionParams +---@field fields jdtls.VariableBinding[] +---@field regenerate boolean + +---@param params jdtls.GenerateHashCodeEqualsParams +---@return lsp.WorkspaceEdit +function JdtlsClient:java_generate_hash_code_equals(params) + return self:request('java/generateHashCodeEquals', params) +end + +---@class jdtls.DelegateField +---@field field jdtls.VariableBinding +---@field delegateMethods jdtls.MethodBinding[] + +---@class jdtls.CheckDelegateMethodsResponse +---@field delegateFields jdtls.DelegateField[] + +---@param params lsp.CodeActionParams +---@return jdtls.CheckDelegateMethodsResponse +function JdtlsClient:java_check_delegate_methods_status(params) + return self:request('java/checkDelegateMethodsStatus', params) +end + +---@class jdtls.DelegateEntry +---@field field jdtls.VariableBinding +---@field delegateMethod jdtls.MethodBinding + +---@class jdtls.GenerateDelegateMethodsParams +---@field context lsp.CodeActionParams +---@field delegateEntries jdtls.DelegateEntry[] + +---@param params jdtls.GenerateDelegateMethodsParams +---@return lsp.WorkspaceEdit +function JdtlsClient:java_generate_delegate_methods(params) + return self:request('java/generateDelegateMethods', params) +end + +---@class jdtls.GenerateConstructorsParams +---@field context lsp.CodeActionParams +---@field constructors jdtls.MethodBinding[] +---@field fields jdtls.VariableBinding[] + +---Returns refactor details +---@param command jdtls.CodeActionCommand +---@param action_params lsp.CodeActionParams +---@param formatting_options lsp.FormattingOptions +---@param selection_info jdtls.SelectionInfo[]; +---@param buffer? number +---@return jdtls.RefactorWorkspaceEdit +function JdtlsClient:java_get_refactor_edit(command, action_params, formatting_options, selection_info, buffer) + local params = { + command = command, + context = action_params, + options = formatting_options, + commandArguments = selection_info, + } + + return self:request('java/getRefactorEdit', params, buffer) +end + +---Returns a list of methods that can be overridden +---@param params lsp.CodeActionParams +---@param buffer? number +---@return jdtls.listOverridableMethodsResponse +function JdtlsClient:list_overridable_methods(params, buffer) + return self:request('java/listOverridableMethods', params, buffer) +end + +---Returns a list of methods that can be overridden +---@param context lsp.CodeActionParams +---@param overridable_methods jdtls.OverridableMethod[] +---@param buffer? number +---@return lsp.WorkspaceEdit +function JdtlsClient:add_overridable_methods(context, overridable_methods, buffer) + return self:request('java/addOverridableMethods', { + context = context, + overridableMethods = overridable_methods, + }, buffer) +end + +---Compile the workspace +---@param is_full_compile boolean if true, a complete full compile of the +---workspace will be executed +---@param buffer number +---@return java-core.CompileWorkspaceStatus +function JdtlsClient:java_build_workspace(is_full_compile, buffer) + ---@diagnostic disable-next-line: param-type-mismatch + return self:request('java/buildWorkspace', is_full_compile, buffer) +end + +---Returns the decompiled class file content +---@param uri string uri of the class file +---@return string # decompiled file content +function JdtlsClient:java_decompile(uri) + ---@type string + return self:workspace_execute_command('java.decompile', { uri }) +end + +function JdtlsClient:get_capability(...) + local capability = self.client.server_capabilities + + for _, value in ipairs({ ... }) do + if type(capability) ~= 'table' then + log.fmt_warn('Looking for capability: %s in value %s', value, capability) + return nil + end + + capability = capability[value] + end + + return capability +end + +---Updates JDTLS settings via workspace/didChangeConfiguration +---@param settings JavaConfigurationSettings +---@return boolean +function JdtlsClient:workspace_did_change_configuration(settings) + local params = { settings = settings } + return self:notify('workspace/didChangeConfiguration', params) +end + +---Returns true if the LS supports the given command +---@param command_name string name of the command +---@return boolean # true if the command is supported +function JdtlsClient:has_command(command_name) + local commands = self:get_capability('executeCommandProvider', 'commands') + + if not commands then + return false + end + + return vim.tbl_contains(commands, command_name) +end + +return JdtlsClient diff --git a/lua/java-core/ls/servers/jdtls/cmd.lua b/lua/java-core/ls/servers/jdtls/cmd.lua new file mode 100644 index 0000000..371c358 --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/cmd.lua @@ -0,0 +1,133 @@ +local List = require('java-core.utils.list') +local path = require('java-core.utils.path') +local Manager = require('pkgm.manager') +local conf = require('java.config') +local system = require('java-core.utils.system') +local log = require('java-core.utils.log2') +local err = require('java-core.utils.errors') +local java_version_map = require('java-core.constants.java_version') +local lsp_utils = require('java-core.utils.lsp') + +local M = {} + +local jdtls_root = Manager:get_install_dir('jdtls', conf.jdtls.version) + +--- Returns a function that returns the command to start jdtls +---@param opts { use_lombok: boolean } +function M.get_cmd(opts) + ---@param dispatchers? vim.lsp.rpc.Dispatchers + ---@param config vim.lsp.ClientConfig + return function(dispatchers, config) + local cmd = M.get_jvm_args(opts):concat(M.get_jar_args()) + + M.validate_java_version() + + log.debug('Starting jdtls with cmd', cmd) + + local result = vim.lsp.rpc.start(cmd, dispatchers, { + cwd = config.cmd_cwd, + env = config.cmd_env, + detached = config.detached, + }) + + return result + end +end + +---@private +---@param opts { use_lombok: boolean } +---@return java-core.List +function M.get_jvm_args(opts) + local jdtls_config = path.join(jdtls_root, system.get_config_suffix()) + + local jvm_args = List:new({ + 'java', + '-Declipse.application=org.eclipse.jdt.ls.core.id1', + '-Dosgi.bundles.defaultStartLevel=4', + '-Declipse.product=org.eclipse.jdt.ls.core.product', + '-Dosgi.checkConfiguration=true', + '-Dosgi.sharedConfiguration.area=' .. jdtls_config, + '-Dosgi.sharedConfiguration.area.readOnly=true', + '-Dosgi.configuration.cascaded=true', + '-Xms1G', + '--add-modules=ALL-SYSTEM', + + '--add-opens', + 'java.base/java.util=ALL-UNNAMED', + + '--add-opens', + 'java.base/java.lang=ALL-UNNAMED', + }) + + -- Adding lombok + if opts.use_lombok then + local lombok_root = Manager:get_install_dir('lombok', conf.lombok.version) + local lombok_path = vim.fn.glob(path.join(lombok_root, 'lombok*.jar')) + jvm_args:push('-javaagent:' .. lombok_path) + end + + return jvm_args +end + +---@private +---@param cwd? string +---@return java-core.List +function M.get_jar_args(cwd) + cwd = cwd or vim.fn.getcwd() + + local launcher_reg = path.join(jdtls_root, 'plugins', 'org.eclipse.equinox.launcher_*.jar') + local equinox_launcher = vim.fn.glob(path.join(jdtls_root, 'plugins', 'org.eclipse.equinox.launcher_*.jar')) + + if equinox_launcher == '' then + -- stylua: ignore + local msg = string.format('JDTLS equinox launcher not found. Expected path: %s. ', launcher_reg) + err.throw(msg) + end + + return List:new({ + '-jar', + equinox_launcher, + + '-configuration', + lsp_utils.get_jdtls_cache_conf_path(), + + '-data', + lsp_utils.get_jdtls_cache_data_path(cwd), + }) +end + +---@private +function M.validate_java_version() + local v = M.get_java_major_version() + local exp_ver = java_version_map[conf.jdtls.version] + + if v <= exp_ver.from and v >= exp_ver.to then + local msg = string.format( + 'Java version mismatch: JDTLS %s requires Java %d - %d, but found Java %d', + conf.jdtls.version, + exp_ver.from, + exp_ver.to, + v + ) + + err.throw(msg) + end +end + +---@private +function M.get_java_major_version() + local version = vim.fn.system('java -version') + local major = version:match('version (%d+)') + or version:match('version "(%d+)') + or version:match('openjdk (%d+)') + or version:match('java (%d+)') + + if major then + return tonumber(major) + end + + local msg = 'Could not determine java version from::' .. version + err.throw(msg) +end + +return M diff --git a/lua/java-core/ls/servers/jdtls/conf.lua b/lua/java-core/ls/servers/jdtls/conf.lua new file mode 100644 index 0000000..bf5cf2c --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/conf.lua @@ -0,0 +1,33 @@ +return { + init_options = { + extendedClientCapabilities = { + actionableRuntimeNotificationSupport = true, + advancedExtractRefactoringSupport = true, + advancedGenerateAccessorsSupport = true, + advancedIntroduceParameterRefactoringSupport = true, + advancedOrganizeImportsSupport = true, + advancedUpgradeGradleSupport = true, + classFileContentsSupport = true, + clientDocumentSymbolProvider = true, + clientHoverProvider = false, + executeClientCommandSupport = true, + extractInterfaceSupport = true, + generateConstructorsPromptSupport = true, + generateDelegateMethodsPromptSupport = true, + generateToStringPromptSupport = true, + gradleChecksumWrapperPromptSupport = true, + hashCodeEqualsPromptSupport = true, + inferSelectionSupport = { + 'extractConstant', + 'extractField', + 'extractInterface', + 'extractMethod', + 'extractVariableAllOccurrence', + 'extractVariable', + }, + moveRefactoringSupport = true, + onCompletionItemSelectedCommand = 'editor.action.triggerParameterHints', + overrideMethodsPromptSupport = true, + }, + }, +} diff --git a/lua/java-core/ls/servers/jdtls/env.lua b/lua/java-core/ls/servers/jdtls/env.lua new file mode 100644 index 0000000..3e1bed2 --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/env.lua @@ -0,0 +1,32 @@ +local path = require('java-core.utils.path') +local Manager = require('pkgm.manager') +local log = require('java-core.utils.log2') + +--- @TODO: importing stuff from java main package feels wrong. +--- We should fix this in the future +local config = require('java.config') + +local M = {} + +--- @param opts { use_jdk: boolean } +function M.get_env(opts) + if not opts.use_jdk then + log.debug('use_jdk disabled, returning empty env') + return {} + end + + local jdk_root = Manager:get_install_dir('openjdk', config.jdk.version) + local java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*')) + local java_bin = path.join(java_home, 'bin') + + local env = { + ['PATH'] = java_bin .. ':' .. vim.fn.getenv('PATH'), + ['JAVA_HOME'] = java_home, + } + + log.debug('env set - JAVA_HOME:', env.JAVA_HOME, 'PATH:', env.PATH) + + return env +end + +return M diff --git a/lua/java-core/ls/servers/jdtls/init.lua b/lua/java-core/ls/servers/jdtls/init.lua new file mode 100644 index 0000000..8367a36 --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/init.lua @@ -0,0 +1,26 @@ +local M = {} + +--- Returns jdtls config +---@param opts { use_jdk: boolean, use_lombok: boolean, plugins: string[] } +function M.get_config(opts) + local conf = require('java-core.ls.servers.jdtls.conf') + local plugins = require('java-core.ls.servers.jdtls.plugins') + local cmd = require('java-core.ls.servers.jdtls.cmd') + local env = require('java-core.ls.servers.jdtls.env') + local root = require('java-core.ls.servers.jdtls.root') + local log = require('java-core.utils.log2') + + log.debug('get_config called with opts:', opts) + + local base_conf = vim.deepcopy(conf, true) + + base_conf.cmd = cmd.get_cmd(opts) + base_conf.cmd_env = env.get_env(opts) + base_conf.init_options.bundles = plugins.get_plugins(opts) + base_conf.filetypes = { 'java' } + base_conf.root_markers = root.get_root_markers() + + return base_conf +end + +return M diff --git a/lua/java-core/ls/servers/jdtls/plugins.lua b/lua/java-core/ls/servers/jdtls/plugins.lua new file mode 100644 index 0000000..086b02c --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/plugins.lua @@ -0,0 +1,66 @@ +local file = require('java-core.utils.file') +local List = require('java-core.utils.list') +local Manager = require('pkgm.manager') +local log = require('java-core.utils.log2') + +--- @TODO: importing stuff from java main package feels wrong. +--- We should fix this in the future +local config = require('java.config') + +local M = {} + +local plug_jar_map = { + ['java-test'] = { + 'extension/server/junit-jupiter-api_*.jar', + 'extension/server/junit-jupiter-engine_*.jar', + 'extension/server/junit-jupiter-migrationsupport_*.jar', + 'extension/server/junit-jupiter-params_*.jar', + 'extension/server/junit-platform-commons_*.jar', + 'extension/server/junit-platform-engine_*.jar', + 'extension/server/junit-platform-launcher_*.jar', + 'extension/server/junit-platform-runner_*.jar', + 'extension/server/junit-platform-suite-api_*.jar', + 'extension/server/junit-platform-suite-commons_*.jar', + 'extension/server/junit-platform-suite-engine_*.jar', + 'extension/server/junit-vintage-engine_*.jar', + 'extension/server/org.apiguardian.api_*.jar', + 'extension/server/org.eclipse.jdt.junit4.runtime_*.jar', + 'extension/server/org.eclipse.jdt.junit5.runtime_*.jar', + 'extension/server/org.opentest4j_*.jar', + 'extension/server/com.microsoft.java.test.plugin-*.jar', + }, + ['java-debug'] = { + 'extension/server/com.microsoft.java.debug.plugin-*.jar', + }, + ['spring-boot-tools'] = { 'extension/jars/*.jar' }, +} + +local plugin_version_map = { + ['java-test'] = config.java_test.version, + ['java-debug'] = config.java_debug_adapter.version, + ['spring-boot-tools'] = config.spring_boot_tools.version, +} + +---Returns a list of .jar file paths for given list of jdtls plugins +---@param opts { plugins: string[] } +---@return string[] # list of .jar file paths +function M.get_plugins(opts) + return List:new(opts.plugins) + :map(function(plugin_name) + local version = plugin_version_map[plugin_name] + local root = Manager:get_install_dir(plugin_name, version) + local jars = file.resolve_paths(root, plug_jar_map[plugin_name]) + + if #jars == 0 then + -- stylua: ignore + log.error(string.format( 'No jars found for plugin "%s" (version: %s) at %s', plugin_name, version, root)) + -- stylua: ignore + error(string.format( 'Failed to load plugin "%s". No jars found at %s', plugin_name, root)) + end + + return jars + end) + :flatten() +end + +return M diff --git a/lua/java-core/ls/servers/jdtls/root.lua b/lua/java-core/ls/servers/jdtls/root.lua new file mode 100644 index 0000000..beda3f2 --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/root.lua @@ -0,0 +1,29 @@ +local M = {} + +local root_markers1 = { + -- Multi-module projects + 'mvnw', -- Maven + 'gradlew', -- Gradle + 'settings.gradle', -- Gradle + 'settings.gradle.kts', -- Gradle + -- Use git directory as last resort for multi-module maven projects + -- In multi-module maven projects it is not really possible to determine what is the parent directory + -- and what is submodule directory. And jdtls does not break if the parent directory is at higher level than + -- actual parent pom.xml so propagating all the way to root git directory is fine + '.git', +} + +local root_markers2 = { + -- Single-module projects + 'build.xml', -- Ant + 'pom.xml', -- Maven + 'build.gradle', -- Gradle + 'build.gradle.kts', -- Gradle +} + +function M.get_root_markers() + return vim.fn.has('nvim-0.11.3') == 1 and { root_markers1, root_markers2 } + or vim.list_extend(root_markers1, root_markers2) +end + +return M diff --git a/lua/java-core/types/jdtls-types.lua b/lua/java-core/types/jdtls-types.lua new file mode 100644 index 0000000..d6749a6 --- /dev/null +++ b/lua/java-core/types/jdtls-types.lua @@ -0,0 +1,11 @@ +local M = {} + +---@enum java-core.CompileWorkspaceStatus +M.CompileWorkspaceStatus = { + FAILED = 0, + SUCCEED = 1, + WITHERROR = 2, + CANCELLED = 3, +} + +return M diff --git a/lua/java-core/types/nvim-types.lua b/lua/java-core/types/nvim-types.lua new file mode 100644 index 0000000..d16632b --- /dev/null +++ b/lua/java-core/types/nvim-types.lua @@ -0,0 +1,6 @@ +---@class nvim.CodeActionParamsResponse +---@field bufnr number +---@field client_id number +---@field method string +---@field params lsp.CodeActionParams +---@field version number diff --git a/lua/java/utils/buffer.lua b/lua/java-core/utils/buffer.lua similarity index 100% rename from lua/java/utils/buffer.lua rename to lua/java-core/utils/buffer.lua diff --git a/lua/java-core/utils/class.lua b/lua/java-core/utils/class.lua new file mode 100644 index 0000000..cd9ec76 --- /dev/null +++ b/lua/java-core/utils/class.lua @@ -0,0 +1,280 @@ +--- Provides a reuseable and convenient framework for creating classes in Lua. +-- Two possible notations: +-- +-- B = class(A) +-- class.B(A) +-- +-- The latter form creates a named class within the current environment. Note +-- that this implicitly brings in `pl.utils` as a dependency. +-- +-- See the Guide for further @{01-introduction.md.Simplifying_Object_Oriented_Programming_in_Lua|discussion} +-- @module pl.class + +local error, getmetatable, io, pairs, rawget, rawset, setmetatable, tostring, type = + _G.error, _G.getmetatable, _G.io, _G.pairs, _G.rawget, _G.rawset, _G.setmetatable, _G.tostring, _G.type +local compat + +-- this trickery is necessary to prevent the inheritance of 'super' and +-- the resulting recursive call problems. +local function call_ctor(c, obj, ...) + local init = rawget(c, '_init') + local parent_with_init = rawget(c, '_parent_with_init') + + if parent_with_init then + if not init then -- inheriting an init + init = rawget(parent_with_init, '_init') + parent_with_init = rawget(parent_with_init, '_parent_with_init') + end + if parent_with_init then -- super() points to one above whereever _init came from + rawset(obj, 'super', function(loc_obj, ...) + call_ctor(parent_with_init, loc_obj, ...) + end) + end + else + -- Without this, calling super() where none exists will sometimes loop and stack overflow + rawset(obj, 'super', nil) + end + + local res = init(obj, ...) + if parent_with_init then -- If this execution of call_ctor set a super, unset it + rawset(obj, 'super', nil) + end + + return res +end + +--- initializes an __instance__ upon creation. +-- @function class:_init +-- @param ... parameters passed to the constructor +-- @usage local Cat = class() +-- function Cat:_init(name) +-- --self:super(name) -- call the ancestor initializer if needed +-- self.name = name +-- end +-- +-- local pussycat = Cat("pussycat") +-- print(pussycat.name) --> pussycat + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `class_of`. It has two ways of using; +-- 1) call with a class to check against, 2) call without params. +-- @function instance:is_a +-- @param some_class class to check against, or `nil` to return the class +-- @return `true` if `instance` is derived from `some_class`, or if `some_class == nil` then +-- it returns the class table of the instance +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if pussycat:is_a(Cat) then +-- -- it's true, it is a Lion, but also a Cat +-- end +-- +-- if pussycat:is_a() == Lion then +-- -- It's true +-- end +local function is_a(self, klass) + if klass == nil then + -- no class provided, so return the class this instance is derived from + return getmetatable(self) + end + local m = getmetatable(self) + if not m then + return false + end --*can't be an object! + while m do + if m == klass then + return true + end + m = rawget(m, '_base') + end + return false +end + +--- checks whether an __instance__ is derived from some class. +-- Works the other way around as `is_a`. +-- @function some_class:class_of +-- @param some_instance instance to check against +-- @return `true` if `some_instance` is derived from `some_class` +-- @usage local pussycat = Lion() -- assuming Lion derives from Cat +-- if Cat:class_of(pussycat) then +-- -- it's true +-- end +local function class_of(klass, obj) + if type(klass) ~= 'table' or not rawget(klass, 'is_a') then + return false + end + return klass.is_a(obj, klass) +end + +--- cast an object to another class. +-- It is not clever (or safe!) so use carefully. +-- @param some_instance the object to be changed +-- @function some_class:cast +local function cast(klass, obj) + return setmetatable(obj, klass) +end + +local function _class_tostring(obj) + local mt = obj._class + local name = rawget(mt, '_name') + setmetatable(obj, nil) + local str = tostring(obj) + setmetatable(obj, mt) + if name then + str = name .. str:gsub('table', '') + end + return str +end + +local function tupdate(td, ts, dont_override) + for k, v in pairs(ts) do + if not dont_override or td[k] == nil then + td[k] = v + end + end +end + +local function _class(base, c_arg, c) + -- the class `c` will be the metatable for all its objects, + -- and they will look up their methods in it. + local mt = {} -- a metatable for the class to support __call and _handler + -- can define class by passing it a plain table of methods + local plain = type(base) == 'table' and not getmetatable(base) + if plain then + c = base + base = c._base + else + c = c or {} + end + + if type(base) == 'table' then + -- our new class is a shallow copy of the base class! + -- but be careful not to wipe out any methods we have been given at this point! + tupdate(c, base, plain) + c._base = base + -- inherit the 'not found' handler, if present + if rawget(c, '_handler') then + mt.__index = c._handler + end + elseif base ~= nil then + error('must derive from a table type', 3) + end + + c.__index = c + setmetatable(c, mt) + if not plain then + if base and rawget(base, '_init') then + c._parent_with_init = base + end -- For super and inherited init + c._init = nil + end + + if base and rawget(base, '_class_init') then + base._class_init(c, c_arg) + end + + -- expose a ctor which can be called by () + mt.__call = function(_class_tbl, ...) + local obj + if rawget(c, '_create') then + obj = c._create(...) + end + if not obj then + obj = {} + end + setmetatable(obj, c) + + if rawget(c, '_init') or rawget(c, '_parent_with_init') then -- constructor exists + local res = call_ctor(c, obj, ...) + if res then -- _if_ a ctor returns a value, it becomes the object... + obj = res + setmetatable(obj, c) + end + end + + if base and rawget(base, '_post_init') then + base._post_init(obj) + end + + return obj + end + -- Call Class.catch to set a handler for methods/properties not found in the class! + c.catch = function(self, handler) + if type(self) == 'function' then + -- called using . instead of : + handler = self + end + c._handler = handler + mt.__index = handler + end + c.is_a = is_a + c.class_of = class_of + c.cast = cast + c._class = c + + if not rawget(c, '__tostring') then + c.__tostring = _class_tostring + end + + return c +end + +--- create a new class, derived from a given base class. +-- Supporting two class creation syntaxes: +-- either `Name = class(base)` or `class.Name(base)`. +-- The first form returns the class directly and does not set its `_name`. +-- The second form creates a variable `Name` in the current environment set +-- to the class, and also sets `_name`. +-- @function class +-- @param base optional base class +-- @param c_arg optional parameter to class constructor +-- @param c optional table to be used as class +local class +class = setmetatable({}, { + __call = function(_fun, ...) + return _class(...) + end, + __index = function(_tbl, key) + if key == 'class' then + io.stderr:write('require("pl.class").class is deprecated. Use require("pl.class")\n') + return class + end + compat = compat or require('pl.compat') + local env = compat.getfenv(2) + return function(...) + local c = _class(...) + c._name = key + rawset(env, key, c) + return c + end + end, +}) + +class.properties = class() + +function class.properties._class_init(klass) + klass.__index = function(t, key) + -- normal class lookup! + local v = klass[key] + if v then + return v + end + -- is it a getter? + v = rawget(klass, 'get_' .. key) + if v then + return v(t) + end + -- is it a field? + return rawget(t, '_' .. key) + end + klass.__newindex = function(t, key, value) + -- if there's a setter, use that, otherwise directly set table + local p = 'set_' .. key + local setter = klass[p] + if setter then + setter(t, value) + else + rawset(t, key, value) + end + end +end + +return class diff --git a/lua/java-core/utils/command.lua b/lua/java-core/utils/command.lua new file mode 100644 index 0000000..a35c7d8 --- /dev/null +++ b/lua/java-core/utils/command.lua @@ -0,0 +1,53 @@ +local M = {} + +---Converts a path array to command name +---@param path string[] +---@return string +function M.path_to_command_name(path) + local name = 'Java' + + for _, word in ipairs(path) do + local sub_words = vim.split(word, '_') + local changed_word = '' + + for _, sub_word in ipairs(sub_words) do + local first_char = sub_word:sub(1, 1):upper() + local rest = sub_word:sub(2) + changed_word = changed_word .. first_char .. rest + end + + name = name .. changed_word + end + + return name +end + +---Registers an API by creating a user command and adding to module table +---@param module table +---@param path string[] +---@param command fun() +---@param opts vim.api.keyset.user_command +function M.register_api(module, path, command, opts) + local name = M.path_to_command_name(path) + + vim.api.nvim_create_user_command(name, command, opts or {}) + + local last_index = #path + local func_name = path[last_index] + + table.remove(path, last_index) + + local node = module + + for _, v in ipairs(path) do + if not node[v] then + node[v] = {} + end + + node = node[v] + end + + node[func_name] = command +end + +return M diff --git a/lua/java/handlers/error.lua b/lua/java-core/utils/error_handler.lua similarity index 78% rename from lua/java/handlers/error.lua rename to lua/java-core/utils/error_handler.lua index 45aa6de..bc0f096 100644 --- a/lua/java/handlers/error.lua +++ b/lua/java-core/utils/error_handler.lua @@ -1,5 +1,4 @@ local notify = require('java-core.utils.notify') -local log = require('java.utils.log') local function table_tostring(tbl) local str = '' @@ -12,8 +11,9 @@ end ---Returns a error handler ---@param msg string messages to show in the error +---@param log table|nil log instance to use (optional, defaults to no logging) ---@return fun(err: any) # function that log and notify the error -local function get_error_handler(msg) +local function get_error_handler(msg, log) return function(err) local trace = debug.traceback() @@ -23,7 +23,9 @@ local function get_error_handler(msg) local log_str = table_tostring(log_obj) - log.error(log_str) + if log then + log.error(log_str) + end notify.error(log_str) error(log_str) end diff --git a/lua/java-core/utils/errors.lua b/lua/java-core/utils/errors.lua new file mode 100644 index 0000000..65fddb4 --- /dev/null +++ b/lua/java-core/utils/errors.lua @@ -0,0 +1,14 @@ +local notify = require('java-core.utils.notify') +local log = require('java-core.utils.log2') + +local M = {} + +---Notifies user, logs error, and throws error +---@param msg string error message +function M.throw(msg) + notify.error(msg) + log.error(msg) + error(msg) +end + +return M diff --git a/lua/java-core/utils/event.lua b/lua/java-core/utils/event.lua new file mode 100644 index 0000000..e175839 --- /dev/null +++ b/lua/java-core/utils/event.lua @@ -0,0 +1,23 @@ +local M = {} + +---@param opts { once?: boolean, group?: number, callback: fun(client: vim.lsp.Client) } +function M.on_jdtls_attach(opts) + local id + + id = vim.api.nvim_create_autocmd('LspAttach', { + group = opts.group, + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + + if client and client.name == 'jdtls' then + opts.callback(client) + + if opts.once then + vim.api.nvim_del_autocmd(id) + end + end + end, + }) +end + +return M diff --git a/lua/java-core/utils/file.lua b/lua/java-core/utils/file.lua new file mode 100644 index 0000000..c439548 --- /dev/null +++ b/lua/java-core/utils/file.lua @@ -0,0 +1,15 @@ +local path = require('java-core.utils.path') + +local List = require('java-core.utils.list') + +local M = {} + +function M.resolve_paths(root, paths) + return List:new(paths) + :map(function(path_pattern) + return vim.fn.glob(path.join(root, path_pattern), true, true) + end) + :flatten() +end + +return M diff --git a/lua/java-core/utils/list.lua b/lua/java-core/utils/list.lua new file mode 100644 index 0000000..250e2c2 --- /dev/null +++ b/lua/java-core/utils/list.lua @@ -0,0 +1,185 @@ +---@class java-core.List +local M = {} + +---Returns a new list +---@param o? table +---@return java-core.List +function M:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +---Appends a value into to the list +---@param value any +function M:push(value) + table.insert(self, value) +end + +---Appends a value into to the list +---@param value any +function M:push_if(condition, value) + if condition then + self:push(value) + end +end + +---Finds the matching value in a list +---@param finder fun(value: any): boolean +---@return any +function M:find(finder) + for _, value in ipairs(self) do + if finder(value) then + return value + end + end + + return nil +end + +--- Finds the matching value in a list before a given index +--- @param finder fun(value: any): boolean +--- @return boolean +function M:contains(finder) + if type(finder) == 'function' then + for _, value in ipairs(self) do + if finder(value) then + return true + end + end + else + for _, value in ipairs(self) do + if value == finder then + return true + end + end + end + + return false +end + +--- Finds the matching value in a list after a given index +--- +--- @param finder fun(value: any): boolean +--- @return number +function M:find_index(finder) + for i, value in ipairs(self) do + if finder(value) then + return i + end + end + + return -1 +end + +--- Finds the matching value in a list after a given index +---@param index number +---@param finder fun(value: any): boolean +---@return any|nil +function M:find_after(index, finder) + for i, value in ipairs(self) do + if i > index and finder(value) then + return value + end + end +end + +---Returns a list of mapped values +---@param mapper fun(value: any, index: number): any +---@return java-core.List +function M:map(mapper) + local mapped = M:new() + + for i, v in ipairs(self) do + mapped:push(mapper(v, i)) + end + + return mapped +end + +---Flatten a list +---@return java-core.List +function M:flatten() + local flatten = M:new() + + for _, v1 in ipairs(self) do + for _, v2 in ipairs(v1) do + flatten:push(v2) + end + end + + return flatten +end + +---Merge a given list values to current list +---@param list any[] +---@return java-core.List +function M:concat(list) + local new_list = M:new() + + for _, v in ipairs(self) do + new_list:push(v) + end + + for _, v in ipairs(list) do + new_list:push(v) + end + + return new_list +end + +function M:includes(value) + for _, v in ipairs(self) do + if v == value then + return true + end + end + + return false +end + +---Join list items to a string +---@param separator string +---@return string +function M:join(separator) + return table.concat(self, separator) +end + +---Calls the given callback for each element +---@param callback fun(value: any, index: integer) +function M:for_each(callback) + for i, v in ipairs(self) do + callback(v, i) + end +end + +---Returns true if every element in the list passes the validation +---@param validator fun(value: any, index: integer): boolean +---@return boolean +function M:every(validator) + for i, v in ipairs(self) do + if not validator(v, i) then + return false + end + end + + return true +end + +---Returns a filtered list +---@param filter fun(value: any, index: integer): boolean +---@return java-core.List +function M:filter(filter) + local new_list = M:new() + + self:for_each(function(value, index) + if filter(value, index) then + new_list:push(value) + end + end) + + return new_list +end + +return M diff --git a/lua/java/utils/log.lua b/lua/java-core/utils/log.lua similarity index 86% rename from lua/java/utils/log.lua rename to lua/java-core/utils/log.lua index 9eb90e5..1d10846 100644 --- a/lua/java/utils/log.lua +++ b/lua/java-core/utils/log.lua @@ -16,16 +16,16 @@ local default_config = { plugin = 'nvim-java', -- Should print the output to neovim while running - use_console = false, + use_console = true, -- Should highlighting be used in console (using echohl) highlights = true, -- Should write to a file - use_file = true, + use_file = false, -- Any messages above this level will be logged. - level = 'trace', + level = 'debug', -- Level configuration modes = { @@ -49,11 +49,7 @@ local unpack = unpack or table.unpack log.new = function(config, standalone) config = vim.tbl_deep_extend('force', default_config, config) - local outfile = string.format( - '%s/%s.log', - vim.api.nvim_call_function('stdpath', { 'data' }), - config.plugin - ) + local outfile = string.format('%s/%s.log', vim.api.nvim_call_function('stdpath', { 'data' }), config.plugin) local obj if standalone then @@ -104,13 +100,7 @@ log.new = function(config, standalone) -- Output to console if config.use_console then - local console_string = string.format( - '[%-6s%s] %s: %s', - nameupper, - os.date('%H:%M:%S'), - lineinfo, - msg - ) + local console_string = string.format('[%-6s%s] %s: %s', nameupper, os.date('%H:%M:%S'), lineinfo, msg) if config.highlights and level_config.hl then vim.cmd(string.format('echohl %s', level_config.hl)) @@ -118,13 +108,7 @@ log.new = function(config, standalone) local split_console = vim.split(console_string, '\n') for _, v in ipairs(split_console) do - vim.cmd( - string.format( - [[echom "[%s] %s"]], - config.plugin, - vim.fn.escape(v, '"') - ) - ) + vim.cmd(string.format([[echom "[%s] %s"]], config.plugin, vim.fn.escape(v, '"'))) end if config.highlights and level_config.hl then @@ -135,9 +119,10 @@ log.new = function(config, standalone) -- Output to log file if config.use_file then local fp = io.open(outfile, 'a') - local str = - string.format('[%-6s%s] %s: %s\n', nameupper, os.date(), lineinfo, msg) + local str = string.format('[%-6s%s] %s: %s\n', nameupper, os.date(), lineinfo, msg) + assert(fp, 'cannot open file: ' .. ' to write logs') + fp:write(str) fp:close() end diff --git a/lua/java-core/utils/log2.lua b/lua/java-core/utils/log2.lua new file mode 100644 index 0000000..8fffeeb --- /dev/null +++ b/lua/java-core/utils/log2.lua @@ -0,0 +1,147 @@ +local M = {} + +---@alias java-core.LogLevel 'trace'|'debug'|'info'|'warn'|'error'|'fatal' + +---@class java-core.Log2Config +---@field use_console boolean Enable console logging +---@field use_file boolean Enable file logging +---@field level java-core.LogLevel Minimum log level to display +---@field log_file string Path to log file +---@field max_lines number Maximum lines to keep in log file +---@field show_location boolean Show file location in log messages + +---@class java-core.PartialLog2Config +---@field use_console? boolean Enable console logging +---@field use_file? boolean Enable file logging +---@field level? java-core.LogLevel Minimum log level to display +---@field log_file? string Path to log file +---@field max_lines? number Maximum lines to keep in log file +---@field show_location? boolean Show file location in log messages + +---@type java-core.Log2Config +local default_config = { + use_console = false, + use_file = true, + level = 'info', + log_file = vim.fn.stdpath('log') .. '/nvim-java.log', + max_lines = 100, + show_location = false, +} + +---@type java-core.Log2Config +local config = vim.deepcopy(default_config) + +local log_levels = { + trace = 1, + debug = 2, + info = 3, + warn = 4, + error = 5, + fatal = 6, +} + +local highlights = { + trace = 'Comment', + debug = 'Debug', + info = 'DiagnosticInfo', + warn = 'DiagnosticWarn', + error = 'DiagnosticError', + fatal = 'ErrorMsg', +} + +---@param user_config? java-core.PartialLog2Config +function M.setup(user_config) + config = vim.tbl_deep_extend('force', config, user_config or {}) +end + +--- Write message to log file with line limit +---@param msg string +---@private +local function write_to_file(msg) + local log_file = config.log_file + local lines = {} + + local file = io.open(log_file, 'r') + if file then + for line in file:lines() do + table.insert(lines, line) + end + file:close() + end + + table.insert(lines, msg) + + while #lines > config.max_lines do + table.remove(lines, 1) + end + + file = io.open(log_file, 'w') + if file then + for _, line in ipairs(lines) do + file:write(line .. '\n') + end + file:close() + end +end + +--- Log a message +---@param level java-core.LogLevel +---@param ... any +local function log(level, ...) + if log_levels[level] < log_levels[config.level] then + return + end + + local logs = {} + + for _, v in ipairs({ ... }) do + table.insert(logs, vim.inspect(v)) + end + + local location = '' + if config.show_location then + local info = debug.getinfo(3, 'Sl') + if info then + local file = info.short_src or info.source or 'unknown' + local line = info.currentline or 0 + location = '[' .. file .. ':' .. line .. ']' + end + end + + local msg = level:upper() .. (location ~= '' and '::' .. location or '') .. '::' .. table.concat(logs, '::') + + if config.use_console then + local hl = highlights[level] or 'Normal' + vim.api.nvim_echo({ { msg, hl } }, true, {}) + end + + if config.use_file then + write_to_file(msg) + end +end + +function M.info(...) + log('info', ...) +end + +function M.debug(...) + log('debug', ...) +end + +function M.fatal(...) + log('fatal', ...) +end + +function M.error(...) + log('error', ...) +end + +function M.trace(...) + log('trace', ...) +end + +function M.warn(...) + log('warn', ...) +end + +return M diff --git a/lua/java-core/utils/lsp.lua b/lua/java-core/utils/lsp.lua new file mode 100644 index 0000000..af15372 --- /dev/null +++ b/lua/java-core/utils/lsp.lua @@ -0,0 +1,66 @@ +local M = {} + +---Get JDTLS LSP client +---@return vim.lsp.Client +function M.get_jdtls() + local err = require('java-core.utils.errors') + + local clients = vim.lsp.get_clients({ name = 'jdtls' }) + + if #clients == 0 then + vim.print(debug.traceback()) + err.throw('No JDTLS client found') + end + + return clients[1] +end + +--- Returns the path to the jdtls cache directory +---@return string +function M.get_jdtls_cache_root_path() + local path = require('java-core.utils.path') + local cache_root = path.join(vim.fn.stdpath('cache'), 'jdtls') + return cache_root +end + +--- Returns the path to the jdtls config file +---@return string +function M.get_jdtls_cache_conf_path() + local path = require('java-core.utils.path') + local cache_root = M.get_jdtls_cache_root_path() + local conf_path = path.join(cache_root, 'config') + return conf_path +end + +--- Returns the path to the workspace cache directory +---@param cwd string +---@return string +function M.get_jdtls_cache_data_path(cwd) + cwd = cwd or vim.fn.getcwd() + + local path = require('java-core.utils.path') + local cache_root = M.get_jdtls_cache_root_path() + local workspace_path = path.join(cache_root, 'workspace', 'proj_' .. vim.fn.sha256(cwd)) + return workspace_path +end + +---Restart given LSP server +---@param ls string +function M.restart_ls(ls) + if vim.lsp.config[ls] == nil then + vim.notify(("Invalid server name '%s'"):format(ls)) + else + vim.lsp.enable(ls, false) + + vim.iter(vim.lsp.get_clients({ name = ls })):each(function(client) + client:stop(true) + end) + end + + local timer = assert(vim.uv.new_timer()) + timer:start(500, 0, function() + vim.schedule_wrap(vim.lsp.enable)(ls) + end) +end + +return M diff --git a/lua/java-core/utils/notify.lua b/lua/java-core/utils/notify.lua new file mode 100644 index 0000000..ac2d8e1 --- /dev/null +++ b/lua/java-core/utils/notify.lua @@ -0,0 +1,22 @@ +local M = { + opts = {}, +} + +local function index(this, level) + return function(msg, opts) + vim.notify(msg, vim.log.levels[level:upper()], vim.tbl_deep_extend('force', this.opts or {}, opts or {})) + end +end + +setmetatable(M, { + __index = index, + __call = function(_, opts) + return setmetatable({ opts = opts or {} }, { + __index = index, + }) + end, +}) + +return M({ + title = 'Java', +}) diff --git a/lua/java-core/utils/path.lua b/lua/java-core/utils/path.lua new file mode 100644 index 0000000..34129dc --- /dev/null +++ b/lua/java-core/utils/path.lua @@ -0,0 +1,16 @@ +local M = {} + +if vim.fn.has('win32') == 1 or vim.fn.has('win32unix') == 1 then + M.path_separator = '\\' +else + M.path_separator = '/' +end + +---Join a given list of paths to one path +---@param ... string paths to join +---@return string # joined path +function M.join(...) + return table.concat({ ... }, M.path_separator) +end + +return M diff --git a/lua/java-core/utils/set.lua b/lua/java-core/utils/set.lua new file mode 100644 index 0000000..2d0043a --- /dev/null +++ b/lua/java-core/utils/set.lua @@ -0,0 +1,22 @@ +---@class Set: List +local M = {} + +---Returns a new set +---@param o? any[] +---@return Set +function M:new(o) + o = o or {} + setmetatable(o, self) + self.__index = self + return o +end + +---Appends a value into to the list if the value is not already in the list +---@param value any +function M:push(value) + if not self:includes(value) then + table.insert(self, value) + end +end + +return M diff --git a/lua/java-core/utils/system.lua b/lua/java-core/utils/system.lua new file mode 100644 index 0000000..b25b4f5 --- /dev/null +++ b/lua/java-core/utils/system.lua @@ -0,0 +1,45 @@ +local M = {} + +function M.get_os() + if vim.fn.has('mac') == 1 then + return 'mac' + elseif vim.fn.has('win32') == 1 or vim.fn.has('win64') == 1 then + return 'win' + else + return 'linux' + end +end + +---@return boolean +function M.is_arm() + local arch = jit.arch + return arch == 'arm' or arch == 'arm64' or arch == 'aarch64' +end + +---@return 'arm'|'x86' +function M.get_arch() + return M.is_arm() and 'arm' or 'x86' +end + +---@return '32bit'|'64bit' +function M.get_bit_depth() + local arch = jit.arch + if arch == 'x64' or arch == 'arm64' or arch == 'aarch64' then + return '64bit' + end + return '32bit' +end + +---@return string +function M.get_config_suffix() + local os = M.get_os() + local suffix = 'config_' .. os + + if os ~= 'win' and M.is_arm() then + suffix = suffix .. '_arm' + end + + return suffix +end + +return M diff --git a/lua/java-dap/data-adapters.lua b/lua/java-dap/data-adapters.lua new file mode 100644 index 0000000..31c4066 --- /dev/null +++ b/lua/java-dap/data-adapters.lua @@ -0,0 +1,44 @@ +local List = require('java-core.utils.list') +local Set = require('java-core.utils.set') + +local M = {} + +---Returns the dap config record +---@param main java-dap.JavaDebugResolveMainClassRecord +---@return java-dap.DapLauncherConfig +function M.main_to_dap_launch_config(main) + local project_name = main.projectName + local main_class = main.mainClass + + return { + request = 'launch', + type = 'java', + name = string.format('%s -> %s', project_name, main_class), + projectName = project_name, + mainClass = main_class, + } +end + +---Returns the launcher config +---@param launch_args java-core.JavaCoreTestJunitLaunchArguments +---@param java_exec string +---@param config { debug: boolean, label: string } +---@return java-dap.DapLauncherConfig +function M.junit_launch_args_to_dap_config(launch_args, java_exec, config) + return { + name = config.label, + type = 'java', + request = 'launch', + mainClass = launch_args.mainClass, + projectName = launch_args.projectName, + noDebug = not config.debug, + javaExec = java_exec, + cwd = launch_args.workingDirectory, + classPaths = Set:new(launch_args.classpath), + modulePaths = Set:new(launch_args.modulepath), + vmArgs = List:new(launch_args.vmArguments):join(' '), + args = List:new(launch_args.programArguments):join(' '), + } +end + +return M diff --git a/lua/java-dap/init.lua b/lua/java-dap/init.lua new file mode 100644 index 0000000..f93db4b --- /dev/null +++ b/lua/java-dap/init.lua @@ -0,0 +1,74 @@ +local M = {} + +function M.setup() + local event = require('java-core.utils.event') + local project_config = require('java.api.profile_config') + local err = require('java-core.utils.errors') + + local has_dap = pcall(require, 'dap') + + if not has_dap then + err.throw([[ + Please install https://github.com/mfussenegger/nvim-dap to enable debugging + or disable the java_debug_adapter.enable option in your config + ]]) + end + + event.on_jdtls_attach({ + callback = function() + project_config.setup() + M.config_dap() + end, + }) +end + +---Configure dap +function M.config_dap() + local get_error_handler = require('java-core.utils.error_handler') + local runner = require('async.runner') + + return runner(function() + local lsp_utils = require('java-core.utils.lsp') + local nvim_dap = require('dap') + local profile_config = require('java.api.profile_config') + local DapSetup = require('java-dap.setup') + + local client = lsp_utils.get_jdtls() + local dap = DapSetup(client) + + ---------------------------------------------------------------------- + -- adapter -- + ---------------------------------------------------------------------- + nvim_dap.adapters.java = function(callback) + runner(function() + local adapter = dap:get_dap_adapter() + callback(adapter --[[@as dap.Adapter]]) + end).run() + end + + ---------------------------------------------------------------------- + -- config -- + ---------------------------------------------------------------------- + + local dap_config = dap:get_dap_config() + + for _, config in ipairs(dap_config) do + local profile = profile_config.get_active_profile(config.name) + if profile then + config.vmArgs = profile.vm_args + config.args = profile.prog_args + end + end + + if nvim_dap.session then + nvim_dap.terminate() + end + + nvim_dap.configurations.java = nvim_dap.configurations.java or {} + vim.list_extend(nvim_dap.configurations.java, dap_config) + end) + .catch(get_error_handler('dap configuration failed')) + .run() +end + +return M diff --git a/lua/java-dap/runner.lua b/lua/java-dap/runner.lua new file mode 100644 index 0000000..1676337 --- /dev/null +++ b/lua/java-dap/runner.lua @@ -0,0 +1,72 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-dap.DapRunner +---@field private server uv_tcp_t +local Runner = class() + +---@return java-dap.DapRunner +function Runner:new() + local o = { + server = nil, + } + + setmetatable(o, self) + self.__index = self + return o +end + +---Dap run with given config +---@param config java-dap.DapLauncherConfig +---@param report java-test.JUnitTestReport +function Runner:run_by_config(config, report) + log.debug('running dap with config: ', config) + + require('dap').run(config --[[@as Configuration]], { + before = function(conf) + return self:before(conf, report) + end, + + after = function() + return self:after() + end, + }) +end + +---Runs before the dap run +---@private +---@param conf java-dap.DapLauncherConfig +---@param report java-test.JUnitTestReport +---@return java-dap.DapLauncherConfig +function Runner:before(conf, report) + log.debug('running "before" callback') + + self.server = assert(vim.loop.new_tcp(), 'uv.new_tcp() must return handle') + self.server:bind('127.0.0.1', 0) + self.server:listen(128, function(err) + assert(not err, err) + + local sock = assert(vim.loop.new_tcp(), 'uv.new_tcp must return handle') + self.server:accept(sock) + local success = sock:read_start(report:get_stream_reader(sock)) + assert(success == 0, 'failed to listen to reader') + end) + + -- replace the port number in the generated args + conf.args = conf.args:gsub('-port ([0-9]+)', '-port ' .. self.server:getsockname().port) + + return conf +end + +---Runs after the dap run +---@private +function Runner:after() + log.debug('running "after" callback') + + if self.server then + self.server:shutdown() + self.server:close() + end +end + +return Runner diff --git a/lua/java-dap/setup.lua b/lua/java-dap/setup.lua new file mode 100644 index 0000000..5973998 --- /dev/null +++ b/lua/java-dap/setup.lua @@ -0,0 +1,125 @@ +local adapters = require('java-dap.data-adapters') +local class = require('java-core.utils.class') +local JavaDebug = require('java-core.ls.clients.java-debug-client') + +---@class java-dap.Setup +---@field private client vim.lsp.Client +---@field private java_debug java-core.DebugClient +local Setup = class() + +---@param client any +function Setup:_init(client) + self.client = client + self.java_debug = JavaDebug(client) +end + +---@class java-dap.DapAdapter +---@field type string +---@field host string +---@field port integer + +---Returns the dap adapter config +---@return java-dap.DapAdapter # dap adapter details +function Setup:get_dap_adapter() + local port = self.java_debug:start_debug_session() + + return { + type = 'server', + host = '127.0.0.1', + port = port, + enrich_config = function(config, callback) + local updated_config = self:enrich_config(config) + callback(updated_config) + end, + } +end + +---@private +---Returns the launch config filled with required data if missing in the passed config +---@param config java-dap.DapLauncherConfigOverridable +---@return java-dap.DapLauncherConfigOverridable +function Setup:enrich_config(config) + config = vim.deepcopy(config) + + local main = config.mainClass + local project = config.projectName + + assert(main, 'To enrich the config, mainClass should already be present') + assert(project, 'To enrich the config, projectName should already be present') + + if config.request == 'launch' then + self.java_debug:build_workspace(main, project, nil, false) + end + + if not config.classPaths or config.modulePaths then + local paths = self.java_debug:resolve_classpath(main, project) + + if not config.modulePaths then + config.modulePaths = paths[1] + end + + if not config.classPaths then + config.classPaths = paths[2] + end + end + + if not config.javaExec then + local java_exec = self.java_debug:resolve_java_executable(main, project) + config.javaExec = java_exec + end + + return config +end + +---Returns the dap configuration for the current project +---@return java-dap.DapLauncherConfig[] # dap configuration details +function Setup:get_dap_config() + local mains = self.java_debug:resolve_main_class() + local config = {} + + for _, main in ipairs(mains) do + table.insert(config, adapters.main_to_dap_launch_config(main)) + end + + return config +end + +return Setup + +---@class java-dap.DapLauncherConfigOverridable +---@field name? string +---@field type? string +---@field request? string +---@field mainClass? string +---@field projectName? string +---@field cwd? string +---@field classPaths? string[] +---@field modulePaths? string[] +---@field vmArgs? string +---@field noDebug? boolean +---@field javaExec? string +---@field args? string +---@field env? { [string]: string; } +---@field envFile? string +---@field sourcePaths? string[] +---@field preLaunchTask? string +---@field postDebugTask? string + +---@class java-dap.DapLauncherConfig +---@field name string +---@field type string +---@field request string +---@field mainClass string +---@field projectName string +---@field cwd string +---@field classPaths string[] +---@field modulePaths string[] +---@field vmArgs string +---@field noDebug boolean +---@field javaExec string +---@field args string +---@field env? { [string]: string; } +---@field envFile? string +---@field sourcePaths string[] +---@field preLaunchTask? string +---@field postDebugTask? string diff --git a/lua/java-refactor/action.lua b/lua/java-refactor/action.lua new file mode 100644 index 0000000..25ba482 --- /dev/null +++ b/lua/java-refactor/action.lua @@ -0,0 +1,303 @@ +local ui = require('java.ui.utils') +local class = require('java-core.utils.class') +local JdtlsClient = require('java-core.ls.clients.jdtls-client') +local RefactorCommands = require('java-refactor.refactor') +local notify = require('java-core.utils.notify') +local List = require('java-core.utils.list') +local lsp_utils = require('java-core.utils.lsp') + +---@class java-refactor.Action +---@field client vim.lsp.Client +---@field jdtls java-core.JdtlsClient +local Action = class() + +---@param client vim.lsp.Client +function Action:_init(client) + self.client = client + self.jdtls = JdtlsClient(client) + self.refactor = RefactorCommands(client) +end + +---@class java-refactor.RenameAction +---@field length number +---@field offset number +---@field uri string + +---@param params java-refactor.RenameAction[] +function Action:rename(params) + for _, rename in ipairs(params) do + local buffer = vim.uri_to_bufnr(rename.uri) + + local line + + vim.api.nvim_buf_call(buffer, function() + line = vim.fn.byte2line(rename.offset) + end) + + local start_char = rename.offset - vim.fn.line2byte(line) + 1 + + vim.api.nvim_win_set_cursor(0, { line, start_char }) + + vim.lsp.buf.rename(nil, { + name = 'jdtls', + bufnr = buffer, + }) + end +end + +---@param params nvim.CodeActionParamsResponse +function Action:generate_constructor(params) + local status = self.jdtls:java_check_constructors_status(params.params) + + if not status or not status.constructors then + return + end + + local selected_constructor = ui.select( + 'Select super class constructor(s).', + status.constructors, + function(constructor) + return string.format('%s %s', constructor.name, table.concat(constructor.parameters, ', ')) + end + ) + + if not selected_constructor then + return + end + + local selected_fields = ui.multi_select('Select Fields:', status.fields, function(field) + return field.name + end) + + local edit = self.jdtls:java_generate_constructor({ + context = params.params, + constructors = { selected_constructor }, + fields = selected_fields or {}, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') +end + +---@param params nvim.CodeActionParamsResponse +function Action:generate_to_string(params) + local status = self.jdtls:java_check_to_string_status(params.params) + + if status.exists then + local prompt = string.format( + 'Method "toString()" already exists in the Class %s. Do you want to replace the implementation?', + status.type + ) + local choice = ui.select(prompt, { 'Replace', 'Cancel' }) + + if choice ~= 'Replace' then + return + end + end + + local fields = ui.multi_select( + 'Select the fields to include in the toString() method.', + status.fields, + function(field) + return field.name + end + ) + + if not fields then + return + end + + local edit = self.jdtls:java_generate_to_string({ + context = params.params, + fields = fields, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') +end + +---@param params nvim.CodeActionParamsResponse +function Action:generate_hash_code_and_equals(params) + local status = self.jdtls:java_check_hash_code_equals_status(params.params) + + if not status or not status.fields or #status.fields < 1 then + local message = string.format('The operation is not applicable to the type %s.', status.type) + notify.warn(message) + return + end + + local regenerate = false + + if status.existingMethods and #status.existingMethods > 0 then + local prompt = string.format( + 'Methods %s already exists in the Class %s. Do you want to regenerate the implementation?', + 'Regenerate', + 'Cancel' + ) + + local choice = ui.select(prompt, { 'Regenerate', 'Cancel' }) + + if choice == 'Regenerate' then + regenerate = true + end + end + + local fields = ui.multi_select( + 'Select the fields to include in the hashCode() and equals() methods.', + status.fields, + function(field) + return field.name + end + ) + + if not fields or #fields < 1 then + return + end + + local edit = self.jdtls:java_generate_hash_code_equals({ + context = params.params, + fields = fields, + regenerate = regenerate, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') +end + +---@param params nvim.CodeActionParamsResponse +function Action:generate_delegate_methods_prompt(params) + local status = self.jdtls:java_check_delegate_methods_status(params.params) + + if not status or not status.delegateFields or #status.delegateFields < 1 then + notify.warn('All delegatable methods are already implemented.') + return + end + + local selected_delegate_field = ui.select( + 'Select target to generate delegates for.', + status.delegateFields, + function(field) + return field.field.name .. ': ' .. field.field.type + end + ) + + if not selected_delegate_field then + return + end + + if #selected_delegate_field.delegateMethods < 1 then + notify.warn('All delegatable methods are already implemented.') + return + end + + local selected_delegate_methods = ui.multi_select( + 'Select methods to generate delegates for.', + selected_delegate_field.delegateMethods, + function(method) + return string.format( + '%s.%s(%s)', + selected_delegate_field.field.name, + method.name, + table.concat(method.parameters, ', ') + ) + end + ) + + if not selected_delegate_methods or #selected_delegate_methods < 1 then + return + end + + local delegate_entries = List:new(selected_delegate_methods):map( + ---@param method jdtls.MethodBinding + function(method) + return { + field = selected_delegate_field.field, + delegateMethod = method, + } + end + ) + + local edit = self.jdtls:java_generate_delegate_methods({ + context = params.params, + delegateEntries = delegate_entries, + }) + + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') +end + +---@param command lsp.Command +function Action:apply_refactoring_command(command) + local action_name = command.arguments[1] --[[@as jdtls.CodeActionCommand]] + local action_context = command.arguments[2] --[[@as lsp.CodeActionParams]] + local action_info = command.arguments[3] --[[@as lsp.LSPAny]] + + self.refactor:refactor(action_name, action_context, action_info) +end + +---comment +---@param is_full_compile boolean +---@return java-core.CompileWorkspaceStatus +function Action:build_workspace(is_full_compile) + return self.jdtls:java_build_workspace(is_full_compile, 0) +end + +function Action:clean_workspace() + local client = lsp_utils.get_jdtls() + local data_path = lsp_utils.get_jdtls_cache_data_path(client.root_dir) + + local prompt = string.format('Do you want to delete "%s"', data_path) + + local choice = ui.select(prompt, { 'Yes', 'No' }) + + if choice ~= 'Yes' then + return + end + + return vim.fn.delete(data_path, 'rf') +end + +---@class java-refactor.ApplyRefactoringCommandParams +---@field bufnr number +---@field client_id number +---@field method string +---@field params lsp.CodeActionParams +---@field version number + +---@param params nvim.CodeActionParamsResponse +function Action:override_methods_prompt(params) + local status = self.jdtls:list_overridable_methods(params.params) + + if not status or not status.methods or #status.methods < 1 then + notify.warn('No methods to override.') + return + end + + local selected_methods = ui.multi_select('Select methods to override.', status.methods, function(method) + return string.format('%s(%s)', method.name, table.concat(method.parameters, ', ')) + end) + + if not selected_methods or #selected_methods < 1 then + return + end + + local edit = self.jdtls:add_overridable_methods(params.params, selected_methods) + vim.lsp.util.apply_workspace_edit(edit, 'utf-8') +end + +---@param selections jdtls.ImportSelection[] +function Action:choose_imports(selections) + local selected_candidates = {} + + for _, selection in ipairs(selections) do + local selected_candidate = ui.select_sync( + 'Select methods to override.', + selection.candidates, + function(candidate, index) + return index .. ' ' .. candidate.fullyQualifiedName + end + ) + + table.insert(selected_candidates, selected_candidate) + end + + return selected_candidates +end + +return Action diff --git a/lua/java-refactor/api/build.lua b/lua/java-refactor/api/build.lua new file mode 100644 index 0000000..60e8fcb --- /dev/null +++ b/lua/java-refactor/api/build.lua @@ -0,0 +1,19 @@ +---@param client_command jdtls.ClientCommand +local function run_client_command(client_command, ...) + local handlers = require('java-refactor.client-command-handlers') + handlers[client_command](...) +end + +local M = { + build_workspace = function() + local ClientCommand = require('java-refactor.client-command') + run_client_command(ClientCommand.COMPILE_WORKSPACE, true) + end, + + clean_workspace = function() + local ClientCommand = require('java-refactor.client-command') + run_client_command(ClientCommand.CLEAN_WORKSPACE) + end, +} + +return M diff --git a/lua/java-refactor/api/refactor.lua b/lua/java-refactor/api/refactor.lua new file mode 100644 index 0000000..2616243 --- /dev/null +++ b/lua/java-refactor/api/refactor.lua @@ -0,0 +1,38 @@ +---@param action_type string +---@param filter? string +local function run_code_action(action_type, filter) + vim.lsp.buf.code_action({ + apply = true, + context = { + diagnostics = vim.lsp.diagnostic.get_line_diagnostics(0), + only = { action_type }, + }, + filter = filter and function(refactor) + return refactor.command.arguments[1] == filter + end or nil, + }) +end + +local M = { + extract_variable = function() + run_code_action('refactor.extract.variable', 'extractVariable') + end, + + extract_variable_all_occurrence = function() + run_code_action('refactor.extract.variable', 'extractVariableAllOccurrence') + end, + + extract_constant = function() + run_code_action('refactor.extract.constant') + end, + + extract_method = function() + run_code_action('refactor.extract.function') + end, + + extract_field = function() + run_code_action('refactor.extract.field') + end, +} + +return M diff --git a/lua/java-refactor/client-command-handlers.lua b/lua/java-refactor/client-command-handlers.lua new file mode 100644 index 0000000..905a998 --- /dev/null +++ b/lua/java-refactor/client-command-handlers.lua @@ -0,0 +1,129 @@ +local ClientCommand = require('java-refactor.client-command') + +---@param message string +---@param func fun(action: java-refactor.Action) +local run = function(message, func) + local runner = require('async.runner') + local get_error_handler = require('java-refactor.utils.error_handler') + local instance = require('java-refactor.utils.instance-factory') + + runner(function() + func(instance.get_action()) + end) + .catch(get_error_handler(message)) + .run() +end + +local M = { + ---@param params java-refactor.RenameAction[] + [ClientCommand.RENAME_COMMAND] = function(params) + run('Failed to rename the symbol', function(action) + action.rename(params) + end) + end, + + ---@param params nvim.CodeActionParamsResponse + [ClientCommand.GENERATE_CONSTRUCTORS_PROMPT] = function(_, params) + run('Failed to generate constructor', function(action) + action:generate_constructor(params) + end) + end, + + ---@param params nvim.CodeActionParamsResponse + [ClientCommand.GENERATE_TOSTRING_PROMPT] = function(_, params) + run('Failed to generate toString', function(action) + action:generate_to_string(params) + end) + end, + + ---@param params nvim.CodeActionParamsResponse + [ClientCommand.HASHCODE_EQUALS_PROMPT] = function(_, params) + run('Failed to generate hash code and equals', function(action) + action:generate_hash_code_and_equals(params) + end) + end, + + ---@param params nvim.CodeActionParamsResponse + [ClientCommand.GENERATE_DELEGATE_METHODS_PROMPT] = function(_, params) + run('Failed to generate delegate methods', function(action) + action:generate_delegate_methods_prompt(params) + end) + end, + + ---@param command lsp.Command + [ClientCommand.APPLY_REFACTORING_COMMAND] = function(command) + run('Failed to apply refactoring command', function(action) + action:apply_refactoring_command(command) + end) + end, + + ---@param params nvim.CodeActionParamsResponse + [ClientCommand.OVERRIDE_METHODS_PROMPT] = function(_, params) + run('Failed to get overridable methods', function(action) + action:override_methods_prompt(params) + require('java-core.utils.notify').info('Successfully built the workspace') + end) + end, + + ---@param params [string, jdtls.ImportSelection[], boolean] + [ClientCommand.CHOOSE_IMPORTS] = function(params) + local get_error_handler = require('java-refactor.utils.error_handler') + local instance = require('java-refactor.utils.instance-factory') + local action = instance.get_action() + + local selections = params[2] + local ok, result = pcall(function() + return action.choose_imports(selections) + end) + + if not ok then + get_error_handler('Failed to choose imports')(result) + return + end + + return result or {} + end, + + ---@param is_full_build boolean + [ClientCommand.COMPILE_WORKSPACE] = function(is_full_build) + run('Failed to build workspace', function(action) + local notify = require('java-core.utils.notify') + + action:build_workspace(is_full_build) + notify.info('Successfully built the workspace') + end) + end, + + [ClientCommand.CLEAN_WORKSPACE] = function() + run('Failed to clean workspace', function(action) + local lsp_utils = require('java-core.utils.lsp') + + local result = action:clean_workspace() + if result == 0 then + lsp_utils.restart_ls('jdtls') + end + end) + end, +} + +local ignored_commands = { ClientCommand.REFRESH_BUNDLES_COMMAND } + +for _, command in pairs(ClientCommand) do + if not M[command] and not vim.tbl_contains(ignored_commands, command) then + local message = string.format( + '"%s" is not supported yet!' + .. '\nPlease request the feature using below link' + .. '\nhttps://github.com/nvim-java/nvim-java/issues/new?assignees=' + .. '&labels=enhancement&projects=&template=feature_request.yml&title=feature%%3A+', + command + ) + + M[command] = function() + require('java-core.utils.notify').warn(message) + + return vim.lsp.rpc_response_error(vim.lsp.protocol.ErrorCodes.MethodNotFound, 'Not implemented yet') + end + end +end + +return M diff --git a/lua/java-refactor/client-command.lua b/lua/java-refactor/client-command.lua new file mode 100644 index 0000000..aadd352 --- /dev/null +++ b/lua/java-refactor/client-command.lua @@ -0,0 +1,93 @@ +---@enum jdtls.ClientCommand +local M = { + ADD_TO_SOURCEPATH = 'java.project.addToSourcePath', + ADD_TO_SOURCEPATH_CMD = 'java.project.addToSourcePath.command', + APPLY_REFACTORING_COMMAND = 'java.action.applyRefactoringCommand', + APPLY_WORKSPACE_EDIT = 'java.apply.workspaceEdit', + BUILD_PROJECT = 'java.project.build', + CHANGE_BASE_TYPE = 'java.action.changeBaseType', + CHANGE_IMPORTED_PROJECTS = 'java.project.changeImportedProjects', + CHOOSE_IMPORTS = 'java.action.organizeImports.chooseImports', + CLEAN_SHARED_INDEXES = 'java.clean.sharedIndexes', + CLEAN_WORKSPACE = 'java.clean.workspace', + CLIPBOARD_ONPASTE = 'java.action.clipboardPasteAction', + COMPILE_WORKSPACE = 'java.workspace.compile', + CONFIGURATION_UPDATE = 'java.projectConfiguration.update', + CREATE_MODULE_INFO = 'java.project.createModuleInfo', + CREATE_MODULE_INFO_COMMAND = 'java.project.createModuleInfo.command', + EXECUTE_WORKSPACE_COMMAND = 'java.execute.workspaceCommand', + FILESEXPLORER_ONPASTE = 'java.action.filesExplorerPasteAction', + GENERATE_ACCESSORS_PROMPT = 'java.action.generateAccessorsPrompt', + GENERATE_CONSTRUCTORS_PROMPT = 'java.action.generateConstructorsPrompt', + GENERATE_DELEGATE_METHODS_PROMPT = 'java.action.generateDelegateMethodsPrompt', + GENERATE_TOSTRING_PROMPT = 'java.action.generateToStringPrompt', + GET_ALL_JAVA_PROJECTS = 'java.project.getAll', + GET_CLASSPATHS = 'java.project.getClasspaths', + GET_DECOMPILED_SOURCE = 'java.decompile', + GET_PROJECT_SETTINGS = 'java.project.getSettings', + GET_WORKSPACE_PATH = '_java.workspace.path', + GOTO_LOCATION = 'editor.action.goToLocations', + HANDLE_PASTE_EVENT = 'java.edit.handlePasteEvent', + HASHCODE_EQUALS_PROMPT = 'java.action.hashCodeEqualsPrompt', + IGNORE_INCOMPLETE_CLASSPATH = 'java.ignoreIncompleteClasspath', + IGNORE_INCOMPLETE_CLASSPATH_HELP = 'java.ignoreIncompleteClasspath.help', + IMPORT_PROJECTS = 'java.project.import', + IMPORT_PROJECTS_CMD = 'java.project.import.command', + IS_TEST_FILE = 'java.project.isTestFile', + LEARN_MORE_ABOUT_CLEAN_UPS = '_java.learnMoreAboutCleanUps', + LEARN_MORE_ABOUT_REFACTORING = '_java.learnMoreAboutRefactorings', + LIST_SOURCEPATHS = 'java.project.listSourcePaths', + LIST_SOURCEPATHS_CMD = 'java.project.listSourcePaths.command', + LOMBOK_CONFIGURE = 'java.lombokConfigure', + MANUAL_CLEANUP = 'java.action.doCleanup', + MARKDOWN_API_RENDER = 'markdown.api.render', + METADATA_FILES_GENERATION = '_java.metadataFilesGeneration', + NAVIGATE_TO_SUPER_IMPLEMENTATION_COMMAND = 'java.action.navigateToSuperImplementation', + NOT_COVERED_EXECUTION = '_java.notCoveredExecution', + NULL_ANALYSIS_SET_MODE = 'java.compile.nullAnalysis.setMode', + OPEN_BROWSER = 'vscode.open', + OPEN_CLIENT_LOG = 'java.open.clientLog', + OPEN_FILE = 'java.open.file', + OPEN_FORMATTER = 'java.open.formatter.settings', + OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson', + OPEN_LOGS = 'java.open.logs', + OPEN_OUTPUT = 'java.open.output', + OPEN_SERVER_LOG = 'java.open.serverLog', + OPEN_SERVER_STDERR_LOG = 'java.open.serverStderrLog', + OPEN_SERVER_STDOUT_LOG = 'java.open.serverStdoutLog', + OPEN_STATUS_SHORTCUT = '_java.openShortcuts', + OPEN_TYPE_HIERARCHY = 'java.navigate.openTypeHierarchy', + ORGANIZE_IMPORTS = 'java.action.organizeImports', + ORGANIZE_IMPORTS_SILENTLY = 'java.edit.organizeImports', + OVERRIDE_METHODS_PROMPT = 'java.action.overrideMethodsPrompt', + PROJECT_CONFIGURATION_STATUS = 'java.projectConfiguration.status', + REFRESH_BUNDLES = 'java.reloadBundles', + REFRESH_BUNDLES_COMMAND = '_java.reloadBundles.command', + RELOAD_WINDOW = 'workbench.action.reloadWindow', + REMOVE_FROM_SOURCEPATH = 'java.project.removeFromSourcePath', + REMOVE_FROM_SOURCEPATH_CMD = 'java.project.removeFromSourcePath.command', + RENAME_COMMAND = 'java.action.rename', + RESOLVE_PASTED_TEXT = 'java.project.resolveText', + RESOLVE_SOURCE_ATTACHMENT = 'java.project.resolveSourceAttachment', + RESOLVE_TYPE_HIERARCHY = 'java.navigate.resolveTypeHierarchy', + RESOLVE_WORKSPACE_SYMBOL = 'java.project.resolveWorkspaceSymbol', + RESTART_LANGUAGE_SERVER = 'java.server.restart', + RUNTIME_VALIDATION_OPEN = 'java.runtimeValidation.open', + SHOW_CLASS_HIERARCHY = 'java.action.showClassHierarchy', + SHOW_JAVA_IMPLEMENTATIONS = 'java.show.implementations', + SHOW_JAVA_REFERENCES = 'java.show.references', + SHOW_REFERENCES = 'editor.action.showReferences', + SHOW_SERVER_TASK_STATUS = 'java.show.server.task.status', + SHOW_SUBTYPE_HIERARCHY = 'java.action.showSubtypeHierarchy', + SHOW_SUPERTYPE_HIERARCHY = 'java.action.showSupertypeHierarchy', + SHOW_TYPE_HIERARCHY = 'java.action.showTypeHierarchy', + SMARTSEMICOLON_DETECTION = 'java.edit.smartSemicolonDetection', + SWITCH_SERVER_MODE = 'java.server.mode.switch', + TEMPLATE_VARIABLES = '_java.templateVariables', + UPDATE_SOURCE_ATTACHMENT = 'java.project.updateSourceAttachment', + UPDATE_SOURCE_ATTACHMENT_CMD = 'java.project.updateSourceAttachment.command', + UPGRADE_GRADLE_WRAPPER = 'java.project.upgradeGradle', + UPGRADE_GRADLE_WRAPPER_CMD = 'java.project.upgradeGradle.command', +} + +return M diff --git a/lua/java-refactor/init.lua b/lua/java-refactor/init.lua new file mode 100644 index 0000000..60655da --- /dev/null +++ b/lua/java-refactor/init.lua @@ -0,0 +1,46 @@ +local event = require('java-core.utils.event') +local cmd_util = require('java-core.utils.command') + +local M = {} + +local group = vim.api.nvim_create_augroup('java-refactor-command-register', {}) + +M.setup = function() + event.on_jdtls_attach({ + group = group, + once = true, + callback = function() + M.reg_client_commands() + M.reg_refactor_commands() + M.reg_build_commands() + end, + }) +end + +M.reg_client_commands = function() + local code_action_handlers = require('java-refactor.client-command-handlers') + + for key, handler in pairs(code_action_handlers) do + vim.lsp.commands[key] = handler + end +end + +M.reg_refactor_commands = function() + local java = require('java') + local code_action_api = require('java-refactor.api.refactor') + + for api_name, api in pairs(code_action_api) do + cmd_util.register_api(java, { 'refactor', api_name }, api, { range = 2 }) + end +end + +M.reg_build_commands = function() + local java = require('java') + local code_action_api = require('java-refactor.api.build') + + for api_name, api in pairs(code_action_api) do + cmd_util.register_api(java, { 'build', api_name }, api, {}) + end +end + +return M diff --git a/lua/java-refactor/refactor.lua b/lua/java-refactor/refactor.lua new file mode 100644 index 0000000..6b2f731 --- /dev/null +++ b/lua/java-refactor/refactor.lua @@ -0,0 +1,359 @@ +local class = require('java-core.utils.class') +local notify = require('java-core.utils.notify') +local JdtlsClient = require('java-core.ls.clients.jdtls-client') +local List = require('java-core.utils.list') +local ui = require('java.ui.utils') + +local refactor_edit_request_needed_actions = { + 'convertVariableToField', + 'extractConstant', + 'extractField', + 'extractMethod', + 'extractVariable', + 'extractVariableAllOccurrence', +} + +local available_actions = List:new({ + 'assignField', + 'assignVariable', + 'convertAnonymousClassToNestedCommand', + 'introduceParameter', + 'invertVariable', + -- 'moveFile', + 'moveInstanceMethod', + 'moveStaticMember', + 'moveType', + -- 'changeSignature', + -- 'extractInterface', +}):concat(refactor_edit_request_needed_actions) + +---@class java-refactor.Refactor +---@field jdtls_client java-core.JdtlsClient +local Refactor = class() + +---@param client vim.lsp.Client +function Refactor:_init(client) + self.jdtls_client = JdtlsClient(client) +end + +---Run refactor command +---@param action_name jdtls.CodeActionCommand +---@param action_context lsp.CodeActionParams +---@param action_info lsp.LSPAny +function Refactor:refactor(action_name, action_context, action_info) + if not vim.tbl_contains(available_actions, action_name) then + notify.error(string.format('Refactoring command "%s" is not supported', action_name)) + return + end + + if vim.tbl_contains(refactor_edit_request_needed_actions, action_name) then + local formatting_options = self:make_formatting_options() + local selections + + if vim.tbl_contains(refactor_edit_request_needed_actions, action_name) then + selections = self:get_selections(action_name, action_context) + end + + local changes = self.jdtls_client:java_get_refactor_edit( + action_name, + action_context, + formatting_options, + selections, + vim.api.nvim_get_current_buf() + ) + + self:perform_refactor_edit(changes) + elseif action_name == 'moveFile' then + self:move_file(action_info --[[@as jdtls.CodeActionMoveTypeCommandInfo]]) + elseif action_name == 'moveType' then + self:move_type(action_context, action_info --[[@as jdtls.CodeActionMoveTypeCommandInfo]]) + elseif action_name == 'moveStaticMember' then + self:move_static_member(action_context, action_info --[[@as jdtls.CodeActionMoveTypeCommandInfo]]) + elseif action_name == 'moveInstanceMethod' then + self:move_instance_method(action_context, action_info --[[@as jdtls.CodeActionMoveTypeCommandInfo]]) + end +end + +---@private +---@param action_info jdtls.CodeActionMoveTypeCommandInfo +function Refactor:move_file(action_info) + if not action_info or not action_info.uri then + return + end + + local move_des = self.jdtls_client:get_move_destination({ + moveKind = 'moveResource', + sourceUris = { action_info.uri }, + params = nil, + }) + + if not move_des or not move_des.destinations or #move_des.destinations < 1 then + notify.error('Cannot find available Java packages to move the selected files to.') + return + end + + ---@type jdtls.ResourceMoveDestination[] + local destinations = move_des.destinations + + local selected_destination = ui.select('Choose the target package', destinations, function(destination) + return destination.displayName .. ' ' .. destination.path + end) + + if not selected_destination then + return + end + + local changes = self.jdtls_client:java_move({ + moveKind = 'moveResource', + sourceUris = { action_info.uri }, + params = nil, + destination = selected_destination, + }) + + self:perform_refactor_edit(changes) +end + +---@private +---@param action_context lsp.CodeActionParams +---@param action_info jdtls.CodeActionMoveTypeCommandInfo +function Refactor:move_instance_method(action_context, action_info) + local move_des = self.jdtls_client:get_move_destination({ + moveKind = 'moveInstanceMethod', + sourceUris = { action_context.textDocument.uri }, + params = action_context, + }) + + if move_des and move_des.errorMessage then + notify.error(move_des.errorMessage) + return + end + + if not move_des or not move_des.destinations or #move_des.destinations < 1 then + notify.error('Cannot find possible class targets to move the selected method to.') + return + end + + ---@type jdtls.InstanceMethodMoveDestination[] + local destinations = move_des.destinations + + local method_name = action_info and action_info.displayName or '' + + local selected_destination = ui.select( + string.format('Select the new class for the instance method %s', method_name), + destinations, + function(destination) + return destination.type .. ' ' .. destination.name + end, + { prompt_single = true } + ) + + if not selected_destination then + return + end + + self:perform_move('moveInstanceMethod', action_context, selected_destination) +end + +---@private +---@param action_context lsp.CodeActionParams +---@param action_info jdtls.CodeActionMoveTypeCommandInfo +function Refactor:move_static_member(action_context, action_info) + local exclude = List:new() + + if action_info.enclosingTypeName then + exclude:push(action_info.enclosingTypeName) + if action_info.memberType == 55 or action_info.memberType == 71 or action_info.memberType == 81 then + exclude:push(action_info.enclosingTypeName .. '.' .. action_info.displayName) + end + end + + local project_name = action_info and action_info.projectName or nil + local member_name = action_info and action_info.displayName and action_info.displayName or '' + + local selected_class = self:select_target_class( + string.format('Select the new class for the static member %s.', member_name), + project_name, + exclude + ) + + if not selected_class then + return + end + + self:perform_move('moveStaticMember', action_context, selected_class) +end + +---@private +---@param action_context lsp.CodeActionParams +---@param action_info jdtls.CodeActionMoveTypeCommandInfo +function Refactor:move_type(action_context, action_info) + if not action_info or not action_info.supportedDestinationKinds then + return + end + + local selected_destination_kind = ui.select( + 'What would you like to do?', + action_info.supportedDestinationKinds, + function(kind) + if kind == 'newFile' then + return string.format('Move type "%s" to new file', action_info.displayName) + else + return string.format('Move type "%s" to another class', action_info.displayName) + end + end + ) + + if not selected_destination_kind then + return + end + + if selected_destination_kind == 'newFile' then + self:perform_move('moveTypeToNewFile', action_context) + else + local exclude = List:new() + + if action_info.enclosingTypeName then + exclude:push(action_info.enclosingTypeName) + exclude:push(action_info.enclosingTypeName .. ':' .. action_info.displayName) + end + + local selected_class = self:select_target_class( + string.format('Select the new class for the type %s.', action_info.displayName), + action_info.projectName, + exclude + ) + + if not selected_class then + return + end + + self:perform_move('moveStaticMember', action_context, selected_class) + end +end + +---@private +---@param move_kind string +---@param action_context lsp.CodeActionParams +---@param destination? jdtls.InstanceMethodMoveDestination | jdtls.ResourceMoveDestination | lsp.SymbolInformation +function Refactor:perform_move(move_kind, action_context, destination) + local changes = self.jdtls_client:java_move({ + moveKind = move_kind, + sourceUris = { action_context.textDocument.uri }, + params = action_context, + destination = destination, + }) + + self:perform_refactor_edit(changes) +end + +---@private +---@param changes jdtls.RefactorWorkspaceEdit +function Refactor:perform_refactor_edit(changes) + if not changes then + notify.warn('No edits suggested for the code action') + return + end + + if changes.errorMessage then + notify.error(changes.errorMessage) + return + end + vim.lsp.util.apply_workspace_edit(changes.edit, 'utf-8') + + if changes.command then + self:run_lsp_client_command(changes.command.command, changes.command.arguments) + end +end + +---@private +---@param prompt string +---@param project_name string +---@param exclude string[] +function Refactor:select_target_class(prompt, project_name, exclude) + local classes = self.jdtls_client:java_search_symbols({ + query = '*', + projectName = project_name, + sourceOnly = true, + }) + + ---@type lsp.SymbolInformation[] + local filtered_classes = List:new(classes):filter(function(cls) + local type_name = cls.containerName .. '.' .. cls.name + return not vim.tbl_contains(exclude, type_name) + end) + + local selected = ui.select(prompt, filtered_classes, function(cls) + return cls.containerName .. '.' .. cls.name + end) + + return selected +end + +---@private +---@param command_name string +---@param arguments any +function Refactor:run_lsp_client_command(command_name, arguments) + local command = vim.lsp.commands[command_name] + + if not command then + notify.error('Command "' .. command_name .. '" is not supported') + return + end + + command(arguments) +end + +---@private +---@return lsp.FormattingOptions +function Refactor:make_formatting_options() + return { + tabSize = vim.bo.tabstop, + insertSpaces = vim.bo.expandtab, + } +end + +---@private +---@param refactor_type jdtls.CodeActionCommand +---@param params lsp.CodeActionParams +---@return jdtls.SelectionInfo[] +function Refactor:get_selections(refactor_type, params) + local selections = List:new() + local buffer = vim.api.nvim_get_current_buf() + + if + params.range.start.character == params.range['end'].character + and params.range.start.line == params.range['end'].line + then + local selection_res = self.jdtls_client:java_infer_selection(refactor_type, params, buffer) + + if not selection_res then + return selections + end + + local selection = selection_res[1] + + if selection.params and vim.islist(selection.params) then + local initialize_in = ui.select('Initialize the field in', selection.params) + + if not initialize_in then + return selections + end + + selections:push(initialize_in) + end + + selections:push(selection) + end + + return selections +end + +---@class jdtls.CodeActionMoveTypeCommandInfo +---@field displayName string +---@field enclosingTypeName string +---@field memberType number +---@field projectName string +---@field supportedDestinationKinds string[] +---@field uri? string + +return Refactor diff --git a/lua/java-refactor/utils/error_handler.lua b/lua/java-refactor/utils/error_handler.lua new file mode 100644 index 0000000..a09ed73 --- /dev/null +++ b/lua/java-refactor/utils/error_handler.lua @@ -0,0 +1,9 @@ +local get_error_handler = require('java-core.utils.error_handler') +local log = require('java-core.utils.log2') + +---Returns a error handler +---@param msg string messages to show in the error +---@return fun(err: any) # function that log and notify the error +return function(msg) + return get_error_handler(msg, log) +end diff --git a/lua/java-refactor/utils/instance-factory.lua b/lua/java-refactor/utils/instance-factory.lua new file mode 100644 index 0000000..742a4dd --- /dev/null +++ b/lua/java-refactor/utils/instance-factory.lua @@ -0,0 +1,12 @@ +local M = {} + +---@return java-refactor.Action +function M.get_action() + local lsp_utils = require('java-core.utils.lsp') + local Action = require('java-refactor.action') + local client = lsp_utils.get_jdtls() + + return Action(client) +end + +return M diff --git a/lua/java/runner/run-logger.lua b/lua/java-runner/run-logger.lua similarity index 100% rename from lua/java/runner/run-logger.lua rename to lua/java-runner/run-logger.lua diff --git a/lua/java/runner/run.lua b/lua/java-runner/run.lua similarity index 94% rename from lua/java/runner/run.lua rename to lua/java-runner/run.lua index 1691823..c110589 100644 --- a/lua/java/runner/run.lua +++ b/lua/java-runner/run.lua @@ -13,7 +13,6 @@ local notify = require('java-core.utils.notify') local Run = class() ---@param dap_config java-dap.DapLauncherConfig ----@param cmd string[] function Run:_init(dap_config) self.name = dap_config.name self.main_class = dap_config.mainClass @@ -53,8 +52,8 @@ function Run:stop() self.job_chan_id = nil end ----Send data to execution job channel ---@private +---Send data to execution job channel ---@param data string function Run:send_job(data) if self.job_chan_id then @@ -62,19 +61,18 @@ function Run:send_job(data) end end ----Send message to terminal channel ---@private +---Send message to terminal channel ---@param data string function Run:send_term(data) vim.fn.chansend(self.term_chan_id, data) end ----Runs when the current job exists ---@private +---Runs when the current job exists ---@param exit_code number function Run:on_job_exit(exit_code) - local message = - string.format('Process finished with exit code::%s', exit_code) + local message = string.format('Process finished with exit code::%s', exit_code) self:send_term(message) self.is_running = false diff --git a/lua/java/runner/runner.lua b/lua/java-runner/runner.lua similarity index 84% rename from lua/java/runner/runner.lua rename to lua/java-runner/runner.lua index 1120224..bce939e 100644 --- a/lua/java/runner/runner.lua +++ b/lua/java-runner/runner.lua @@ -1,10 +1,10 @@ -local ui = require('java.utils.ui') +local ui = require('java.ui.utils') local class = require('java-core.utils.class') -local jdtls = require('java.utils.jdtls2') +local lsp_utils = require('java-core.utils.lsp') local profile_config = require('java.api.profile_config') -local Run = require('java.runner.run') -local RunLogger = require('java.runner.run-logger') -local DapSetup = require('java-dap.api.setup') +local Run = require('java-runner.run') +local RunLogger = require('java-runner.run-logger') +local DapSetup = require('java-dap.setup') ---@class java.Runner ---@field runs table @@ -20,7 +20,7 @@ end ---Starts a new run ---@param args string function Runner:start_run(args) - local cmd, dap_config = Runner.select_dap_config(args) + local cmd, dap_config = self:select_dap_config(args) if not cmd or not dap_config then return @@ -100,17 +100,13 @@ end ---@param args string additional program arguments to pass ---@return string[] | nil ---@return java-dap.DapLauncherConfig | nil -function Runner.select_dap_config(args) - local dap = DapSetup(jdtls()) +function Runner:select_dap_config(args) + local dap = DapSetup(lsp_utils.get_jdtls()) local dap_config_list = dap:get_dap_config() - local selected_dap_config = ui.select( - 'Select the main class (module -> mainClass)', - dap_config_list, - function(config) - return config.name - end - ) + local selected_dap_config = ui.select('Select the main class (module -> mainClass)', dap_config_list, function(config) + return config.name + end) if not selected_dap_config then return nil, nil diff --git a/lua/java-test/adapters.lua b/lua/java-test/adapters.lua new file mode 100644 index 0000000..ed10d1e --- /dev/null +++ b/lua/java-test/adapters.lua @@ -0,0 +1,42 @@ +local List = require('java-core.utils.list') +local JavaTestClient = require('java-core.ls.clients.java-test-client') + +local M = {} + +---Returns launch argument parameters for given test or tests +---@param tests java-core.TestDetails | java-core.TestDetails[] +---@return java-core.JavaCoreTestResolveJUnitLaunchArgumentsParams junit launch arguments +function M.tests_to_junit_launch_params(tests) + if not vim.islist(tests) then + return { + projectName = tests.projectName, + testLevel = tests.testLevel, + testKind = tests.testKind, + testNames = M.get_test_names({ tests }), + } + end + + local first_test = tests[1] + + return { + projectName = first_test.projectName, + testLevel = first_test.testLevel, + testKind = first_test.testKind, + testNames = M.get_test_names(tests), + } +end + +---Returns a list of test names to be passed to test launch arguments resolver +---@param tests java-core.TestDetails[] +---@return java-core.List +function M.get_test_names(tests) + return List:new(tests):map(function(test) + if test.testKind == JavaTestClient.TestKind.TestNG or test.testLevel == JavaTestClient.TestLevel.Class then + return test.fullName + end + + return test.jdtHandler + end) +end + +return M diff --git a/lua/java-test/api.lua b/lua/java-test/api.lua new file mode 100644 index 0000000..e4879fd --- /dev/null +++ b/lua/java-test/api.lua @@ -0,0 +1,142 @@ +local log = require('java-core.utils.log2') +local notify = require('java-core.utils.notify') +local test_adapters = require('java-test.adapters') +local dap_adapters = require('java-dap.data-adapters') +local buf_util = require('java-core.utils.buffer') +local win_util = require('java.utils.window') + +local DebugClient = require('java-core.ls.clients.java-debug-client') +local TestClient = require('java-core.ls.clients.java-test-client') + +---@class java_test.TestApi +---@field private client java-core.JdtlsClient +---@field private debug_client java-core.DebugClient +---@field private test_client java-core.TestClient +---@field private runner java-dap.DapRunner +local M = {} + +---Returns a new test helper client +---@param args { client: vim.lsp.Client, runner: java-dap.DapRunner } +---@return java_test.TestApi +function M:new(args) + local o = { + client = args.client, + } + + o.debug_client = DebugClient(args.client) + o.test_client = TestClient(args.client) + o.runner = args.runner + + setmetatable(o, self) + self.__index = self + + return o +end + +---Returns a list of test methods +---@param file_uri string uri of the class +---@return java-core.TestDetailsWithRange[] # list of test methods +function M:get_test_methods(file_uri) + local classes = self.test_client:find_test_types_and_methods(file_uri) + local methods = {} + + for _, class in ipairs(classes) do + for _, method in ipairs(class.children) do + ---@diagnostic disable-next-line: inject-field + method.class = class + table.insert(methods, method) + end + end + + return methods +end + +---comment +---@param buffer number +---@param report java-test.JUnitTestReport +---@param config? java-dap.DapLauncherConfigOverridable config to override the default values in test launcher config +function M:run_class_by_buffer(buffer, report, config) + local tests = self:get_test_class_by_buffer(buffer) + + if #tests < 1 then + notify.warn('No tests found in the current buffer') + return + end + + self:run_test(tests, report, config) +end + +---Returns test classes in the given buffer +---@private +---@param buffer integer +---@return java-core.TestDetailsWithChildrenAndRange # get test class details +function M:get_test_class_by_buffer(buffer) + log.debug('finding test class by buffer') + + local uri = vim.uri_from_bufnr(buffer) + return self.test_client:find_test_types_and_methods(uri) +end + +---Run the given test +---@param tests java-core.TestDetails[] +---@param report java-test.JUnitTestReport +---@param config? java-dap.DapLauncherConfigOverridable config to override the default values in test launcher config +function M:run_test(tests, report, config) + local launch_args = self.test_client:resolve_junit_launch_arguments(test_adapters.tests_to_junit_launch_params(tests)) + + local java_exec = self.debug_client:resolve_java_executable(launch_args.mainClass, launch_args.projectName) + + local dap_launcher_config = dap_adapters.junit_launch_args_to_dap_config(launch_args, java_exec, { + debug = true, + label = 'Launch All Java Tests', + }) + + dap_launcher_config = vim.tbl_deep_extend('force', dap_launcher_config, config or {}) + + self.runner:run_by_config(dap_launcher_config, report) +end + +---Run the current test class +---@param report java-test.JUnitTestReport +---@param config java-dap.DapLauncherConfigOverridable +function M:execute_current_test_class(report, config) + log.debug('running the current class') + + return self:run_class_by_buffer(buf_util.get_curr_buf(), report, config) +end + +---Run the current test method +---@param report java-test.JUnitTestReport +---@param config java-dap.DapLauncherConfigOverridable +function M:execute_current_test_method(report, config) + log.debug('running the current method') + + local method = self:find_current_test_method() + + if not method then + notify.warn('cursor is not on a test method') + return + end + + self:run_test({ method }, report, config) +end + +---Find the test method at the current cursor position +---@return java-core.TestDetailsWithRange | nil +function M:find_current_test_method() + log.debug('finding the current test method') + + local cursor = win_util.get_cursor() + local methods = self:get_test_methods(buf_util.get_curr_uri()) + + for _, method in ipairs(methods) do + local line_start = method.range.start.line + local line_end = method.range['end'].line + + if cursor.line >= line_start and cursor.line <= line_end then + return method + end + end +end + +return M diff --git a/lua/java-test/init.lua b/lua/java-test/init.lua new file mode 100644 index 0000000..2b2f481 --- /dev/null +++ b/lua/java-test/init.lua @@ -0,0 +1,87 @@ +local log = require('java-core.utils.log2') +local lsp_utils = require('java-core.utils.lsp') +local get_error_handler = require('java-core.utils.error_handler') + +local runner = require('async.runner') + +local JavaTestApi = require('java-test.api') +local DapRunner = require('java-dap.runner') +local JUnitReport = require('java-test.reports.junit') +local ResultParserFactory = require('java-test.results.result-parser-factory') +local ReportViewer = require('java-test.ui.floating-report-viewer') + +local M = { + ---@type java-test.JUnitTestReport + last_report = nil, +} + +function M.run_current_class() + log.info('run current test class') + + return runner(function() + local test_api = JavaTestApi:new({ + client = lsp_utils.get_jdtls(), + runner = DapRunner(), + }) + return test_api:execute_current_test_class(M.get_report(), { noDebug = true }) + end) + .catch(get_error_handler('failed to run the current test class')) + .run() +end + +function M.debug_current_class() + log.info('debug current test class') + + return runner(function() + local test_api = JavaTestApi:new({ + client = lsp_utils.get_jdtls(), + runner = DapRunner(), + }) + test_api:execute_current_test_class(M.get_report(), {}) + end) + .catch(get_error_handler('failed to debug the current test class')) + .run() +end + +function M.debug_current_method() + log.info('debug current test method') + + return runner(function() + local test_api = JavaTestApi:new({ + client = lsp_utils.get_jdtls(), + runner = DapRunner(), + }) + return test_api:execute_current_test_method(M.get_report(), {}) + end) + .catch(get_error_handler('failed to run the current test method')) + .run() +end + +function M.run_current_method() + log.info('run current test method') + + return runner(function() + local test_api = JavaTestApi:new({ + client = lsp_utils.get_jdtls(), + runner = DapRunner(), + }) + return test_api:execute_current_test_method(M.get_report(), { noDebug = true }) + end) + .catch(get_error_handler('failed to run the current test method')) + .run() +end + +function M.view_last_report() + if M.last_report then + M.last_report:show_report() + end +end + +---@private +function M.get_report() + local report = JUnitReport(ResultParserFactory(), ReportViewer()) + M.last_report = report + return report +end + +return M diff --git a/lua/java-test/reports/junit.lua b/lua/java-test/reports/junit.lua new file mode 100644 index 0000000..01edb1e --- /dev/null +++ b/lua/java-test/reports/junit.lua @@ -0,0 +1,73 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-test.JUnitTestReport +---@field private conn uv_tcp_t +---@field private result_parser java-test.TestParser +---@field private result_parser_fac java-test.TestParserFactory +---@field private report_viewer java-test.ReportViewer +---@overload fun(result_parser_factory: java-test.TestParserFactory, test_viewer: java-test.ReportViewer) +local JUnitReport = class() + +---Init +---@param result_parser_factory java-test.TestParserFactory +function JUnitReport:_init(result_parser_factory, report_viewer) + self.conn = nil + self.result_parser_fac = result_parser_factory + self.report_viewer = report_viewer +end + +---Returns the test results +---@return java-test.TestResults[] +function JUnitReport:get_results() + return self.result_parser:get_test_details() +end + +---Shows the test report +function JUnitReport:show_report() + self.report_viewer:show(self:get_results()) +end + +---Returns a stream reader function +---@param conn uv_tcp_t +---@return fun(err: string, buffer: string) # callback function +function JUnitReport:get_stream_reader(conn) + self.conn = conn + self.result_parser = self.result_parser_fac:get_parser() + + return vim.schedule_wrap(function(err, buffer) + if err then + self:on_error(err) + self:on_close() + self.conn:close() + return + end + + if buffer then + self:on_update(buffer) + else + self:on_close() + self.conn:close() + end + end) +end + +---Runs on connection update +---@private +---@param text string +function JUnitReport:on_update(text) + self.result_parser:parse(text) +end + +---Runs on connection close +---@private +function JUnitReport:on_close() end + +---Runs on connection error +---@private +---@param err string error +function JUnitReport:on_error(err) + log.error('Error while running test', err) +end + +return JUnitReport diff --git a/lua/java-test/results/execution-status.lua b/lua/java-test/results/execution-status.lua new file mode 100644 index 0000000..18008c0 --- /dev/null +++ b/lua/java-test/results/execution-status.lua @@ -0,0 +1,7 @@ +---@enum java-test.TestExecutionStatus +local TestStatus = { + Started = 'started', + Ended = 'ended', +} + +return TestStatus diff --git a/lua/java-test/results/message-id.lua b/lua/java-test/results/message-id.lua new file mode 100644 index 0000000..28a37a5 --- /dev/null +++ b/lua/java-test/results/message-id.lua @@ -0,0 +1,30 @@ +---@enum MessageId +local MessageId = { + -- Notification about a test inside the test suite. + -- TEST_TREE + testId + "," + testName + "," + isSuite + "," + testCount + "," + isDynamicTest + + -- "," + parentId + "," + displayName + "," + parameterTypes + "," + uniqueId + + -- isSuite = "true" or "false" + -- isDynamicTest = "true" or "false" + -- parentId = the unique id of its parent if it is a dynamic test, otherwise can be "-1" + -- displayName = the display name of the test + -- parameterTypes = comma-separated list of method parameter types if applicable, otherwise an + -- empty string + -- uniqueId = the unique ID of the test provided by JUnit launcher, otherwise an empty string + + TestTree = '%TSTTREE', + TestStart = '%TESTS', + TestEnd = '%TESTE', + TestFailed = '%FAILED', + TestError = '%ERROR', + ExpectStart = '%EXPECTS', + ExpectEnd = '%EXPECTE', + ActualStart = '%ACTUALS', + ActualEnd = '%ACTUALE', + TraceStart = '%TRACES', + TraceEnd = '%TRACEE', + IGNORE_TEST_PREFIX = '@Ignore: ', + ASSUMPTION_FAILED_TEST_PREFIX = '@AssumptionFailure: ', +} + +return MessageId diff --git a/lua/java-test/results/result-parser-factory.lua b/lua/java-test/results/result-parser-factory.lua new file mode 100644 index 0000000..4d635f8 --- /dev/null +++ b/lua/java-test/results/result-parser-factory.lua @@ -0,0 +1,13 @@ +local class = require('java-core.utils.class') +local TestParser = require('java-test.results.result-parser') + +---@class java-test.TestParserFactory +local TestParserFactory = class() + +---Returns a test parser of given type +---@return java-test.TestParser +function TestParserFactory:get_parser() + return TestParser() +end + +return TestParserFactory diff --git a/lua/java-test/results/result-parser.lua b/lua/java-test/results/result-parser.lua new file mode 100644 index 0000000..00468fc --- /dev/null +++ b/lua/java-test/results/result-parser.lua @@ -0,0 +1,196 @@ +local class = require('java-core.utils.class') + +local MessageId = require('java-test.results.message-id') +local TestStatus = require('java-test.results.result-status') +local TestExecStatus = require('java-test.results.execution-status') + +---@class java-test.TestParser +---@field private test_details java-test.TestResults[] +local TestParser = class() + +---Init +function TestParser:_init() + self.test_details = {} +end + +---@private +TestParser.node_parsers = { + [MessageId.TestTree] = 'parse_test_tree', + [MessageId.TestStart] = 'parse_test_start', + [MessageId.TestEnd] = 'parse_test_end', + [MessageId.TestFailed] = 'parse_test_failed', +} + +---@private +TestParser.strtobool = { + ['true'] = true, + ['false'] = false, +} + +---Parse a given text into test details +---@param text string test result buffer +function TestParser:parse(text) + if text:sub(-1) ~= '\n' then + text = text .. '\n' + end + + local line_iter = text:gmatch('(.-)\n') + + local line = line_iter() + + while line ~= nil do + local message_id = line:sub(1, 8):gsub('%s+', '') + local content = line:sub(9) + + local node_parser = TestParser.node_parsers[message_id] + + if node_parser then + local data = vim.split(content, ',', { plain = true, trimempty = true }) + + if self[TestParser.node_parsers[message_id]] then + self[TestParser.node_parsers[message_id]](self, data, line_iter) + end + end + + line = line_iter() + end +end + +---Returns the parsed test details +---@return java-test.TestResults # parsed test details +function TestParser:get_test_details() + return self.test_details +end + +---@private +function TestParser:parse_test_tree(data) + local node = { + test_id = tonumber(data[1]), + test_name = data[2], + is_suite = TestParser.strtobool[data[3]], + test_count = tonumber(data[4]), + is_dynamic_test = TestParser.strtobool[data[5]], + parent_id = tonumber(data[6]), + display_name = data[7], + parameter_types = data[8], + unique_id = data[9], + } + + local parent = self:find_result_node(node.parent_id) + + if not parent then + table.insert(self.test_details, node) + else + parent.children = parent.children or {} + table.insert(parent.children, node) + end +end + +---@private +function TestParser:parse_test_start(data) + local test_id = tonumber(data[1]) + local node = self:find_result_node(test_id) + assert(node) + node.result = {} + node.result.execution = TestExecStatus.Started +end + +---@private +function TestParser:parse_test_end(data) + local test_id = tonumber(data[1]) + local node = self:find_result_node(test_id) + assert(node) + node.result.execution = TestExecStatus.Ended +end + +---@private +function TestParser:parse_test_failed(data, line_iter) + local test_id = tonumber(data[1]) + local node = self:find_result_node(test_id) + assert(node) + + node.result.status = TestStatus.Failed + + while true do + local line = line_iter() + + if line == nil then + break + end + + -- EXPECTED + if vim.startswith(line, MessageId.ExpectStart) then + node.result.expected = self:get_content_until_end_tag(MessageId.ExpectEnd, line_iter) + + -- ACTUAL + elseif vim.startswith(line, MessageId.ActualStart) then + node.result.actual = self:get_content_until_end_tag(MessageId.ActualEnd, line_iter) + + -- TRACE + elseif vim.startswith(line, MessageId.TraceStart) then + node.result.trace = self:get_content_until_end_tag(MessageId.TraceEnd, line_iter) + end + end +end + +---@private +function TestParser:get_content_until_end_tag(end_tag, line_iter) + local content = {} + + while true do + local line = line_iter() + + if line == nil or vim.startswith(line, end_tag) then + break + end + + table.insert(content, line) + end + + return content +end + +---@private +function TestParser:find_result_node(id) + local function find_node(nodes) + if not nodes or #nodes == 0 then + return + end + + for _, node in ipairs(nodes) do + if node.test_id == id then + return node + end + + local _node = find_node(node.children) + + if _node then + return _node + end + end + end + + return find_node(self.test_details) +end + +return TestParser + +---@class java-test.TestResultExecutionDetails +---@field actual string[] lines +---@field expected string[] lines +---@field status java-test.TestStatus +---@field execution java-test.TestExecutionStatus +---@field trace string[] lines + +---@class java-test.TestResults +---@field display_name string +---@field is_dynamic_test boolean +---@field is_suite boolean +---@field parameter_types string +---@field parent_id integer +---@field test_count integer +---@field test_id integer +---@field test_name string +---@field unique_id string +---@field result java-test.TestResultExecutionDetails +---@field children java-test.TestResults[] diff --git a/lua/java-test/results/result-status.lua b/lua/java-test/results/result-status.lua new file mode 100644 index 0000000..90014bc --- /dev/null +++ b/lua/java-test/results/result-status.lua @@ -0,0 +1,7 @@ +---@enum java-test.TestStatus +local TestStatus = { + Failed = 'failed', + Skipped = 'skipped', +} + +return TestStatus diff --git a/lua/java-test/ui/floating-report-viewer.lua b/lua/java-test/ui/floating-report-viewer.lua new file mode 100644 index 0000000..4cd5401 --- /dev/null +++ b/lua/java-test/ui/floating-report-viewer.lua @@ -0,0 +1,88 @@ +local class = require('java-core.utils.class') +local StringBuilder = require('java-test.utils.string-builder') +local TestStatus = require('java-test.results.result-status') +local ReportViewer = require('java-test.ui.report-viewer') + +---@class java-test.FloatingReportViewer +---@field window number|nil +---@field buffer number|nil +---@overload fun(): java-test.FloatingReportViewer +local FloatingReportViewer = class(ReportViewer) + +---Shows the test results in a floating window +---@param test_results java-test.TestResults[] +function FloatingReportViewer:show(test_results) + ---@param results java-test.TestResults[] + local function build_result(results, indentation, prefix) + local ts = StringBuilder() + + for _, result in ipairs(results) do + local tc = StringBuilder() + + tc.append(prefix .. indentation) + + if result.is_suite then + tc.append(' ' .. result.test_name).lbreak() + else + if result.result.status == TestStatus.Failed then + tc.append('󰅙 ' .. result.test_name).lbreak().append(indentation).append(result.result.trace, indentation) + elseif result.result.status == TestStatus.Skipped then + tc.append(' ' .. result.test_name).lbreak() + else + tc.append(' ' .. result.test_name).lbreak() + end + end + + if result.children then + tc.append(build_result(result.children, indentation .. '\t', '')) + end + + ts.append(tc.build()) + end + + return ts.build() + end + + local res = build_result(test_results, '', '') + + self:show_in_window(vim.split(res, '\n')) +end + +function FloatingReportViewer:show_in_window(content) + local Popup = require('nui.popup') + local event = require('nui.utils.autocmd').event + + local popup = Popup({ + enter = true, + focusable = true, + border = { + style = 'rounded', + }, + position = '50%', + relative = 'editor', + size = { + width = '80%', + height = '60%', + }, + win_options = { + foldmethod = 'indent', + foldlevel = 1, + }, + }) + + -- mount/open the component + popup:mount() + + -- unmount component when cursor leaves buffer + popup:on(event.BufLeave, function() + popup:unmount() + end) + + -- set content + vim.api.nvim_buf_set_lines(popup.bufnr, 0, 1, false, content) + + vim.bo[popup.bufnr].modifiable = false + vim.bo[popup.bufnr].readonly = true +end + +return FloatingReportViewer diff --git a/lua/java-test/ui/report-viewer.lua b/lua/java-test/ui/report-viewer.lua new file mode 100644 index 0000000..2fd853a --- /dev/null +++ b/lua/java-test/ui/report-viewer.lua @@ -0,0 +1,12 @@ +local class = require('java-core.utils.class') + +---@class java-test.ReportViewer +local ReportViewer = class() + +---Shows the test results in a floating window +---@param _ java-test.TestResults[] +function ReportViewer:show(_) + error('not implemented') +end + +return ReportViewer diff --git a/lua/java-test/utils/string-builder.lua b/lua/java-test/utils/string-builder.lua new file mode 100644 index 0000000..91ce178 --- /dev/null +++ b/lua/java-test/utils/string-builder.lua @@ -0,0 +1,30 @@ +local StringBuilder = function() + local str = '' + + local M = {} + M.append = function(text, prefix) + prefix = prefix or '' + if type(text) == 'table' then + for _, line in ipairs(text) do + str = str .. prefix .. line .. '\n' + end + else + assert(type(text) == 'string') + str = str .. prefix .. text + end + return M + end + + M.lbreak = function() + str = str .. '\n' + return M + end + + M.build = function() + return str + end + + return M +end + +return StringBuilder diff --git a/lua/java.lua b/lua/java.lua index 3e3515d..5315e6c 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -1,98 +1,98 @@ -local decomple_watch = require('java.startup.decompile-watcher') -local mason_dep = require('java.startup.mason-dep') -local setup_wrap = require('java.startup.lspconfig-setup-wrap') -local startup_check = require('java.startup.startup-check') - -local command_util = require('java.utils.command') - -local test_api = require('java.api.test') -local dap_api = require('java.api.dap') local runner_api = require('java.api.runner') local settings_api = require('java.api.settings') local profile_ui = require('java.ui.profile') -local global_config = require('java.config') - local M = {} +---@param custom_config java.PartialConfig | nil function M.setup(custom_config) - vim.api.nvim_exec_autocmds('User', { pattern = 'JavaPreSetup' }) + local default_conf = require('java.config') + local test_api = require('java-test') - local config = - vim.tbl_deep_extend('force', global_config, custom_config or {}) + local config = vim.tbl_deep_extend('force', default_conf, custom_config or {}) vim.g.nvim_java_config = config - vim.api.nvim_exec_autocmds( - 'User', - { pattern = 'JavaSetup', data = { config = config } } - ) - - mason_dep.add_custom_registries(config.mason.registries) - - if not startup_check() then - return + ---------------------------------------------------------------------- + -- neovim version check -- + ---------------------------------------------------------------------- + if config.checks.nvim_version then + if vim.fn.has('nvim-0.11.5') ~= 1 then + local err = require('java-core.utils.errors') + err.throw([[ + nvim-java is only tested on Neovim 0.11.5 or greater + Please upgrade to Neovim 0.11.5 or greater. + If you are sure it works on your version, disable the version check: + checks = { nvim_version = false }' + ]]) + end end - local is_installing = mason_dep.install(config) - - if is_installing then - return + ---------------------------------------------------------------------- + -- logger setup -- + ---------------------------------------------------------------------- + if config.log then + require('java-core.utils.log2').setup(config.log --[[@as java-core.PartialLog2Config]]) end - setup_wrap.setup(config) - decomple_watch.setup() - dap_api.setup_dap_on_lsp_attach() - - vim.api.nvim_exec_autocmds( - 'User', - { pattern = 'JavaPostSetup', data = { config = config } } - ) -end - ----@param path string[] ----@param command fun() ----@param opts vim.api.keyset.user_command -function M.register_api(path, command, opts) - local name = command_util.path_to_command_name(path) - - vim.api.nvim_create_user_command(name, command, opts or {}) + ---------------------------------------------------------------------- + -- package installation -- + ---------------------------------------------------------------------- + local Manager = require('pkgm.manager') + local pkgm = Manager() - local last_index = #path - local func_name = path[last_index] + pkgm:install('jdtls', config.jdtls.version) - table.remove(path, last_index) + if config.java_test.enable then + ---------------------------------------------------------------------- + -- test -- + ---------------------------------------------------------------------- + pkgm:install('java-test', config.java_test.version) - local node = M + M.test = { + run_current_class = test_api.run_current_class, + debug_current_class = test_api.debug_current_class, - for _, v in ipairs(path) do - if not node[v] then - node[v] = {} - end + run_current_method = test_api.run_current_method, + debug_current_method = test_api.debug_current_method, - node = node[v] + view_last_report = test_api.view_last_report, + } end - node[func_name] = command -end + if config.java_debug_adapter.enable then + ---------------------------------------------------------------------- + -- debugger -- + ---------------------------------------------------------------------- + pkgm:install('java-debug', config.java_debug_adapter.version) + require('java-dap').setup() + + M.dap = { + config_dap = function() + require('java-dap').config_dap() + end, + } + end ----------------------------------------------------------------------- --- DAP APIs -- ----------------------------------------------------------------------- -M.dap = {} -M.dap.config_dap = dap_api.config_dap + if config.spring_boot_tools.enable then + pkgm:install('spring-boot-tools', config.spring_boot_tools.version) + end ----------------------------------------------------------------------- --- Test APIs -- ----------------------------------------------------------------------- -M.test = {} -M.test.run_current_class = test_api.run_current_class -M.test.debug_current_class = test_api.debug_current_class + if config.lombok.enable then + pkgm:install('lombok', config.lombok.version) + end -M.test.run_current_method = test_api.run_current_method -M.test.debug_current_method = test_api.debug_current_method + if config.jdk.auto_install then + pkgm:install('openjdk', config.jdk.version) + end -M.test.view_last_report = test_api.view_last_report + ---------------------------------------------------------------------- + -- init -- + ---------------------------------------------------------------------- + require('java.startup.lsp_setup').setup(config) + require('java.startup.decompile-watcher').setup() + require('java-refactor').setup() +end ---------------------------------------------------------------------- -- Runner APIs -- diff --git a/lua/java/api/dap.lua b/lua/java/api/dap.lua deleted file mode 100644 index 8cb0936..0000000 --- a/lua/java/api/dap.lua +++ /dev/null @@ -1,58 +0,0 @@ -local JavaDap = require('java.dap') - -local log = require('java.utils.log') -local get_error_handler = require('java.handlers.error') -local jdtls = require('java.utils.jdtls') -local async = require('java-core.utils.async').sync -local notify = require('java-core.utils.notify') -local project_config = require('java.api.profile_config') - -local M = {} - ----Setup dap config & adapter on jdtls attach event -function M.setup_dap_on_lsp_attach() - log.info('add LspAttach event handlers to setup dap adapter & config') - - M.even_id = vim.api.nvim_create_autocmd('LspAttach', { - callback = M.on_jdtls_attach, - group = vim.api.nvim_create_augroup('nvim-java-dap-config', {}), - }) -end - -function M.config_dap() - log.info('configuring dap') - - return async(function() - local config = vim.g.nvim_java_config - if config.notifications.dap then - notify.warn('Configuring DAP') - end - JavaDap:new(jdtls()):config_dap() - if config.notifications.dap then - notify.info('DAP configured') - end - end) - .catch(get_error_handler('dap configuration failed')) - .run() -end - -function M.on_jdtls_attach(ev) - local client = vim.lsp.get_client_by_id(ev.data.client_id) - - if client == nil then - return - end - - local server_name = client.name - - if server_name == 'jdtls' then - log.info('setup java dap config & adapter') - - project_config.setup() - M.config_dap() - -- removing the event handler after configuring dap - vim.api.nvim_del_autocmd(M.even_id) - end -end - -return M diff --git a/lua/java/api/profile_config.lua b/lua/java/api/profile_config.lua index 3614d02..6389cae 100644 --- a/lua/java/api/profile_config.lua +++ b/lua/java/api/profile_config.lua @@ -1,5 +1,5 @@ -local async = require('java-core.utils.async').sync -local get_error_handler = require('java.handlers.error') +local runner = require('async.runner') +local get_error_handler = require('java-core.utils.error_handler') local class = require('java-core.utils.class') local config_path = vim.fn.stdpath('data') .. '/nvim-java-profiles.json' @@ -89,19 +89,15 @@ function M.load_current_project_profiles() for dap_config_name, dap_config_name_val in pairs(current) do result[dap_config_name] = {} for _, profile in pairs(dap_config_name_val) do - result[dap_config_name][profile.name] = Profile( - profile.vm_args, - profile.prog_args, - profile.name, - profile.is_active - ) + result[dap_config_name][profile.name] = + Profile(profile.vm_args, profile.prog_args, profile.name, profile.is_active) end end return result end function M.save() - return async(function() + return runner(function() local full_config = read_full_config() local updated_profiles = {} for dap_config_name, val in pairs(M.project_profiles) do @@ -157,7 +153,7 @@ end --- @param dap_config_name string --- @param profile_name string function M.set_active_profile(profile_name, dap_config_name) - if not M.__has_profile(profile_name, dap_config_name) then + if not M.has_profile(profile_name, dap_config_name) then return end @@ -175,11 +171,7 @@ end --- @param dap_config_name string --- @param current_profile_name string|nil --- @param new_profile Profile -function M.add_or_update_profile( - new_profile, - current_profile_name, - dap_config_name -) +function M.add_or_update_profile(new_profile, current_profile_name, dap_config_name) assert(new_profile.name, 'Profile name is required') if current_profile_name then M.project_profiles[dap_config_name][current_profile_name] = nil @@ -205,7 +197,7 @@ end --- @param dap_config_name string --- @param profile_name string function M.delete_profile(profile_name, dap_config_name) - if not M.__has_profile(profile_name, dap_config_name) then + if not M.has_profile(profile_name, dap_config_name) then return end @@ -213,10 +205,11 @@ function M.delete_profile(profile_name, dap_config_name) M.save() end +---@private ---Returns true if a profile exists by given name ---@param profile_name string ---@param dap_config_name string -function M.__has_profile(profile_name, dap_config_name) +function M.has_profile(profile_name, dap_config_name) if M.project_profiles[dap_config_name][profile_name] then return true end @@ -228,7 +221,7 @@ end M.Profile = Profile M.setup = function() - async(function() + runner(function() M.current_project_path = vim.fn.getcwd() M.project_profiles = M.load_current_project_profiles() end) diff --git a/lua/java/api/runner.lua b/lua/java/api/runner.lua index d5e34c5..ae1d8aa 100644 --- a/lua/java/api/runner.lua +++ b/lua/java/api/runner.lua @@ -1,6 +1,6 @@ -local async = require('java-core.utils.async').sync -local get_error_handler = require('java.handlers.error') -local Runner = require('java.runner.runner') +local runner = require('async.runner') +local get_error_handler = require('java-core.utils.error_handler') +local Runner = require('java-runner.runner') local M = { built_in = {}, @@ -11,7 +11,7 @@ local M = { --- @param opts {} function M.built_in.run_app(opts) - async(function() + runner(function() M.runner:start_run(opts.args) end) .catch(get_error_handler('Failed to run app')) @@ -19,7 +19,7 @@ function M.built_in.run_app(opts) end function M.built_in.toggle_logs() - async(function() + runner(function() M.runner:toggle_open_log() end) .catch(get_error_handler('Failed to run app')) @@ -27,7 +27,7 @@ function M.built_in.toggle_logs() end function M.built_in.switch_app() - async(function() + runner(function() M.runner:switch_log() end) .catch(get_error_handler('Failed to switch run')) @@ -35,7 +35,7 @@ function M.built_in.switch_app() end function M.built_in.stop_app() - async(function() + runner(function() M.runner:stop_run() end) .catch(get_error_handler('Failed to stop run')) diff --git a/lua/java/api/settings.lua b/lua/java/api/settings.lua index 8cc47a1..0349d90 100644 --- a/lua/java/api/settings.lua +++ b/lua/java/api/settings.lua @@ -1,22 +1,18 @@ -local get_jdtls = require('java.utils.jdtls2') +local lsp_utils = require('java-core.utils.lsp') local JdtlsClient = require('java-core.ls.clients.jdtls-client') local conf_utils = require('java.utils.config') local notify = require('java-core.utils.notify') -local ui = require('java.utils.ui') -local async = require('java-core.utils.async').sync -local get_error_handler = require('java.handlers.error') +local ui = require('java.ui.utils') +local runner = require('async.runner') +local get_error_handler = require('java-core.utils.error_handler') local M = {} function M.change_runtime() - local client = get_jdtls() + local client = lsp_utils.get_jdtls() ---@type RuntimeOption[] - local runtimes = conf_utils.get_property_from_conf( - client.config, - 'settings.java.configuration.runtimes', - {} - ) + local runtimes = conf_utils.get_property_from_conf(client.config, 'settings.java.configuration.runtimes', {}) if #runtimes < 1 then notify.error( @@ -29,18 +25,12 @@ function M.change_runtime() local jdtls = JdtlsClient(client) - async(function() - local sel_runtime = ui.select( - 'Select Runtime', - runtimes, - function(runtime) - return runtime.name .. '::' .. runtime.path - end - ) + runner(function() + local sel_runtime = ui.select('Select Runtime', runtimes, function(runtime) + return runtime.name .. '::' .. runtime.path + end) - for _, runtime in - ipairs(client.config.settings.java.configuration.runtimes) - do + for _, runtime in ipairs(client.config.settings.java.configuration.runtimes) do if sel_runtime.path == runtime.path then runtime.default = true else diff --git a/lua/java/api/test.lua b/lua/java/api/test.lua deleted file mode 100644 index 60aded8..0000000 --- a/lua/java/api/test.lua +++ /dev/null @@ -1,86 +0,0 @@ -local JavaDap = require('java.dap') - -local log = require('java.utils.log') -local jdtls = require('java.utils.jdtls') -local get_error_handler = require('java.handlers.error') - -local async = require('java-core.utils.async').sync - -local JUnitReport = require('java-test.reports.junit') -local ResultParserFactory = require('java-test.results.result-parser-factory') -local ReportViewer = require('java-test.ui.floating-report-viewer') - -local M = { - ---@type java_test.JUnitTestReport - last_report = nil, -} - ----Setup dap config & adapter on jdtls attach event -function M.run_current_class() - log.info('run current test class') - - return async(function() - return JavaDap:new(jdtls()) - :execute_current_test_class(M.get_report(), { noDebug = true }) - end) - .catch(get_error_handler('failed to run the current test class')) - .run() -end - -function M.debug_current_class() - log.info('debug current test class') - - return async(function() - JavaDap:new(jdtls()):execute_current_test_class(M.get_report(), {}) - end) - .catch(get_error_handler('failed to debug the current test class')) - .run() -end - -function M.debug_current_method() - log.info('debug current test method') - - return async(function() - return JavaDap:new(jdtls()) - :execute_current_test_method(M.get_report(), {}) - end) - .catch(get_error_handler('failed to run the current test method')) - .run() -end - -function M.run_current_method() - log.info('run current test method') - - return async(function() - return JavaDap:new(jdtls()) - :execute_current_test_method(M.get_report(), { noDebug = true }) - end) - .catch(get_error_handler('failed to run the current test method')) - .run() -end - -function M.view_last_report() - if M.last_report then - M.last_report:show_report() - end -end - ----@private -function M.config_dap() - log.info('configuring dap') - - return async(function() - JavaDap:new(jdtls()):config_dap() - end) - .catch(get_error_handler('dap configuration failed')) - .run() -end - ----@private -function M.get_report() - local report = JUnitReport(ResultParserFactory(), ReportViewer()) - M.last_report = report - return report -end - -return M diff --git a/lua/java/config.lua b/lua/java/config.lua index 47467ee..b398135 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -1,57 +1,74 @@ +local JDTLS_VERSION = '1.43.0' + +local jdtls_version_map = { + ['1.43.0'] = { + lombok = '1.18.40', + java_test = '0.40.1', + java_debug_adapter = '0.58.2', + spring_boot_tools = '1.55.1', + jdk = '17', + }, +} + +local V = jdtls_version_map[JDTLS_VERSION] + ---@class java.Config ----@field root_markers string[] +---@field checks { nvim_version: boolean } ---@field jdtls { version: string } ----@field lombok { version: string } +---@field lombok { enable: boolean, version: string } ---@field java_test { enable: boolean, version: string } ---@field java_debug_adapter { enable: boolean, version: string } ---@field spring_boot_tools { enable: boolean, version: string } ---@field jdk { auto_install: boolean, version: string } ---@field notifications { dap: boolean } ----@field verification { invalid_order: boolean, duplicate_setup_calls: boolean, invalid_mason_registry: boolean } ----@field mason { registries: string[] } +---@field log java-core.Log2Config + +---@class java.PartialConfig +---@field checks? { nvim_version?: boolean } +---@field jdtls? { version?: string } +---@field lombok? { enable?: boolean, version?: string } +---@field java_test? { enable?: boolean, version?: string } +---@field java_debug_adapter? { enable?: boolean, version?: string } +---@field spring_boot_tools? { enable?: boolean, version?: string } +---@field jdk? { auto_install?: boolean, version?: string } +---@field notifications? { dap?: boolean } +---@field log? java-core.PartialLog2Config + +---@type java.Config local config = { - -- list of file that exists in root of the project - root_markers = { - 'settings.gradle', - 'settings.gradle.kts', - 'pom.xml', - 'build.gradle', - 'mvnw', - 'gradlew', - 'build.gradle', - 'build.gradle.kts', - '.git', + checks = { + nvim_version = true, }, jdtls = { - version = 'v1.43.0', + version = JDTLS_VERSION, }, lombok = { - version = 'nightly', + enable = true, + version = V.lombok, }, -- load java test plugins java_test = { enable = true, - version = '0.40.1', + version = V.java_test, }, -- load java debugger plugins java_debug_adapter = { enable = true, - version = '0.58.2', + version = V.java_debug_adapter, }, spring_boot_tools = { enable = true, - version = '1.55.1', + version = V.spring_boot_tools, }, jdk = { - -- install jdk using mason.nvim auto_install = true, - version = '17.0.2', + version = V.jdk, }, notifications = { @@ -59,38 +76,13 @@ local config = { dap = true, }, - -- We do multiple verifications to make sure things are in place to run this - -- plugin - verification = { - -- nvim-java checks for the order of execution of following - -- * require('java').setup() - -- * require('lspconfig').jdtls.setup() - -- IF they are not executed in the correct order, you will see a error - -- notification. - -- Set following to false to disable the notification if you know what you - -- are doing - invalid_order = true, - - -- nvim-java checks if the require('java').setup() is called multiple - -- times. - -- IF there are multiple setup calls are executed, an error will be shown - -- Set following property value to false to disable the notification if - -- you know what you are doing - duplicate_setup_calls = true, - - -- nvim-java checks if nvim-java/mason-registry is added correctly to - -- mason.nvim plugin. - -- IF it's not registered correctly, an error will be thrown and nvim-java - -- will stop setup - invalid_mason_registry = false, - }, - - mason = { - -- These mason registries will be prepended to the existing mason - -- configuration - registries = { - 'github:nvim-java/mason-registry', - }, + log = { + use_console = true, + use_file = true, + level = 'info', + log_file = vim.fn.stdpath('state') .. '/nvim-java.log', + max_lines = 1000, + show_location = false, }, } diff --git a/lua/java/dap/init.lua b/lua/java/dap/init.lua deleted file mode 100644 index 15804a9..0000000 --- a/lua/java/dap/init.lua +++ /dev/null @@ -1,112 +0,0 @@ -local log = require('java.utils.log') -local buf_util = require('java.utils.buffer') -local win_util = require('java.utils.window') - -local async = require('java-core.utils.async').sync -local notify = require('java-core.utils.notify') - -local DapSetup = require('java-dap.api.setup') -local DapRunner = require('java-dap.api.runner') - -local JavaCoreTestApi = require('java-core.api.test') -local profile_config = require('java.api.profile_config') - ----@class JavaDap ----@field private client LspClient ----@field private dap JavaCoreDap ----@field private test_api java_core.TestApi ----@field private test_client java-core.TestClient -local M = {} - ----@param args { client: LspClient } ----@return JavaDap -function M:new(args) - local o = { - client = args.client, - } - - o.test_api = JavaCoreTestApi:new({ - client = args.client, - runner = DapRunner(), - }) - - o.dap = DapSetup(args.client) - - setmetatable(o, self) - self.__index = self - return o -end - ----Run the current test class ----@param report java_test.JUnitTestReport ----@param config JavaCoreDapLauncherConfigOverridable -function M:execute_current_test_class(report, config) - log.debug('running the current class') - - return self.test_api:run_class_by_buffer( - buf_util.get_curr_buf(), - report, - config - ) -end - -function M:execute_current_test_method(report, config) - log.debug('running the current method') - - local method = self:find_current_test_method() - - if not method then - notify.warn('cursor is not on a test method') - return - end - - self.test_api:run_test({ method }, report, config) -end - -function M:find_current_test_method() - log.debug('finding the current test method') - - local cursor = win_util.get_cursor() - local methods = self.test_api:get_test_methods(buf_util.get_curr_uri()) - - for _, method in ipairs(methods) do - local line_start = method.range.start.line - local line_end = method.range['end'].line - - if cursor.line >= line_start and cursor.line <= line_end then - return method - end - end -end - -function M:config_dap() - log.debug('set dap adapter callback function') - local nvim_dap = require('dap') - nvim_dap.adapters.java = function(callback) - async(function() - local adapter = self.dap:get_dap_adapter() - callback(adapter --[[@as Adapter]]) - end).run() - end - local dap_config = self.dap:get_dap_config() - - for _, config in ipairs(dap_config) do - local profile = profile_config.get_active_profile(config.name) - if profile then - config.vmArgs = profile.vm_args - config.args = profile.prog_args - end - end - -- if dap is already running, need to terminate it to apply new config - if nvim_dap.session then - nvim_dap.terminate() - if vim.g.nvim_java_config.notifications.dap then - notify.warn('Terminating current dap session') - end - end - -- end - nvim_dap.configurations.java = nvim_dap.configurations.java or {} - vim.list_extend(nvim_dap.configurations.java, dap_config) -end - -return M diff --git a/lua/java/startup/decompile-watcher.lua b/lua/java/startup/decompile-watcher.lua index 9c3e9c1..d512773 100644 --- a/lua/java/startup/decompile-watcher.lua +++ b/lua/java/startup/decompile-watcher.lua @@ -1,7 +1,7 @@ -local jdtls = require('java.utils.jdtls2') -local get_error_handler = require('java.handlers.error') +local lsp_utils = require('java-core.utils.lsp') +local get_error_handler = require('java-core.utils.error_handler') -local async = require('java-core.utils.async').sync +local runner = require('async.runner') local JavaCoreJdtlsClient = require('java-core.ls.clients.jdtls-client') @@ -21,8 +21,8 @@ function M.setup() callback = function(opts) local done = false - async(function() - local client = jdtls() + runner(function() + local client = lsp_utils.get_jdtls() local buffer = opts.buf local text = JavaCoreJdtlsClient(client):java_decompile(opts.file) @@ -43,7 +43,7 @@ function M.setup() done = true end) - .catch(get_error_handler('decompilation failed for ' .. opts.file)) + .catch(get_error_handler('Decompilation failed for ' .. opts.file)) .run() vim.wait(10000, function() diff --git a/lua/java/startup/duplicate-setup-check.lua b/lua/java/startup/duplicate-setup-check.lua deleted file mode 100644 index cbe38eb..0000000 --- a/lua/java/startup/duplicate-setup-check.lua +++ /dev/null @@ -1,27 +0,0 @@ -local M = {} - -local message = 'require("java").setup() is called more than once' - .. '\nnvim-java will continue to setup but nvim-java configurations might not work as expected' - .. '\nThis might be due to old installation instructions.' - .. '\nPlease check the latest guide at https://github.com/nvim-java/nvim-java#hammer-how-to-install' - .. '\nIf you know what you are doing, you can disable the check from the config' - .. '\nhttps://github.com/nvim-java/nvim-java#wrench-configuration' - -function M.is_valid() - if vim.g.nvim_java_setup_is_called then - return { - success = false, - continue = true, - message = message, - } - end - - vim.g.nvim_java_setup_is_called = true - - return { - success = true, - continue = true, - } -end - -return M diff --git a/lua/java/startup/exec-order-check.lua b/lua/java/startup/exec-order-check.lua deleted file mode 100644 index 259f9f6..0000000 --- a/lua/java/startup/exec-order-check.lua +++ /dev/null @@ -1,48 +0,0 @@ -local lspconfig = require('lspconfig') - -local M = {} - -lspconfig.util.on_setup = lspconfig.util.add_hook_before( - lspconfig.util.on_setup, - function(config) - if config.name == 'jdtls' then - vim.g.nvim_java_jdtls_setup_is_called = true - end - end -) - -local message = 'Looks like require("lspconfig").jdtls.setup() is called before require("java").setup().' - .. '\nnvim-java will continue to setup but most features may not work as expected' - .. '\nThis might be due to old installation instructions.' - .. '\nPlease check the latest guide at https://github.com/nvim-java/nvim-java#hammer-how-to-install' - .. '\nIf you know what you are doing, you can disable the check from the config' - .. '\nhttps://github.com/nvim-java/nvim-java#wrench-configuration' - -function M.is_valid() - if vim.g.nvim_java_jdtls_setup_is_called then - return { - success = false, - continue = true, - message = message, - } - end - - local clients = vim.lsp.get_clients - and vim.lsp.get_clients({ name = 'jdtls' }) - or vim.lsp.get_active_clients({ name = 'jdtls' }) - - if #clients > 0 then - return { - success = false, - continue = true, - message = message, - } - end - - return { - success = true, - continue = true, - } -end - -return M diff --git a/lua/java/startup/lsp_setup.lua b/lua/java/startup/lsp_setup.lua new file mode 100644 index 0000000..ea1646b --- /dev/null +++ b/lua/java/startup/lsp_setup.lua @@ -0,0 +1,43 @@ +local path = require('java-core.utils.path') +local List = require('java-core.utils.list') +local Manager = require('pkgm.manager') + +local server = require('java-core.ls.servers.jdtls') + +local M = {} + +---comment +---@param config java.Config +function M.setup(config) + local jdtls_plugins = List:new() + + if config.java_test.enable then + jdtls_plugins:push('java-test') + end + + if config.java_debug_adapter.enable then + jdtls_plugins:push('java-debug') + end + + if config.spring_boot_tools.enable then + jdtls_plugins:push('spring-boot-tools') + + local spring_boot_root = Manager:get_install_dir('spring-boot-tools', config.spring_boot_tools.version) + + require('spring_boot').setup({ + ls_path = path.join(spring_boot_root, 'extension', 'language-server'), + }) + + require('spring_boot').init_lsp_commands() + end + + local default_config = server.get_config({ + plugins = jdtls_plugins, + use_jdk = config.jdk.auto_install, + use_lombok = config.lombok.enable, + }) + + vim.lsp.config('jdtls', default_config) +end + +return M diff --git a/lua/java/startup/lspconfig-setup-wrap.lua b/lua/java/startup/lspconfig-setup-wrap.lua deleted file mode 100644 index 2c92189..0000000 --- a/lua/java/startup/lspconfig-setup-wrap.lua +++ /dev/null @@ -1,52 +0,0 @@ -local lspconfig = require('lspconfig') -local log = require('java.utils.log') -local mason_util = require('java-core.utils.mason') - -local server = require('java-core.ls.servers.jdtls') - -local M = {} - ----comment ----@param config java.Config -function M.setup(config) - log.info('wrap lspconfig.java.setup function to inject a custom java config') - ---@type fun(config: LspSetupConfig) - local org_setup = lspconfig.jdtls.setup - - lspconfig.jdtls.setup = function(user_config) - vim.api.nvim_exec_autocmds('User', { pattern = 'JavaJdtlsSetup' }) - - local jdtls_plugins = {} - - if config.java_test.enable then - table.insert(jdtls_plugins, 'java-test') - end - - if config.java_debug_adapter.enable then - table.insert(jdtls_plugins, 'java-debug-adapter') - end - - if config.spring_boot_tools.enable then - table.insert(jdtls_plugins, 'spring-boot-tools') - end - - local default_config = server.get_config({ - root_markers = config.root_markers, - jdtls_plugins = jdtls_plugins, - use_mason_jdk = config.jdk.auto_install, - }) - - if config.spring_boot_tools.enable then - require('spring_boot').setup({ - ls_path = mason_util.get_pkg_path('spring-boot-tools') - .. '/extension/language-server', - }) - - require('spring_boot').init_lsp_commands() - end - - org_setup(vim.tbl_extend('force', default_config, user_config)) - end -end - -return M diff --git a/lua/java/startup/mason-dep.lua b/lua/java/startup/mason-dep.lua deleted file mode 100644 index 129c777..0000000 --- a/lua/java/startup/mason-dep.lua +++ /dev/null @@ -1,105 +0,0 @@ -local log = require('java.utils.log') -local mason_ui = require('mason.ui') -local mason_util = require('java.utils.mason') -local list_util = require('java-core.utils.list') -local notify = require('java-core.utils.notify') -local async = require('java-core.utils.async') -local lazy = require('java.ui.lazy') -local sync = async.sync -local mason_v2 = require('mason.version').MAJOR_VERSION == 2 - -local List = require('java-core.utils.list') - -local M = {} - ----Add custom registries to Mason 1.x ----@param registries java.Config -local function add_custom_registries_v1(registries) - local mason_default_config = require('mason.settings').current - - local new_registries = - list_util:new(registries):concat(mason_default_config.registries) - - require('mason').setup({ - registries = new_registries, - }) -end - ----Add custom registries to Mason 2.x ----@param registries java.Config -local function add_custom_registries_v2(registries) - for _, reg in ipairs(registries) do - ---@diagnostic disable-next-line: undefined-field - require('mason-registry').sources:prepend(reg) - end -end - -if mason_v2 then - M.add_custom_registries = add_custom_registries_v2 -else - M.add_custom_registries = add_custom_registries_v1 -end - ----Install mason package dependencies for nvim-java ----@param config java.Config -function M.install(config) - local packages = M.get_pkg_list(config) - local is_outdated = mason_util.is_outdated(packages) - - if is_outdated then - sync(function() - M.refresh_and_install(packages) - end) - .catch(function(err) - notify.error('Failed to setup nvim-java ' .. tostring(err)) - log.error('failed to setup nvim-java ' .. tostring(err)) - end) - .run() - end - - return is_outdated -end - -function M.refresh_and_install(packages) - vim.schedule(function() - -- lazy covers mason - -- https://github.com/nvim-java/nvim-java/issues/51 - lazy.close_lazy_if_opened() - - mason_ui.open() - notify.warn('Please close and re-open after dependencies are installed') - end) - - mason_util.refresh_registry() - mason_util.install_pkgs(packages) -end - ----Returns a list of dependency packages ----@param config java.Config ----@return table -function M.get_pkg_list(config) - local deps = List:new({ - { name = 'jdtls', version = config.jdtls.version }, - { name = 'lombok-nightly', version = config.lombok.version }, - { name = 'java-test', version = config.java_test.version }, - { - name = 'java-debug-adapter', - version = config.java_debug_adapter.version, - }, - }) - - if config.jdk.auto_install then - deps:push({ name = 'openjdk-17', version = config.jdk.version }) - end - - if config.spring_boot_tools.enable then - deps:push({ - name = 'spring-boot-tools', - version = config.spring_boot_tools.version, - }) - end - - return deps -end - -return M diff --git a/lua/java/startup/mason-registry-check.lua b/lua/java/startup/mason-registry-check.lua deleted file mode 100644 index ce8db28..0000000 --- a/lua/java/startup/mason-registry-check.lua +++ /dev/null @@ -1,52 +0,0 @@ -local mason_v2 = require('mason.version').MAJOR_VERSION == 2 - -local mason_sources - -if mason_v2 then - -- compiler will complain when Mason 1.x is used - ---@diagnostic disable-next-line: undefined-field - mason_sources = require('mason-registry').sources -else - mason_sources = require('mason-registry.sources') -end - -local M = {} -if mason_v2 then - M.JAVA_REG_ID = 'nvim-java/mason-registry' -else - M.JAVA_REG_ID = 'github:nvim-java/mason-registry' -end - -function M.is_valid() - local iterator - - if mason_v2 then - -- the compiler will complain when Mason 1.x is in use - ---@diagnostic disable-next-line: undefined-field - iterator = mason_sources.iterate - else - -- the compiler will complain when Mason 2.x is in use - ---@diagnostic disable-next-line: undefined-field - iterator = mason_sources.iter - end - - for reg in iterator(mason_sources) do - if reg.id == M.JAVA_REG_ID then - return { - success = true, - continue = true, - } - end - end - - return { - success = false, - continue = false, - message = 'nvim-java mason registry is not added correctly!' - .. '\nThis occurs when mason.nvim configured incorrectly' - .. '\nPlease refer the link below to fix the issue' - .. '\nhttps://github.com/nvim-java/nvim-java/wiki/Q-&-A#no_entry-cannot-find-package-xxxxx', - } -end - -return M diff --git a/lua/java/startup/nvim-dep.lua b/lua/java/startup/nvim-dep.lua deleted file mode 100644 index 10c485e..0000000 --- a/lua/java/startup/nvim-dep.lua +++ /dev/null @@ -1,67 +0,0 @@ -local log = require('java.utils.log') - -local pkgs = { - { - name = 'mason-registry', - err = [[mason.nvim is not installed. nvim-java requires mason.nvim to install dependecies. - Please follow the install guide in https://github.com/nvim-java/nvim-java to install nvim-java]], - }, - { - name = 'dap', - err = [[nvim-dap is not installed. nvim-java requires nvim-dap to setup the debugger. -Please follow the install guide in https://github.com/nvim-java/nvim-java to install nvim-java]], - }, - { - name = 'lspconfig', - err = [[nvim-lspconfig is not installed. nvim-lspconfig requires nvim-lspconfig to show diagnostics & auto completion. -Please follow the install guide in https://github.com/nvim-java/nvim-java to install nvim-java]], - }, - { - name = 'java-refactor', - warn = [[nvim-java-refactor is not installed. nvim-java-refactor requires nvim-java to do code refactoring -Please add nvim-java-refactor to the current dependency list - -{ - "nvim-java/nvim-java", - dependencies = { - "nvim-java/nvim-java-refactor", - .... - } -} - -Please follow the install guide in https://github.com/nvim-java/nvim-java to install nvim-java]], - }, -} - -local M = {} - -function M.is_valid() - log.info('check neovim plugin dependencies') - - for _, pkg in ipairs(pkgs) do - local ok, _ = pcall(require, pkg.name) - - if not ok then - if pkg.warn then - return { - success = false, - continue = true, - message = pkg.warn, - } - else - return { - success = false, - continue = false, - message = pkg.err, - } - end - end - end - - return { - success = true, - continue = true, - } -end - -return M diff --git a/lua/java/startup/startup-check.lua b/lua/java/startup/startup-check.lua deleted file mode 100644 index a1bf5bd..0000000 --- a/lua/java/startup/startup-check.lua +++ /dev/null @@ -1,53 +0,0 @@ -local log = require('java.utils.log') -local notify = require('java-core.utils.notify') - -local function get_checkers() - local config = vim.g.nvim_java_config - local checks = {} - - if config.verification.invalid_mason_registry then - table.insert( - checks, - select(1, require('java.startup.mason-registry-check')) - ) - end - - if config.verification.invalid_order then - table.insert(checks, select(1, require('java.startup.exec-order-check'))) - end - - if config.verification.duplicate_setup_calls then - table.insert( - checks, - select(1, require('java.startup.duplicate-setup-check')) - ) - end - - table.insert(checks, select(1, require('java.startup.nvim-dep'))) - - return checks -end - -return function() - local checkers = get_checkers() - - for _, check in ipairs(checkers) do - local check_res = check.is_valid() - - if check_res.message then - if not check_res.success then - log.error(check_res.message) - notify.error(check_res.message) - else - log.warn(check_res.message) - notify.warn(check_res.message) - end - end - - if not check_res.continue then - return false - end - end - - return true -end diff --git a/lua/java/treesitter/init.lua b/lua/java/treesitter/init.lua deleted file mode 100644 index 162a5e5..0000000 --- a/lua/java/treesitter/init.lua +++ /dev/null @@ -1,26 +0,0 @@ -local queries = require('java.treesitter.queries') - -local M = {} - ----Finds a main method in the given buffer and returns the line number ----@return integer | nil line number of the main method -function M.find_main_method(buffer) - local query = vim.treesitter.query.parse('java', queries.main_class) - local parser = vim.treesitter.get_parser(buffer, 'java') - local root = parser:parse()[1]:root() - - for _, match, _ in query:iter_matches(root, buffer, 0, -1) do - for id, node in pairs(match) do - local capture_name = query.captures[id] - - if capture_name == 'main_method' then - -- first element is the line number - return ({ node:start() })[1] - end - end - end - - return nil -end - -return M diff --git a/lua/java/treesitter/queries.lua b/lua/java/treesitter/queries.lua deleted file mode 100644 index 78f419a..0000000 --- a/lua/java/treesitter/queries.lua +++ /dev/null @@ -1,15 +0,0 @@ -local M = {} - -M.main_class = [[ -(method_declaration - (modifiers) @modifiers (#eq? @modifiers "public static") - type: (void_type) @return_type - name: (identifier) @name (#eq? @name "main") - parameters: (formal_parameters - (formal_parameter - type: (array_type - element: (type_identifier) @arg_type (#eq? @arg_type "String")))) -) @main_method -]] - -return M diff --git a/lua/java/ui/profile.lua b/lua/java/ui/profile.lua index 54bb1ac..2549c0d 100644 --- a/lua/java/ui/profile.lua +++ b/lua/java/ui/profile.lua @@ -1,15 +1,16 @@ local event = require('nui.utils.autocmd').event -local Layout = require('nui.layout') -local Menu = require('nui.menu') -local Popup = require('nui.popup') local notify = require('java-core.utils.notify') local profile_config = require('java.api.profile_config') local class = require('java-core.utils.class') -local dap_api = require('java.api.dap') -local log = require('java.utils.log') -local DapSetup = require('java-dap.api.setup') -local jdtls = require('java.utils.jdtls') -local ui = require('java.utils.ui') +local dap_api = require('java-dap') +local log = require('java-core.utils.log2') +local lsp_utils = require('java-core.utils.lsp') +local ui = require('java.ui.utils') + +local Layout = require('nui.layout') +local Menu = require('nui.menu') +local Popup = require('nui.popup') +local DapSetup = require('java-dap.setup') local new_profile = 'New Profile' @@ -147,27 +148,22 @@ function ProfileUI:get_menu() }, on_submit = function(item) if item.text == new_profile then - self:_open_profile_editor() + self:open_profile_editor() else local profile_name = clear_active_postfix(item.text) - self:_open_profile_editor(profile_name) + self:open_profile_editor(profile_name) end end, }) end +---@private --- @param title string --- @param key string --- @param target_profile string --- @param enter boolean|nil --- @param keymaps boolean|nil -function ProfileUI:_get_and_fill_popup( - title, - key, - target_profile, - enter, - keymaps -) +function ProfileUI:get_and_fill_popup(title, key, target_profile, enter, keymaps) local style = self.style local text = { top = '[' .. title .. ']', @@ -203,29 +199,12 @@ function ProfileUI:_get_and_fill_popup( return popup end -function ProfileUI:_open_profile_editor(target_profile) +---@private +function ProfileUI:open_profile_editor(target_profile) local popups = { - name = self:_get_and_fill_popup( - 'Name', - 'name', - target_profile, - true, - false - ), - vm_args = self:_get_and_fill_popup( - 'VM arguments', - 'vm_args', - target_profile, - false, - false - ), - prog_args = self:_get_and_fill_popup( - 'Program arguments', - 'prog_args', - target_profile, - false, - true - ), + name = self:get_and_fill_popup('Name', 'name', target_profile, true, false), + vm_args = self:get_and_fill_popup('VM arguments', 'vm_args', target_profile, false, false), + prog_args = self:get_and_fill_popup('Program arguments', 'prog_args', target_profile, false, true), } local layout = Layout( @@ -279,20 +258,18 @@ function ProfileUI:_open_profile_editor(target_profile) ) end +---@private --- @return boolean -function ProfileUI:_is_selected_profile_modifiable() - if - self.focus_item == nil - or self.focus_item.text == nil - or self.focus_item.text == new_profile - then +function ProfileUI:is_selected_profile_modifiable() + if self.focus_item == nil or self.focus_item.text == nil or self.focus_item.text == new_profile then return false end return true end -function ProfileUI:_set_active_profile() - if not self:_is_selected_profile_modifiable() then +---@private +function ProfileUI:set_active_profile() + if not self:is_selected_profile_modifiable() then notify.error('Failed to set profile as active') return end @@ -307,8 +284,9 @@ function ProfileUI:_set_active_profile() self:openMenu() end -function ProfileUI:_delete_profile() - if not self:_is_selected_profile_modifiable() then +---@private +function ProfileUI:delete_profile() + if not self:is_selected_profile_modifiable() then notify.error('Failed to delete profile') return end @@ -339,38 +317,34 @@ function ProfileUI:openMenu() self.menu:unmount() end, { noremap = true, nowait = true }) self.menu:map('n', 'a', function() - self:_set_active_profile() + self:set_active_profile() end, { noremap = true, nowait = true }) -- delete self.menu:map('n', 'd', function() - self:_delete_profile() + self:delete_profile() end, { noremap = true, nowait = true }) end local M = {} -local async = require('java-core.utils.async').sync -local get_error_handler = require('java.handlers.error') +local runner = require('async.runner') +local get_error_handler = require('java-core.utils.error_handler') --- @type ProfileUI M.ProfileUI = ProfileUI function M.ui() - return async(function() - local configs = DapSetup(jdtls().client):get_dap_config() + return runner(function() + local configs = DapSetup(lsp_utils.get_jdtls()):get_dap_config() if not configs or #configs == 0 then notify.error('No classes with main methods are found') return end - local selected_config = ui.select( - 'Select the main class (module -> mainClass)', - configs, - function(config) - return config.name - end - ) + local selected_config = ui.select('Select the main class (module -> mainClass)', configs, function(config) + return config.name + end) if not selected_config then return diff --git a/lua/java/utils/ui.lua b/lua/java/ui/utils.lua similarity index 92% rename from lua/java/utils/ui.lua rename to lua/java/ui/utils.lua index 91860d4..d04a243 100644 --- a/lua/java/utils/ui.lua +++ b/lua/java/ui/utils.lua @@ -73,8 +73,7 @@ function M.multi_select(prompt, values, format_item) prompt = prompt, format_item = function(item) local prefix = item.is_selected and '* ' or '' - return prefix - .. (format_item and format_item(item.value) or item.value) + return prefix .. (format_item and format_item(item.value) or item.value) end, }, function(selected) if not selected then @@ -90,8 +89,7 @@ function M.multi_select(prompt, values, format_item) return end - wrapped_items[selected.index].is_selected = - not wrapped_items[selected.index].is_selected + wrapped_items[selected.index].is_selected = not wrapped_items[selected.index].is_selected open_select() end) diff --git a/lua/java/utils/command.lua b/lua/java/utils/command.lua deleted file mode 100644 index 4170b33..0000000 --- a/lua/java/utils/command.lua +++ /dev/null @@ -1,25 +0,0 @@ -local M = {} - ----Converts a path array to command name ----@param path string[] ----@return string -function M.path_to_command_name(path) - local name = 'Java' - - for _, word in ipairs(path) do - local sub_words = vim.split(word, '_') - local changed_word = '' - - for _, sub_word in ipairs(sub_words) do - local first_char = sub_word:sub(1, 1):upper() - local rest = sub_word:sub(2) - changed_word = changed_word .. first_char .. rest - end - - name = name .. changed_word - end - - return name -end - -return M diff --git a/lua/java/utils/instance_factory.lua b/lua/java/utils/instance_factory.lua deleted file mode 100644 index 56787be..0000000 --- a/lua/java/utils/instance_factory.lua +++ /dev/null @@ -1,24 +0,0 @@ -local class = require('java-core.utils.class') - -local M = class() - ----@return vim.lsp.Client -local function get_client() - local clients = vim.lsp.get_clients({ name = 'jdtls' }) - - if #clients < 1 then - local message = string.format('No jdtls client found to instantiate class') - require('java-core.utils.notify').error(message) - require('java.utils.log').error(message) - error(message) - end - - return clients[1] -end - ----@return java-core.JdtlsClient -function M.jdtls_client() - return require('java-core.ls.clients.jdtls-client')(get_client()) -end - -return M diff --git a/lua/java/utils/jdtls.lua b/lua/java/utils/jdtls.lua deleted file mode 100644 index 80addcd..0000000 --- a/lua/java/utils/jdtls.lua +++ /dev/null @@ -1,17 +0,0 @@ -local get_error_handler = require('java.handlers.error') - ----Returns an active jdtls client ----@return { client: LspClient } -local function get_jdtls() - local clients = vim.lsp.get_active_clients({ name = 'jdtls' }) - - if #clients == 0 then - get_error_handler('could not find an active jdtls client')() - end - - return { - client = clients[1], - } -end - -return get_jdtls diff --git a/lua/java/utils/jdtls2.lua b/lua/java/utils/jdtls2.lua deleted file mode 100644 index 2eb49c9..0000000 --- a/lua/java/utils/jdtls2.lua +++ /dev/null @@ -1,21 +0,0 @@ -local get_error_handler = require('java.handlers.error') - ----Returns an active jdtls client ----@return vim.lsp.Client -local function get_jdtls() - local clients - - if vim.lsp.get_clients then - clients = vim.lsp.get_clients({ name = 'jdtls' }) - else - clients = vim.lsp.get_active_clients({ name = 'jdtls' }) - end - - if #clients == 0 then - get_error_handler('could not find an active jdtls client')() - end - - return clients[1] -end - -return get_jdtls diff --git a/lua/java/utils/mason.lua b/lua/java/utils/mason.lua deleted file mode 100644 index 824d425..0000000 --- a/lua/java/utils/mason.lua +++ /dev/null @@ -1,108 +0,0 @@ -local log = require('java.utils.log') -local mason_reg = require('mason-registry') -local async = require('java-core.utils.async') -local await = async.wait_handle_ok -local mason_v2 = require('mason.version').MAJOR_VERSION == 2 - -local M = {} - -function M.is_available(package_name, package_version) - -- get_package errors if the package is not available in Mason 2.x - -- it works fine in Mason 1.x this way too. - local has_pkg, pkg = pcall(mason_reg.get_package, package_name) - - if not has_pkg then - return false - end - - local installed_version - if mason_v2 then - -- the compiler will complain when Mason 1.x is in use - ---@diagnostic disable-next-line: missing-parameter - installed_version = pkg:get_installed_version() - else - -- the compiler will complain when mason 2.x is in use - ---@diagnostic disable-next-line: param-type-mismatch - pkg:get_installed_version(function(success, version) - if success then - installed_version = version - end - end) - end - - return installed_version == package_version -end - -function M.is_installed(package_name, package_version) - -- get_package errors if the package is not available in Mason 2.x - -- it works fine in Mason 1.x this way too. - local found, pkg = pcall(mason_reg.get_package, package_name) - - if not found or not pkg:is_installed() then - return false - end - - local installed_version - if mason_v2 then - -- the compiler will complain when Mason 1.x is in use - ---@diagnostic disable-next-line: missing-parameter - installed_version = pkg:get_installed_version() - else - -- the compiler will complain when Mason 2.x is in use - ---@diagnostic disable-next-line: param-type-mismatch - pkg:get_installed_version(function(success, version) - if success then - installed_version = version - end - end) - end - - return installed_version == package_version -end - -function M.is_outdated(packages) - for _, pkg in ipairs(packages) do - if not M.is_available(pkg.name, pkg.version) then - return true - end - - if not M.is_installed(pkg.name, pkg.version) then - return true - end - end -end - -function M.refresh_registry() - await(function(callback) - mason_reg.update(callback) - end) -end - -function M.install_pkgs(packages) - log.info('check mason dependecies') - - for _, dep in ipairs(packages) do - if not M.is_installed(dep.name, dep.version) then - local pkg = mason_reg.get_package(dep.name) - - -- install errors if installation is already running in Mason 2.x - local guard - if mason_v2 then - -- guard if the package is already installing in Mason 2.x - -- the compiler will complain about the following line with Mason 1.x - ---@diagnostic disable-next-line: undefined-field - guard = pkg:is_installing() - else - guard = false - end - if not guard then - pkg:install({ - version = dep.version, - force = true, - }) - end - end - end -end - -return M diff --git a/lua/pkgm/downloaders/factory.lua b/lua/pkgm/downloaders/factory.lua new file mode 100644 index 0000000..f041fe6 --- /dev/null +++ b/lua/pkgm/downloaders/factory.lua @@ -0,0 +1,40 @@ +local system = require('java-core.utils.system') +local Wget = require('pkgm.downloaders.wget') +local PowerShell = require('pkgm.downloaders.powershell') +local log = require('java-core.utils.log2') +local err_util = require('java-core.utils.errors') + +local M = {} + +---Get appropriate downloader based on platform and binary availability +---@param opts table Downloader options (url, dest, retry_count, timeout) +---@return table # Downloader instance +function M.get_downloader(opts) + local os = system.get_os() + log.debug('Getting downloader for OS:', os) + + -- On Windows, prefer PowerShell + if os == 'win' then + if vim.fn.executable('pwsh') == 1 or vim.fn.executable('powershell') == 1 then + log.debug('Using PowerShell downloader') + return PowerShell(opts) + end + end + + -- Check for wget on all platforms + if vim.fn.executable('wget') == 1 then + log.debug('Using wget downloader') + return Wget(opts) + end + + -- On Windows, fallback to PowerShell if available + if os == 'win' and (vim.fn.executable('pwsh') == 1 or vim.fn.executable('powershell') == 1) then + log.debug('Using PowerShell downloader (fallback)') + return PowerShell(opts) + end + + local err = 'No downloader available (wget or powershell not found)' + err_util.throw(err) +end + +return M diff --git a/lua/pkgm/downloaders/powershell.lua b/lua/pkgm/downloaders/powershell.lua new file mode 100644 index 0000000..e2236d7 --- /dev/null +++ b/lua/pkgm/downloaders/powershell.lua @@ -0,0 +1,69 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') +local err_util = require('java-core.utils.errors') + +---@class java-core.PowerShell +---@field url string +---@field dest string +---@field retry_count number +---@field timeout number +local PowerShell = class() + +---@class java-core.PowerShellOpts +---@field url string URL to download +---@field dest? string Destination path (optional, uses temp if not provided) +---@field retry_count? number Retry count (optional, defaults to 5) +---@field timeout? number Timeout in seconds (optional, defaults to 30) + +---@param opts java-core.PowerShellOpts +function PowerShell:_init(opts) + self.url = opts.url + + if not opts.dest then + local filename = vim.fs.basename(opts.url) + self.dest = vim.fn.tempname() .. '-' .. filename + log.debug('Using temp destination:', self.dest) + else + self.dest = opts.dest + log.debug('Using provided destination:', self.dest) + end + + self.retry_count = opts.retry_count or 5 + self.timeout = opts.timeout or 30 +end + +---Download file using PowerShell +---@return string # Path to downloaded file +function PowerShell:download() + local pwsh = vim.fn.executable('pwsh') == 1 and 'pwsh' or 'powershell' + log.debug('PowerShell downloading:', self.url, 'to', self.dest) + log.debug('Using PowerShell binary:', pwsh) + + local pwsh_cmd = string.format( + 'iwr -TimeoutSec %d -UseBasicParsing -Method "GET" -Uri %q -OutFile %q;', + self.timeout, + self.url, + self.dest + ) + + local cmd = string.format( + -- luacheck: ignore + "%s -NoProfile -NonInteractive -Command \"$ProgressPreference = 'SilentlyContinue'; $ErrorActionPreference = 'Stop'; [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; %s\"", + pwsh, + pwsh_cmd + ) + log.debug('PowerShell command:', cmd) + + local result = vim.fn.system(cmd) + local exit_code = vim.v.shell_error + + if exit_code ~= 0 then + local err = string.format('PowerShell download failed (exit %d): %s', exit_code, result) + err_util.throw(err) + end + + log.debug('PowerShell download completed:', self.dest) + return self.dest +end + +return PowerShell diff --git a/lua/pkgm/downloaders/wget.lua b/lua/pkgm/downloaders/wget.lua new file mode 100644 index 0000000..fff6629 --- /dev/null +++ b/lua/pkgm/downloaders/wget.lua @@ -0,0 +1,63 @@ +local path = require('java-core.utils.path') +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-core.Wget +---@field url string +---@field dest string +---@field retry_count number +---@field timeout number +local Wget = class() + +---@class java-core.WgetOpts +---@field url string URL to download +---@field dest? string Destination path (optional, uses temp if not provided) +---@field retry_count? number Retry count (optional, defaults to 5) +---@field timeout? number Timeout in seconds (optional, defaults to 30) + +---@param opts java-core.WgetOpts +function Wget:_init(opts) + self.url = opts.url + + if not opts.dest then + local filename = vim.fs.basename(opts.url) + local tmp_dir = vim.fn.tempname() + vim.fn.mkdir(tmp_dir, 'p') + self.dest = path.join(tmp_dir, filename) + log.debug('Using temp destination:', self.dest) + else + self.dest = opts.dest + log.debug('Using provided destination:', self.dest) + end + + self.retry_count = opts.retry_count or 5 + self.timeout = opts.timeout or 30 +end + +---Download file using wget +---@return string|nil # Path to downloaded file, or nil on failure +---@return string|nil # Error message if failed +function Wget:download() + log.debug('wget downloading:', self.url, 'to', self.dest) + local cmd = string.format( + 'wget -t %d -T %d -O %s %s', + self.retry_count, + self.timeout, + vim.fn.shellescape(self.dest), + vim.fn.shellescape(self.url) + ) + log.debug('wget command:', cmd) + + local result = vim.fn.system(cmd) + local exit_code = vim.v.shell_error + + if exit_code ~= 0 then + log.error('wget failed:', exit_code, result) + return nil, string.format('wget failed (exit %d): %s', exit_code, result) + end + + log.debug('wget download completed:', self.dest) + return self.dest, nil +end + +return Wget diff --git a/lua/pkgm/extractors/factory.lua b/lua/pkgm/extractors/factory.lua new file mode 100644 index 0000000..a36b6ba --- /dev/null +++ b/lua/pkgm/extractors/factory.lua @@ -0,0 +1,77 @@ +local system = require('java-core.utils.system') +local Unzip = require('pkgm.extractors.unzip') +local Tar = require('pkgm.extractors.tar') +local PowerShellExtractor = require('pkgm.extractors.powershell') +local Uncompressed = require('pkgm.extractors.uncompressed') +local log = require('java-core.utils.log2') +local err_util = require('java-core.utils.errors') + +local M = {} + +---Get appropriate extractor based on file extension +---@param opts table Extractor options (source, dest) +---@return table # Extractor instance +function M.get_extractor(opts) + local source = opts.source + local lower_source = source:lower() + local os = system.get_os() + log.debug('Getting extractor for:', source, 'on OS:', os) + + -- Check for zip files + if lower_source:match('%.zip$') or lower_source:match('%.vsix$') then + log.debug('Detected zip file') + -- On Windows, prefer PowerShell + if os == 'win' then + if vim.fn.executable('pwsh') == 1 or vim.fn.executable('powershell') == 1 then + log.debug('Using PowerShell extractor') + return PowerShellExtractor(opts) + end + end + + -- Check for unzip on all platforms + if vim.fn.executable('unzip') == 1 then + log.debug('Using unzip extractor') + return Unzip(opts) + end + + -- Fallback to PowerShell on Windows if available + if os == 'win' and (vim.fn.executable('pwsh') == 1 or vim.fn.executable('powershell') == 1) then + log.debug('Using PowerShell extractor (fallback)') + return PowerShellExtractor(opts) + end + + local err = 'No zip extractor available (unzip or powershell not found)' + err_util.throw(err) + end + + -- Check for tar files + if + lower_source:match('%.tar$') + or lower_source:match('%.tar%.gz$') + or lower_source:match('%.tgz$') + or lower_source:match('%.tar%.xz$') + or lower_source:match('%.tar%.bz2$') + then + log.debug('Detected tar file') + local tar_cmd = vim.fn.executable('gtar') == 1 and 'gtar' or 'tar' + if vim.fn.executable(tar_cmd) == 1 then + log.debug('Using tar extractor:', tar_cmd) + return Tar(opts) + else + local err = 'tar not available' + err_util.throw(err) + end + end + + -- Check for jar files + if lower_source:match('%.jar$') then + log.debug('Detected jar file') + log.debug('Using uncompressed extractor') + return Uncompressed(opts) + end + + local err = string.format('Unsupported archive format: %s', source) + err_util.throw(err) +end + +return M diff --git a/lua/pkgm/extractors/powershell.lua b/lua/pkgm/extractors/powershell.lua new file mode 100644 index 0000000..6aec31c --- /dev/null +++ b/lua/pkgm/extractors/powershell.lua @@ -0,0 +1,65 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-core.PowerShellExtractor +---@field source string +---@field dest string +local PowerShellExtractor = class() + +---@class java-core.PowerShellExtractorOpts +---@field source string Path to zip file +---@field dest string Destination directory + +---@param opts java-core.PowerShellExtractorOpts +function PowerShellExtractor:_init(opts) + self.source = opts.source + self.dest = opts.dest +end + +---Extract zip file using PowerShell Expand-Archive +---@return boolean|nil # true on success, nil on failure +---@return string|nil # Error message if failed +function PowerShellExtractor:extract() + local pwsh = vim.fn.executable('pwsh') == 1 and 'pwsh' or 'powershell' + log.debug('PowerShell extracting:', self.source, 'to', self.dest) + log.debug('Using PowerShell binary:', pwsh) + + -- Expand-Archive requires .zip extension + local source_file = self.source + if not source_file:lower():match('%.zip$') then + log.debug('Renaming file to add .zip extension') + source_file = source_file .. '.zip' + local ok = vim.fn.rename(self.source, source_file) + if ok ~= 0 then + log.error('Failed to rename file to .zip extension') + return nil, 'Failed to rename file to .zip extension' + end + end + + local pwsh_cmd = string.format( + 'Microsoft.PowerShell.Archive\\Expand-Archive -Path %q -DestinationPath %q -Force', + source_file, + self.dest + ) + + local cmd = string.format( + --luacheck: ignore + "%s -NoProfile -NonInteractive -Command \"$ProgressPreference = 'SilentlyContinue'; $ErrorActionPreference = 'Stop'; %s\"", + pwsh, + pwsh_cmd + ) + log.debug('PowerShell command:', cmd) + + local result = vim.fn.system(cmd) + local exit_code = vim.v.shell_error + + if exit_code ~= 0 then + log.error('PowerShell extraction failed:', exit_code, result) + return nil, string.format('PowerShell extraction failed (exit %d): %s', exit_code, result) + end + + log.debug('PowerShell extraction completed') + return true, nil +end + +return PowerShellExtractor diff --git a/lua/pkgm/extractors/tar.lua b/lua/pkgm/extractors/tar.lua new file mode 100644 index 0000000..58c873b --- /dev/null +++ b/lua/pkgm/extractors/tar.lua @@ -0,0 +1,57 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') +local system = require('java-core.utils.system') + +---@class java-core.Tar +---@field source string +---@field dest string +local Tar = class() + +---@class java-core.TarOpts +---@field source string Path to tar file (supports .tar, .tar.gz, .tgz, .tar.xz) +---@field dest string Destination directory + +---@param opts java-core.TarOpts +function Tar:_init(opts) + self.source = opts.source + self.dest = opts.dest +end + +---Extract tar file using tar +---@return boolean|nil # true on success, nil on failure +---@return string|nil # Error message if failed +function Tar:extract() + local tar_cmd = vim.fn.executable('gtar') == 1 and 'gtar' or 'tar' + log.debug('tar extracting:', self.source, 'to', self.dest) + log.debug('Using tar binary:', tar_cmd) + + local cmd + if system.get_os() == 'win' then + -- Windows: convert backslashes to forward slashes (tar accepts them) + local source = self.source:gsub('\\', '/') + local dest = self.dest:gsub('\\', '/') + cmd = string.format('%s --no-same-owner --force-local -xf "%s" -C "%s"', tar_cmd, source, dest) + else + -- Unix: use shellescape + cmd = string.format( + '%s --no-same-owner -xf %s -C %s', + tar_cmd, + vim.fn.shellescape(self.source), + vim.fn.shellescape(self.dest) + ) + end + log.debug('tar command:', cmd) + + local result = vim.fn.system(cmd) + local exit_code = vim.v.shell_error + + if exit_code ~= 0 then + log.error('tar extraction failed:', exit_code, result) + return nil, string.format('tar failed (exit %d): %s', exit_code, result) + end + + log.debug('tar extraction completed') + return true, nil +end + +return Tar diff --git a/lua/pkgm/extractors/uncompressed.lua b/lua/pkgm/extractors/uncompressed.lua new file mode 100644 index 0000000..da2fe1b --- /dev/null +++ b/lua/pkgm/extractors/uncompressed.lua @@ -0,0 +1,47 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-core.Uncompressed +---@field source string +---@field dest string +local Uncompressed = class() + +---@class java-core.UncompressedOpts +---@field source string Path to jar file +---@field dest string Destination directory + +---@param opts java-core.UncompressedOpts +function Uncompressed:_init(opts) + self.source = opts.source + self.dest = opts.dest +end + +---Move jar file to destination +---@return boolean|nil # true on success, nil on failure +---@return string|nil # Error message if failed +function Uncompressed:extract() + log.debug('Moving uncompressed file:', self.source, 'to', self.dest) + + if not self.source:lower():match('%.jar$') then + local err = 'Only .jar files are supported' + log.error(err) + return nil, err + end + + local filename = vim.fn.fnamemodify(self.source, ':t') + local dest_path = vim.fn.resolve(self.dest .. '/' .. filename) + + log.debug('Destination path:', dest_path) + + local success = vim.loop.fs_copyfile(self.source, dest_path) + if not success then + local err = string.format('Failed to copy %s to %s', self.source, dest_path) + log.error(err) + return nil, err + end + + log.debug('File move completed') + return true, nil +end + +return Uncompressed diff --git a/lua/pkgm/extractors/unzip.lua b/lua/pkgm/extractors/unzip.lua new file mode 100644 index 0000000..5314703 --- /dev/null +++ b/lua/pkgm/extractors/unzip.lua @@ -0,0 +1,39 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-core.Unzip +---@field source string +---@field dest string +local Unzip = class() + +---@class java-core.UnzipOpts +---@field source string Path to zip file +---@field dest string Destination directory + +---@param opts java-core.UnzipOpts +function Unzip:_init(opts) + self.source = opts.source + self.dest = opts.dest +end + +---Extract zip file using unzip +---@return boolean|nil # true on success, nil on failure +---@return string|nil # Error message if failed +function Unzip:extract() + log.debug('unzip extracting:', self.source, 'to', self.dest) + local cmd = string.format('unzip -q -o %s -d %s', vim.fn.shellescape(self.source), vim.fn.shellescape(self.dest)) + log.debug('unzip command:', cmd) + + local result = vim.fn.system(cmd) + local exit_code = vim.v.shell_error + + if exit_code ~= 0 then + log.error('unzip extraction failed:', exit_code, result) + return nil, string.format('unzip failed (exit %d): %s', exit_code, result) + end + + log.debug('unzip extraction completed') + return true, nil +end + +return Unzip diff --git a/lua/pkgm/manager.lua b/lua/pkgm/manager.lua new file mode 100644 index 0000000..f5b8257 --- /dev/null +++ b/lua/pkgm/manager.lua @@ -0,0 +1,183 @@ +local class = require('java-core.utils.class') +local path = require('java-core.utils.path') +local downloader_factory = require('pkgm.downloaders.factory') +local extractor_factory = require('pkgm.extractors.factory') +local log = require('java-core.utils.log2') +local default_specs = require('pkgm.specs.init') +local notify = require('java-core.utils.notify') +local err_util = require('java-core.utils.errors') + +---@class pkgm.Manager +---@field specs pkgm.PackageSpec[] +local Manager = class() + +Manager.packages_root = path.join(vim.fn.stdpath('data'), 'nvim-java', 'packages') + +---@param specs? pkgm.PackageSpec[] +function Manager:_init(specs) + self.specs = specs or default_specs + log.debug('Manager initialized with ' .. #self.specs .. ' specs') +end + +---Download and extract a package +---@param name string +---@param version string +---@return string # Installation directory path +function Manager:install(name, version) + log.debug('Installing package:', name, version) + + if self:is_installed(name, version) then + local install_dir = self:get_install_dir(name, version) + log.debug('Package already installed:', install_dir) + return install_dir + end + + notify.info('Installing package ' .. name .. ' version ' .. version) + + local spec = self:find_spec(name, version) + local url = spec:get_url(name, version) + local downloaded_file = self:download_package(url) + local install_dir = self:get_install_dir(name, version) + + log.debug('Install directory:', install_dir) + + self:extract_package(downloaded_file, install_dir) + + log.debug('Package installed successfully:', install_dir) + + return install_dir +end + +---Check if package is installed +---@param name string +---@param version string +---@return boolean # true if package is installed +function Manager:is_installed(name, version) + local install_dir = self:get_install_dir(name, version) + local installed = vim.fn.isdirectory(install_dir) == 1 + log.debug('Checking if package installed:', name, version, installed) + return installed +end + +---Uninstall a package +---@param name string +---@param version string +---@return boolean|nil # true on success, nil on failure +---@return string|nil # Error message if failed +function Manager:uninstall(name, version) + log.debug('Uninstalling package:', name, version) + local install_dir = self:get_install_dir(name, version) + + if vim.fn.isdirectory(install_dir) == 0 then + log.warn('Package not installed:', install_dir) + return nil, 'Package not installed' + end + + log.debug('Deleting directory:', install_dir) + local result = vim.fn.delete(install_dir, 'rf') + if result ~= 0 then + log.error('Failed to delete package directory:', install_dir) + return nil, 'Failed to delete package directory' + end + + log.debug('Package uninstalled successfully') + return true, nil +end + +---Find matching spec for package name and version +---@private +---@param name string +---@param version string +---@return pkgm.PackageSpec # Matching spec, or nil if not found +function Manager:find_spec(name, version) + log.debug('Finding spec for ' .. name .. ' version ' .. version) + + for _, spec in ipairs(self.specs) do + if spec:is_match(name, version) then + log.debug('Found matching spec') + return spec + end + end + + local err = string.format('No matching spec for %s version %s', name, version) + err_util.throw(err) +end + +---Get platform-specific URL from package spec +---@private +---@param spec pkgm.PackageSpec +---@param name string +---@param version string +---@return string|nil # URL for current platform, or nil if not available +---@return string|nil # Error message if URL not available +function Manager:get_platform_url(spec, name, version) + local success, result = pcall(function() + return spec:get_url(name, version) + end) + + if not success then + return nil, result + end + + return result, nil +end + +---Get package installation directory +---@param name string +---@param version string +---@return string # Installation directory path +function Manager:get_install_dir(name, version) + return path.join(Manager.packages_root, name, version) +end + +---Download package from URL +---@private +---@param url string +---@return string # Downloaded file path +function Manager:download_package(url) + log.debug('Using URL:', url) + + local downloader = downloader_factory.get_downloader({ url = url }) + + log.debug('Starting download...') + + local downloaded_file, err = downloader:download() + + if not downloaded_file then + err_util.throw(err or 'Download failed') + end + + log.debug('Downloaded to:', downloaded_file) + + return downloaded_file +end + +---Extract package to installation directory +---@private +---@param downloaded_file string +---@param install_dir string +function Manager:extract_package(downloaded_file, install_dir) + local extractor = extractor_factory.get_extractor({ + source = downloaded_file, + dest = install_dir, + }) + + vim.fn.mkdir(install_dir, 'p') + + log.debug('Starting extraction...') + + local success, err = extractor:extract() + + if not success then + vim.fn.delete(install_dir, 'rf') + vim.fn.delete(downloaded_file) + err_util.throw(err or 'Extraction failed') + end + + log.debug('Extraction completed') + + vim.fn.delete(downloaded_file) + log.debug('Cleaned up temporary file') +end + +return Manager diff --git a/lua/pkgm/pkgs/jdtls.lua b/lua/pkgm/pkgs/jdtls.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/pkgm/specs/base-spec.lua b/lua/pkgm/specs/base-spec.lua new file mode 100644 index 0000000..817d12d --- /dev/null +++ b/lua/pkgm/specs/base-spec.lua @@ -0,0 +1,220 @@ +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') +local system = require('java-core.utils.system') +local err = require('java-core.utils.errors') + +---@class pkgm.VersionRange +---@field from string +---@field to string + +---@alias pkgm.UrlValue string|table + +---@class pkgm.BaseSpecConfig +---@field name string +---@field version string|'*' +---@field version_range? pkgm.VersionRange +---@field urls? table +---@field url? string|pkgm.UrlValue +---@field [string] string + +---@class pkgm.BaseSpec: pkgm.PackageSpec +local BaseSpec = class() + +---@param config pkgm.BaseSpecConfig +function BaseSpec:_init(config) + log.debug('Initializing BaseSpec with config: ' .. vim.inspect(config)) + + self._name = config.name + self._version = config.version + self._version_range = config.version_range + self._template_vars = {} + + if not config.url and not config.urls then + err.throw('BaseSpec: Neither url nor urls provided') + end + + self._url = config.url + self._urls = config.urls + + for key, value in pairs(config) do + if key ~= 'name' and key ~= 'version' and key ~= 'version_range' and key ~= 'urls' and key ~= 'url' then + self._template_vars[key] = value + end + end + + log.debug('Template vars: ' .. vim.inspect(self._template_vars)) +end + +---@return string +function BaseSpec:get_name() + log.debug('get_name() returning: ' .. self._name) + return self._name +end + +---@return string +function BaseSpec:get_version() + log.debug('get_version() returning: ' .. self._version) + return self._version +end + +---@param name string +---@param version string +---@return boolean +function BaseSpec:is_match(name, version) + local version_desc = self._version or 'range' + log.debug( + 'is_match() checking name=' + .. name + .. ' version=' + .. version + .. ' against spec name=' + .. self._name + .. ' spec version=' + .. version_desc + ) + + if name ~= self._name then + log.debug('Name mismatch') + return false + end + + if self._version == '*' then + log.debug('Wildcard version match') + return true + end + + if self._version_range then + local in_range = self:is_version_in_range(version, self._version_range.from, self._version_range.to) + log.debug('Version range check: ' .. tostring(in_range)) + return in_range + end + + local exact_match = version == self._version + log.debug('Exact version match: ' .. tostring(exact_match)) + return exact_match +end + +---@param name string +---@param version string +---@return string +function BaseSpec:get_url(name, version) + log.debug('get_url() called with name=' .. name .. ' version=' .. version) + + if self._url then + log.debug('Resolving url') + local url_template = self:resolve_hierarchical_url(self._url) + return self:parse_url_template(url_template, name, version) + end + + if not self._urls then + err.throw('BaseSpec: Neither url nor urls provided') + end + + log.debug('Resolving urls table') + local url_template = self:resolve_hierarchical_url(self._urls) + + if not url_template then + err.throw('BaseSpec: No url found for current system configuration') + end + + return self:parse_url_template(url_template, name, version) +end + +---@private +---@param url_value string|table +---@return string|nil +function BaseSpec:resolve_hierarchical_url(url_value) + if type(url_value) == 'string' then + log.debug('URL is string: ' .. url_value) + return url_value + end + + if type(url_value) ~= 'table' then + log.error('URL value must be string or table') + return nil + end + + local platform = system.get_os() + log.debug('Platform: ' .. platform) + + local platform_value = url_value[platform] + if not platform_value then + log.error('No URL for platform: ' .. platform) + return nil + end + + if type(platform_value) == 'string' then + log.debug('Platform-specific URL: ' .. platform_value) + return platform_value + end + + local arch = system.get_arch() + log.debug('Architecture: ' .. arch) + + local arch_value = platform_value[arch] + if not arch_value then + log.error('No URL for architecture: ' .. arch) + return nil + end + + if type(arch_value) == 'string' then + log.debug('Architecture-specific URL: ' .. arch_value) + return arch_value + end + + local bit_depth = system.get_bit_depth() + log.debug('Bit depth: ' .. bit_depth) + + local bit_value = arch_value[bit_depth] + if not bit_value or type(bit_value) ~= 'string' then + log.error('No URL for bit depth: ' .. bit_depth) + return nil + end + + log.debug('Bit-depth-specific URL: ' .. bit_value) + return bit_value +end + +---@private +---@param url_template string +---@param name string +---@param version string +---@return string +function BaseSpec:parse_url_template(url_template, name, version) + local vars = vim.tbl_extend('force', { + name = name, + version = version, + }, self._template_vars) + + return self:parse_template(url_template, vars) +end + +---@private +---@param version string +---@param from string +---@param to string +---@return boolean +function BaseSpec:is_version_in_range(version, from, to) + log.debug('Checking if ' .. version .. ' is between ' .. from .. ' and ' .. to) + return version >= from and version <= to +end + +---@protected +---@param template string +---@param vars table +---@return string +function BaseSpec:parse_template(template, vars) + log.debug('Parsing template: ' .. template) + local result = template + + for key, value in pairs(vars) do + local pattern = '{{' .. key .. '}}' + result = result:gsub(pattern, value) + log.debug('Replaced ' .. pattern .. ' with ' .. value) + end + + log.debug('Parsed result: ' .. result) + return result +end + +return BaseSpec diff --git a/lua/pkgm/specs/init.lua b/lua/pkgm/specs/init.lua new file mode 100644 index 0000000..2396fb1 --- /dev/null +++ b/lua/pkgm/specs/init.lua @@ -0,0 +1,78 @@ +local BaseSpec = require('pkgm.specs.base-spec') +local JdtlsSpec = require('pkgm.specs.jdtls-spec') + +---@class pkgm.PackageSpec +---@field get_name fun(self: pkgm.PackageSpec): string +---@field get_version fun(self: pkgm.PackageSpec): string +---@field get_url fun(self: pkgm.PackageSpec, name: string, version: string): string +---@field is_match fun(self: pkgm.PackageSpec, name: string, version: string): boolean + +return { + JdtlsSpec({ + name = 'jdtls', + version_range = { from = '1.43.0', to = '1.53.0' }, + url = 'https://download.eclipse.org/{{name}}/milestones/' + .. '{{version}}/jdt-language-server-{{version}}-{{timestamp}}.tar.gz', + }), + BaseSpec({ + name = 'java-test', + version = '*', + url = 'https://openvsxorg.blob.core.windows.net/resources/vscjava/vscode-java-test' + .. '/{{version}}/vscjava.vscode-java-test-{{version}}.vsix', + }), + + BaseSpec({ + name = 'java-debug', + version = '*', + url = 'https://openvsxorg.blob.core.windows.net/resources/vscjava/vscode-java-debug/' + .. '{{version}}/vscjava.vscode-java-debug-{{version}}.vsix', + }), + + BaseSpec({ + name = 'spring-boot-tools', + version = '*', + url = 'https://openvsxorg.blob.core.windows.net/resources/VMware/vscode-spring-boot' + .. '/{{version}}/VMware.vscode-spring-boot-{{version}}.vsix', + }), + + BaseSpec({ + name = 'lombok', + version = 'nightly', + url = 'https://projectlombok.org/lombok-edge.jar', + }), + + BaseSpec({ + name = 'lombok', + version = '*', + url = 'https://projectlombok.org/downloads/lombok-{{version}}.jar', + }), + + BaseSpec({ + name = 'openjdk', + version = '17', + full_version = '17.0.12', + urls = { + linux = { + arm = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/archive/jdk-{{full_version}}_linux-aarch64_bin.tar.gz', + }, + x86 = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/archive/jdk-{{full_version}}_linux-x64_bin.tar.gz', + }, + }, + mac = { + arm = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/archive/jdk-{{full_version}}_macos-aarch64_bin.tar.gz', + }, + x86 = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/archive/jdk-{{full_version}}_macos-x64_bin.tar.gz', + }, + }, + win = { + x86 = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/archive/jdk-{{full_version}}_windows-x64_bin.zip', + }, + }, + }, + }), +} diff --git a/lua/pkgm/specs/jdtls-spec/init.lua b/lua/pkgm/specs/jdtls-spec/init.lua new file mode 100644 index 0000000..4622a0b --- /dev/null +++ b/lua/pkgm/specs/jdtls-spec/init.lua @@ -0,0 +1,42 @@ +local class = require('java-core.utils.class') +local BaseSpec = require('pkgm.specs.base-spec') +local version_map = require('pkgm.specs.jdtls-spec.version-map') +local err = require('java-core.utils.errors') + +---@class pkgm.JdtlsSpec: pkgm.BaseSpec +local JdtlsSpec = class(BaseSpec) + +function JdtlsSpec:_init(config) + ---@diagnostic disable-next-line: undefined-field + self:super(config) +end + +function JdtlsSpec:get_url(name, version) + ---@diagnostic disable-next-line: undefined-field + local url = self._base.get_url(self, name, version) + + if not version_map[version] then + local message = string.format( + [[ + %s@%s is not defined in the version map. + You can update the version map yourself and create a PR. + nvim-java/lua/pkgm/specs/jdtls-spec/version-map.lua + or + Please create an issue at: + https://github.com/s1n7ax/nvim-java/issues to add the missing version. + ]], + name, + version + ) + + err.throw(message) + end + + local new_url = self:parse_template(url, { + timestamp = version_map[version], + }) + + return new_url +end + +return JdtlsSpec diff --git a/lua/pkgm/specs/jdtls-spec/version-map.lua b/lua/pkgm/specs/jdtls-spec/version-map.lua new file mode 100644 index 0000000..62282f9 --- /dev/null +++ b/lua/pkgm/specs/jdtls-spec/version-map.lua @@ -0,0 +1,22 @@ +-- To update JDTLS version map: +-- 1. Visit https://download.eclipse.org/jdtls/milestones/ +-- 2. Click on version link in 'Directory Contents' section +-- 3. Find file like: jdt-language-server-X.Y.Z-YYYYMMDDHHSS.tar.gz +-- 4. Extract package version (X.Y.Z) and timestamp (YYYYMMDDHHSS) +-- 5. Add entry: ['X.Y.Z'] = 'YYYYMMDDHHSS' +-- Example: jdt-language-server-1.43.0-202412191447.tar.gz +-- → ['1.43.0'] = '202412191447' +return { + ['1.43.0'] = '202412191447', + ['1.44.0'] = '202501221502', + ['1.45.0'] = '202502271238', + ['1.46.0'] = '202503271314', + ['1.46.1'] = '202504011455', + ['1.47.0'] = '202505151856', + ['1.48.0'] = '202506271502', + ['1.49.0'] = '202507311558', + ['1.50.0'] = '202509041425', + ['1.51.0'] = '202510022025', + ['1.52.0'] = '202510301627', + ['1.53.0'] = '202511192211', +} diff --git a/plugin/java.lua b/plugin/java.lua index a2d4a83..524dc3d 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -7,15 +7,39 @@ end local cmd_map = { JavaSettingsChangeRuntime = { java.settings.change_runtime }, - JavaDapConfig = { java.dap.config_dap }, - - JavaTestRunCurrentClass = { java.test.run_current_class }, - JavaTestDebugCurrentClass = { java.test.debug_current_class }, - - JavaTestRunCurrentMethod = { java.test.run_current_method }, - JavaTestDebugCurrentMethod = { java.test.debug_current_method }, - - JavaTestViewLastReport = { java.test.view_last_report }, + JavaDapConfig = { + function() + require('java-dap').config_dap() + end, + }, + + JavaTestRunCurrentClass = { + function() + require('java-test').run_current_class() + end, + }, + JavaTestDebugCurrentClass = { + function() + require('java-test').debug_current_class() + end, + }, + + JavaTestRunCurrentMethod = { + function() + require('java-test').run_current_method() + end, + }, + JavaTestDebugCurrentMethod = { + function() + require('java-test').debug_current_method() + end, + }, + + JavaTestViewLastReport = { + function() + require('java-test').view_last_report() + end, + }, JavaRunnerRunMain = { java.runner.built_in.run_app, { nargs = '?' } }, JavaRunnerStopMain = { java.runner.built_in.stop_app }, diff --git a/tests/constants/capabilities.lua b/tests/constants/capabilities.lua new file mode 100644 index 0000000..a7a9d89 --- /dev/null +++ b/tests/constants/capabilities.lua @@ -0,0 +1,79 @@ +local List = require('java-core.utils.list') + +local M = {} + +M.required_cmds = List:new({ + 'java.completion.onDidSelect', + 'java.decompile', + 'java.edit.handlePasteEvent', + 'java.edit.organizeImports', + 'java.edit.smartSemicolonDetection', + 'java.edit.stringFormatting', + 'java.navigate.openTypeHierarchy', + 'java.navigate.resolveTypeHierarchy', + 'java.project.addToSourcePath', + 'java.project.changeImportedProjects', + 'java.project.createModuleInfo', + 'java.project.getAll', + 'java.project.getClasspaths', + 'java.project.getSettings', + 'java.project.import', + 'java.project.isTestFile', + 'java.project.listSourcePaths', + 'java.project.refreshDiagnostics', + 'java.project.removeFromSourcePath', + 'java.project.resolveSourceAttachment', + 'java.project.resolveStackTraceLocation', + 'java.project.resolveText', + 'java.project.resolveWorkspaceSymbol', + 'java.project.updateClassPaths', + 'java.project.updateJdk', + 'java.project.updateSettings', + 'java.project.updateSourceAttachment', + 'java.project.upgradeGradle', + 'java.protobuf.generateSources', + 'java.reloadBundles', + 'java.vm.getAllInstalls', + 'sts.java.addClasspathListener', + 'sts.java.code.completions', + 'sts.java.hierarchy.subtypes', + 'sts.java.hierarchy.supertypes', + 'sts.java.javadoc', + 'sts.java.javadocHoverLink', + 'sts.java.location', + 'sts.java.removeClasspathListener', + 'sts.java.search.packages', + 'sts.java.search.types', + 'sts.java.type', + 'sts.project.gav', + 'vscode.java.buildWorkspace', + 'vscode.java.checkProjectSettings', + 'vscode.java.fetchPlatformSettings', + 'vscode.java.fetchUsageData', + 'vscode.java.inferLaunchCommandLength', + 'vscode.java.isOnClasspath', + 'vscode.java.resolveBuildFiles', + 'vscode.java.resolveClassFilters', + 'vscode.java.resolveClasspath', + 'vscode.java.resolveElementAtSelection', + 'vscode.java.resolveInlineVariables', + 'vscode.java.resolveJavaExecutable', + 'vscode.java.resolveMainClass', + 'vscode.java.resolveMainMethod', + 'vscode.java.resolveSourceUri', + 'vscode.java.startDebugSession', + 'vscode.java.test.findDirectTestChildrenForClass', + 'vscode.java.test.findJavaProjects', + 'vscode.java.test.findTestLocation', + 'vscode.java.test.findTestPackagesAndTypes', + 'vscode.java.test.findTestTypesAndMethods', + 'vscode.java.test.generateTests', + 'vscode.java.test.get.testpath', + 'vscode.java.test.junit.argument', + 'vscode.java.test.navigateToTestOrTarget', + 'vscode.java.test.resolvePath', + 'vscode.java.updateDebugSettings', + 'vscode.java.validateLaunchConfig', +}) + +return M diff --git a/tests/prepare-config.lua b/tests/prepare-config.lua deleted file mode 100644 index cf2d89e..0000000 --- a/tests/prepare-config.lua +++ /dev/null @@ -1,66 +0,0 @@ -local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' - ----@diagnostic disable: assign-type-mismatch ----@param path string ----@return string|nil -local function local_plug(path) - return vim.fn.isdirectory(path) == 1 and path or nil -end - -if not vim.loop.fs_stat(lazypath) then - vim.fn.system({ - 'git', - 'clone', - '--filter=blob:none', - 'https://github.com/folke/lazy.nvim.git', - '--branch=stable', - lazypath, - }) -end - -vim.opt.rtp:prepend(lazypath) - -local temp_path = './.test_plugins' - -require('lazy').setup({ - { - 'nvim-lua/plenary.nvim', - lazy = false, - }, - { - 'nvim-java/nvim-java-test', - ---@diagnostic disable-next-line: assign-type-mismatch - dir = local_plug('~/Workspace/nvim-java-test'), - lazy = false, - }, - { - 'nvim-java/nvim-java-core', - ---@diagnostic disable-next-line: assign-type-mismatch - dir = local_plug('~/Workspace/nvim-java-core'), - lazy = false, - }, - { - 'nvim-java/nvim-java-dap', - ---@diagnostic disable-next-line: assign-type-mismatch - dir = local_plug('~/Workspace/nvim-java-dap'), - lazy = false, - }, - { - 'neovim/nvim-lspconfig', - lazy = false, - }, - { - 'mason-org/mason.nvim', - lazy = false, - }, - { - 'MunifTanjim/nui.nvim', - lazy = false, - }, -}, { - root = temp_path, - lockfile = temp_path .. '/lazy-lock.json', - defaults = { - lazy = false, - }, -}) diff --git a/tests/specs/capabilities_spec.lua b/tests/specs/capabilities_spec.lua new file mode 100644 index 0000000..1fe36f1 --- /dev/null +++ b/tests/specs/capabilities_spec.lua @@ -0,0 +1,28 @@ +local lsp_utils = dofile('tests/utils/lsp-utils.lua') +local capabilities = dofile('tests/constants/capabilities.lua') +local List = require('java-core.utils.list') +local assert = require('luassert') +local log = require('java-core.utils.log2') + +describe('LSP Capabilities', function() + it('should have all required commands', function() + vim.cmd.edit('HelloWorld.java') + + local client = lsp_utils.wait_for_lsp_attach('jdtls', 30000) + local commands = client.server_capabilities.executeCommandProvider.commands + local actual_cmds = List:new(commands) + + for _, required_cmd in ipairs(capabilities.required_cmds) do + assert.is_true(actual_cmds:contains(required_cmd), 'Missing required command: ' .. required_cmd) + end + + local extra_cmds = actual_cmds:filter(function(cmd) + return not capabilities.required_cmds:contains(cmd) + end) + + if #extra_cmds > 0 then + log.error('Additional commands found that are not in required list:', extra_cmds) + error('Additional commands found that are not in required list:' .. vim.inspect(extra_cmds)) + end + end) +end) diff --git a/tests/specs/jdtls_extensions_spec.lua b/tests/specs/jdtls_extensions_spec.lua new file mode 100644 index 0000000..6d8f41d --- /dev/null +++ b/tests/specs/jdtls_extensions_spec.lua @@ -0,0 +1,34 @@ +local lsp_utils = dofile('tests/utils/lsp-utils.lua') +local assert = require('luassert') + +describe('JDTLS Extensions', function() + it('should bundle java-test, java-debug, and spring-boot-tools extensions', function() + vim.cmd.edit('HelloWorld.java') + + local client = lsp_utils.wait_for_lsp_attach('jdtls', 30000) + local bundles = client.config.init_options.bundles + + assert.is_not_nil(bundles, 'Bundles should be configured') + assert.is_true(#bundles > 0, 'Bundles should not be empty') + + local has_java_test = false + local has_java_debug = false + local has_spring_boot = false + + for _, bundle in ipairs(bundles) do + if bundle:match('java%-test') and bundle:match('com%.microsoft%.java%.test%.plugin') then + has_java_test = true + end + if bundle:match('java%-debug') and bundle:match('com%.microsoft%.java%.debug%.plugin') then + has_java_debug = true + end + if bundle:match('spring%-boot%-tools') and bundle:match('jdt%-ls%-extension%.jar') then + has_spring_boot = true + end + end + + assert.is_true(has_java_test, 'java-test extension (com.microsoft.java.test.plugin) should be bundled') + assert.is_true(has_java_debug, 'java-debug extension (com.microsoft.java.debug.plugin) should be bundled') + assert.is_true(has_spring_boot, 'spring-boot-tools extension (jdt-ls-extension.jar) should be bundled') + end) +end) diff --git a/tests/specs/lsp_spec.lua b/tests/specs/lsp_spec.lua new file mode 100644 index 0000000..8ef81b7 --- /dev/null +++ b/tests/specs/lsp_spec.lua @@ -0,0 +1,14 @@ +local lsp_utils = dofile('tests/utils/lsp-utils.lua') +local assert = require('luassert') + +describe('LSP Attach', function() + it('should attach when opening a Java buffer', function() + vim.cmd.edit('HelloWorld.java') + + local jdtls = lsp_utils.wait_for_lsp_attach('jdtls', 30000) + local spring = lsp_utils.wait_for_lsp_attach('spring-boot', 30000) + + assert.is_not_nil(jdtls, 'JDTLS should attach to Java buffer') + assert.is_not_nil(spring, 'Spring Boot should attach to Java buffer') + end) +end) diff --git a/tests/test-config.lua b/tests/test-config.lua deleted file mode 100644 index bf89a3f..0000000 --- a/tests/test-config.lua +++ /dev/null @@ -1,26 +0,0 @@ ----@diagnostic disable: assign-type-mismatch ----@param dev_path string ----@param plug_path string ----@return string|nil -local function local_plug(dev_path, plug_path) - return (vim.fn.isdirectory(dev_path) == 1) and dev_path or plug_path -end - -local plug_path = './.test_plugins' - -vim.opt.rtp:append(plug_path .. '/plenary.nvim') -vim.opt.rtp:append(plug_path .. '/nvim-lspconfig') -vim.opt.rtp:append(plug_path .. '/mason.nvim') -vim.opt.rtp:append(plug_path .. '/nui.nvim') - -vim.opt.rtp:append( - local_plug('~/Workspace/nvim-java-core', plug_path .. '/nvim-java-core') -) - -vim.opt.rtp:append( - local_plug('~/Workspace/nvim-java-test', plug_path .. '/nvim-java-test') -) - -vim.opt.rtp:append( - local_plug('~/Workspace/nvim-java-dap', plug_path .. '/nvim-java-dap') -) diff --git a/tests/utils/lsp-utils.lua b/tests/utils/lsp-utils.lua new file mode 100644 index 0000000..c5ea757 --- /dev/null +++ b/tests/utils/lsp-utils.lua @@ -0,0 +1,25 @@ +local M = {} + +---Wait for LSP client to attach +---@param name string LSP client name +---@param timeout? number Timeout in milliseconds (defaults to 30000) +---@return vim.lsp.Client client The attached LSP client +function M.wait_for_lsp_attach(name, timeout) + timeout = timeout or 30000 + + local is_attached = function() + local clients = vim.lsp.get_clients({ name = name }) + return #clients > 0 + end + + local success = vim.wait(timeout, is_attached, 100) + + if not success then + error(string.format('LSP client "%s" failed to attach within %dms', name, timeout)) + end + + local clients = vim.lsp.get_clients({ name = name }) + return clients[1] +end + +return M diff --git a/tests/utils/prepare-config.lua b/tests/utils/prepare-config.lua new file mode 100644 index 0000000..544f21e --- /dev/null +++ b/tests/utils/prepare-config.lua @@ -0,0 +1,56 @@ +local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim' + +if not vim.loop.fs_stat(lazypath) then + vim.fn.system({ + 'git', + 'clone', + '--filter=blob:none', + 'https://github.com/folke/lazy.nvim.git', + '--branch=stable', + lazypath, + }) +end + +vim.opt.rtp:prepend(lazypath) + +local temp_path = './.test_plugins' + +-- Setup lazy.nvim +require('lazy').setup({ + { + 'nvim-lua/plenary.nvim', + lazy = false, + }, + 'MunifTanjim/nui.nvim', + 'mfussenegger/nvim-dap', + { + 'JavaHello/spring-boot.nvim', + commit = '218c0c26c14d99feca778e4d13f5ec3e8b1b60f0', + }, + { + 'nvim-java/nvim-java', + dir = '.', + config = function() + require('java').setup({ + jdk = { + auto_install = false, + }, + }) + vim.lsp.enable('jdtls') + end, + }, +}, { + root = temp_path, + lockfile = temp_path .. '/lazy-lock.json', + defaults = { lazy = false }, +}) + +vim.api.nvim_create_autocmd('LspAttach', { + callback = function(args) + -- stylua: ignore + vim.lsp.completion.enable(true, args.data.client_id, args.buf, { autotrigger = true }) + vim.keymap.set('i', '', function() + vim.lsp.completion.get() + end, { buffer = args.buf }) + end, +}) diff --git a/tests/utils/test-config.lua b/tests/utils/test-config.lua new file mode 100644 index 0000000..bd1fb1f --- /dev/null +++ b/tests/utils/test-config.lua @@ -0,0 +1,28 @@ +local jdtls_cache_path = vim.fn.stdpath('cache') .. '/jdtls' +local gradle_cache_path = vim.fn.expand('~') .. '/.gradle' + +print('removing cache') +vim.fn.delete(jdtls_cache_path, 'rf') +vim.fn.delete(gradle_cache_path, 'rf') + +vim.o.swapfile = false +vim.o.backup = false +vim.o.writebackup = false + +local temp_path = './.test_plugins' + +vim.opt.runtimepath:append(temp_path .. '/') +vim.opt.runtimepath:append(temp_path .. '/nui.nvim') +vim.opt.runtimepath:append(temp_path .. '/spring-boot.nvim') +vim.opt.runtimepath:append(temp_path .. '/nvim-dap') +vim.opt.runtimepath:append('.') + +local is_nixos = vim.fn.filereadable('/etc/NIXOS') == 1 + +require('java').setup({ + jdk = { + auto_install = not is_nixos, + }, +}) + +vim.lsp.enable('jdtls') From 8befd5c2f1a39f0e36d7e577c191a892111f4e9a Mon Sep 17 00:00:00 2001 From: Grace Petryk Date: Fri, 12 Sep 2025 18:49:35 -0700 Subject: [PATCH 101/147] status colors + handling for skipped/errored test results --- lua/java-test/reports/junit.lua | 4 ++-- lua/java-test/results/message-id.lua | 2 -- lua/java-test/results/result-parser.lua | 21 ++++++++++++++++++++- lua/java-test/results/result-status.lua | 11 ++++++++--- lua/java-test/ui/floating-report-viewer.lua | 21 ++++++++++++++------- 5 files changed, 44 insertions(+), 15 deletions(-) diff --git a/lua/java-test/reports/junit.lua b/lua/java-test/reports/junit.lua index 01edb1e..4ad687e 100644 --- a/lua/java-test/reports/junit.lua +++ b/lua/java-test/reports/junit.lua @@ -2,7 +2,7 @@ local class = require('java-core.utils.class') local log = require('java-core.utils.log2') ---@class java-test.JUnitTestReport ----@field private conn uv_tcp_t +---@field private conn uv.uv_tcp_t ---@field private result_parser java-test.TestParser ---@field private result_parser_fac java-test.TestParserFactory ---@field private report_viewer java-test.ReportViewer @@ -29,7 +29,7 @@ function JUnitReport:show_report() end ---Returns a stream reader function ----@param conn uv_tcp_t +---@param conn uv.uv_tcp_t ---@return fun(err: string, buffer: string) # callback function function JUnitReport:get_stream_reader(conn) self.conn = conn diff --git a/lua/java-test/results/message-id.lua b/lua/java-test/results/message-id.lua index 28a37a5..5e11174 100644 --- a/lua/java-test/results/message-id.lua +++ b/lua/java-test/results/message-id.lua @@ -23,8 +23,6 @@ local MessageId = { ActualEnd = '%ACTUALE', TraceStart = '%TRACES', TraceEnd = '%TRACEE', - IGNORE_TEST_PREFIX = '@Ignore: ', - ASSUMPTION_FAILED_TEST_PREFIX = '@AssumptionFailure: ', } return MessageId diff --git a/lua/java-test/results/result-parser.lua b/lua/java-test/results/result-parser.lua index 00468fc..583333a 100644 --- a/lua/java-test/results/result-parser.lua +++ b/lua/java-test/results/result-parser.lua @@ -19,6 +19,13 @@ TestParser.node_parsers = { [MessageId.TestStart] = 'parse_test_start', [MessageId.TestEnd] = 'parse_test_end', [MessageId.TestFailed] = 'parse_test_failed', + [MessageId.TestError] = 'parse_test_failed', +} + +---@private +TestParser.skip_prefixes = { + '@Ignore:', + '@AssumptionFailure:', } ---@private @@ -101,6 +108,12 @@ function TestParser:parse_test_end(data) local node = self:find_result_node(test_id) assert(node) node.result.execution = TestExecStatus.Ended + + for _, prefix in ipairs(TestParser.skip_prefixes) do + if string.match(data[2], '^'..prefix) then + node.result.status = TestStatus.Skipped + end + end end ---@private @@ -109,7 +122,13 @@ function TestParser:parse_test_failed(data, line_iter) local node = self:find_result_node(test_id) assert(node) - node.result.status = TestStatus.Failed + node.result.status = node.result.status or TestStatus.Failed + + for _, prefix in ipairs(TestParser.skip_prefixes) do + if string.match(data[2], '^'..prefix) then + node.result.status = TestStatus.Skipped + end + end while true do local line = line_iter() diff --git a/lua/java-test/results/result-status.lua b/lua/java-test/results/result-status.lua index 90014bc..697632b 100644 --- a/lua/java-test/results/result-status.lua +++ b/lua/java-test/results/result-status.lua @@ -1,7 +1,12 @@ ----@enum java-test.TestStatus +---@class java-test.TestStatus +---@field icon string +---@field highlight string + +---@type { [string]: java-test.TestStatus} local TestStatus = { - Failed = 'failed', - Skipped = 'skipped', + Failed = { icon = ' ', highlight = 'DiagnosticError'}, + Skipped = { icon = ' ', highlight = 'DiagnosticWarn'}, + Passed = { icon = ' ', highlight = 'DiagnosticOk'}, } return TestStatus diff --git a/lua/java-test/ui/floating-report-viewer.lua b/lua/java-test/ui/floating-report-viewer.lua index 4cd5401..85ebb80 100644 --- a/lua/java-test/ui/floating-report-viewer.lua +++ b/lua/java-test/ui/floating-report-viewer.lua @@ -24,12 +24,10 @@ function FloatingReportViewer:show(test_results) if result.is_suite then tc.append(' ' .. result.test_name).lbreak() else + local status = result.result.status or TestStatus.Passed + tc.append(status.icon .. result.test_name).lbreak() if result.result.status == TestStatus.Failed then - tc.append('󰅙 ' .. result.test_name).lbreak().append(indentation).append(result.result.trace, indentation) - elseif result.result.status == TestStatus.Skipped then - tc.append(' ' .. result.test_name).lbreak() - else - tc.append(' ' .. result.test_name).lbreak() + tc.append(indentation).append(result.result.trace, indentation) end end @@ -45,10 +43,19 @@ function FloatingReportViewer:show(test_results) local res = build_result(test_results, '', '') - self:show_in_window(vim.split(res, '\n')) + FloatingReportViewer.show_in_window(vim.split(res, '\n')) end -function FloatingReportViewer:show_in_window(content) +function FloatingReportViewer.show_in_window(content) + vim.api.nvim_create_autocmd('BufWinEnter', { + once = true, + callback = function() + for _, status in pairs(TestStatus) do + vim.fn.matchadd(status.highlight, status.icon) + end + end, + }) + local Popup = require('nui.popup') local event = require('nui.utils.autocmd').event From d199986d3cc945a5f63731506bb0813303f4778d Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 15:39:56 +0530 Subject: [PATCH 102/147] chore: code format --- lua/java-test/results/result-parser.lua | 4 ++-- lua/java-test/results/result-status.lua | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lua/java-test/results/result-parser.lua b/lua/java-test/results/result-parser.lua index 583333a..3a0d1e2 100644 --- a/lua/java-test/results/result-parser.lua +++ b/lua/java-test/results/result-parser.lua @@ -110,7 +110,7 @@ function TestParser:parse_test_end(data) node.result.execution = TestExecStatus.Ended for _, prefix in ipairs(TestParser.skip_prefixes) do - if string.match(data[2], '^'..prefix) then + if string.match(data[2], '^' .. prefix) then node.result.status = TestStatus.Skipped end end @@ -125,7 +125,7 @@ function TestParser:parse_test_failed(data, line_iter) node.result.status = node.result.status or TestStatus.Failed for _, prefix in ipairs(TestParser.skip_prefixes) do - if string.match(data[2], '^'..prefix) then + if string.match(data[2], '^' .. prefix) then node.result.status = TestStatus.Skipped end end diff --git a/lua/java-test/results/result-status.lua b/lua/java-test/results/result-status.lua index 697632b..cff718e 100644 --- a/lua/java-test/results/result-status.lua +++ b/lua/java-test/results/result-status.lua @@ -1,12 +1,12 @@ ----@class java-test.TestStatus +---@class java-test.TestStatus ---@field icon string ---@field highlight string ---@type { [string]: java-test.TestStatus} local TestStatus = { - Failed = { icon = ' ', highlight = 'DiagnosticError'}, - Skipped = { icon = ' ', highlight = 'DiagnosticWarn'}, - Passed = { icon = ' ', highlight = 'DiagnosticOk'}, + Failed = { icon = ' ', highlight = 'DiagnosticError' }, + Skipped = { icon = ' ', highlight = 'DiagnosticWarn' }, + Passed = { icon = ' ', highlight = 'DiagnosticOk' }, } return TestStatus From 20191fe610d0c11dc12e7a6310f18bd56ea68d4d Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 17:00:24 +0530 Subject: [PATCH 103/147] fix: workspace_execute calling client command handler --- lua/java-core/ls/clients/jdtls-client.lua | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lua/java-core/ls/clients/jdtls-client.lua b/lua/java-core/ls/clients/jdtls-client.lua index 6a33eac..98ecc01 100644 --- a/lua/java-core/ls/clients/jdtls-client.lua +++ b/lua/java-core/ls/clients/jdtls-client.lua @@ -94,15 +94,11 @@ end ---@param arguments? lsp.LSPAny[] ---@param buffer? integer ---@return lsp.LSPAny -function JdtlsClient:workspace_execute_command(command, arguments, buffer) - return await(function(callback) - self.client:exec_cmd( - ---@diagnostic disable-next-line: missing-fields - { command = command, arguments = arguments }, - { bufnr = buffer }, - callback - ) - end) +function JdtlsClient:workspace_execute_command(command, params, buffer) + return self:request('workspace/executeCommand', { + command = command, + arguments = params, + }, buffer) end ---@class jdtls.ResourceMoveDestination From 2c81c36eb5d60973f3a024487243b043c266067f Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 17:00:34 +0530 Subject: [PATCH 104/147] chore: add keymap in dev config --- .devcontainer/config/nvim/init.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.devcontainer/config/nvim/init.lua b/.devcontainer/config/nvim/init.lua index 56e8214..2b7a52d 100644 --- a/.devcontainer/config/nvim/init.lua +++ b/.devcontainer/config/nvim/init.lua @@ -123,4 +123,8 @@ vim.keymap.set('n', 'dt', function() require('dap').terminate() end, { desc = 'Terminate' }) +vim.keymap.set('n', 'gd', function() + vim.lsp.buf.definition() +end, { desc = 'Terminate' }) + vim.keymap.set('n', 'm', "vnewput = execute('messages')") From 81c58bc3a5aff82e961ab9f58ea8cefb068260b9 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 17:33:09 +0530 Subject: [PATCH 105/147] chore: add debug messages --- lua/java-test/api.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lua/java-test/api.lua b/lua/java-test/api.lua index e4879fd..f222a7f 100644 --- a/lua/java-test/api.lua +++ b/lua/java-test/api.lua @@ -37,6 +37,8 @@ end ---@param file_uri string uri of the class ---@return java-core.TestDetailsWithRange[] # list of test methods function M:get_test_methods(file_uri) + log.debug('finding test methods for uri: ' .. file_uri) + local classes = self.test_client:find_test_types_and_methods(file_uri) local methods = {} @@ -48,6 +50,8 @@ function M:get_test_methods(file_uri) end end + log.debug('found ' .. #methods .. ' test methods') + return methods end @@ -56,6 +60,8 @@ end ---@param report java-test.JUnitTestReport ---@param config? java-dap.DapLauncherConfigOverridable config to override the default values in test launcher config function M:run_class_by_buffer(buffer, report, config) + log.debug('running test class from buffer: ' .. buffer) + local tests = self:get_test_class_by_buffer(buffer) if #tests < 1 then @@ -63,6 +69,8 @@ function M:run_class_by_buffer(buffer, report, config) return end + log.debug('found ' .. #tests .. ' test classes') + self:run_test(tests, report, config) end @@ -82,10 +90,16 @@ end ---@param report java-test.JUnitTestReport ---@param config? java-dap.DapLauncherConfigOverridable config to override the default values in test launcher config function M:run_test(tests, report, config) + log.debug('running ' .. #tests .. ' tests') + local launch_args = self.test_client:resolve_junit_launch_arguments(test_adapters.tests_to_junit_launch_params(tests)) + log.debug('resolved launch args - mainClass: ' .. launch_args.mainClass .. ', projectName: ' .. launch_args.projectName) + local java_exec = self.debug_client:resolve_java_executable(launch_args.mainClass, launch_args.projectName) + log.debug('java executable: ' .. vim.inspect(java_exec)) + local dap_launcher_config = dap_adapters.junit_launch_args_to_dap_config(launch_args, java_exec, { debug = true, label = 'Launch All Java Tests', @@ -93,6 +107,8 @@ function M:run_test(tests, report, config) dap_launcher_config = vim.tbl_deep_extend('force', dap_launcher_config, config or {}) + log.debug('launching tests with config: ' .. vim.inspect(dap_launcher_config)) + self.runner:run_by_config(dap_launcher_config, report) end From 120d33221812f5050d9dcc06b68aa5dddf526238 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 18:44:47 +0530 Subject: [PATCH 106/147] refactor: move checks to separate directory - Create lua/java/checks/ directory structure - Extract nvim version check to nvim-version.lua - Add nvim-jdtls conflict check - Update config to support nvim_jdtls_conflict check --- lua/java.lua | 14 ++------------ lua/java/checks/init.lua | 10 ++++++++++ lua/java/checks/nvim-jdtls.lua | 21 +++++++++++++++++++++ lua/java/checks/nvim-version.lua | 21 +++++++++++++++++++++ lua/java/config.lua | 5 +++-- 5 files changed, 57 insertions(+), 14 deletions(-) create mode 100644 lua/java/checks/init.lua create mode 100644 lua/java/checks/nvim-jdtls.lua create mode 100644 lua/java/checks/nvim-version.lua diff --git a/lua/java.lua b/lua/java.lua index 5315e6c..59332f3 100644 --- a/lua/java.lua +++ b/lua/java.lua @@ -14,19 +14,9 @@ function M.setup(custom_config) vim.g.nvim_java_config = config ---------------------------------------------------------------------- - -- neovim version check -- + -- checks -- ---------------------------------------------------------------------- - if config.checks.nvim_version then - if vim.fn.has('nvim-0.11.5') ~= 1 then - local err = require('java-core.utils.errors') - err.throw([[ - nvim-java is only tested on Neovim 0.11.5 or greater - Please upgrade to Neovim 0.11.5 or greater. - If you are sure it works on your version, disable the version check: - checks = { nvim_version = false }' - ]]) - end - end + require('java.checks').run(config) ---------------------------------------------------------------------- -- logger setup -- diff --git a/lua/java/checks/init.lua b/lua/java/checks/init.lua new file mode 100644 index 0000000..15e29f3 --- /dev/null +++ b/lua/java/checks/init.lua @@ -0,0 +1,10 @@ +local M = {} + +---Run all checks +---@param config java.Config +function M.run(config) + require('java.checks.nvim-version'):run(config) + require('java.checks.nvim-jdtls'):run(config) +end + +return M diff --git a/lua/java/checks/nvim-jdtls.lua b/lua/java/checks/nvim-jdtls.lua new file mode 100644 index 0000000..b3e6290 --- /dev/null +++ b/lua/java/checks/nvim-jdtls.lua @@ -0,0 +1,21 @@ +local M = {} + +---Check if nvim-jdtls plugin is installed +---@param config java.Config +function M:run(config) + if not config.checks.nvim_jdtls_conflict then + return + end + + local ok = pcall(require, 'jdtls') + if ok then + local err = require('java-core.utils.errors') + err.throw([[ + nvim-jdtls plugin detected! + nvim-java and nvim-jdtls should not be used together. + Please remove nvim-jdtls from your configuration. + ]]) + end +end + +return M diff --git a/lua/java/checks/nvim-version.lua b/lua/java/checks/nvim-version.lua new file mode 100644 index 0000000..300c932 --- /dev/null +++ b/lua/java/checks/nvim-version.lua @@ -0,0 +1,21 @@ +local M = {} + +---Run nvim version check +---@param config java.Config +function M:run(config) + if not config.checks.nvim_version then + return + end + + if vim.fn.has('nvim-0.11.5') ~= 1 then + local err = require('java-core.utils.errors') + err.throw([[ + nvim-java is only tested on Neovim 0.11.5 or greater + Please upgrade to Neovim 0.11.5 or greater. + If you are sure it works on your version, disable the version check: + checks = { nvim_version = false }' + ]]) + end +end + +return M diff --git a/lua/java/config.lua b/lua/java/config.lua index b398135..8c9439d 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -13,7 +13,7 @@ local jdtls_version_map = { local V = jdtls_version_map[JDTLS_VERSION] ---@class java.Config ----@field checks { nvim_version: boolean } +---@field checks { nvim_version: boolean, nvim_jdtls_conflict: boolean } ---@field jdtls { version: string } ---@field lombok { enable: boolean, version: string } ---@field java_test { enable: boolean, version: string } @@ -24,7 +24,7 @@ local V = jdtls_version_map[JDTLS_VERSION] ---@field log java-core.Log2Config ---@class java.PartialConfig ----@field checks? { nvim_version?: boolean } +---@field checks? { nvim_version?: boolean, nvim_jdtls_conflict?: boolean } ---@field jdtls? { version?: string } ---@field lombok? { enable?: boolean, version?: string } ---@field java_test? { enable?: boolean, version?: string } @@ -38,6 +38,7 @@ local V = jdtls_version_map[JDTLS_VERSION] local config = { checks = { nvim_version = true, + nvim_jdtls_conflict = true, }, jdtls = { From 56e82afd514a47b28437b158dd43332814f08523 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 18:45:57 +0530 Subject: [PATCH 107/147] feat: add jproperties filetype support - Extract filetype config to separate module - Add jproperties to supported filetypes --- lua/java-core/ls/servers/jdtls/filetype.lua | 10 ++++++++++ lua/java-core/ls/servers/jdtls/init.lua | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 lua/java-core/ls/servers/jdtls/filetype.lua diff --git a/lua/java-core/ls/servers/jdtls/filetype.lua b/lua/java-core/ls/servers/jdtls/filetype.lua new file mode 100644 index 0000000..3fe9ecb --- /dev/null +++ b/lua/java-core/ls/servers/jdtls/filetype.lua @@ -0,0 +1,10 @@ +local M = {} + +function M.get_filetypes() + return { + 'java', + 'jproperties', + } +end + +return M diff --git a/lua/java-core/ls/servers/jdtls/init.lua b/lua/java-core/ls/servers/jdtls/init.lua index 8367a36..271625b 100644 --- a/lua/java-core/ls/servers/jdtls/init.lua +++ b/lua/java-core/ls/servers/jdtls/init.lua @@ -8,6 +8,7 @@ function M.get_config(opts) local cmd = require('java-core.ls.servers.jdtls.cmd') local env = require('java-core.ls.servers.jdtls.env') local root = require('java-core.ls.servers.jdtls.root') + local filetype = require('java-core.ls.servers.jdtls.filetype') local log = require('java-core.utils.log2') log.debug('get_config called with opts:', opts) @@ -17,8 +18,8 @@ function M.get_config(opts) base_conf.cmd = cmd.get_cmd(opts) base_conf.cmd_env = env.get_env(opts) base_conf.init_options.bundles = plugins.get_plugins(opts) - base_conf.filetypes = { 'java' } base_conf.root_markers = root.get_root_markers() + base_conf.filetypes = filetype.get_filetypes() return base_conf end From b83ebf72e3c1825fd31eddad5b6f12d073642998 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 18:46:16 +0530 Subject: [PATCH 108/147] chore: remove nvim-jdtls from devcontainer --- .devcontainer/config/nvim/lazy-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/config/nvim/lazy-lock.json b/.devcontainer/config/nvim/lazy-lock.json index c076196..73cbd4c 100644 --- a/.devcontainer/config/nvim/lazy-lock.json +++ b/.devcontainer/config/nvim/lazy-lock.json @@ -2,5 +2,6 @@ "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, "nvim-dap": { "branch": "master", "commit": "b38f7d30366d9169d0a623c4c85fbcf99d8d58bb" }, + "nvim-jdtls": { "branch": "master", "commit": "943e2398aba6b7e976603708450c6c93c600e830" }, "spring-boot.nvim": { "branch": "main", "commit": "218c0c26c14d99feca778e4d13f5ec3e8b1b60f0" } } From 919ed0b8b5ea4f1cc1ad90d70aac97f45a0b2e6e Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 18:46:27 +0530 Subject: [PATCH 109/147] docs: update README for v4.0.0 release --- README.md | 498 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 497 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 16d1022..c540b14 100644 --- a/README.md +++ b/README.md @@ -7,4 +7,500 @@ ![Neovim](https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white) ![Lua](https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white) -monorepo is a complete rewrite. Do not use this plugin. This will be released as nvim-java 4.0.0 in the original repo soon. +Just install and start writing `public static void main(String[] args)`. + +> [!CAUTION] +> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` before installing this + +> [!TIP] +> You can find cool tips & tricks here https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks + +> [!NOTE] +> If you are facing errors while using, please check troubleshoot wiki https://github.com/nvim-java/nvim-java/wiki/Troubleshooting + +## :loudspeaker: Demo + + + +## :dizzy: Features + +- :white_check_mark: Spring Boot Tools +- :white_check_mark: Diagnostics & Auto Completion +- :white_check_mark: Automatic Debug Configuration +- :white_check_mark: Organize Imports & Code Formatting +- :white_check_mark: Running Tests +- :white_check_mark: Run & Debug Profiles +- :white_check_mark: [Code Actions](https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks#running-code-actions) + +## :bulb: Why + +- Everything necessary will be installed automatically +- Uses [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) to setup `jdtls` +- Realtime server settings updates is possible using [neoconf](https://github.com/folke/neoconf.nvim) +- Auto loads necessary `jdtls` plugins + - Supported plugins are, + - `spring-boot-tools` + - `lombok` + - `java-test` + - `java-debug-adapter` + +## :hammer: How to Install + +
+ +:small_orange_diamond:details + +### Starter Configs (Recommend for newbies) + +Following are forks of original repositories pre-configured for java. If you +don't know how to get started, use one of the following to get started. +You can click on **n commits ahead of** link to see the changes made on top of the original project + +- [LazyVim](https://github.com/nvim-java/starter-lazyvim) +- [Kickstart](https://github.com/nvim-java/starter-kickstart) +- [AstroNvim](https://github.com/nvim-java/starter-astronvim) + +### Custom Configuration Instructions + +- Install the plugin + +Using [lazy.nvim](https://github.com/folke/lazy.nvim) + +```lua +return {'nvim-java/nvim-java'} +``` + +- Setup nvim-java before `lspconfig` + +```lua +require('java').setup() +``` + +- Setup jdtls like you would usually do + +```lua +require('lspconfig').jdtls.setup({}) +``` + +Yep! That's all :) + +
+ +## :keyboard: Commands + +
+ +:small_orange_diamond:details + +### Build + +- `JavaBuildBuildWorkspace` - Runs a full workspace build + +- `JavaBuildCleanWorkspace` - Clear the workspace cache + (for now you have to close and reopen to restart the language server after + the deletion) + +### Runner + +- `JavaRunnerRunMain` - Runs the application or selected main class (if there + are multiple main classes) + +```vim +:JavaRunnerRunMain +:JavaRunnerRunMain +``` + +- `JavaRunnerStopMain` - Stops the running application +- `JavaRunnerToggleLogs` - Toggle between show & hide runner log window + +### DAP + +- `JavaDapConfig` - DAP is autoconfigured on start up, but in case you want to + force configure it again, you can use this API + +### Test + +- `JavaTestRunCurrentClass` - Run the test class in the active buffer +- `JavaTestDebugCurrentClass` - Debug the test class in the active buffer +- `JavaTestRunCurrentMethod` - Run the test method on the cursor +- `JavaTestDebugCurrentMethod` - Debug the test method on the cursor +- `JavaTestViewLastReport` - Open the last test report in a popup window + +### Profiles + +- `JavaProfile` - Opens the profiles UI + +### Refactor + +- `JavaRefactorExtractVariable` - Create a variable from value at cursor/selection +- `JavaRefactorExtractVariableAllOccurrence` - Create a variable for all + occurrences from value at cursor/selection +- `JavaRefactorExtractConstant` - Create a constant from the value at cursor/selection +- `JavaRefactorExtractMethod` - Create a method from the value at cursor/selection +- `JavaRefactorExtractField` - Create a field from the value at cursor/selection + +### Settings + +- `JavaSettingsChangeRuntime` - Change the JDK version to another + +
+ +## :computer: APIs + +
+ +:small_orange_diamond:details + +### Build + +- `build.build_workspace` - Runs a full workspace build + +```lua +require('java').build.build_workspace() +``` + +- `build.clean_workspace` - Clear the workspace cache + (for now you have to close and reopen to restart the language server after + the deletion) + +```lua +require('java').build.clean_workspace() +``` + +### Runner + +- `built_in.run_app` - Runs the application or selected main class (if there + are multiple main classes) + +```lua +require('java').runner.built_in.run_app({}) +require('java').runner.built_in.run_app({'arguments', 'to', 'pass', 'to', 'main'}) +``` + +- `built_in.stop_app` - Stops the running application + +```lua +require('java').runner.built_in.stop_app() +``` + +- `built_in.toggle_logs` - Toggle between show & hide runner log window + +```lua +require('java').runner.built_in.toggle_logs() +``` + +### DAP + +- `config_dap` - DAP is autoconfigured on start up, but in case you want to force + configure it again, you can use this API + +```lua +require('java').dap.config_dap() +``` + +### Test + +- `run_current_class` - Run the test class in the active buffer + +```lua +require('java').test.run_current_class() +``` + +- `debug_current_class` - Debug the test class in the active buffer + +```lua +require('java').test.debug_current_class() +``` + +- `run_current_method` - Run the test method on the cursor + +```lua +require('java').test.run_current_method() +``` + +- `debug_current_method` - Debug the test method on the cursor + +```lua +require('java').test.debug_current_method() +``` + +- `view_report` - Open the last test report in a popup window + +```lua +require('java').test.view_last_report() +``` + +### Profiles + +```lua +require('java').profile.ui() +``` + +### Refactor + +- `extract_variable` - Create a variable from value at cursor/selection + +```lua +require('java').refactor.extract_variable() +``` + +- `extract_variable_all_occurrence` - Create a variable for all occurrences from + value at cursor/selection + +```lua +require('java').refactor.extract_variable_all_occurrence() +``` + +- `extract_constant` - Create a constant from the value at cursor/selection + +```lua +require('java').refactor.extract_constant() +``` + +- `extract_method` - Create method from the value at cursor/selection + +```lua +require('java').refactor.extract_method() +``` + +- `extract_field` - Create a field from the value at cursor/selection + +```lua +require('java').refactor.extract_field() +``` + +### Settings + +- `change_runtime` - Change the JDK version to another + +```lua +require('java').settings.change_runtime() +``` + +
+ +## :clamp: How to Use JDK X.X Version? + +
+ +:small_orange_diamond:details + +### Method 1 + +[Neoconf](https://github.com/folke/neoconf.nvim) can be used to manage LSP +setting including jdtls. Neoconf allows global configuration as well as project-wise +configurations. Here is how you can set Jdtls setting on `neoconf.json` + +```json +{ + "lspconfig": { + "jdtls": { + "java.configuration.runtimes": [ + { + "name": "JavaSE-21", + "path": "/opt/jdk-21", + "default": true + } + ] + } + } +} +``` + +### Method 2 + +Pass the settings to Jdtls setup. + +```lua +require('lspconfig').jdtls.setup({ + settings = { + java = { + configuration = { + runtimes = { + { + name = "JavaSE-21", + path = "/opt/jdk-21", + default = true, + } + } + } + } + } +}) +``` + +
+ +## :wrench: Configuration + +
+ +:small_orange_diamond:details + +For most users changing the default configuration is not necessary. But if you +want, following options are available + +```lua +{ + -- list of file that exists in root of the project + root_markers = { + 'settings.gradle', + 'settings.gradle.kts', + 'pom.xml', + 'build.gradle', + 'mvnw', + 'gradlew', + 'build.gradle', + 'build.gradle.kts', + '.git', + }, + + jdtls = { + version = 'v1.43.0', + }, + + lombok = { + version = 'nightly', + }, + + -- load java test plugins + java_test = { + enable = true, + version = '0.40.1', + }, + + -- load java debugger plugins + java_debug_adapter = { + enable = true, + version = '0.58.1', + }, + + spring_boot_tools = { + enable = true, + version = '1.55.1', + }, + + jdk = { + -- install jdk using mason.nvim + auto_install = true, + version = '17.0.2', + }, + + notifications = { + -- enable 'Configuring DAP' & 'DAP configured' messages on start up + dap = true, + }, + + -- We do multiple verifications to make sure things are in place to run this + -- plugin + verification = { + -- nvim-java checks for the order of execution of following + -- * require('java').setup() + -- * require('lspconfig').jdtls.setup() + -- IF they are not executed in the correct order, you will see a error + -- notification. + -- Set following to false to disable the notification if you know what you + -- are doing + invalid_order = true, + + -- nvim-java checks if the require('java').setup() is called multiple + -- times. + -- IF there are multiple setup calls are executed, an error will be shown + -- Set following property value to false to disable the notification if + -- you know what you are doing + duplicate_setup_calls = true, + + -- nvim-java checks if nvim-java/mason-registry is added correctly to + -- mason.nvim plugin. + -- IF it's not registered correctly, an error will be thrown and nvim-java + -- will stop setup + invalid_mason_registry = false, + }, + + mason = { + -- These mason registries will be prepended to the existing mason + -- configuration + registries = { + 'github:nvim-java/mason-registry', + }, + }, +} + +``` + +
+ +## :golf: Architecture + +
+ +:small_orange_diamond:details + +Following is the high level idea. Jdtls is the language server nvim-java +communicates with. However, we don't have all the features we need just in +Jdtls. So, we are loading java-test & java-debug-adapter extensions when we +launch Jdtls. Once the language server is started, we communicate with the +language server to do stuff. + +For instance, to run the current test, + +- Request Jdtls for test classes +- Request Jdtls for class paths, module paths, java executable +- Request Jdtls to start a debug session and send the port of the session back +- Prepare TCP connections to listen to the test results +- Start nvim-dap and let user interactions to be handled by nvim-dap +- Parse the test results as they come in +- Once the execution is done, open a window show the test results + +```text + ┌────────────┐ ┌────────────┐ + │ │ │ │ + │ Neovim │ │ VSCode │ + │ │ │ │ + └─────▲──────┘ └──────▲─────┘ + │ │ + │ │ + │ │ + │ │ +┌───────▼───────┐ ┌──────────────▼──────────────┐ +│ │ │ │ +│ nvim-java │ │ Extension Pack for Java │ +│ │ │ │ +└───────▲───────┘ └──────────────▲──────────────┘ + │ │ + │ │ + │ │ + │ │ + │ │ + │ ┌───────────┐ │ + │ │ │ │ + └──────────────► JDTLS ◄────────────┘ + │ │ + └───▲───▲───┘ + │ │ + │ │ + │ │ + │ │ + │ │ + ┌───────────────┐ │ │ ┌────────────────────────┐ + │ │ │ │ │ │ + │ java-test ◄────────┘ └─────────► java-debug-adapter │ + │ │ │ │ + └───────────────┘ └────────────────────────┘ +``` + +
+ +## :bookmark_tabs: Projects Acknowledgement + +- [spring-boot.nvim](https://github.com/JavaHello/spring-boot.nvim) is the one + that starts sts4 & do other necessary `jdtls` `sts4` sync command registration + in `nvim-java`. + +- [nvim-jdtls](https://github.com/mfussenegger/nvim-jdtls) is a plugin that follows + "Keep it simple, stupid!" approach. If you love customizing things by yourself, + then give nvim-jdtls a try. + +> [!WARNING] +> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` +> before installing this From 084ef68a57a1dff5dc43a351726af958d4fe1233 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 19:05:37 +0530 Subject: [PATCH 110/147] chore: disable line length check in luacheck --- .luacheckrc | 1 + 1 file changed, 1 insertion(+) diff --git a/.luacheckrc b/.luacheckrc index 33c63e5..232e3ba 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -15,3 +15,4 @@ read_globals = { ignore = { '212/self', } +max_line_length = false From 04ce07ea8a67e2d6608bf7159f42f4b2537095d1 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 19:06:26 +0530 Subject: [PATCH 111/147] chore(ci): enable debug logging in CI tests --- tests/utils/prepare-config.lua | 18 +++++++++++++++--- tests/utils/test-config.lua | 14 ++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tests/utils/prepare-config.lua b/tests/utils/prepare-config.lua index 544f21e..ef1bfc2 100644 --- a/tests/utils/prepare-config.lua +++ b/tests/utils/prepare-config.lua @@ -31,11 +31,23 @@ require('lazy').setup({ 'nvim-java/nvim-java', dir = '.', config = function() - require('java').setup({ + local is_nixos = vim.fn.filereadable('/etc/NIXOS') == 1 + local is_ci = vim.env.CI ~= nil + + local config = { jdk = { - auto_install = false, + auto_install = not is_nixos, }, - }) + } + + if is_ci then + config.log = { + level = 'debug', + use_console = true, + } + end + + require('java').setup(config) vim.lsp.enable('jdtls') end, }, diff --git a/tests/utils/test-config.lua b/tests/utils/test-config.lua index bd1fb1f..c2cca10 100644 --- a/tests/utils/test-config.lua +++ b/tests/utils/test-config.lua @@ -18,11 +18,21 @@ vim.opt.runtimepath:append(temp_path .. '/nvim-dap') vim.opt.runtimepath:append('.') local is_nixos = vim.fn.filereadable('/etc/NIXOS') == 1 +local is_ci = vim.env.CI ~= nil -require('java').setup({ +local config = { jdk = { auto_install = not is_nixos, }, -}) +} + +if is_ci then + config.log = { + level = 'debug', + use_console = true, + } +end + +require('java').setup(config) vim.lsp.enable('jdtls') From 38bd7425fadb3d0914730d05a5885dcfbacf2eed Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 19:06:47 +0530 Subject: [PATCH 112/147] docs: update README with native lsp config info --- README.md | 5 +---- lua/java-test/api.lua | 4 +++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c540b14..4ffb603 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,6 @@ Just install and start writing `public static void main(String[] args)`. -> [!CAUTION] -> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` before installing this - > [!TIP] > You can find cool tips & tricks here https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks @@ -35,7 +32,7 @@ Just install and start writing `public static void main(String[] args)`. ## :bulb: Why - Everything necessary will be installed automatically -- Uses [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) to setup `jdtls` +- Uses native `vim.lsp.config` to setup `jdtls` - Realtime server settings updates is possible using [neoconf](https://github.com/folke/neoconf.nvim) - Auto loads necessary `jdtls` plugins - Supported plugins are, diff --git a/lua/java-test/api.lua b/lua/java-test/api.lua index f222a7f..48fe2b5 100644 --- a/lua/java-test/api.lua +++ b/lua/java-test/api.lua @@ -94,7 +94,9 @@ function M:run_test(tests, report, config) local launch_args = self.test_client:resolve_junit_launch_arguments(test_adapters.tests_to_junit_launch_params(tests)) - log.debug('resolved launch args - mainClass: ' .. launch_args.mainClass .. ', projectName: ' .. launch_args.projectName) + log.debug( + 'resolved launch args - mainClass: ' .. launch_args.mainClass .. ', projectName: ' .. launch_args.projectName + ) local java_exec = self.debug_client:resolve_java_executable(launch_args.mainClass, launch_args.projectName) From 8dbbc549af331a80055ecbcb9f7ffebeb26aadab Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 19:11:54 +0530 Subject: [PATCH 113/147] docs: clarify feat: usage in commit messages --- CLAUDE.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index d716f17..65aa678 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -149,5 +149,8 @@ log.fatal('fatal message') ## Git Guidelines - Use conventional commit messages per [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +- `feat:` is ONLY for end-user features (e.g., `feat: add code completion`) + - CI/internal features use `chore(ci):` (e.g., `chore(ci): enable debug logs`) + - Build/tooling features use `chore(build):`, `chore(test):`, etc. - Never append "generated by AI" message - Split unrelated changes into separate commits From dacc4d648a3efaf0bc721ac6e1501332b7f8617b Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 22:19:04 +0530 Subject: [PATCH 114/147] docs: update installation instructions for native LSP --- README.md | 163 ++++++++++++++++++++---------------------------------- 1 file changed, 60 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index 4ffb603..3193795 100644 --- a/README.md +++ b/README.md @@ -57,26 +57,40 @@ You can click on **n commits ahead of** link to see the changes made on top of t - [Kickstart](https://github.com/nvim-java/starter-kickstart) - [AstroNvim](https://github.com/nvim-java/starter-astronvim) -### Custom Configuration Instructions +### Manual Installation Instructions -- Install the plugin +**Requirements:** Neovim 0.11+ -Using [lazy.nvim](https://github.com/folke/lazy.nvim) +#### Using `vim.pack` ```lua -return {'nvim-java/nvim-java'} -``` +vim.pack.add({ + { + src = 'https://github.com/JavaHello/spring-boot.nvim', + version = '218c0c26c14d99feca778e4d13f5ec3e8b1b60f0', + }, + 'https://github.com/MunifTanjim/nui.nvim', + 'https://github.com/mfussenegger/nvim-dap', -- Setup nvim-java before `lspconfig` + 'https://github.com/nvim-java/nvim-java', +}) -```lua require('java').setup() +vim.lsp.enable('jdtls') ``` -- Setup jdtls like you would usually do +#### Using `lazy.nvim` + +Install using [lazy.nvim](https://github.com/folke/lazy.nvim): ```lua -require('lspconfig').jdtls.setup({}) +{ + 'nvim-java/nvim-java', + config = function() + require('java').setup() + vim.lsp.enable('jdtls') + end, +} ``` Yep! That's all :) @@ -282,42 +296,20 @@ require('java').settings.change_runtime() :small_orange_diamond:details -### Method 1 - -[Neoconf](https://github.com/folke/neoconf.nvim) can be used to manage LSP -setting including jdtls. Neoconf allows global configuration as well as project-wise -configurations. Here is how you can set Jdtls setting on `neoconf.json` - -```json -{ - "lspconfig": { - "jdtls": { - "java.configuration.runtimes": [ - { - "name": "JavaSE-21", - "path": "/opt/jdk-21", - "default": true - } - ] - } - } -} -``` - -### Method 2 - -Pass the settings to Jdtls setup. +Use `vim.lsp.config()` to override the default JDTLS settings: ```lua -require('lspconfig').jdtls.setup({ - settings = { - java = { - configuration = { - runtimes = { - { - name = "JavaSE-21", - path = "/opt/jdk-21", - default = true, +vim.lsp.config('jdtls', { + default_config = { + settings = { + java = { + configuration = { + runtimes = { + { + name = "JavaSE-21", + path = "/opt/jdk-21", + default = true, + } } } } @@ -335,41 +327,35 @@ require('lspconfig').jdtls.setup({ :small_orange_diamond:details For most users changing the default configuration is not necessary. But if you -want, following options are available +want, following options are available: ```lua -{ - -- list of file that exists in root of the project - root_markers = { - 'settings.gradle', - 'settings.gradle.kts', - 'pom.xml', - 'build.gradle', - 'mvnw', - 'gradlew', - 'build.gradle', - 'build.gradle.kts', - '.git', +require('java').setup({ + -- Startup checks + checks = { + nvim_version = true, -- Check Neovim version + nvim_jdtls_conflict = true, -- Check for nvim-jdtls conflict }, + -- JDTLS configuration jdtls = { - version = 'v1.43.0', + version = '1.43.0', }, + -- Extensions lombok = { - version = 'nightly', + enable = true, + version = '1.18.40', }, - -- load java test plugins java_test = { enable = true, version = '0.40.1', }, - -- load java debugger plugins java_debug_adapter = { enable = true, - version = '0.58.1', + version = '0.58.2', }, spring_boot_tools = { @@ -377,52 +363,27 @@ want, following options are available version = '1.55.1', }, + -- JDK installation jdk = { - -- install jdk using mason.nvim auto_install = true, - version = '17.0.2', + version = '17', }, + -- Notifications notifications = { - -- enable 'Configuring DAP' & 'DAP configured' messages on start up - dap = true, + dap = true, -- Show DAP configuration messages }, - -- We do multiple verifications to make sure things are in place to run this - -- plugin - verification = { - -- nvim-java checks for the order of execution of following - -- * require('java').setup() - -- * require('lspconfig').jdtls.setup() - -- IF they are not executed in the correct order, you will see a error - -- notification. - -- Set following to false to disable the notification if you know what you - -- are doing - invalid_order = true, - - -- nvim-java checks if the require('java').setup() is called multiple - -- times. - -- IF there are multiple setup calls are executed, an error will be shown - -- Set following property value to false to disable the notification if - -- you know what you are doing - duplicate_setup_calls = true, - - -- nvim-java checks if nvim-java/mason-registry is added correctly to - -- mason.nvim plugin. - -- IF it's not registered correctly, an error will be thrown and nvim-java - -- will stop setup - invalid_mason_registry = false, + -- Logging + log = { + use_console = true, + use_file = true, + level = 'info', + log_file = vim.fn.stdpath('state') .. '/nvim-java.log', + max_lines = 1000, + show_location = false, }, - - mason = { - -- These mason registries will be prepended to the existing mason - -- configuration - registries = { - 'github:nvim-java/mason-registry', - }, - }, -} - +}) ``` @@ -497,7 +458,3 @@ For instance, to run the current test, - [nvim-jdtls](https://github.com/mfussenegger/nvim-jdtls) is a plugin that follows "Keep it simple, stupid!" approach. If you love customizing things by yourself, then give nvim-jdtls a try. - -> [!WARNING] -> You cannot use `nvim-java` alongside `nvim-jdtls`. So remove `nvim-jdtls` -> before installing this From c3e31292f731f281f6294adb543b9943a8331593 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 22:23:42 +0530 Subject: [PATCH 115/147] fix: update nvim version check to 0.11 --- lua/java/checks/nvim-version.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lua/java/checks/nvim-version.lua b/lua/java/checks/nvim-version.lua index 300c932..a18325f 100644 --- a/lua/java/checks/nvim-version.lua +++ b/lua/java/checks/nvim-version.lua @@ -7,11 +7,11 @@ function M:run(config) return end - if vim.fn.has('nvim-0.11.5') ~= 1 then + if vim.fn.has('nvim-0.11') ~= 1 then local err = require('java-core.utils.errors') err.throw([[ - nvim-java is only tested on Neovim 0.11.5 or greater - Please upgrade to Neovim 0.11.5 or greater. + nvim-java is only tested on Neovim 0.11 or greater + Please upgrade to Neovim 0.11 or greater. If you are sure it works on your version, disable the version check: checks = { nvim_version = false }' ]]) From 32c0b75a8e88f083d4abfc9e080f3ba2a66db986 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 22:46:59 +0530 Subject: [PATCH 116/147] chore(ci): add lsp log output for mac tests --- tests/specs/lsp_spec.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/specs/lsp_spec.lua b/tests/specs/lsp_spec.lua index 8ef81b7..a8c349d 100644 --- a/tests/specs/lsp_spec.lua +++ b/tests/specs/lsp_spec.lua @@ -1,8 +1,13 @@ local lsp_utils = dofile('tests/utils/lsp-utils.lua') +local system = require('java-core.utils.system') local assert = require('luassert') describe('LSP Attach', function() it('should attach when opening a Java buffer', function() + if system.get_os() == 'mac' then + vim.print(vim.fn.readfile('/Users/runner/.local/state/nvim/lsp.log')) + end + vim.cmd.edit('HelloWorld.java') local jdtls = lsp_utils.wait_for_lsp_attach('jdtls', 30000) From 293ee310b4c51c14d7571be24117c09025f320bf Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 23:10:19 +0530 Subject: [PATCH 117/147] fix: nvim-java does not work on mac with the embeded jdk --- lua/java-core/constants/java_version.lua | 2 +- lua/java-core/ls/servers/jdtls/env.lua | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lua/java-core/constants/java_version.lua b/lua/java-core/constants/java_version.lua index 4dc8c05..d8c8459 100644 --- a/lua/java-core/constants/java_version.lua +++ b/lua/java-core/constants/java_version.lua @@ -1,5 +1,5 @@ return { - ['1.43.0'] = { from = 17, to = 21 }, + ['1.43.0'] = { from = 17, to = 17 }, ['1.44.0'] = { from = 17, to = 21 }, ['1.45.0'] = { from = 21, to = 21 }, ['1.46.0'] = { from = 21, to = 21 }, diff --git a/lua/java-core/ls/servers/jdtls/env.lua b/lua/java-core/ls/servers/jdtls/env.lua index 3e1bed2..6fcf040 100644 --- a/lua/java-core/ls/servers/jdtls/env.lua +++ b/lua/java-core/ls/servers/jdtls/env.lua @@ -1,6 +1,7 @@ local path = require('java-core.utils.path') local Manager = require('pkgm.manager') local log = require('java-core.utils.log2') +local system = require('java-core.utils.system') --- @TODO: importing stuff from java main package feels wrong. --- We should fix this in the future @@ -16,7 +17,15 @@ function M.get_env(opts) end local jdk_root = Manager:get_install_dir('openjdk', config.jdk.version) - local java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*')) + + local java_home + + if system.get_os() == 'mac' then + java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*', 'Contents', 'Home')) + else + java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*')) + end + local java_bin = path.join(java_home, 'bin') local env = { From ad5d3701ea007720960062110ea11d588e4f3311 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Sun, 30 Nov 2025 23:35:44 +0530 Subject: [PATCH 118/147] fix: java validation is invalid --- lua/java-core/ls/servers/jdtls/cmd.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/java-core/ls/servers/jdtls/cmd.lua b/lua/java-core/ls/servers/jdtls/cmd.lua index 371c358..2be1cfc 100644 --- a/lua/java-core/ls/servers/jdtls/cmd.lua +++ b/lua/java-core/ls/servers/jdtls/cmd.lua @@ -98,16 +98,16 @@ end ---@private function M.validate_java_version() - local v = M.get_java_major_version() + local curr_ver = M.get_java_major_version() local exp_ver = java_version_map[conf.jdtls.version] - if v <= exp_ver.from and v >= exp_ver.to then + if not (curr_ver >= exp_ver.to and curr_ver <= exp_ver.from) then local msg = string.format( - 'Java version mismatch: JDTLS %s requires Java %d - %d, but found Java %d', + 'Java version mismatch: JDTLS %s requires Java %d <= java >= %d, but found Java %d', conf.jdtls.version, exp_ver.from, exp_ver.to, - v + curr_ver ) err.throw(msg) From 3db02daef1245353023f2cb8b70f7bb560280e97 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 1 Dec 2025 01:22:51 +0530 Subject: [PATCH 119/147] chore: remove unwanted code --- tests/specs/lsp_spec.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/specs/lsp_spec.lua b/tests/specs/lsp_spec.lua index a8c349d..8ef81b7 100644 --- a/tests/specs/lsp_spec.lua +++ b/tests/specs/lsp_spec.lua @@ -1,13 +1,8 @@ local lsp_utils = dofile('tests/utils/lsp-utils.lua') -local system = require('java-core.utils.system') local assert = require('luassert') describe('LSP Attach', function() it('should attach when opening a Java buffer', function() - if system.get_os() == 'mac' then - vim.print(vim.fn.readfile('/Users/runner/.local/state/nvim/lsp.log')) - end - vim.cmd.edit('HelloWorld.java') local jdtls = lsp_utils.wait_for_lsp_attach('jdtls', 30000) From 41d72bf9fb9ec8d6edf8cdaea2cee970276d6c55 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 1 Dec 2025 01:36:06 +0530 Subject: [PATCH 120/147] fix: java version check does not consider env passed for LSP --- lua/java-core/ls/servers/jdtls/cmd.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lua/java-core/ls/servers/jdtls/cmd.lua b/lua/java-core/ls/servers/jdtls/cmd.lua index 2be1cfc..39d1e6b 100644 --- a/lua/java-core/ls/servers/jdtls/cmd.lua +++ b/lua/java-core/ls/servers/jdtls/cmd.lua @@ -20,7 +20,7 @@ function M.get_cmd(opts) return function(dispatchers, config) local cmd = M.get_jvm_args(opts):concat(M.get_jar_args()) - M.validate_java_version() + M.validate_java_version(config.cmd_env) log.debug('Starting jdtls with cmd', cmd) @@ -97,8 +97,9 @@ function M.get_jar_args(cwd) end ---@private -function M.validate_java_version() - local curr_ver = M.get_java_major_version() +---@param env table +function M.validate_java_version(env) + local curr_ver = M.get_java_major_version(env) local exp_ver = java_version_map[conf.jdtls.version] if not (curr_ver >= exp_ver.to and curr_ver <= exp_ver.from) then @@ -115,8 +116,11 @@ function M.validate_java_version() end ---@private -function M.get_java_major_version() - local version = vim.fn.system('java -version') +---@param env table +function M.get_java_major_version(env) + local proc = vim.system({ 'java', '-version' }, { env = env }):wait() + local version = proc.stderr or proc.stdout or '' + local major = version:match('version (%d+)') or version:match('version "(%d+)') or version:match('openjdk (%d+)') From 9743ddefa6e0c1af78cfa5094658231215918794 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Mon, 1 Dec 2025 01:41:22 +0530 Subject: [PATCH 121/147] fix: PATH env separator is not windows compatible --- lua/java-core/ls/servers/jdtls/env.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lua/java-core/ls/servers/jdtls/env.lua b/lua/java-core/ls/servers/jdtls/env.lua index 6fcf040..128e5f6 100644 --- a/lua/java-core/ls/servers/jdtls/env.lua +++ b/lua/java-core/ls/servers/jdtls/env.lua @@ -28,8 +28,10 @@ function M.get_env(opts) local java_bin = path.join(java_home, 'bin') + local separator = system.get_os() == 'win' and ';' or ':' + local env = { - ['PATH'] = java_bin .. ':' .. vim.fn.getenv('PATH'), + ['PATH'] = java_bin .. separator .. vim.fn.getenv('PATH'), ['JAVA_HOME'] = java_home, } From e1bb01db00f20be381eb9937a8517a347fa57bf6 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Wed, 3 Dec 2025 22:22:54 +0530 Subject: [PATCH 122/147] fix: windows compatibility issue (#439) --- .devcontainer/config/nvim/lazy-lock.json | 3 +-- lua/java-core/ls/servers/jdtls/cmd.lua | 28 ++++++++++++++++++++++-- lua/pkgm/downloaders/powershell.lua | 5 ++++- lua/pkgm/extractors/tar.lua | 20 ++++++++++++++++- tests/specs/lsp_spec.lua | 4 ++++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/.devcontainer/config/nvim/lazy-lock.json b/.devcontainer/config/nvim/lazy-lock.json index 73cbd4c..3737ab9 100644 --- a/.devcontainer/config/nvim/lazy-lock.json +++ b/.devcontainer/config/nvim/lazy-lock.json @@ -1,7 +1,6 @@ { "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, - "nvim-dap": { "branch": "master", "commit": "b38f7d30366d9169d0a623c4c85fbcf99d8d58bb" }, - "nvim-jdtls": { "branch": "master", "commit": "943e2398aba6b7e976603708450c6c93c600e830" }, + "nvim-dap": { "branch": "master", "commit": "5860c7c501eb428d3137ee22c522828d20cca0b3" }, "spring-boot.nvim": { "branch": "main", "commit": "218c0c26c14d99feca778e4d13f5ec3e8b1b60f0" } } diff --git a/lua/java-core/ls/servers/jdtls/cmd.lua b/lua/java-core/ls/servers/jdtls/cmd.lua index 39d1e6b..8ac1691 100644 --- a/lua/java-core/ls/servers/jdtls/cmd.lua +++ b/lua/java-core/ls/servers/jdtls/cmd.lua @@ -20,7 +20,14 @@ function M.get_cmd(opts) return function(dispatchers, config) local cmd = M.get_jvm_args(opts):concat(M.get_jar_args()) - M.validate_java_version(config.cmd_env) + -- NOTE: eventhough we are setting the PATH env var, due to a bug, it's not + -- working on Windows. So just lanching 'java' will result in executing the + -- system java. So as a workaround, we use the absolute path to java instead + -- So following check is not needed when we have auto_install set to true + -- @see https://github.com/neovim/neovim/issues/36818 + if not conf.jdk.auto_install then + M.validate_java_version(config.cmd_env) + end log.debug('Starting jdtls with cmd', cmd) @@ -40,8 +47,25 @@ end function M.get_jvm_args(opts) local jdtls_config = path.join(jdtls_root, system.get_config_suffix()) + local java_exe = 'java' + + -- NOTE: eventhough we are setting the PATH env var, due to a bug, it's not + -- working on Windows. So we are using the absolute path to java instead + -- @see https://github.com/neovim/neovim/issues/36818 + if conf.jdk.auto_install then + local jdk_root = Manager:get_install_dir('openjdk', conf.jdk.version) + local java_home + if system.get_os() == 'mac' then + java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*', 'Contents', 'Home')) + else + java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*')) + end + + java_exe = path.join(java_home, 'bin', 'java') + end + local jvm_args = List:new({ - 'java', + java_exe, '-Declipse.application=org.eclipse.jdt.ls.core.id1', '-Dosgi.bundles.defaultStartLevel=4', '-Declipse.product=org.eclipse.jdt.ls.core.product', diff --git a/lua/pkgm/downloaders/powershell.lua b/lua/pkgm/downloaders/powershell.lua index e2236d7..f0a5434 100644 --- a/lua/pkgm/downloaders/powershell.lua +++ b/lua/pkgm/downloaders/powershell.lua @@ -1,6 +1,7 @@ local class = require('java-core.utils.class') local log = require('java-core.utils.log2') local err_util = require('java-core.utils.errors') +local path = require('java-core.utils.path') ---@class java-core.PowerShell ---@field url string @@ -21,7 +22,9 @@ function PowerShell:_init(opts) if not opts.dest then local filename = vim.fs.basename(opts.url) - self.dest = vim.fn.tempname() .. '-' .. filename + local tmp_dir = vim.fn.tempname() + vim.fn.mkdir(tmp_dir, 'p') + self.dest = path.join(tmp_dir, filename) log.debug('Using temp destination:', self.dest) else self.dest = opts.dest diff --git a/lua/pkgm/extractors/tar.lua b/lua/pkgm/extractors/tar.lua index 58c873b..3d88036 100644 --- a/lua/pkgm/extractors/tar.lua +++ b/lua/pkgm/extractors/tar.lua @@ -17,6 +17,18 @@ function Tar:_init(opts) self.dest = opts.dest end +---@private +---Check if tar supports --force-local +---@param tar_cmd string +---@return boolean +function Tar:tar_supports_force_local(tar_cmd) + local ok, out = pcall(vim.fn.system, { tar_cmd, '--help' }) + if not ok then + return false + end + return out:match('%-%-force%-local') ~= nil +end + ---Extract tar file using tar ---@return boolean|nil # true on success, nil on failure ---@return string|nil # Error message if failed @@ -30,7 +42,13 @@ function Tar:extract() -- Windows: convert backslashes to forward slashes (tar accepts them) local source = self.source:gsub('\\', '/') local dest = self.dest:gsub('\\', '/') - cmd = string.format('%s --no-same-owner --force-local -xf "%s" -C "%s"', tar_cmd, source, dest) + cmd = string.format( + '%s --no-same-owner %s -xf "%s" -C "%s"', + tar_cmd, + self:tar_supports_force_local(tar_cmd) and '--force-local' or '', + source, + dest + ) else -- Unix: use shellescape cmd = string.format( diff --git a/tests/specs/lsp_spec.lua b/tests/specs/lsp_spec.lua index 8ef81b7..459de77 100644 --- a/tests/specs/lsp_spec.lua +++ b/tests/specs/lsp_spec.lua @@ -1,5 +1,6 @@ local lsp_utils = dofile('tests/utils/lsp-utils.lua') local assert = require('luassert') +local system = require('java-core.utils.system') describe('LSP Attach', function() it('should attach when opening a Java buffer', function() @@ -8,6 +9,9 @@ describe('LSP Attach', function() local jdtls = lsp_utils.wait_for_lsp_attach('jdtls', 30000) local spring = lsp_utils.wait_for_lsp_attach('spring-boot', 30000) + if system.get_os() == 'win' then + vim.fn.readfile('C:/Users/runneradmin/AppData/Local/nvim-data/lsp.log') + end assert.is_not_nil(jdtls, 'JDTLS should attach to Java buffer') assert.is_not_nil(spring, 'Spring Boot should attach to Java buffer') end) From 202c3e1c3ad991e50dc7a7e44c09998299586f7f Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Thu, 4 Dec 2025 03:26:32 +0530 Subject: [PATCH 123/147] chore(doc): update docs (#440) --- README.md | 15 +++++++++------ doc/nvim-java.txt | 7 +------ lua/java/config.lua | 7 ------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 3193795..05f4289 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,12 @@ ![Neovim](https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white) ![Lua](https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white) +![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black) +![Windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows11&logoColor=white) +![macOS](https://img.shields.io/badge/macOS-000000?style=for-the-badge&logo=apple&logoColor=white) + +--- + Just install and start writing `public static void main(String[] args)`. > [!TIP] @@ -27,13 +33,15 @@ Just install and start writing `public static void main(String[] args)`. - :white_check_mark: Organize Imports & Code Formatting - :white_check_mark: Running Tests - :white_check_mark: Run & Debug Profiles +- :white_check_mark: Built-in Application Runner with Log Viewer +- :white_check_mark: Profile Management UI +- :white_check_mark: Decompiler Support - :white_check_mark: [Code Actions](https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks#running-code-actions) ## :bulb: Why - Everything necessary will be installed automatically - Uses native `vim.lsp.config` to setup `jdtls` -- Realtime server settings updates is possible using [neoconf](https://github.com/folke/neoconf.nvim) - Auto loads necessary `jdtls` plugins - Supported plugins are, - `spring-boot-tools` @@ -369,11 +377,6 @@ require('java').setup({ version = '17', }, - -- Notifications - notifications = { - dap = true, -- Show DAP configuration messages - }, - -- Logging log = { use_console = true, diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 31ad3be..d2bebe8 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -407,12 +407,7 @@ want, following options are available auto_install = true, version = '17.0.2', }, - - notifications = { - -- enable 'Configuring DAP' & 'DAP configured' messages on start up - dap = true, - }, - + -- We do multiple verifications to make sure things are in place to run this -- plugin verification = { diff --git a/lua/java/config.lua b/lua/java/config.lua index 8c9439d..b42419a 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -20,7 +20,6 @@ local V = jdtls_version_map[JDTLS_VERSION] ---@field java_debug_adapter { enable: boolean, version: string } ---@field spring_boot_tools { enable: boolean, version: string } ---@field jdk { auto_install: boolean, version: string } ----@field notifications { dap: boolean } ---@field log java-core.Log2Config ---@class java.PartialConfig @@ -31,7 +30,6 @@ local V = jdtls_version_map[JDTLS_VERSION] ---@field java_debug_adapter? { enable?: boolean, version?: string } ---@field spring_boot_tools? { enable?: boolean, version?: string } ---@field jdk? { auto_install?: boolean, version?: string } ----@field notifications? { dap?: boolean } ---@field log? java-core.PartialLog2Config ---@type java.Config @@ -72,11 +70,6 @@ local config = { version = V.jdk, }, - notifications = { - -- enable 'Configuring DAP' & 'DAP configured' messages on start up - dap = true, - }, - log = { use_console = true, use_file = true, From 68db686ea59d47bef72cf17fd2857295f2a79ad7 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 4 Dec 2025 03:42:53 +0530 Subject: [PATCH 124/147] docs: remove starter configs, simplify install structure --- README.md | 16 ++-------------- doc/nvim-java.txt | 12 ------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 05f4289..b10b0b3 100644 --- a/README.md +++ b/README.md @@ -55,21 +55,9 @@ Just install and start writing `public static void main(String[] args)`. :small_orange_diamond:details -### Starter Configs (Recommend for newbies) - -Following are forks of original repositories pre-configured for java. If you -don't know how to get started, use one of the following to get started. -You can click on **n commits ahead of** link to see the changes made on top of the original project - -- [LazyVim](https://github.com/nvim-java/starter-lazyvim) -- [Kickstart](https://github.com/nvim-java/starter-kickstart) -- [AstroNvim](https://github.com/nvim-java/starter-astronvim) - -### Manual Installation Instructions - **Requirements:** Neovim 0.11+ -#### Using `vim.pack` +### Using `vim.pack` ```lua vim.pack.add({ @@ -87,7 +75,7 @@ require('java').setup() vim.lsp.enable('jdtls') ``` -#### Using `lazy.nvim` +### Using `lazy.nvim` Install using [lazy.nvim](https://github.com/folke/lazy.nvim): diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index d2bebe8..6ab22ff 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -68,18 +68,6 @@ HOW TO INSTALL *nvim-java-how-to-install* :small_orange_diamond:details ~ -STARTER CONFIGS (RECOMMEND FOR NEWBIES) ~ - -Following are forks of original repositories pre-configured for java. If you -don’t know how to get started, use one of the following to get started. You -can click on **n commits ahead of** link to see the changes made on top of the -original project - -- LazyVim -- Kickstart -- AstroNvim - - CUSTOM CONFIGURATION INSTRUCTIONS ~ - Install the plugin From a7dbd3a6f0132ef7c105cb63952642af1840c37c Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 4 Dec 2025 03:48:00 +0530 Subject: [PATCH 125/147] docs: remove why section --- README.md | 16 ++-------------- doc/nvim-java.txt | 14 -------------- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index b10b0b3..3110ad2 100644 --- a/README.md +++ b/README.md @@ -15,12 +15,11 @@ Just install and start writing `public static void main(String[] args)`. +--- + > [!TIP] > You can find cool tips & tricks here https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks -> [!NOTE] -> If you are facing errors while using, please check troubleshoot wiki https://github.com/nvim-java/nvim-java/wiki/Troubleshooting - ## :loudspeaker: Demo @@ -38,17 +37,6 @@ Just install and start writing `public static void main(String[] args)`. - :white_check_mark: Decompiler Support - :white_check_mark: [Code Actions](https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks#running-code-actions) -## :bulb: Why - -- Everything necessary will be installed automatically -- Uses native `vim.lsp.config` to setup `jdtls` -- Auto loads necessary `jdtls` plugins - - Supported plugins are, - - `spring-boot-tools` - - `lombok` - - `java-test` - - `java-debug-adapter` - ## :hammer: How to Install
diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 6ab22ff..72a3ccc 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -6,7 +6,6 @@ Table of Contents *nvim-java-table-of-contents* 1. nvim-java |nvim-java-nvim-java| - Demo |nvim-java-demo| - Features |nvim-java-features| - - Why |nvim-java-why| - How to Install |nvim-java-how-to-install| - Commands |nvim-java-commands| - APIs |nvim-java-apis| @@ -50,19 +49,6 @@ FEATURES *nvim-java-features* - Code Actions -WHY *nvim-java-why* - -- Everything necessary will be installed automatically -- Uses nvim-lspconfig to setup `jdtls` -- Realtime server settings updates is possible using neoconf -- Auto loads necessary `jdtls` plugins - - Supported plugins are, - - `spring-boot-tools` - - `lombok` - - `java-test` - - `java-debug-adapter` - - HOW TO INSTALL *nvim-java-how-to-install* :small_orange_diamond:details ~ From 47f3ea45db76563c54f8876358c226eeebdd2705 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 4 Dec 2025 03:54:34 +0530 Subject: [PATCH 126/147] chore: release 4.0.0 Release-As: 4.0.0 From 2b08e047958e6fcb168851ebb5200480f504f34d Mon Sep 17 00:00:00 2001 From: s1n7ax <18459807+s1n7ax@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:23:04 +0000 Subject: [PATCH 127/147] chore(doc): automatic vimdoc update --- doc/nvim-java.txt | 180 ++++++++++++++++++---------------------------- 1 file changed, 70 insertions(+), 110 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 72a3ccc..aba0b52 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2025 February 16 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2025 December 03 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -20,18 +20,16 @@ Table of Contents *nvim-java-table-of-contents* -Just install and start writing `public static void main(String[] args)`. - [!CAUTION] You cannot use `nvim-java` alongside `nvim-jdtls`. So remove - `nvim-jdtls` before installing this +------------------------------------------------------------------------------ +Just install and start writing `public static void main(String[] args)`. + +------------------------------------------------------------------------------ [!TIP] You can find cool tips & tricks here https://github.com/nvim-java/nvim-java/wiki/Tips-&-Tricks - [!NOTE] If you are facing errors while using, please check troubleshoot wiki - https://github.com/nvim-java/nvim-java/wiki/Troubleshooting - DEMO *nvim-java-demo* @@ -46,6 +44,9 @@ FEATURES *nvim-java-features* - Organize Imports & Code Formatting - Running Tests - Run & Debug Profiles +- Built-in Application Runner with Log Viewer +- Profile Management UI +- Decompiler Support - Code Actions @@ -53,27 +54,40 @@ HOW TO INSTALL *nvim-java-how-to-install* :small_orange_diamond:details ~ +**Requirements:** Neovim 0.11+ -CUSTOM CONFIGURATION INSTRUCTIONS ~ - -- Install the plugin -Using lazy.nvim +USING VIM.PACK ~ >lua - return {'nvim-java/nvim-java'} + vim.pack.add({ + { + src = 'https://github.com/JavaHello/spring-boot.nvim', + version = '218c0c26c14d99feca778e4d13f5ec3e8b1b60f0', + }, + 'https://github.com/MunifTanjim/nui.nvim', + 'https://github.com/mfussenegger/nvim-dap', + + 'https://github.com/nvim-java/nvim-java', + }) + + require('java').setup() + vim.lsp.enable('jdtls') < -- Setup nvim-java before `lspconfig` ->lua - require('java').setup() -< +USING LAZY.NVIM ~ -- Setup jdtls like you would usually do +Install using lazy.nvim : >lua - require('lspconfig').jdtls.setup({}) + { + 'nvim-java/nvim-java', + config = function() + require('java').setup() + vim.lsp.enable('jdtls') + end, + } < Yep! That’s all :) @@ -282,44 +296,20 @@ HOW TO USE JDK X.X VERSION? *nvim-java-how-to-use-jdk-x.x-version?* :small_orange_diamond:details ~ - -METHOD 1 ~ - -Neoconf can be used to manage LSP -setting including jdtls. Neoconf allows global configuration as well as project -vice configurations. Here is how you can set Jdtls setting on `neoconf.json` - ->json - { - "lspconfig": { - "jdtls": { - "java.configuration.runtimes": [ - { - "name": "JavaSE-21", - "path": "/opt/jdk-21", - "default": true - } - ] - } - } - } -< - - -METHOD 2 ~ - -Pass the settings to Jdtls setup. +Use `vim.lsp.config()` to override the default JDTLS settings: >lua - require('lspconfig').jdtls.setup({ - settings = { - java = { - configuration = { - runtimes = { - { - name = "JavaSE-21", - path = "/opt/jdk-21", - default = true, + vim.lsp.config('jdtls', { + default_config = { + settings = { + java = { + configuration = { + runtimes = { + { + name = "JavaSE-21", + path = "/opt/jdk-21", + default = true, + } } } } @@ -334,41 +324,35 @@ CONFIGURATION *nvim-java-configuration* :small_orange_diamond:details ~ For most users changing the default configuration is not necessary. But if you -want, following options are available +want, following options are available: >lua - { - -- list of file that exists in root of the project - root_markers = { - 'settings.gradle', - 'settings.gradle.kts', - 'pom.xml', - 'build.gradle', - 'mvnw', - 'gradlew', - 'build.gradle', - 'build.gradle.kts', - '.git', + require('java').setup({ + -- Startup checks + checks = { + nvim_version = true, -- Check Neovim version + nvim_jdtls_conflict = true, -- Check for nvim-jdtls conflict }, + -- JDTLS configuration jdtls = { - version = 'v1.43.0', + version = '1.43.0', }, + -- Extensions lombok = { - version = 'nightly', + enable = true, + version = '1.18.40', }, - -- load java test plugins java_test = { enable = true, version = '0.40.1', }, - -- load java debugger plugins java_debug_adapter = { enable = true, - version = '0.58.1', + version = '0.58.2', }, spring_boot_tools = { @@ -376,46 +360,22 @@ want, following options are available version = '1.55.1', }, + -- JDK installation jdk = { - -- install jdk using mason.nvim auto_install = true, - version = '17.0.2', + version = '17', }, - - -- We do multiple verifications to make sure things are in place to run this - -- plugin - verification = { - -- nvim-java checks for the order of execution of following - -- * require('java').setup() - -- * require('lspconfig').jdtls.setup() - -- IF they are not executed in the correct order, you will see a error - -- notification. - -- Set following to false to disable the notification if you know what you - -- are doing - invalid_order = true, - -- nvim-java checks if the require('java').setup() is called multiple - -- times. - -- IF there are multiple setup calls are executed, an error will be shown - -- Set following property value to false to disable the notification if - -- you know what you are doing - duplicate_setup_calls = true, - - -- nvim-java checks if nvim-java/mason-registry is added correctly to - -- mason.nvim plugin. - -- IF it's not registered correctly, an error will be thrown and nvim-java - -- will stop setup - invalid_mason_registry = false, + -- Logging + log = { + use_console = true, + use_file = true, + level = 'info', + log_file = vim.fn.stdpath('state') .. '/nvim-java.log', + max_lines = 1000, + show_location = false, }, - - mason = { - -- These mason registries will be prepended to the existing mason - -- configuration - registries = { - 'github:nvim-java/mason-registry', - }, - }, - } + }) < @@ -486,9 +446,6 @@ PROJECTS ACKNOWLEDGEMENT *nvim-java-projects-acknowledgement* follows "Keep it simple, stupid!" approach. If you love customizing things by yourself, then give nvim-jdtls a try. - - [!WARNING] You cannot use `nvim-java` alongside `nvim-jdtls`. So remove - `nvim-jdtls` before installing this ============================================================================== 2. Links *nvim-java-links* @@ -498,6 +455,9 @@ PROJECTS ACKNOWLEDGEMENT *nvim-java-projects-acknowledgement* 4. *Apache Maven*: https://img.shields.io/badge/Apache%20Maven-C71A36?style=for-the-badge&logo=Apache%20Maven&logoColor=white 5. *Neovim*: https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white 6. *Lua*: https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white +7. *Linux*: https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black +8. *Windows*: https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows11&logoColor=white +9. *macOS*: https://img.shields.io/badge/macOS-000000?style=for-the-badge&logo=apple&logoColor=white Generated by panvimdoc From c9607007922cc985037fea54a24d41392eca5490 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:25:16 +0000 Subject: [PATCH 128/147] chore(main): release 4.0.0 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b11fa0..52db803 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # Changelog +## [4.0.0](https://github.com/nvim-java/nvim-java/compare/v3.0.0...v4.0.0) (2025-12-03) + + +### Features + +* add jproperties filetype support ([56e82af](https://github.com/nvim-java/nvim-java/commit/56e82afd514a47b28437b158dd43332814f08523)) +* v4.0.0 ([84b9253](https://github.com/nvim-java/nvim-java/commit/84b92531b1de55ee9bd1d0614a05e9965481d386)) + + +### Bug Fixes + +* java validation is invalid ([ad5d370](https://github.com/nvim-java/nvim-java/commit/ad5d3701ea007720960062110ea11d588e4f3311)) +* java version check does not consider env passed for LSP ([41d72bf](https://github.com/nvim-java/nvim-java/commit/41d72bf9fb9ec8d6edf8cdaea2cee970276d6c55)) +* nvim-java does not work on mac with the embeded jdk ([293ee31](https://github.com/nvim-java/nvim-java/commit/293ee310b4c51c14d7571be24117c09025f320bf)) +* PATH env separator is not windows compatible ([9743dde](https://github.com/nvim-java/nvim-java/commit/9743ddefa6e0c1af78cfa5094658231215918794)) +* update nvim version check to 0.11 ([c3e3129](https://github.com/nvim-java/nvim-java/commit/c3e31292f731f281f6294adb543b9943a8331593)) +* windows compatibility issue ([#439](https://github.com/nvim-java/nvim-java/issues/439)) ([e1bb01d](https://github.com/nvim-java/nvim-java/commit/e1bb01db00f20be381eb9937a8517a347fa57bf6)) +* workspace_execute calling client command handler ([20191fe](https://github.com/nvim-java/nvim-java/commit/20191fe610d0c11dc12e7a6310f18bd56ea68d4d)) + + +### Miscellaneous Chores + +* release 4.0.0 ([47f3ea4](https://github.com/nvim-java/nvim-java/commit/47f3ea45db76563c54f8876358c226eeebdd2705)) + ## [3.0.0](https://github.com/nvim-java/nvim-java/compare/v2.1.2...v3.0.0) (2025-08-06) From 03d0d036c7761fc8de0c5a78adeff228aa8a9545 Mon Sep 17 00:00:00 2001 From: s1n7ax Date: Thu, 4 Dec 2025 08:37:45 +0530 Subject: [PATCH 129/147] chore: remove unwanted log --- tests/specs/lsp_spec.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/specs/lsp_spec.lua b/tests/specs/lsp_spec.lua index 459de77..8ef81b7 100644 --- a/tests/specs/lsp_spec.lua +++ b/tests/specs/lsp_spec.lua @@ -1,6 +1,5 @@ local lsp_utils = dofile('tests/utils/lsp-utils.lua') local assert = require('luassert') -local system = require('java-core.utils.system') describe('LSP Attach', function() it('should attach when opening a Java buffer', function() @@ -9,9 +8,6 @@ describe('LSP Attach', function() local jdtls = lsp_utils.wait_for_lsp_attach('jdtls', 30000) local spring = lsp_utils.wait_for_lsp_attach('spring-boot', 30000) - if system.get_os() == 'win' then - vim.fn.readfile('C:/Users/runneradmin/AppData/Local/nvim-data/lsp.log') - end assert.is_not_nil(jdtls, 'JDTLS should attach to Java buffer') assert.is_not_nil(spring, 'Spring Boot should attach to Java buffer') end) From 787523792f71816d049772d4ffc16ded6bac497f Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sat, 6 Dec 2025 02:47:31 +0530 Subject: [PATCH 130/147] fix: user config is not applied due to default config import in some modules (#443) --- lua/java-core/ls/servers/jdtls/cmd.lua | 48 +++++++++++----------- lua/java-core/ls/servers/jdtls/env.lua | 12 ++---- lua/java-core/ls/servers/jdtls/init.lua | 8 ++-- lua/java-core/ls/servers/jdtls/plugins.lua | 25 +++++------ lua/java/startup/lsp_setup.lua | 3 +- lua/java/ui/profile.lua | 2 - 6 files changed, 47 insertions(+), 51 deletions(-) diff --git a/lua/java-core/ls/servers/jdtls/cmd.lua b/lua/java-core/ls/servers/jdtls/cmd.lua index 8ac1691..3e7c150 100644 --- a/lua/java-core/ls/servers/jdtls/cmd.lua +++ b/lua/java-core/ls/servers/jdtls/cmd.lua @@ -1,7 +1,6 @@ local List = require('java-core.utils.list') local path = require('java-core.utils.path') local Manager = require('pkgm.manager') -local conf = require('java.config') local system = require('java-core.utils.system') local log = require('java-core.utils.log2') local err = require('java-core.utils.errors') @@ -10,31 +9,29 @@ local lsp_utils = require('java-core.utils.lsp') local M = {} -local jdtls_root = Manager:get_install_dir('jdtls', conf.jdtls.version) - --- Returns a function that returns the command to start jdtls ----@param opts { use_lombok: boolean } -function M.get_cmd(opts) +---@param config java.Config +function M.get_cmd(config) ---@param dispatchers? vim.lsp.rpc.Dispatchers - ---@param config vim.lsp.ClientConfig - return function(dispatchers, config) - local cmd = M.get_jvm_args(opts):concat(M.get_jar_args()) + ---@param lsp_config vim.lsp.ClientConfig + return function(dispatchers, lsp_config) + local cmd = M.get_jvm_args(config):concat(M.get_jar_args(config)) -- NOTE: eventhough we are setting the PATH env var, due to a bug, it's not -- working on Windows. So just lanching 'java' will result in executing the -- system java. So as a workaround, we use the absolute path to java instead -- So following check is not needed when we have auto_install set to true -- @see https://github.com/neovim/neovim/issues/36818 - if not conf.jdk.auto_install then - M.validate_java_version(config.cmd_env) + if not config.jdk.auto_install then + M.validate_java_version(config, lsp_config.cmd_env) end log.debug('Starting jdtls with cmd', cmd) local result = vim.lsp.rpc.start(cmd, dispatchers, { - cwd = config.cmd_cwd, - env = config.cmd_env, - detached = config.detached, + cwd = lsp_config.cmd_cwd, + env = lsp_config.cmd_env, + detached = lsp_config.detached, }) return result @@ -42,9 +39,11 @@ function M.get_cmd(opts) end ---@private ----@param opts { use_lombok: boolean } +---@param config java.Config ---@return java-core.List -function M.get_jvm_args(opts) +function M.get_jvm_args(config) + local use_lombok = config.lombok.enable + local jdtls_root = Manager:get_install_dir('jdtls', config.jdtls.version) local jdtls_config = path.join(jdtls_root, system.get_config_suffix()) local java_exe = 'java' @@ -52,8 +51,8 @@ function M.get_jvm_args(opts) -- NOTE: eventhough we are setting the PATH env var, due to a bug, it's not -- working on Windows. So we are using the absolute path to java instead -- @see https://github.com/neovim/neovim/issues/36818 - if conf.jdk.auto_install then - local jdk_root = Manager:get_install_dir('openjdk', conf.jdk.version) + if config.jdk.auto_install then + local jdk_root = Manager:get_install_dir('openjdk', config.jdk.version) local java_home if system.get_os() == 'mac' then java_home = vim.fn.glob(path.join(jdk_root, 'jdk-*', 'Contents', 'Home')) @@ -84,8 +83,8 @@ function M.get_jvm_args(opts) }) -- Adding lombok - if opts.use_lombok then - local lombok_root = Manager:get_install_dir('lombok', conf.lombok.version) + if use_lombok then + local lombok_root = Manager:get_install_dir('lombok', config.lombok.version) local lombok_path = vim.fn.glob(path.join(lombok_root, 'lombok*.jar')) jvm_args:push('-javaagent:' .. lombok_path) end @@ -94,9 +93,11 @@ function M.get_jvm_args(opts) end ---@private +---@param config java.Config ---@param cwd? string ---@return java-core.List -function M.get_jar_args(cwd) +function M.get_jar_args(config, cwd) + local jdtls_root = Manager:get_install_dir('jdtls', config.jdtls.version) cwd = cwd or vim.fn.getcwd() local launcher_reg = path.join(jdtls_root, 'plugins', 'org.eclipse.equinox.launcher_*.jar') @@ -121,15 +122,16 @@ function M.get_jar_args(cwd) end ---@private +---@param config java.Config ---@param env table -function M.validate_java_version(env) +function M.validate_java_version(config, env) local curr_ver = M.get_java_major_version(env) - local exp_ver = java_version_map[conf.jdtls.version] + local exp_ver = java_version_map[config.jdtls.version] if not (curr_ver >= exp_ver.to and curr_ver <= exp_ver.from) then local msg = string.format( 'Java version mismatch: JDTLS %s requires Java %d <= java >= %d, but found Java %d', - conf.jdtls.version, + config.jdtls.version, exp_ver.from, exp_ver.to, curr_ver diff --git a/lua/java-core/ls/servers/jdtls/env.lua b/lua/java-core/ls/servers/jdtls/env.lua index 128e5f6..2c6ab81 100644 --- a/lua/java-core/ls/servers/jdtls/env.lua +++ b/lua/java-core/ls/servers/jdtls/env.lua @@ -3,16 +3,12 @@ local Manager = require('pkgm.manager') local log = require('java-core.utils.log2') local system = require('java-core.utils.system') ---- @TODO: importing stuff from java main package feels wrong. ---- We should fix this in the future -local config = require('java.config') - local M = {} ---- @param opts { use_jdk: boolean } -function M.get_env(opts) - if not opts.use_jdk then - log.debug('use_jdk disabled, returning empty env') +--- @param config java.Config +function M.get_env(config) + if not config.jdk.auto_install then + log.debug('config.jdk.auto_install disabled, returning empty env') return {} end diff --git a/lua/java-core/ls/servers/jdtls/init.lua b/lua/java-core/ls/servers/jdtls/init.lua index 271625b..654c6b8 100644 --- a/lua/java-core/ls/servers/jdtls/init.lua +++ b/lua/java-core/ls/servers/jdtls/init.lua @@ -1,7 +1,7 @@ local M = {} --- Returns jdtls config ----@param opts { use_jdk: boolean, use_lombok: boolean, plugins: string[] } +---@param opts { plugins: string[], config: java.Config } function M.get_config(opts) local conf = require('java-core.ls.servers.jdtls.conf') local plugins = require('java-core.ls.servers.jdtls.plugins') @@ -15,9 +15,9 @@ function M.get_config(opts) local base_conf = vim.deepcopy(conf, true) - base_conf.cmd = cmd.get_cmd(opts) - base_conf.cmd_env = env.get_env(opts) - base_conf.init_options.bundles = plugins.get_plugins(opts) + base_conf.cmd = cmd.get_cmd(opts.config) + base_conf.cmd_env = env.get_env(opts.config) + base_conf.init_options.bundles = plugins.get_plugins(opts.config, opts.plugins) base_conf.root_markers = root.get_root_markers() base_conf.filetypes = filetype.get_filetypes() diff --git a/lua/java-core/ls/servers/jdtls/plugins.lua b/lua/java-core/ls/servers/jdtls/plugins.lua index 086b02c..16ddb2f 100644 --- a/lua/java-core/ls/servers/jdtls/plugins.lua +++ b/lua/java-core/ls/servers/jdtls/plugins.lua @@ -3,10 +3,6 @@ local List = require('java-core.utils.list') local Manager = require('pkgm.manager') local log = require('java-core.utils.log2') ---- @TODO: importing stuff from java main package feels wrong. ---- We should fix this in the future -local config = require('java.config') - local M = {} local plug_jar_map = { @@ -35,17 +31,22 @@ local plug_jar_map = { ['spring-boot-tools'] = { 'extension/jars/*.jar' }, } -local plugin_version_map = { - ['java-test'] = config.java_test.version, - ['java-debug'] = config.java_debug_adapter.version, - ['spring-boot-tools'] = config.spring_boot_tools.version, -} +function M.get_plugin_version_map(config) + return { + ['java-test'] = config.java_test.version, + ['java-debug'] = config.java_debug_adapter.version, + ['spring-boot-tools'] = config.spring_boot_tools.version, + } +end ---Returns a list of .jar file paths for given list of jdtls plugins ----@param opts { plugins: string[] } +---@param config java.Config +---@param plugins string[] ---@return string[] # list of .jar file paths -function M.get_plugins(opts) - return List:new(opts.plugins) +function M.get_plugins(config, plugins) + local plugin_version_map = M.get_plugin_version_map(config) + + return List:new(plugins) :map(function(plugin_name) local version = plugin_version_map[plugin_name] local root = Manager:get_install_dir(plugin_name, version) diff --git a/lua/java/startup/lsp_setup.lua b/lua/java/startup/lsp_setup.lua index ea1646b..49e5cbd 100644 --- a/lua/java/startup/lsp_setup.lua +++ b/lua/java/startup/lsp_setup.lua @@ -32,9 +32,8 @@ function M.setup(config) end local default_config = server.get_config({ + config = config, plugins = jdtls_plugins, - use_jdk = config.jdk.auto_install, - use_lombok = config.lombok.enable, }) vim.lsp.config('jdtls', default_config) diff --git a/lua/java/ui/profile.lua b/lua/java/ui/profile.lua index 2549c0d..83876ff 100644 --- a/lua/java/ui/profile.lua +++ b/lua/java/ui/profile.lua @@ -3,7 +3,6 @@ local notify = require('java-core.utils.notify') local profile_config = require('java.api.profile_config') local class = require('java-core.utils.class') local dap_api = require('java-dap') -local log = require('java-core.utils.log2') local lsp_utils = require('java-core.utils.lsp') local ui = require('java.ui.utils') @@ -184,7 +183,6 @@ function ProfileUI:get_and_fill_popup(title, key, target_profile, enter, keymaps win_options = self.win_options, }) - log.error(vim.inspect(popup.border)) -- fill the popup with the config value -- if target_profile is nil, it's a new profile if target_profile then From 432f3a3740a6571f1928c5261410791a1ca73735 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:17:55 +0000 Subject: [PATCH 131/147] chore(main): release 4.0.1 --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52db803..7e2a7fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [4.0.1](https://github.com/nvim-java/nvim-java/compare/v4.0.0...v4.0.1) (2025-12-05) + + +### Bug Fixes + +* user config is not applied due to default config import in some modules ([#443](https://github.com/nvim-java/nvim-java/issues/443)) ([7875237](https://github.com/nvim-java/nvim-java/commit/787523792f71816d049772d4ffc16ded6bac497f)) + ## [4.0.0](https://github.com/nvim-java/nvim-java/compare/v3.0.0...v4.0.0) (2025-12-03) From 389ef7c938f89730f112054132d6a08c180733af Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Sat, 6 Dec 2025 22:34:00 +0530 Subject: [PATCH 132/147] chore: upgrde packages --- .devcontainer/config/nvim/init.lua | 86 ++++++++++++++-------- .devcontainer/devcontainer.json | 1 + lua/java-core/constants/java_version.lua | 12 +-- lua/java-core/ls/servers/jdtls/cmd.lua | 16 +++- lua/java-core/ls/servers/jdtls/init.lua | 2 + lua/java-core/ls/servers/jdtls/plugins.lua | 61 ++++++--------- lua/java-core/utils/errors.lua | 10 +-- lua/java-core/utils/str.lua | 10 +++ lua/java-test/api.lua | 4 +- lua/java/config.lua | 9 ++- lua/pkgm/specs/base-spec.lua | 4 +- lua/pkgm/specs/init.lua | 32 +++++++- lua/pkgm/specs/jdtls-spec/version-map.lua | 1 + tests/constants/capabilities.lua | 2 + tests/specs/capabilities_spec.lua | 5 +- 15 files changed, 158 insertions(+), 97 deletions(-) create mode 100644 lua/java-core/utils/str.lua diff --git a/.devcontainer/config/nvim/init.lua b/.devcontainer/config/nvim/init.lua index 2b7a52d..99e4398 100644 --- a/.devcontainer/config/nvim/init.lua +++ b/.devcontainer/config/nvim/init.lua @@ -21,7 +21,15 @@ require('lazy').setup({ 'nvim-java/nvim-java', dir = '/workspaces/nvim-java', config = function() - require('java').setup() + require('java').setup({ + jdk = { + auto_install = false, + }, + log = { + use_console = false, + level = 'debug', + }, + }) vim.lsp.config('jdtls', { handlers = { ['language/status'] = function(_, data) @@ -47,84 +55,98 @@ vim.opt.shiftwidth = 2 vim.opt.expandtab = false vim.opt.completeopt = { 'menu', 'menuone', 'noselect' } -vim.keymap.set('n', '', 'q') +local k = vim.keymap.set + +k('n', '', 'q') if colemak then - vim.keymap.set('n', '', '') - vim.keymap.set('n', 'E', 'K') - vim.keymap.set('n', 'H', 'I') - vim.keymap.set('n', 'K', 'N') - vim.keymap.set('n', 'L', 'E') - vim.keymap.set('n', 'N', 'J') - vim.keymap.set('n', 'e', '') - vim.keymap.set('n', 'h', 'i') - vim.keymap.set('n', 'i', '') - vim.keymap.set('n', 'j', 'm') - vim.keymap.set('n', 'k', 'n') - vim.keymap.set('n', 'l', 'e') - vim.keymap.set('n', 'm', '') - vim.keymap.set('n', 'n', '') + k('n', '', '') + k('n', 'E', 'K') + k('n', 'H', 'I') + k('n', 'K', 'N') + k('n', 'L', 'E') + k('n', 'N', 'J') + k('n', 'e', '') + k('n', 'h', 'i') + k('n', 'i', '') + k('n', 'j', 'm') + k('n', 'k', 'n') + k('n', 'l', 'e') + k('n', 'm', '') + k('n', 'n', '') end vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) vim.lsp.completion.enable(true, args.data.client_id, args.buf, { autotrigger = true }) - vim.keymap.set('i', '', function() + k('i', '', function() vim.lsp.completion.get() end, { buffer = args.buf }) if colemak then - vim.keymap.set('i', '', '', { buffer = args.buf }) - vim.keymap.set('i', '', '', { buffer = args.buf }) + k('i', '', '', { buffer = args.buf }) + k('i', '', '', { buffer = args.buf }) end end, }) -vim.keymap.set('n', ']d', function() +k('n', ']d', function() vim.diagnostic.jump({ count = 1, float = true }) end, { desc = 'Jump to next diagnostic' }) -vim.keymap.set('n', '[d', function() +k('n', '[d', function() vim.diagnostic.jump({ count = -1, float = true }) end, { desc = 'Jump to previous diagnostic' }) -vim.keymap.set('n', 'ta', vim.lsp.buf.code_action, {}) +k('n', 'ta', vim.lsp.buf.code_action, {}) -- DAP keymaps -vim.keymap.set('n', 'dd', function() +k('n', 'dd', function() require('dap').toggle_breakpoint() end, { desc = 'Toggle breakpoint' }) -vim.keymap.set('n', 'dc', function() +k('n', 'dc', function() require('dap').continue() end, { desc = 'Continue' }) -vim.keymap.set('n', 'dn', function() +k('n', 'dn', function() require('dap').step_over() end, { desc = 'Step over' }) -vim.keymap.set('n', 'di', function() +k('n', 'di', function() require('dap').step_into() end, { desc = 'Step into' }) -vim.keymap.set('n', 'do', function() +k('n', 'do', function() require('dap').step_out() end, { desc = 'Step out' }) -vim.keymap.set('n', 'dr', function() +k('n', 'dr', function() require('dap').repl.open() end, { desc = 'Open REPL' }) -vim.keymap.set('n', 'dl', function() +k('n', 'dl', function() require('dap').run_last() end, { desc = 'Run last' }) -vim.keymap.set('n', 'dt', function() +k('n', 'dt', function() require('dap').terminate() end, { desc = 'Terminate' }) -vim.keymap.set('n', 'gd', function() +k('n', 'gd', function() vim.lsp.buf.definition() end, { desc = 'Terminate' }) -vim.keymap.set('n', 'm', "vnewput = execute('messages')") +k('n', 'm', "vnewput = execute('messages')") + +k('n', 'nn', 'JavaRunnerRunMain', { desc = 'Run main' }) +k('n', 'ne', 'JavaRunnerStopMain', { desc = 'Stop main' }) +k('n', 'nt', 'JavaTestDebugCurrentClass', { desc = 'Debug test' }) +k('n', 'ns', 'JavaTestRunCurrentClass', { desc = 'Run test' }) + +k('t', 'yy', '', { desc = 'Exit terminal mode' }) + +k('n', '', 'j', { desc = 'Window down' }) +k('n', '', 'k', { desc = 'Window up' }) +k('n', '', 'h', { desc = 'Window left' }) +k('n', '', 'l', { desc = 'Window right' }) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 8fe9ee8..6fcecea 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,6 +6,7 @@ "ghcr.io/devcontainers/features/git:1": {}, "ghcr.io/devcontainers/features/java:1": {}, "ghcr.io/devcontainers/features/python:1": {}, + "ghcr.io/devcontainers/features/node:1": {}, "ghcr.io/devcontainers-extra/features/wget-apt-get:1": {}, "ghcr.io/duduribeiro/devcontainer-features/neovim:1": { "version": "nightly" diff --git a/lua/java-core/constants/java_version.lua b/lua/java-core/constants/java_version.lua index d8c8459..2fc2de4 100644 --- a/lua/java-core/constants/java_version.lua +++ b/lua/java-core/constants/java_version.lua @@ -1,14 +1,4 @@ return { ['1.43.0'] = { from = 17, to = 17 }, - ['1.44.0'] = { from = 17, to = 21 }, - ['1.45.0'] = { from = 21, to = 21 }, - ['1.46.0'] = { from = 21, to = 21 }, - ['1.46.1'] = { from = 21, to = 21 }, - ['1.47.0'] = { from = 21, to = 21 }, - ['1.48.0'] = { from = 21, to = 21 }, - ['1.49.0'] = { from = 21, to = 21 }, - ['1.50.0'] = { from = 21, to = 21 }, - ['1.51.0'] = { from = 21, to = 21 }, - ['1.52.0'] = { from = 21, to = 21 }, - ['1.53.0'] = { from = 21, to = 21 }, + ['1.54.0'] = { from = 21, to = 25 }, } diff --git a/lua/java-core/ls/servers/jdtls/cmd.lua b/lua/java-core/ls/servers/jdtls/cmd.lua index 3e7c150..bc63ec2 100644 --- a/lua/java-core/ls/servers/jdtls/cmd.lua +++ b/lua/java-core/ls/servers/jdtls/cmd.lua @@ -6,6 +6,7 @@ local log = require('java-core.utils.log2') local err = require('java-core.utils.errors') local java_version_map = require('java-core.constants.java_version') local lsp_utils = require('java-core.utils.lsp') +local str = require('java-core.utils.str') local M = {} @@ -128,7 +129,20 @@ function M.validate_java_version(config, env) local curr_ver = M.get_java_major_version(env) local exp_ver = java_version_map[config.jdtls.version] - if not (curr_ver >= exp_ver.to and curr_ver <= exp_ver.from) then + if not exp_ver then + err.throw( + str.multiline( + 'We maintain a jdlts to java version map to provide a better error message.', + 'The jdtls version you are using is not supported yet', + 'Please open an issue on https://github.com/nvim-java/nvim-java/issues', + 'OR submit a PR', + 'Version map:', + 'https://github.com/nvim-java/nvim-java/blob/main/lua/java-core/constants/java_version.lua' + ) + ) + end + + if not (curr_ver >= exp_ver.from and curr_ver <= exp_ver.to) then local msg = string.format( 'Java version mismatch: JDTLS %s requires Java %d <= java >= %d, but found Java %d', config.jdtls.version, diff --git a/lua/java-core/ls/servers/jdtls/init.lua b/lua/java-core/ls/servers/jdtls/init.lua index 654c6b8..dfa263d 100644 --- a/lua/java-core/ls/servers/jdtls/init.lua +++ b/lua/java-core/ls/servers/jdtls/init.lua @@ -21,6 +21,8 @@ function M.get_config(opts) base_conf.root_markers = root.get_root_markers() base_conf.filetypes = filetype.get_filetypes() + log.debug('jdtls config', base_conf) + return base_conf end diff --git a/lua/java-core/ls/servers/jdtls/plugins.lua b/lua/java-core/ls/servers/jdtls/plugins.lua index 16ddb2f..a66ce5b 100644 --- a/lua/java-core/ls/servers/jdtls/plugins.lua +++ b/lua/java-core/ls/servers/jdtls/plugins.lua @@ -1,36 +1,5 @@ -local file = require('java-core.utils.file') -local List = require('java-core.utils.list') -local Manager = require('pkgm.manager') -local log = require('java-core.utils.log2') - local M = {} -local plug_jar_map = { - ['java-test'] = { - 'extension/server/junit-jupiter-api_*.jar', - 'extension/server/junit-jupiter-engine_*.jar', - 'extension/server/junit-jupiter-migrationsupport_*.jar', - 'extension/server/junit-jupiter-params_*.jar', - 'extension/server/junit-platform-commons_*.jar', - 'extension/server/junit-platform-engine_*.jar', - 'extension/server/junit-platform-launcher_*.jar', - 'extension/server/junit-platform-runner_*.jar', - 'extension/server/junit-platform-suite-api_*.jar', - 'extension/server/junit-platform-suite-commons_*.jar', - 'extension/server/junit-platform-suite-engine_*.jar', - 'extension/server/junit-vintage-engine_*.jar', - 'extension/server/org.apiguardian.api_*.jar', - 'extension/server/org.eclipse.jdt.junit4.runtime_*.jar', - 'extension/server/org.eclipse.jdt.junit5.runtime_*.jar', - 'extension/server/org.opentest4j_*.jar', - 'extension/server/com.microsoft.java.test.plugin-*.jar', - }, - ['java-debug'] = { - 'extension/server/com.microsoft.java.debug.plugin-*.jar', - }, - ['spring-boot-tools'] = { 'extension/jars/*.jar' }, -} - function M.get_plugin_version_map(config) return { ['java-test'] = config.java_test.version, @@ -44,22 +13,36 @@ end ---@param plugins string[] ---@return string[] # list of .jar file paths function M.get_plugins(config, plugins) + local file = require('java-core.utils.file') + local List = require('java-core.utils.list') + local Manager = require('pkgm.manager') + local path = require('java-core.utils.path') + local err = require('java-core.utils.errors') + local str = require('java-core.utils.str') + local plugin_version_map = M.get_plugin_version_map(config) return List:new(plugins) :map(function(plugin_name) local version = plugin_version_map[plugin_name] - local root = Manager:get_install_dir(plugin_name, version) - local jars = file.resolve_paths(root, plug_jar_map[plugin_name]) - if #jars == 0 then - -- stylua: ignore - log.error(string.format( 'No jars found for plugin "%s" (version: %s) at %s', plugin_name, version, root)) - -- stylua: ignore - error(string.format( 'Failed to load plugin "%s". No jars found at %s', plugin_name, root)) + local pkg_path = Manager:get_install_dir(plugin_name, version) + local plugin_root = path.join(pkg_path, 'extension') + local package_json_str = vim.fn.readfile(path.join(plugin_root, 'package.json')) + local package_json = vim.json.decode(table.concat(package_json_str, '\n')) + local java_extensions = package_json.contributes.javaExtensions + + local ext_jars = file.resolve_paths(plugin_root, java_extensions) + + if #ext_jars ~= #java_extensions then + err.throw( + str + .multiline('Failed to load some jars for "%s"', 'Expected %d jars but only %d found') + :format(plugin_name, #java_extensions, #ext_jars) + ) end - return jars + return ext_jars end) :flatten() end diff --git a/lua/java-core/utils/errors.lua b/lua/java-core/utils/errors.lua index 65fddb4..9291b1d 100644 --- a/lua/java-core/utils/errors.lua +++ b/lua/java-core/utils/errors.lua @@ -4,11 +4,11 @@ local log = require('java-core.utils.log2') local M = {} ---Notifies user, logs error, and throws error ----@param msg string error message -function M.throw(msg) - notify.error(msg) - log.error(msg) - error(msg) +---@param ... string error message +function M.throw(...) + notify.error(...) + log.error(...) + error(...) end return M diff --git a/lua/java-core/utils/str.lua b/lua/java-core/utils/str.lua new file mode 100644 index 0000000..e9ff7ba --- /dev/null +++ b/lua/java-core/utils/str.lua @@ -0,0 +1,10 @@ +local M = {} + +--- Joins a list of strings with a separator +---@param ... string +---@return string +function M.multiline(...) + return table.concat({ ... }, '\n') +end + +return M diff --git a/lua/java-test/api.lua b/lua/java-test/api.lua index 48fe2b5..93e327a 100644 --- a/lua/java-test/api.lua +++ b/lua/java-test/api.lua @@ -100,7 +100,7 @@ function M:run_test(tests, report, config) local java_exec = self.debug_client:resolve_java_executable(launch_args.mainClass, launch_args.projectName) - log.debug('java executable: ' .. vim.inspect(java_exec)) + log.debug('java executable', java_exec) local dap_launcher_config = dap_adapters.junit_launch_args_to_dap_config(launch_args, java_exec, { debug = true, @@ -109,7 +109,7 @@ function M:run_test(tests, report, config) dap_launcher_config = vim.tbl_deep_extend('force', dap_launcher_config, config or {}) - log.debug('launching tests with config: ' .. vim.inspect(dap_launcher_config)) + log.debug('launching tests with config', dap_launcher_config) self.runner:run_by_config(dap_launcher_config, report) end diff --git a/lua/java/config.lua b/lua/java/config.lua index b42419a..8b65edc 100644 --- a/lua/java/config.lua +++ b/lua/java/config.lua @@ -1,4 +1,4 @@ -local JDTLS_VERSION = '1.43.0' +local JDTLS_VERSION = '1.54.0' local jdtls_version_map = { ['1.43.0'] = { @@ -8,6 +8,13 @@ local jdtls_version_map = { spring_boot_tools = '1.55.1', jdk = '17', }, + ['1.54.0'] = { + lombok = '1.18.42', + java_test = '0.43.2', + java_debug_adapter = '0.58.3', + spring_boot_tools = '1.55.1', + jdk = '25', + }, } local V = jdtls_version_map[JDTLS_VERSION] diff --git a/lua/pkgm/specs/base-spec.lua b/lua/pkgm/specs/base-spec.lua index 817d12d..f97d4ba 100644 --- a/lua/pkgm/specs/base-spec.lua +++ b/lua/pkgm/specs/base-spec.lua @@ -22,7 +22,7 @@ local BaseSpec = class() ---@param config pkgm.BaseSpecConfig function BaseSpec:_init(config) - log.debug('Initializing BaseSpec with config: ' .. vim.inspect(config)) + log.debug('Initializing BaseSpec with config', config) self._name = config.name self._version = config.version @@ -42,7 +42,7 @@ function BaseSpec:_init(config) end end - log.debug('Template vars: ' .. vim.inspect(self._template_vars)) + log.debug('Template vars', self._template_vars) end ---@return string diff --git a/lua/pkgm/specs/init.lua b/lua/pkgm/specs/init.lua index 2396fb1..e92a0cb 100644 --- a/lua/pkgm/specs/init.lua +++ b/lua/pkgm/specs/init.lua @@ -10,7 +10,7 @@ local JdtlsSpec = require('pkgm.specs.jdtls-spec') return { JdtlsSpec({ name = 'jdtls', - version_range = { from = '1.43.0', to = '1.53.0' }, + version_range = { from = '1.43.0', to = '1.54.0' }, url = 'https://download.eclipse.org/{{name}}/milestones/' .. '{{version}}/jdt-language-server-{{version}}-{{timestamp}}.tar.gz', }), @@ -75,4 +75,34 @@ return { }, }, }), + + -- https://download.java.net/java/GA/jdk25.0.1/2fbf10d8c78e40bd87641c434705079d/8/GPL/openjdk-25.0.1_linux-x64_bin.tar.gz + + BaseSpec({ + name = 'openjdk', + version = '25', + urls = { + linux = { + arm = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/latest/jdk-{{version}}_linux-aarch64_bin.tar.gz', + }, + x86 = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/latest/jdk-{{version}}_linux-x64_bin.tar.gz', + }, + }, + mac = { + arm = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/latest/jdk-{{version}}_macos-aarch64_bin.tar.gz', + }, + x86 = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/latest/jdk-{{version}}_macos-x64_bin.tar.gz', + }, + }, + win = { + x86 = { + ['64bit'] = 'https://download.oracle.com/java/{{version}}/latest/jdk-{{version}}_windows-x64_bin.zip', + }, + }, + }, + }), } diff --git a/lua/pkgm/specs/jdtls-spec/version-map.lua b/lua/pkgm/specs/jdtls-spec/version-map.lua index 62282f9..b5b2cc5 100644 --- a/lua/pkgm/specs/jdtls-spec/version-map.lua +++ b/lua/pkgm/specs/jdtls-spec/version-map.lua @@ -19,4 +19,5 @@ return { ['1.51.0'] = '202510022025', ['1.52.0'] = '202510301627', ['1.53.0'] = '202511192211', + ['1.54.0'] = '202511261751', } diff --git a/tests/constants/capabilities.lua b/tests/constants/capabilities.lua index a7a9d89..fa4572b 100644 --- a/tests/constants/capabilities.lua +++ b/tests/constants/capabilities.lua @@ -9,6 +9,7 @@ M.required_cmds = List:new({ 'java.edit.organizeImports', 'java.edit.smartSemicolonDetection', 'java.edit.stringFormatting', + 'java.getTroubleshootingInfo', 'java.navigate.openTypeHierarchy', 'java.navigate.resolveTypeHierarchy', 'java.project.addToSourcePath', @@ -69,6 +70,7 @@ M.required_cmds = List:new({ 'vscode.java.test.findTestTypesAndMethods', 'vscode.java.test.generateTests', 'vscode.java.test.get.testpath', + 'vscode.java.test.jacoco.getCoverageDetail', 'vscode.java.test.junit.argument', 'vscode.java.test.navigateToTestOrTarget', 'vscode.java.test.resolvePath', diff --git a/tests/specs/capabilities_spec.lua b/tests/specs/capabilities_spec.lua index 1fe36f1..fc0550f 100644 --- a/tests/specs/capabilities_spec.lua +++ b/tests/specs/capabilities_spec.lua @@ -2,7 +2,7 @@ local lsp_utils = dofile('tests/utils/lsp-utils.lua') local capabilities = dofile('tests/constants/capabilities.lua') local List = require('java-core.utils.list') local assert = require('luassert') -local log = require('java-core.utils.log2') +local err = require('java-core.utils.errors') describe('LSP Capabilities', function() it('should have all required commands', function() @@ -21,8 +21,7 @@ describe('LSP Capabilities', function() end) if #extra_cmds > 0 then - log.error('Additional commands found that are not in required list:', extra_cmds) - error('Additional commands found that are not in required list:' .. vim.inspect(extra_cmds)) + err.throw('Additional commands found that are not in required list:', extra_cmds) end end) end) From 625f48ffedc735ac6284316e16daa99ba012a996 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Wed, 10 Dec 2025 13:51:36 +0530 Subject: [PATCH 133/147] fix: vim.lsp.buf.document_symbol returns nothing for jdtls (#449) --- .devcontainer/config/nvim/init.lua | 18 ++++++++++++++++++ .devcontainer/config/nvim/lazy-lock.json | 2 ++ .devcontainer/devcontainer.json | 3 ++- lua/java-core/ls/servers/jdtls/conf.lua | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/.devcontainer/config/nvim/init.lua b/.devcontainer/config/nvim/init.lua index 99e4398..b13bd64 100644 --- a/.devcontainer/config/nvim/init.lua +++ b/.devcontainer/config/nvim/init.lua @@ -44,6 +44,18 @@ require('lazy').setup({ vim.lsp.enable('jdtls') end, }, + { + 'ibhagwan/fzf-lua', + -- optional for icon support + dependencies = { 'nvim-tree/nvim-web-devicons' }, + -- or if using mini.icons/mini.nvim + -- dependencies = { "nvim-mini/mini.icons" }, + ---@module "fzf-lua" + ---@type fzf-lua.Config|{} + ---@diagnostics disable: missing-fields + opts = {}, + ---@diagnostics enable: missing-fields + }, }) -- Basic settings @@ -54,6 +66,8 @@ vim.opt.tabstop = 2 vim.opt.shiftwidth = 2 vim.opt.expandtab = false vim.opt.completeopt = { 'menu', 'menuone', 'noselect' } +vim.opt.number = true +vim.opt.relativenumber = true local k = vim.keymap.set @@ -137,6 +151,10 @@ k('n', 'gd', function() vim.lsp.buf.definition() end, { desc = 'Terminate' }) +k('n', 'tt', function() + require('fzf-lua').lsp_document_symbols() +end) + k('n', 'm', "vnewput = execute('messages')") k('n', 'nn', 'JavaRunnerRunMain', { desc = 'Run main' }) diff --git a/.devcontainer/config/nvim/lazy-lock.json b/.devcontainer/config/nvim/lazy-lock.json index 3737ab9..933e059 100644 --- a/.devcontainer/config/nvim/lazy-lock.json +++ b/.devcontainer/config/nvim/lazy-lock.json @@ -1,6 +1,8 @@ { + "fzf-lua": { "branch": "main", "commit": "de0fd4a21ee29cf6532d0c3bcae08a0b25d99b6a" }, "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, "nui.nvim": { "branch": "main", "commit": "de740991c12411b663994b2860f1a4fd0937c130" }, "nvim-dap": { "branch": "master", "commit": "5860c7c501eb428d3137ee22c522828d20cca0b3" }, + "nvim-web-devicons": { "branch": "master", "commit": "8dcb311b0c92d460fac00eac706abd43d94d68af" }, "spring-boot.nvim": { "branch": "main", "commit": "218c0c26c14d99feca778e4d13f5ec3e8b1b60f0" } } diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6fcecea..3fc365c 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -11,7 +11,8 @@ "ghcr.io/duduribeiro/devcontainer-features/neovim:1": { "version": "nightly" }, - "ghcr.io/devcontainers-extra/features/springboot-sdkman:2": {} + "ghcr.io/devcontainers-extra/features/springboot-sdkman:2": {}, + "ghcr.io/devcontainers-extra/features/fzf:1": {} }, "postCreateCommand": "bash .devcontainer/setup.sh", "customizations": { diff --git a/lua/java-core/ls/servers/jdtls/conf.lua b/lua/java-core/ls/servers/jdtls/conf.lua index bf5cf2c..0cd37c1 100644 --- a/lua/java-core/ls/servers/jdtls/conf.lua +++ b/lua/java-core/ls/servers/jdtls/conf.lua @@ -8,7 +8,7 @@ return { advancedOrganizeImportsSupport = true, advancedUpgradeGradleSupport = true, classFileContentsSupport = true, - clientDocumentSymbolProvider = true, + clientDocumentSymbolProvider = false, clientHoverProvider = false, executeClientCommandSupport = true, extractInterfaceSupport = true, From 76a074b9de01c761f406ad221e84e504b988e993 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 13:54:26 +0530 Subject: [PATCH 134/147] chore(main): release 4.0.2 (#450) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e2a7fd..fd72263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [4.0.2](https://github.com/nvim-java/nvim-java/compare/v4.0.1...v4.0.2) (2025-12-10) + + +### Bug Fixes + +* vim.lsp.buf.document_symbol returns nothing for jdtls ([#449](https://github.com/nvim-java/nvim-java/issues/449)) ([625f48f](https://github.com/nvim-java/nvim-java/commit/625f48ffedc735ac6284316e16daa99ba012a996)) + ## [4.0.1](https://github.com/nvim-java/nvim-java/compare/v4.0.0...v4.0.1) (2025-12-05) From df1ff5f85df8d4b7b8d92215084d7c183520893f Mon Sep 17 00:00:00 2001 From: Joao Louceiro <89097431+joaoLouceiro@users.noreply.github.com> Date: Wed, 10 Dec 2025 10:58:37 +0000 Subject: [PATCH 135/147] fix(doc): invalid JDTLS settings in README (#451) The default_config section in JDTLS settings makes it so nvim-java can't find the list of JDKs. --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 3110ad2..5137180 100644 --- a/README.md +++ b/README.md @@ -284,16 +284,14 @@ Use `vim.lsp.config()` to override the default JDTLS settings: ```lua vim.lsp.config('jdtls', { - default_config = { - settings = { - java = { - configuration = { - runtimes = { - { - name = "JavaSE-21", - path = "/opt/jdk-21", - default = true, - } + settings = { + java = { + configuration = { + runtimes = { + { + name = "JavaSE-21", + path = "/opt/jdk-21", + default = true, } } } From 61ba7e0e668863dda8693c3f6489788a9a00eab7 Mon Sep 17 00:00:00 2001 From: Joao Louceiro <89097431+joaoLouceiro@users.noreply.github.com> Date: Wed, 10 Dec 2025 11:07:17 +0000 Subject: [PATCH 136/147] chore(log): fix exit code message formatting in Run:on_job_exit (#452) Adding a new line after the exit code improves the readability of the log. --- lua/java-runner/run.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/java-runner/run.lua b/lua/java-runner/run.lua index c110589..41fc87e 100644 --- a/lua/java-runner/run.lua +++ b/lua/java-runner/run.lua @@ -72,7 +72,7 @@ end ---Runs when the current job exists ---@param exit_code number function Run:on_job_exit(exit_code) - local message = string.format('Process finished with exit code::%s', exit_code) + local message = string.format('Process finished with exit code::%s\n', exit_code) self:send_term(message) self.is_running = false From 2855091884515756383df0414c9a344fb3aca5ad Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:37:51 +0530 Subject: [PATCH 137/147] chore(main): release 4.0.3 (#454) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd72263..eefe98e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [4.0.3](https://github.com/nvim-java/nvim-java/compare/v4.0.2...v4.0.3) (2025-12-10) + + +### Bug Fixes + +* **doc:** invalid JDTLS settings in README ([#451](https://github.com/nvim-java/nvim-java/issues/451)) ([df1ff5f](https://github.com/nvim-java/nvim-java/commit/df1ff5f85df8d4b7b8d92215084d7c183520893f)) + ## [4.0.2](https://github.com/nvim-java/nvim-java/compare/v4.0.1...v4.0.2) (2025-12-10) From 011b52180e75c693071106b774cebf64588aa8b0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:38:40 +0530 Subject: [PATCH 138/147] chore(doc): automatic vimdoc update (#453) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index aba0b52..22733f5 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2025 December 03 +*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2025 December 10 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -300,16 +300,14 @@ Use `vim.lsp.config()` to override the default JDTLS settings: >lua vim.lsp.config('jdtls', { - default_config = { - settings = { - java = { - configuration = { - runtimes = { - { - name = "JavaSE-21", - path = "/opt/jdk-21", - default = true, - } + settings = { + java = { + configuration = { + runtimes = { + { + name = "JavaSE-21", + path = "/opt/jdk-21", + default = true, } } } From 5f047ed757cea1c3fb01e7023b3e1abc876ecb3a Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Thu, 11 Dec 2025 02:07:35 +0530 Subject: [PATCH 139/147] chore: bump min nvim version to 0.11.5 (#456) --- .github/workflows/docs.yml | 2 +- README.md | 2 +- lua/java/checks/nvim-version.lua | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 34df049..13d15ec 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,7 +23,7 @@ jobs: with: vimdoc: "nvim-java" dedupsubheadings: false - version: "Neovim >= 0.9.4" + version: "Neovim >= 0.11.5" demojify: true - name: create pull request diff --git a/README.md b/README.md index 5137180..125650d 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Just install and start writing `public static void main(String[] args)`. :small_orange_diamond:details -**Requirements:** Neovim 0.11+ +**Requirements:** Neovim 0.11.5+ ### Using `vim.pack` diff --git a/lua/java/checks/nvim-version.lua b/lua/java/checks/nvim-version.lua index a18325f..f5b6b49 100644 --- a/lua/java/checks/nvim-version.lua +++ b/lua/java/checks/nvim-version.lua @@ -7,13 +7,11 @@ function M:run(config) return end - if vim.fn.has('nvim-0.11') ~= 1 then + if not vim.version.ge(vim.version(), '0.11.5') then local err = require('java-core.utils.errors') err.throw([[ - nvim-java is only tested on Neovim 0.11 or greater - Please upgrade to Neovim 0.11 or greater. - If you are sure it works on your version, disable the version check: - checks = { nvim_version = false }' + nvim-java is only tested on Neovim 0.11.5 or greater + Please upgrade to Neovim 0.11.5 or greater. ]]) end end From c220ee33f5b022be01b9057a7dcd0bb175abde1d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:10:25 +0530 Subject: [PATCH 140/147] chore(doc): automatic vimdoc update (#457) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index 22733f5..b90a1b7 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.9.4 Last change: 2025 December 10 +*nvim-java.txt* For Neovim >= 0.11.5 Last change: 2025 December 10 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -54,7 +54,7 @@ HOW TO INSTALL *nvim-java-how-to-install* :small_orange_diamond:details ~ -**Requirements:** Neovim 0.11+ +**Requirements:** Neovim 0.11.5+ USING VIM.PACK ~ From 9b6c907e2616a8ada8419d7b3e68e85056ba5d57 Mon Sep 17 00:00:00 2001 From: Srinesh Nisala Date: Thu, 11 Dec 2025 11:25:44 +0530 Subject: [PATCH 141/147] fix(dap): allow running mains without project (#459) --- lua/java-dap/setup.lua | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lua/java-dap/setup.lua b/lua/java-dap/setup.lua index 5973998..cf73550 100644 --- a/lua/java-dap/setup.lua +++ b/lua/java-dap/setup.lua @@ -41,11 +41,17 @@ end function Setup:enrich_config(config) config = vim.deepcopy(config) + -- skip enriching if already enriched + if config.mainClass and config.projectName and config.modulePaths and config.classPaths and config.javaExec then + return config + end + local main = config.mainClass - local project = config.projectName + -- when we set it to empty string, it will create a project with some random + -- string as name + local project = config.projectName or '' assert(main, 'To enrich the config, mainClass should already be present') - assert(project, 'To enrich the config, projectName should already be present') if config.request == 'launch' then self.java_debug:build_workspace(main, project, nil, false) From d196b25eda89ae957110f2966d1753889dc197b2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 11:28:10 +0530 Subject: [PATCH 142/147] chore(main): release 4.0.4 (#460) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eefe98e..6734155 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [4.0.4](https://github.com/nvim-java/nvim-java/compare/v4.0.3...v4.0.4) (2025-12-11) + + +### Bug Fixes + +* **dap:** allow running mains without project ([#459](https://github.com/nvim-java/nvim-java/issues/459)) ([9b6c907](https://github.com/nvim-java/nvim-java/commit/9b6c907e2616a8ada8419d7b3e68e85056ba5d57)) + ## [4.0.3](https://github.com/nvim-java/nvim-java/compare/v4.0.2...v4.0.3) (2025-12-10) From d4050ebc7ed909dcf4b536c109e8ce291112efc3 Mon Sep 17 00:00:00 2001 From: logrusx <45463882+logrusx@users.noreply.github.com> Date: Wed, 4 Feb 2026 04:15:12 +0000 Subject: [PATCH 143/147] feat: add curl downloader (#473) On some Mac systems the user cannot install wget, but curl is available OTB. --- lua/pkgm/downloaders/curl.lua | 63 ++++++++++++++++++++++++++++++++ lua/pkgm/downloaders/factory.lua | 7 ++++ 2 files changed, 70 insertions(+) create mode 100644 lua/pkgm/downloaders/curl.lua diff --git a/lua/pkgm/downloaders/curl.lua b/lua/pkgm/downloaders/curl.lua new file mode 100644 index 0000000..2009ab8 --- /dev/null +++ b/lua/pkgm/downloaders/curl.lua @@ -0,0 +1,63 @@ +local path = require('java-core.utils.path') +local class = require('java-core.utils.class') +local log = require('java-core.utils.log2') + +---@class java-core.Curl +---@field url string +---@field dest string +---@field retry_count number +---@field timeout number +local Curl = class() + +---@class java-core.CurlOpts +---@field url string URL to download +---@field dest? string Destination path (optional, uses temp if not provided) +---@field retry_count? number Retry count (optional, defaults to 5) +---@field timeout? number Timeout in seconds (optional, defaults to 30) + +---@param opts java-core.CurlOpts +function Curl:_init(opts) + self.url = opts.url + + if not opts.dest then + local filename = vim.fs.basename(opts.url) + local tmp_dir = vim.fn.tempname() + vim.fn.mkdir(tmp_dir, 'p') + self.dest = path.join(tmp_dir, filename) + log.debug('Using temp destination:', self.dest) + else + self.dest = opts.dest + log.debug('Using provided destination:', self.dest) + end + + self.retry_count = opts.retry_count or 5 + self.timeout = opts.timeout or 30 +end + +---Download file using curl +---@return string|nil # Path to downloaded file, or nil on failure +---@return string|nil # Error message if failed +function Curl:download() + log.debug('curl downloading:', self.url, 'to', self.dest) + local cmd = string.format( + 'curl --retry %d --connect-timeout %d -o %s %s', + self.retry_count, + self.timeout, + vim.fn.shellescape(self.dest), + vim.fn.shellescape(self.url) + ) + log.debug('curl command:', cmd) + + local result = vim.fn.system(cmd) + local exit_code = vim.v.shell_error + + if exit_code ~= 0 then + log.error('curl failed:', exit_code, result) + return nil, string.format('curl failed (exit %d): %s', exit_code, result) + end + + log.debug('curl download completed:', self.dest) + return self.dest, nil +end + +return Curl diff --git a/lua/pkgm/downloaders/factory.lua b/lua/pkgm/downloaders/factory.lua index f041fe6..a0163c4 100644 --- a/lua/pkgm/downloaders/factory.lua +++ b/lua/pkgm/downloaders/factory.lua @@ -1,4 +1,5 @@ local system = require('java-core.utils.system') +local Curl = require('pkgm.downloaders.curl') local Wget = require('pkgm.downloaders.wget') local PowerShell = require('pkgm.downloaders.powershell') local log = require('java-core.utils.log2') @@ -27,6 +28,12 @@ function M.get_downloader(opts) return Wget(opts) end + -- Check for curl on all platforms + if vim.fn.executable('curl') == 1 then + log.debug('Using curl downloader') + return Curl(opts) + end + -- On Windows, fallback to PowerShell if available if os == 'win' and (vim.fn.executable('pwsh') == 1 or vim.fn.executable('powershell') == 1) then log.debug('Using PowerShell downloader (fallback)') From e4cc304fa7a5daf41c13d6894bc90b4e99e576e6 Mon Sep 17 00:00:00 2001 From: Marco K Date: Wed, 4 Feb 2026 05:20:42 +0100 Subject: [PATCH 144/147] feat: add JavaTestRunAllTests and JavaTestDebugAllTests commands (#467) --- lua/java-test/api.lua | 34 ++++++++++++++++++++++++++++++++++ lua/java-test/init.lua | 28 ++++++++++++++++++++++++++++ plugin/java.lua | 12 ++++++++++++ 3 files changed, 74 insertions(+) diff --git a/lua/java-test/api.lua b/lua/java-test/api.lua index 93e327a..71103f6 100644 --- a/lua/java-test/api.lua +++ b/lua/java-test/api.lua @@ -157,4 +157,38 @@ function M:find_current_test_method() end end +---Run all tests in the workspace +---@param report java-test.JUnitTestReport +---@param config java-dap.DapLauncherConfigOverridable +function M:execute_all_tests(report, config) + log.debug('running all tests') + + local projects = self.test_client:find_java_projects() + + if #projects < 1 then + notify.warn('No Java projects found') + return + end + + -- Discover test classes from all projects + local all_tests = {} + for _, project in ipairs(projects) do + local packages = self.test_client:find_test_packages_and_types(project.jdtHandler) + for _, pkg in ipairs(packages or {}) do + -- Package children are the test classes + for _, test_class in ipairs(pkg.children or {}) do + table.insert(all_tests, test_class) + end + end + end + + if #all_tests < 1 then + notify.warn('No tests found in workspace') + return + end + + log.debug('found ' .. #all_tests .. ' test classes') + self:run_test(all_tests, report, config) +end + return M diff --git a/lua/java-test/init.lua b/lua/java-test/init.lua index 2b2f481..2c525cc 100644 --- a/lua/java-test/init.lua +++ b/lua/java-test/init.lua @@ -71,6 +71,34 @@ function M.run_current_method() .run() end +function M.run_all_tests() + log.info('run all tests') + + return runner(function() + local test_api = JavaTestApi:new({ + client = lsp_utils.get_jdtls(), + runner = DapRunner(), + }) + return test_api:execute_all_tests(M.get_report(), { noDebug = true }) + end) + .catch(get_error_handler('failed to run all tests')) + .run() +end + +function M.debug_all_tests() + log.info('debug all tests') + + return runner(function() + local test_api = JavaTestApi:new({ + client = lsp_utils.get_jdtls(), + runner = DapRunner(), + }) + return test_api:execute_all_tests(M.get_report(), {}) + end) + .catch(get_error_handler('failed to debug all tests')) + .run() +end + function M.view_last_report() if M.last_report then M.last_report:show_report() diff --git a/plugin/java.lua b/plugin/java.lua index 524dc3d..5feba3f 100644 --- a/plugin/java.lua +++ b/plugin/java.lua @@ -35,6 +35,18 @@ local cmd_map = { end, }, + JavaTestRunAllTests = { + function() + require('java-test').run_all_tests() + end, + }, + + JavaTestDebugAllTests = { + function() + require('java-test').debug_all_tests() + end, + }, + JavaTestViewLastReport = { function() require('java-test').view_last_report() From 61e18c47dcf237e29c9689201bf13c0708c288bc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 09:53:20 +0530 Subject: [PATCH 145/147] chore(main): release 4.1.0 (#476) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6734155..b07db49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [4.1.0](https://github.com/nvim-java/nvim-java/compare/v4.0.4...v4.1.0) (2026-02-04) + + +### Features + +* add curl downloader ([#473](https://github.com/nvim-java/nvim-java/issues/473)) ([d4050eb](https://github.com/nvim-java/nvim-java/commit/d4050ebc7ed909dcf4b536c109e8ce291112efc3)) +* add JavaTestRunAllTests and JavaTestDebugAllTests commands ([#467](https://github.com/nvim-java/nvim-java/issues/467)) ([e4cc304](https://github.com/nvim-java/nvim-java/commit/e4cc304fa7a5daf41c13d6894bc90b4e99e576e6)) + ## [4.0.4](https://github.com/nvim-java/nvim-java/compare/v4.0.3...v4.0.4) (2025-12-11) From 1cbc391ebca2921cabdc1e56f84c4f4659cf3a6a Mon Sep 17 00:00:00 2001 From: Marco K Date: Thu, 5 Feb 2026 03:08:44 +0100 Subject: [PATCH 146/147] chore: update README with new test commands (#477) --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 125650d..d7776b2 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,8 @@ Yep! That's all :) - `JavaTestDebugCurrentClass` - Debug the test class in the active buffer - `JavaTestRunCurrentMethod` - Run the test method on the cursor - `JavaTestDebugCurrentMethod` - Debug the test method on the cursor +- `JavaTestRunAllTests` - Run all tests in the workspace +- `JavaTestDebugAllTests` - Debug all tests in the workspace - `JavaTestViewLastReport` - Open the last test report in a popup window ### Profiles @@ -219,6 +221,18 @@ require('java').test.run_current_method() require('java').test.debug_current_method() ``` +- `run_all_tests` - Run all tests in the workspace + +```lua +require('java').test.run_all_tests() +``` + +- `debug_all_tests` - Debug all tests in the workspace + +```lua +require('java').test.debug_all_tests() +``` + - `view_report` - Open the last test report in a popup window ```lua From 602a5f7fa92f9c1d425a2159133ff9de86842f0a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 5 Feb 2026 13:01:39 +0530 Subject: [PATCH 147/147] chore(doc): automatic vimdoc update (#478) Co-authored-by: s1n7ax <18459807+s1n7ax@users.noreply.github.com> --- doc/nvim-java.txt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/nvim-java.txt b/doc/nvim-java.txt index b90a1b7..709c13b 100644 --- a/doc/nvim-java.txt +++ b/doc/nvim-java.txt @@ -1,4 +1,4 @@ -*nvim-java.txt* For Neovim >= 0.11.5 Last change: 2025 December 10 +*nvim-java.txt* For Neovim >= 0.11.5 Last change: 2026 February 05 ============================================================================== Table of Contents *nvim-java-table-of-contents* @@ -131,6 +131,8 @@ TEST ~ - `JavaTestDebugCurrentClass` - Debug the test class in the active buffer - `JavaTestRunCurrentMethod` - Run the test method on the cursor - `JavaTestDebugCurrentMethod` - Debug the test method on the cursor +- `JavaTestRunAllTests` - Run all tests in the workspace +- `JavaTestDebugAllTests` - Debug all tests in the workspace - `JavaTestViewLastReport` - Open the last test report in a popup window @@ -235,6 +237,18 @@ TEST ~ require('java').test.debug_current_method() < +- `run_all_tests` - Run all tests in the workspace + +>lua + require('java').test.run_all_tests() +< + +- `debug_all_tests` - Debug all tests in the workspace + +>lua + require('java').test.debug_all_tests() +< + - `view_report` - Open the last test report in a popup window >lua