Skip to content

Commit

Permalink
Correctly detect the project runtime based on the active language server
Browse files Browse the repository at this point in the history
  • Loading branch information
asmodeus812 committed Jul 2, 2024
1 parent be4de74 commit d04059e
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 13 deletions.
15 changes: 10 additions & 5 deletions lua/neotest-java/build_tool/gradle.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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$"

Expand Down Expand Up @@ -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")
Expand Down
20 changes: 15 additions & 5 deletions lua/neotest-java/build_tool/maven.lua
Original file line number Diff line number Diff line change
@@ -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$"
Expand Down Expand Up @@ -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
Expand All @@ -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"
Expand Down
8 changes: 5 additions & 3 deletions lua/neotest-java/command/binaries.lua
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
65 changes: 65 additions & 0 deletions lua/neotest-java/command/runtime.lua
Original file line number Diff line number Diff line change
@@ -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
76 changes: 76 additions & 0 deletions lua/neotest-java/lsp/init.lua
Original file line number Diff line number Diff line change
@@ -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,
}

0 comments on commit d04059e

Please sign in to comment.