diff --git a/lua/java-core/adapters/init.lua b/lua/java-core/adapters/init.lua new file mode 100644 index 0000000..7d3d6fa --- /dev/null +++ b/lua/java-core/adapters/init.lua @@ -0,0 +1,77 @@ +local List = require('java-core.utils.list') +local Set = require('java-core.utils.set') +local JavaTestClient = require('java-core.ls.clients.java-test-client') + +local M = {} + +---Returns the launcher config +---@param launch_args JavaCoreTestJunitLaunchArguments +---@param java_exec string +---@param config { debug: boolean, label: string } +---@return JavaCoreDapLauncherConfig +function M.get_dap_launcher_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(' '), + -- env: config?.env, + -- envFile: config?.envFile, + -- sourcePaths: config?.sourcePaths, + -- preLaunchTask: config?.preLaunchTask, + -- postDebugTask: config?.postDebugTask, + } + + -- if test_details.testKind == TestKind.TestNG then + -- path.join(extensionContext.extensionPath, 'server', 'com.microsoft.java.test.runner-jar-with-dependencies.jar'), + -- end +end + +---Ruterns the launch argument parameters for given test or tests +---@param tests JavaCoreTestDetails | JavaCoreTestDetails[] +---@return JavaCoreTestResolveJUnitLaunchArgumentsParams +function M.get_junit_launch_argument_params(tests) + if not vim.tbl_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 JavaCoreTestDetails[] +---@return 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-core/api/test.lua b/lua/java-core/api/test.lua new file mode 100644 index 0000000..111c75b --- /dev/null +++ b/lua/java-core/api/test.lua @@ -0,0 +1,116 @@ +local log = require('java-core.utils.log') +local data_adapters = require('java-core.adapters') + +local Promise = require('java-core.utils.promise') +local TestReport = require('java-core.dap.test-report') +local JavaDapRunner = require('java-core.dap.runner') +local JavaDebug = require('java-core.ls.clients.java-debug-client') +local JavaTest = require('java-core.ls.clients.java-test-client') + +---@class JavaCoreTestApi +---@field private client JavaCoreJdtlsClient +---@field private debug_client JavaCoreDebugClient +---@field private test_client JavaCoreTestClient +---@field private runner JavaCoreDapRunner +local M = {} + +---Returns a new test helper client +---@param args { client: LspClient } +---@return JavaCoreTestApi +function M:new(args) + local o = { + client = args.client, + } + + o.debug_client = JavaDebug:new({ + client = args.client, + }) + + o.test_client = JavaTest:new({ + client = args.client, + }) + + o.runner = JavaDapRunner:new({ + reporter = TestReport:new(), + }) + + setmetatable(o, self) + self.__index = self + + return o +end + +---Runs the test class in the given buffer +---@param buffer integer +---@param config JavaCoreDapLauncherConfigOverridable +---@return Promise +function M:run_class_by_buffer(buffer, config) + return self:get_test_class_by_buffer(buffer):thenCall(function(tests) + return self:run_test(tests, config) + end) +end + +---Returns test classes in the given buffer +---@priate +---@param buffer integer +---@return Promise # Promise +function M:get_test_class_by_buffer(buffer) + log.debug('finding test class by buffer') + + return Promise.resolve():thenCall(function() + if not vim.api.nvim_buf_is_valid(buffer) then + local msg = 'passed buffer' .. tostring(buffer) .. ' is not valid' + log.error(msg) + error(msg) + end + + local uri = vim.uri_from_bufnr(buffer) + return self.test_client:find_test_types_and_methods(uri) + end) +end + +---Run the given test +---@private +---@param tests JavaCoreTestDetails[] +---@param config? JavaCoreDapLauncherConfigOverridable config to override the default values in test launcher config +---@return Promise # +function M:run_test(tests, config) + ---@type JavaCoreTestJunitLaunchArguments + local launch_args + + return self.test_client + :resolve_junit_launch_arguments( + data_adapters.get_junit_launch_argument_params(tests) + ) + :thenCall( + ---@param _launch_args JavaCoreTestJunitLaunchArguments + function(_launch_args) + launch_args = _launch_args + + return self.debug_client:resolve_java_executable( + launch_args.mainClass, + launch_args.projectName + ) + end + ) + :thenCall( + ---@param java_exec string + function(java_exec) + local dap_launcher_config = + --@TODO I don't know why the hell debug is hard coded here + data_adapters.get_dap_launcher_config(launch_args, java_exec, { + debug = true, + label = 'Launch All Java Tests', + }) + + log.debug('dap launcher config is: ', dap_launcher_config) + + dap_launcher_config = + vim.tbl_deep_extend('force', dap_launcher_config, config or {}) + + return self.runner:run_by_config(dap_launcher_config) + end + ) +end + +return M diff --git a/lua/java-core/dap/adapters.lua b/lua/java-core/dap/adapters.lua new file mode 100644 index 0000000..801f7f3 --- /dev/null +++ b/lua/java-core/dap/adapters.lua @@ -0,0 +1,26 @@ +local M = {} + +---Returns the dap config record +---@param main JavaDebugResolveMainClassRecord +---@param classpath string[][] +---@param java_exec string +---@return JavaCoreDapLauncherConfig +function M.get_dap_config(main, classpath, java_exec) + local project_name = main.projectName + local main_class = main.mainClass + local module_paths = classpath[1] + local class_paths = classpath[2] + + return { + request = 'launch', + type = 'java', + name = string.format('%s -> %s', project_name, main_class), + projectName = project_name, + mainClass = main_class, + javaExec = java_exec, + modulePaths = module_paths, + classPaths = class_paths, + } +end + +return M diff --git a/lua/java-core/dap.lua b/lua/java-core/dap/init.lua similarity index 52% rename from lua/java-core/dap.lua rename to lua/java-core/dap/init.lua index f82a271..2f038bd 100644 --- a/lua/java-core/dap.lua +++ b/lua/java-core/dap/init.lua @@ -1,16 +1,16 @@ local log = require('java-core.utils.log') -local adapters = require('java-core.ls.adapters.test-adapter') +local adapters = require('java-core.dap.adapters') local List = require('java-core.utils.list') local JavaDebug = require('java-core.ls.clients.java-debug-client') local Promise = require('java-core.utils.promise') ---@class JavaCoreDap ----@field client LSPClient +---@field client LspClient ---@field java_debug JavaCoreDebugClient local M = {} ---Returns a new dap instance ----@param args { client: LSPClient } +---@param args { client: LspClient } ---@return JavaCoreDap function M:new(args) local o = { @@ -26,18 +26,18 @@ function M:new(args) return o end ----@class JavaDapAdapter +---@class JavaCoreDapAdapter ---@field type string ---@field host string ---@field port integer ---Returns the dap adapter config ----@return Promise +---@return Promise # Promise function M:get_dap_adapter() log.info('creating dap adapter for java') return self.java_debug:start_debug_session():thenCall( - ---@param port JavaDebugStartDebugSessionResponse + ---@param port integer function(port) return { type = 'server', @@ -48,15 +48,13 @@ function M:get_dap_adapter() ) end ----@alias JavaDapConfigurationList JavaDapConfiguration[] - ---Returns the dap configuration for the current project ----@return Promise +---@return Promise # Promise function M:get_dap_config() log.info('creating dap configuration for java') return self.java_debug:resolve_main_class():thenCall( - ---@param mains JavaDebugResolveMainClassResponse + ---@param mains JavaDebugResolveMainClassRecord[] function(mains) local config_promises = List:new(mains):map(function(main) return self:get_dap_config_record(main) @@ -69,71 +67,58 @@ end ---Returns the dap config for the given main class ---@param main JavaDebugResolveMainClassRecord ----@return Promise +---@return Promise # Promise function M:get_dap_config_record(main) return Promise.all({ self.java_debug:resolve_classpath(main.mainClass, main.projectName), self.java_debug:resolve_java_executable(main.mainClass, main.projectName), }):thenCall(function(res) - ---@type JavaDebugResolveClasspathResponse + ---@type string[][] local classpaths = res[1] - ---@type JavaDebugResolveJavaExecutableResponse + ---@type string local java_exec = res[2] return adapters.get_dap_config(main, classpaths, java_exec) end) end ----Dap run with given config ----@param config JavaDapConfiguration -function M.dap_run(config) - log.info('running dap with config: ', config) - - local function get_stream_reader(conn) - return vim.schedule_wrap(function(err, buffer) - assert(not err, err) - - if buffer then - vim.print(buffer) - else - conn:close() - end - end) - end - - ---@type uv_tcp_t - local server - - require('dap').run(config --[[@as Configuration]], { - before = function(conf) - log.debug('running before dap callback') - - server = assert(vim.loop.new_tcp(), 'uv.new_tcp() must return handle') - server:bind('127.0.0.1', 0) - server:listen(128, function(err) - assert(not err, err) - - local sock = assert(vim.loop.new_tcp(), 'uv.new_tcp must return handle') - server:accept(sock) - sock:read_start(get_stream_reader(sock)) - end) - - conf.args = - conf.args:gsub('-port ([0-9]+)', '-port ' .. server:getsockname().port) - - return conf - end, - - after = function() - vim.debug('running after dap callback') - - if server then - server:shutdown() - server:close() - end - end, - }) -end - return M + +---@class JavaCoreDapLauncherConfigOverridable +---@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 JavaCoreDapLauncherConfig +---@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-core/dap/runner.lua b/lua/java-core/dap/runner.lua new file mode 100644 index 0000000..3745ca9 --- /dev/null +++ b/lua/java-core/dap/runner.lua @@ -0,0 +1,76 @@ +local log = require('java-core.utils.log') + +---@class JavaCoreDapRunner +---@field reporter JavaCoreDapRunReport +---@field private server uv_tcp_t +local M = {} + +---@param args { reporter: JavaCoreDapRunReport } +---@return JavaCoreDapRunner +function M:new(args) + local o = { + reporter = args.reporter, + server = nil, + } + + setmetatable(o, self) + self.__index = self + return o +end + +---Dap run with given config +---@param config JavaCoreDapLauncherConfig +function M:run_by_config(config) + log.debug('running dap with config: ', config) + + require('dap').run(config --[[@as Configuration]], { + before = function(conf) + return self:before(conf) + end, + + after = function() + return self:after() + end, + }) +end + +---Runs before the dap run +---@private +---@param conf JavaCoreDapLauncherConfig +---@return JavaCoreDapLauncherConfig +function M:before(conf) + 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(self.reporter: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 aafter the dap run +---@private +function M:after() + vim.debug('running "after" callback') + + if self.server then + self.server:shutdown() + self.server:close() + end +end + +return M + +---@class JavaCoreDapRunReport +---@field get_stream_reader fun(self: JavaCoreDapRunReport, conn: uv_tcp_t): fun(err: string|nil, buffer: string|nil) diff --git a/lua/java-core/dap/test-report.lua b/lua/java-core/dap/test-report.lua new file mode 100644 index 0000000..37ce06f --- /dev/null +++ b/lua/java-core/dap/test-report.lua @@ -0,0 +1,50 @@ +local log = require('java-core.utils.log') + +---@class JavaCoreDapTestReport: JavaCoreDapRunReport +---@field private conn uv_tcp_t +local M = {} + +function M:new() + local o = {} + setmetatable(o, self) + self.__index = self + return o +end + +---Returns a stream reader function +---@param conn uv_tcp_t +---@return fun(err: string, buffer: string) +function M:get_stream_reader(conn) + self.conn = conn + + 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 + +function M:on_update(buffer) + vim.print(buffer) +end + +function M:on_close() + vim.print('closing') +end + +function M:on_error(err) + log.error('Error while running test', err) +end + +return M diff --git a/lua/java-core/ls/adapters/test-adapter.lua b/lua/java-core/ls/adapters/test-adapter.lua deleted file mode 100644 index db8780f..0000000 --- a/lua/java-core/ls/adapters/test-adapter.lua +++ /dev/null @@ -1,146 +0,0 @@ -local List = require('java-core.utils.list') -local Set = require('java-core.utils.set') -local JavaTestClient = require('java-core.ls.clients.java-test-client') - -local M = {} - ----@class JavaTestLauncherConfigOverridable: JavaDapConfiguration ----@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 JavaTestLauncherConfig: JavaDapConfiguration ----@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 - ----Returns the launcher config ----@param launch_args JavaTestJunitLaunchArguments ----@param java_exec JavaDebugResolveJavaExecutableResponse ----@param config { debug: boolean, label: string } ----@return JavaTestLauncherConfig -function M.get_dap_launcher_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(' '), - -- env: config?.env, - -- envFile: config?.envFile, - -- sourcePaths: config?.sourcePaths, - -- preLaunchTask: config?.preLaunchTask, - -- postDebugTask: config?.postDebugTask, - } - - -- if test_details.testKind == TestKind.TestNG then - -- path.join(extensionContext.extensionPath, 'server', 'com.microsoft.java.test.runner-jar-with-dependencies.jar'), - -- end -end - ----comment ----@param tests JavaTestDetails | JavaTestDetails[] -function M.get_junit_launch_argument_params(tests) - if not vim.tbl_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 JavaTestDetails[] ----@return 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 - ----@class JavaDapConfiguration ----@field name string ----@field projectName string ----@field mainClass string ----@field javaExec string ----@field modulePaths string[] ----@field classPaths string[] ----@field request string - ----Returns the dap config record ----@param main JavaDebugResolveMainClassRecord ----@param classpath JavaDebugResolveClasspathResponse ----@param java_exec JavaDebugResolveJavaExecutableResponse ----@return JavaDapConfiguration -function M.get_dap_config(main, classpath, java_exec) - local project_name = main.projectName - local main_class = main.mainClass - local module_paths = classpath[1] - local class_paths = classpath[2] - - return { - request = 'launch', - type = 'java', - name = string.format('%s -> %s', project_name, main_class), - projectName = project_name, - mainClass = main_class, - javaExec = java_exec, - modulePaths = module_paths, - classPaths = class_paths, - } -end - -return M diff --git a/lua/java-core/ls/clients/java-debug-client.lua b/lua/java-core/ls/clients/java-debug-client.lua index d21db4e..5baef2a 100644 --- a/lua/java-core/ls/clients/java-debug-client.lua +++ b/lua/java-core/ls/clients/java-debug-client.lua @@ -1,27 +1,23 @@ -local JDTLSClient = require('java-core.ls.clients.jdtls-client') +local JdtlsClient = require('java-core.ls.clients.jdtls-client') ---@class JavaCoreDebugClient: JavaCoreJdtlsClient -local M = JDTLSClient:new() +local M = JdtlsClient:new() ---@class JavaDebugResolveMainClassRecord ---@field mainClass string ---@field projectName string ---@field fileName string ----@alias JavaDebugResolveMainClassResponse JavaDebugResolveMainClassRecord[] - ---Returns a list of main classes in the current workspace ----@return Promise +---@return Promise # Promise function M:resolve_main_class() return self:execute_command('vscode.java.resolveMainClass') end ----@alias JavaDebugResolveClasspathResponse string[][] - ---Returns module paths and class paths of a given main class ---@param project_name string ---@param main_class string ----@return Promise +---@return Promise # Promise function M:resolve_classpath(main_class, project_name) return self:execute_command( 'vscode.java.resolveClasspath', @@ -29,12 +25,10 @@ function M:resolve_classpath(main_class, project_name) ) end ----@alias JavaDebugResolveJavaExecutableResponse string - ---Returns the path to java executable for a given main class ---@param project_name string ---@param main_class string ----@return Promise +---@return Promise # Promise function M:resolve_java_executable(main_class, project_name) return self:execute_command('vscode.java.resolveJavaExecutable', { main_class, @@ -42,14 +36,12 @@ function M:resolve_java_executable(main_class, project_name) }) end ----@alias JavaDebugCheckProjectSettingsResponse boolean - ---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 Promise +---@return Promise # Promise function M:check_project_settings( main_class, project_name, @@ -67,16 +59,36 @@ function M:check_project_settings( ) end ----@alias JavaDebugStartDebugSessionResponse integer - ---Starts a debug session and returns the port number ----@return Promise +---@return Promise # Promise function M:start_debug_session() return self:execute_command('vscode.java.startDebugSession') end -function M:build_workspace() - return self:execute_command('vscode.java.buildWorkspace') +---@enum CompileWorkspaceStatus +M.CompileWorkspaceStatus = { + FAILED = 0, + SUCCEED = 1, + WITHERROR = 2, + CANCELLED = 3, +} + +---Build the workspace +---@param main_class string +---@param project_name? string +---@param file_path? string +---@param is_full_build boolean +---@return Promise # Promise +function M:build_workspace(main_class, project_name, file_path, is_full_build) + return self:execute_command( + 'vscode.java.buildWorkspace', + vim.fn.json_encode({ + mainClass = main_class, + projectName = project_name, + filePath = file_path, + isFullBuild = is_full_build, + }) + ) end return M diff --git a/lua/java-core/ls/clients/java-test-client.lua b/lua/java-core/ls/clients/java-test-client.lua index 5cfe3ea..4bba924 100644 --- a/lua/java-core/ls/clients/java-test-client.lua +++ b/lua/java-core/ls/clients/java-test-client.lua @@ -1,7 +1,7 @@ local log = require('java-core.utils.log') -local JDTLSClient = require('java-core.ls.clients.jdtls-client') +local JdtlsClient = require('java-core.ls.clients.jdtls-client') ----@class JavaTestDetails: Configuration +---@class JavaCoreTestDetails: Configuration ---@field fullName string ---@field id string ---@field jdtHandler string @@ -11,15 +11,15 @@ local JDTLSClient = require('java-core.ls.clients.jdtls-client') ---@field testLevel integer ---@field uri string ----@class JavaTestDetailsWithRange: JavaTestDetails +---@class JavaCoreTestDetailsWithRange: JavaCoreTestDetails ---@field range Range ----@class JavaTestDetailsWithChildren: JavaTestDetails ----@field children JavaTestDetailsWithRange[] +---@class JavaCoreTestDetailsWithChildren: JavaCoreTestDetails +---@field children JavaCoreTestDetailsWithRange[] ----@class JavaTestDetailsWithChildrenAndRange: JavaTestDetails +---@class JavaCoreTestDetailsWithChildrenAndRange: JavaCoreTestDetails ---@field range Range ----@field children JavaTestDetailsWithRange[] +---@field children JavaCoreTestDetailsWithRange[] ---@class Range ---@field start CursorPoint @@ -30,12 +30,10 @@ local JDTLSClient = require('java-core.ls.clients.jdtls-client') ---@field character integer ---@class JavaCoreTestClient: JavaCoreJdtlsClient -local M = JDTLSClient:new() - ----@alias JavaTestFindJavaProjectsResponse JavaTestDetails[] +local M = JdtlsClient:new() ---Returns a list of project details in the current root ----@return Promise +---@return Promise # Promise function M:find_java_projects() return self:execute_command( 'vscode.java.test.findJavaProjects', @@ -43,11 +41,10 @@ function M:find_java_projects() ) end ----@alias JavaTestFindTestPackagesAndTypesResponse JavaTestDetailsWithChildren[] - ---Returns a list of test package details ----@param token any ----@return Promise +---@param handler string +---@param token? string +---@return Promise # Promise function M:find_test_packages_and_types(handler, token) return self:execute_command( 'vscode.java.test.findTestPackagesAndTypes', @@ -55,12 +52,10 @@ function M:find_test_packages_and_types(handler, token) ) end ----@alias JavaTestFindTestTypesAndMethodsResponse JavaTestDetailsWithChildrenAndRange[] - ---Returns test informations in a given file ---@param file_uri string ----@param token any ----@return Promise +---@param token? string +---@return Promise # Promise function M:find_test_types_and_methods(file_uri, token) return self:execute_command( 'vscode.java.test.findTestTypesAndMethods', @@ -68,7 +63,7 @@ function M:find_test_types_and_methods(file_uri, token) ) end ----@class JavaTestJunitLaunchArguments +---@class JavaCoreTestJunitLaunchArguments ---@field classpath string[] ---@field mainClass string ---@field modulepath string[] @@ -77,15 +72,15 @@ end ---@field vmArguments string[] ---@field workingDirectory string ----@class JavaTestResolveJUnitLaunchArgumentsParams +---@class JavaCoreTestResolveJUnitLaunchArgumentsParams ---@field project_name string ---@field test_names string[] ----@field test_level TestLevel ----@field test_kind TestKind +---@field test_level JavaCoreTestLevel +---@field test_kind JavaCoreTestKind ---Returns junit launch arguments ----@param args JavaTestResolveJUnitLaunchArgumentsParams ----@return Promise +---@param args JavaCoreTestResolveJUnitLaunchArgumentsParams +---@return Promise # Promise function M:resolve_junit_launch_arguments(args) return self :execute_command( @@ -94,11 +89,11 @@ function M:resolve_junit_launch_arguments(args) ) :thenCall( - ---@class JavaTestJunitLaunchArgumentsResponse - ---@field body JavaTestJunitLaunchArguments + ---@class JavaCoreTestJunitLaunchArgumentsResponse + ---@field body JavaCoreTestJunitLaunchArguments ---@field status integer - ---@param res JavaTestJunitLaunchArgumentsResponse + ---@param res JavaCoreTestJunitLaunchArgumentsResponse function(res) if not res.body then local msg = 'Failed to retrive JUnit launch arguments' @@ -112,7 +107,7 @@ function M:resolve_junit_launch_arguments(args) ) end ----@enum TestKind +---@enum JavaCoreTestKind M.TestKind = { JUnit5 = 0, JUnit = 1, @@ -120,7 +115,7 @@ M.TestKind = { None = 100, } ----@enum TestLevel +---@enum JavaCoreTestLevel M.TestLevel = { Root = 0, Workspace = 1, diff --git a/lua/java-core/ls/clients/jdtls-client.lua b/lua/java-core/ls/clients/jdtls-client.lua index 27ad80f..fb807e4 100644 --- a/lua/java-core/ls/clients/jdtls-client.lua +++ b/lua/java-core/ls/clients/jdtls-client.lua @@ -2,10 +2,10 @@ local log = require('java-core.utils.log') local Promise = require('java-core.utils.promise') ---@class JavaCoreJdtlsClient ----@field client LSPClient +---@field client LspClient local M = {} ----@param args { client: LSPClient } +---@param args? { client: LspClient } ---@return JavaCoreJdtlsClient function M:new(args) local o = { @@ -29,12 +29,14 @@ function M:execute_command(command, arguments, buffer) arguments = arguments, } - log.debug('workspace/executeCommand: ', cmd_info) + log.debug('executing: workspace/executeCommand - ' .. command) self.client.request('workspace/executeCommand', cmd_info, function(err, res) if err then + log.error(command .. ' failed! arguments: ', arguments, ' error: ', err) reject(err) else + log.debug(command .. ' success! response: ', res) resolve(res) end end, buffer) @@ -63,6 +65,9 @@ function M:get_capability(...) return capability end +---Returns true if the LS supports the given command +---@param command_name string name of the command +---@return boolean function M:has_command(command_name) local commands = self:get_capability('executeCommandProvider', 'commands') diff --git a/lua/java-core/ls/helpers/test-helper.lua b/lua/java-core/ls/helpers/test-helper.lua deleted file mode 100644 index 52f082b..0000000 --- a/lua/java-core/ls/helpers/test-helper.lua +++ /dev/null @@ -1,141 +0,0 @@ -local log = require('java-core.utils.log') -local java_test_adapter = require('java-core.ls.adapters.test-adapter') - -local List = require('java-core.utils.list') -local Promise = require('java-core.utils.promise') -local JavaTestClient = require('java-core.ls.clients.java-test-client') -local JavaDebugClient = require('java-core.ls.clients.java-debug-client') -local JavaDap = require('java-core.dap') - ----@class JavaCoreTestHelper ----@field client JavaCoreJdtlsClient ----@field debug_client JavaCoreDebugClient ----@field test_client JavaCoreTestClient -local M = {} - ----Returns a new test helper client ----@param args { client: LSPClient } ----@return JavaCoreTestHelper -function M:new(args) - local o = { - client = args.client, - debug_client = JavaDebugClient:new({ - client = args.client, - }), - test_client = JavaTestClient:new({ - client = args.client, - }), - } - - setmetatable(o, self) - self.__index = self - return o -end - ---@TODO all_tests are not running ---https://github.com/nvim-java/java-core/issues/9 ----Running all tests ----@return Promise -function M:__run_all() - log.info('running all tests') - - return self.test_client - :find_java_projects() - :thenCall( - ---@param projects JavaTestFindJavaProjectsResponse - function(projects) - local test_pkg_promises = List:new(projects):map( - ---@param project JavaTestDetails - function(project) - return self.test_client:find_test_packages_and_types( - project.jdtHandler - ) - end - ) - - return Promise.all(test_pkg_promises) - end - ) - :thenCall( - ---@param test_pkgs JavaTestFindTestPackagesAndTypesResponse[] - function(test_pkgs) - local tests = List:new(test_pkgs) - :flatten() - :map( - ---@param test JavaTestDetailsWithChildren - function(test) - return test.children - end - ) - :flatten() - - return self:run_test(tests) - end - ) -end - ----Returns test classes in the given buffer ----@param buffer integer ----@return Promise -function M:get_test_class_by_buffer(buffer) - log.info('finding test class by buffer') - - return Promise.resolve() - :thenCall(function() - if not vim.api.nvim_buf_is_valid(buffer) then - local msg = ('passed buffer %s is not valid'):format(buffer) - - log.fmt_error(msg) - error(msg) - end - - return vim.uri_from_bufnr(buffer) - end) - :thenCall(function(uri) - return self.test_client:find_test_types_and_methods(uri) - end) -end - ----Run the given test ----@param tests JavaTestFindJavaProjectsResponse ----@param config? JavaTestLauncherConfigOverridable config to override the default values in test launcher config ----@return Promise -function M:run_test(tests, config) - ---@type JavaTestJunitLaunchArguments - local launch_args - - return self.test_client - :resolve_junit_launch_arguments( - java_test_adapter.get_junit_launch_argument_params(tests) - ) - :thenCall( - ---@param _launch_args JavaTestJunitLaunchArguments - function(_launch_args) - launch_args = _launch_args - - return self.debug_client:resolve_java_executable( - launch_args.mainClass, - launch_args.projectName - ) - end - ) - :thenCall( - ---@param java_exec JavaDebugResolveJavaExecutableResponse - function(java_exec) - local dap_launcher_config = - java_test_adapter.get_dap_launcher_config(launch_args, java_exec, { - debug = true, - label = 'Launch All Java Tests', - }) - - log.debug('dap launcher config is: ', dap_launcher_config) - - dap_launcher_config = - vim.tbl_deep_extend('force', dap_launcher_config, config or {}) - - return JavaDap.dap_run(dap_launcher_config) - end - ) -end - -return M diff --git a/lua/java-core/ls/servers/jdtls/init.lua b/lua/java-core/ls/servers/jdtls/init.lua index 3b88f6b..f957c81 100644 --- a/lua/java-core/ls/servers/jdtls/init.lua +++ b/lua/java-core/ls/servers/jdtls/init.lua @@ -16,15 +16,14 @@ local M = {} ---Returns a configuration for jdtls that you can pass into the setup of nvim-lspconfig ---@param opts JavaCoreGetConfigOptions ----@return LSPSetupConfig # jdtls setup configuration +---@return LspSetupConfig # jdtls setup configuration function M.get_config(opts) log.debug('generating jdtls config') local jdtls_path = mason.get_pkg_path('jdtls') local lombok_path = path.join(jdtls_path, 'lombok.jar') local jdtls_cache_path = path.join(vim.fn.stdpath('cache'), 'jdtls') - local plugin_paths = - plugins.get_plugin_paths({ 'java-test', 'java-debug-adapter' }) + local plugin_paths = plugins.get_plugin_paths(opts.jdtls_plugins) local base_config = config.get_config() @@ -49,16 +48,15 @@ end ---Returns a function that finds the java project root ---@private ---@param root_markers string[] list of files to find the root dir of a project ----@return function +---@return fun(file_name: string): string | nil function M.get_root_finder(root_markers) return function(file_name) - log.info('finding the root_dir') - log.debug('root_markers: ', root_markers) + log.debug('finding the root_dir with root_markers ', root_markers) local root = util.root_pattern(unpack(root_markers))(file_name) if root then - log.fmt_debug('root of: %s is: %s', file_name, root) + log.debug('root of ' .. file_name .. ' is ' .. root) return root end end diff --git a/lua/java-core/jdtls-types.lua b/lua/java-core/ls/servers/jdtls/jdtls-types.lua similarity index 100% rename from lua/java-core/jdtls-types.lua rename to lua/java-core/ls/servers/jdtls/jdtls-types.lua diff --git a/lua/java-core/lspconfig-types.lua b/lua/java-core/ls/servers/jdtls/lspconfig-types.lua similarity index 84% rename from lua/java-core/lspconfig-types.lua rename to lua/java-core/ls/servers/jdtls/lspconfig-types.lua index 1891e83..60ba519 100644 --- a/lua/java-core/lspconfig-types.lua +++ b/lua/java-core/ls/servers/jdtls/lspconfig-types.lua @@ -1,33 +1,33 @@ ----@class LSPSetupConfig +---@class LspSetupConfig ---@field root_dir fun(filename: string, bufnr: number): string | nil Returns either a filepath (string) or nil. The language server will only start if the function returns a filepath. ---@field name? string Defaults to the server's name (`clangd`, `pyright`, etc.). ---@field filetypes string[] | nil Set of filetypes for which to attempt to resolve {root_dir} ---@field autostart? boolean Controls if the `FileType` autocommand that launches a language server is created. If `false`, allows for deferring language servers until manually launched with `:LspStart` (|lspconfig-commands|). ---@field single_file_support? boolean Determines if a server is started without a matching root directory. See |lspconfig-single-file-support|. ----@field on_new_config fun(new_config: LSPSetupConfig, new_root_dir: string) Function executed after a root directory is detected. This is used to modify the server configuration (including `cmd` itself). Most commonly, this is used to inject additional arguments into `cmd`. +---@field on_new_config fun(new_config: LspSetupConfig, new_root_dir: string) Function executed after a root directory is detected. This is used to modify the server configuration (including `cmd` itself). Most commonly, this is used to inject additional arguments into `cmd`. ---@field capabilities table a table which represents the neovim client capabilities. Useful for broadcasting to the server additional functionality (snippets, off-protocol features) provided by plugins. ---@field cmd string[] a list where each entry corresponds to the blankspace delimited part of the command that launches the server. The first entry is the binary used to run the language server. Additional entries are passed as arguments. ---@field handlers table a list of handlers which override the function used to process a response from a given language server. Applied only to the server referenced by setup. See |lsp-handler|. ----@field init_options { bundles?: string[], workspaceFolders?: string, settings?: JavaConfigurationSettings, extendedClientCapabilities: table } +---@field init_options table a table passed during the initialization notification after launching a language server. Equivalent to the `initializationOptions` field found in `InitializeParams` in the LSP specification. ---@field on_attach fun(client: object, bufnr: number) Callback invoked by Nvim's built-in client when attaching a buffer to a language server. Often used to set Nvim (buffer or global) options or to override the Nvim client properties (`server_capabilities`) after a language server attaches. Most commonly used for settings buffer local keybindings. See |lspconfig-keybindings| for a usage example. ---@field settings table The `settings` table is sent in `on_init` via a `workspace/didChangeConfiguration` notification from the Nvim client to the language server. These settings allow a user to change optional runtime settings of the language server. ----@class LSPClientRequestParameters +---@class LspClientRequestParameters ---@field command string ---@field arguments string | string[] | nil ----@class LSPClientResponse ----@field err LSPClientResponseError +---@class LspClientResponse +---@field err LspClientResponseError ----@class LSPClientResponseError +---@class LspClientResponseError ---@field code number ---@field message string ----@class LSPClient +---@class LspClient ---@field attached_buffers table ---@field cancel_request function ---@field commands any[] ----@field config LSPSetupConfig +---@field config LspSetupConfig ---@field handlers {[string]: function} ---@field id number ---@field initialized boolean @@ -36,7 +36,7 @@ ---@field name string ---@field notify fun(method: string, params: object): boolean ---@field offset_encoding string ----@field request fun(method: string, params: LSPClientRequestParameters, callback: fun(err: any, result: any), bufnr?: number): any +---@field request fun(method: string, params: LspClientRequestParameters, callback: fun(err: any, result: any), bufnr?: number): any ---@field request_sync function ---@field requests object ---@field rpc object diff --git a/lua/java-core/utils/list.lua b/lua/java-core/utils/list.lua index 8722563..cc310bb 100644 --- a/lua/java-core/utils/list.lua +++ b/lua/java-core/utils/list.lua @@ -36,7 +36,7 @@ end function M:map(mapper) local mapped = M:new() - for i, v in ipairs(self) do + for _, v in ipairs(self) do mapped:push(mapper(v)) end diff --git a/lua/java-core/utils/log.lua b/lua/java-core/utils/log.lua index 5f113f9..0cb7a41 100644 --- a/lua/java-core/utils/log.lua +++ b/lua/java-core/utils/log.lua @@ -137,6 +137,9 @@ log.new = function(config, standalone) local fp = io.open(outfile, 'a') 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 @@ -162,6 +165,5 @@ log.new = function(config, standalone) end log.new(default_config, true) --- }}} return log diff --git a/lua/java-core/utils/lsp.lua b/lua/java-core/utils/lsp.lua index cb379d4..566d771 100644 --- a/lua/java-core/utils/lsp.lua +++ b/lua/java-core/utils/lsp.lua @@ -5,7 +5,7 @@ local M = {} ---Returns the client by name of the language server ---@param name string name of the language server ----@return LSPClient | nil +---@return LspClient | nil function M.find_client_by_name(name) local clients = List:new(vim.lsp.get_active_clients()) @@ -15,7 +15,7 @@ function M.find_client_by_name(name) end ---Returns the jdtls client object ----@return LSPClient +---@return LspClient function M.get_jdtls_client() local client = M.find_client_by_name('jdtls') diff --git a/lua/java-core/utils/mason.lua b/lua/java-core/utils/mason.lua index e0a84f2..c910aec 100644 --- a/lua/java-core/utils/mason.lua +++ b/lua/java-core/utils/mason.lua @@ -18,7 +18,7 @@ end ---Returns the shared artifact path for a given package ---@param pkg_name string name of the package to get the path of ----@return string +---@return string # path to the shared artifact directory of the package function M.get_shared_path(pkg_name) return vim.fn.glob('$MASON/share/' .. pkg_name) end