diff --git a/lua/neotest-java/build_tool/gradle.lua b/lua/neotest-java/build_tool/gradle.lua index 618ba1c..1b5435c 100644 --- a/lua/neotest-java/build_tool/gradle.lua +++ b/lua/neotest-java/build_tool/gradle.lua @@ -2,9 +2,10 @@ local fun = require("fun") local iter = fun.iter local totable = fun.totable local scan = require("plenary.scandir") -local File = require("neotest.lib.file") + local run = require("neotest-java.command.run") -local binaries = require("neotest-java.command.binaries") +local runtime = require("neotest-java.command.runtime") +local grdl = require("neotest-java.command.binaries").gradle local JAVA_FILE_PATTERN = ".+%.java$" @@ -102,9 +103,13 @@ gradle.get_dependencies_classpath = function() -- '< /dev/null' is necessary -- https://github.com/gradle/gradle/issues/15941#issuecomment-1191510921 - local suc = - os.execute(binaries.gradle() .. " dependencies > build/neotest-java" .. "/dependencies.txt " .. "< /dev/null") - assert(suc, "failed to run") + -- fix: this has to go through system too, to set the env variables, or update nio.run + local command = { grdl(), "-q", "dependencies > build/neotest-java/dependencies.txt < /dev/null" } + local result = vim.system(command, { env = { ["JAVA_HOME"] = runtime() } }):wait() + + if not result or result.code ~= 0 then + error('error while running command "' .. table.concat(command, " ")) + end local output = run("cat " .. gradle.get_output_dir() .. "/dependencies.txt") local output_lines = vim.split(output, "\n") diff --git a/lua/neotest-java/build_tool/maven.lua b/lua/neotest-java/build_tool/maven.lua index 69e6c1f..25d98d2 100644 --- a/lua/neotest-java/build_tool/maven.lua +++ b/lua/neotest-java/build_tool/maven.lua @@ -1,7 +1,10 @@ -local run = require("neotest-java.command.run") local read_xml_tag = require("neotest-java.util.read_xml_tag") -local scan = require("plenary.scandir") + +local run = require("neotest-java.command.run") +local runtime = require("neotest-java.command.runtime") local mvn = require("neotest-java.command.binaries").mvn + +local scan = require("plenary.scandir") local logger = require("neotest.logging") local JAVA_FILE_PATTERN = ".+%.java$" @@ -67,12 +70,19 @@ end local memoized_result ---@return string maven.get_dependencies_classpath = function() + -- fix: this memoization is not going to work, every time the class path changes, this has to be detected, + -- and the class file path has to be re-generated ! if memoized_result then return memoized_result end - local command = mvn() .. " -q dependency:build-classpath -Dmdep.outputFile=target/neotest-java/classpath.txt" - run(command) + local command = { mvn(), "-q", "dependency:build-classpath", "-Dmdep.outputFile=target/neotest-java/classpath.txt" } + local result = vim.system(command, { env = { ["JAVA_HOME"] = runtime() } }):wait() + + if not result or result.code ~= 0 then + error('error while running command "' .. table.concat(command, " ")) + end + local dependency_classpath = run("cat target/neotest-java/classpath.txt") if string.match(dependency_classpath, "ERROR") then @@ -89,7 +99,7 @@ maven.prepare_classpath = function() -- write in file per buffer of 500 characters local classpath_arguments = ([[ -cp %s:%s:%s - ]]):format(table.concat(maven.get_resources(), ":"), classpath, maven.get_output_dir() .. "/classes") + ]]):format(table.concat(maven.get_resources(), ":"), classpath, maven.get_output_dir() .. "/classes") --write manifest file local arguments_filepath = "target/neotest-java/cp_arguments.txt" diff --git a/lua/neotest-java/command/binaries.lua b/lua/neotest-java/command/binaries.lua index 9df8361..b7a8c96 100644 --- a/lua/neotest-java/command/binaries.lua +++ b/lua/neotest-java/command/binaries.lua @@ -1,14 +1,16 @@ local File = require("neotest.lib.file") local ch = require("neotest-java.context_holder") +local runtime = require("neotest-java.command.runtime") local binaries = { - java = function() - return "java" + local runtime_path = runtime() + return runtime_path and vim.fs.normalize(string.format("%s/bin/java", runtime_path)) or "java" end, javac = function() - return "javac" + local runtime_path = runtime() + return runtime_path and vim.fs.normalize(string.format("%s/bin/javac", runtime_path)) or "javac" end, mvn = function() diff --git a/lua/neotest-java/command/runtime.lua b/lua/neotest-java/command/runtime.lua new file mode 100644 index 0000000..e025afc --- /dev/null +++ b/lua/neotest-java/command/runtime.lua @@ -0,0 +1,65 @@ +local log = require("neotest-java.logger") +local lsp = require("neotest-java.lsp") +local nio = require("nio") + +local COMPILER = "org.eclipse.jdt.core.compiler.source" +local LOCATION = "org.eclipse.jdt.ls.core.vm.location" + +local function extract_runtime(bufnr) + local uri = vim.uri_from_bufnr(bufnr) + local error, settings, client = lsp.execute_command({ + command = "java.project.getSettings", + arguments = { uri, { COMPILER, LOCATION } }, + }, bufnr) + + if error ~= nil then + return + end + + local config = client.config.settings.java or {} + config = config.configuration or {} + + local runtimes = config.runtimes + local location = vim.env.JAVA_HOME + local compiler = settings[COMPILER] + + -- we can early exit with location here + if settings[LOCATION] then + location = settings[LOCATION] + else + -- go over available runtimes and resolve it + for _, runtime in ipairs(runtimes or {}) do + -- default runtimes get priority + if runtime.default == true then + location = runtime.path + break + end + -- match runtime against compliance version + local match = runtime.name:match(".*-(.*)") + if match and match == compiler then + location = runtime.path + break + end + end + end + + if location and nio.fn.isdirectory(location) == 0 then + log.error(string.format("Invalid java runtime path location %s", location)) + return + end + return location +end + +---@return string | nil +local function get_runtime(opts) + -- todo: this is not robust, there is no way to know where this is triggered from and if the current buffer is actually a 'java' one needs to be changed !!! + local bufnr = nio.api.nvim_get_current_buf() + local runtime = extract_runtime(bufnr) + if runtime and #runtime > 0 then + return runtime + end + log.error("Unable to extract project runtime") + return nil +end + +return get_runtime diff --git a/lua/neotest-java/lsp/init.lua b/lua/neotest-java/lsp/init.lua new file mode 100644 index 0000000..b8e1a7e --- /dev/null +++ b/lua/neotest-java/lsp/init.lua @@ -0,0 +1,76 @@ +local log = require("neotest-java.logger") +local nio = require("nio") + +-- table that holds the language server settings, this is mostly done for interfacing with coc.nvim, since native neovim lsp clients hold their settings in the clients table +local SETTINGS = {} + +local function execute_command(command, bufnr) + if vim.g.did_coc_loaded ~= nil then + -- cache the settings in case we use coc, since there is no way to obtain the client's settings + -- directly from the coc.nvim api, unlike with native lsp + SETTINGS = nio.fn["coc#util#get_config"]("java") + else + -- native lsp settings are contained in the client table, we are going to use those when executing + -- the native client request sync call. + SETTINGS = {} + end + + if vim.g.did_coc_loaded ~= nil then + if not command.arguments then + command.arguments = {} + end + if type(command.arguments) ~= "table" then + command.arguments = { command.arguments } + end + local ok, result = pcall(nio.fn.CocAction, "runCommand", command.command, unpack(command.arguments)) + if not ok or not result then + log.warn( + string.format( + "Unable to run lsp %s command with payload %s", + command.command, + vim.inspect(command.arguments) + ) + ) + end + local error = not ok and result ~= vim.NIL and { message = result } or nil + return error, + result, + { + -- adapter for the native lsp client talbe format, to simplify external clients using this interface to talk to the lsp client, which ever it happens to be + name = "jdtls", + config = { + settings = { + java = SETTINGS, + }, + }, + } + else + local clients = {} + for _, c in pairs(vim.lsp.get_clients({ bufnr = bufnr }) or {}) do + local command_provider = c.server_capabilities.executeCommandProvider + local commands = type(command_provider) == "table" and command_provider.commands or {} + if vim.tbl_contains(commands, command.command) then + table.insert(clients, c) + end + end + if vim.tbl_count(clients) == 0 then + log.warn(string.format("Unable to find lsp client that supports %s", command.command)) + else + local response, error = clients[1].request_sync("workspace/executeCommand", command) + if error then + log.warn( + string.format( + "Unable to run lsp %s command with payload %s", + command.command, + vim.sinepct(command.arguments) + ) + ) + end + return error, response.result, clients[1] + end + end +end + +return { + execute_command = execute_command, +}