Closed fengwk closed 2 years ago
Could you explain what problem this fixes?
If it is that you're having multiple jdtls clients because you've multiple projects open at the same time, you could add a { bufnr = bufnr }
filter to get_active_clients
call instead.
I use workspaces.nvim to record the paths of different projects and open them. There are times when multiple java projects are opened, which can cause multiple jdtls servers to start.
When I used the super implementation
, I found that the later opened project used the wrong jdtls client (It belongs to the previous project, and it doesn't work correctly).
I'm not familiar with the api, but I happened to find vim.lsp.buf_get_clients(), and the api worked as I expected.
Here is my configuration, is there another way to solve this problem?
-- 每个java缓冲区需要执行setup函数,首次执行将启动lsp并attach,后续仅attach
local jdtls = require "jdtls"
local utils = require "user.utils"
local lsp_utils = require "user.ide.lsp-utils"
local java_home_preset = {
java_home_5 = os.getenv("JAVA_HOME_5"),
java_home_6 = os.getenv("JAVA_HOME_6"),
java_home_7 = os.getenv("JAVA_HOME_7"),
java_home_8 = os.getenv("JAVA_HOME_8"),
java_home_9 = os.getenv("JAVA_HOME_9"),
java_home_10 = os.getenv("JAVA_HOME_10"),
java_home_11 = os.getenv("JAVA_HOME_11"),
java_home_12 = os.getenv("JAVA_HOME_12"),
java_home_13 = os.getenv("JAVA_HOME_13"),
java_home_14 = os.getenv("JAVA_HOME_14"),
java_home_15 = os.getenv("JAVA_HOME_15"),
java_home_16 = os.getenv("JAVA_HOME_16"),
java_home_17 = os.getenv("JAVA_HOME_17"),
java_home_18 = os.getenv("JAVA_HOME_18"),
}
local stdpath_config = vim.fn.stdpath("config")
local stdpath_data = vim.fn.stdpath("data")
local stdpath_cache = vim.fn.stdpath("cache")
local java_home_17 = java_home_preset.java_home_17
local cp_sp = utils.os_name == "win" and ";" or ":"
local cp = "." .. cp_sp .. utils.fs_concat({ java_home_17, "lib", "dt.jar" }) .. cp_sp .. utils.fs_concat({ java_home_17, "lib", "tools.jar" })
local java = utils.fs_concat({ java_home_17, "bin", "java" })
local jdtls_home = utils.fs_concat({ stdpath_data, "mason", "packages", "jdtls" })
local lombok_jar = utils.fs_concat({ jdtls_home, "lombok.jar" })
local launcher_jar = vim.fn.glob(utils.fs_concat({ jdtls_home, "plugins", "org.eclipse.equinox.launcher_*.jar" }))
local config_dir = utils.fs_concat({ jdtls_home, (utils.os_name == "win" and "config_win" or "config_linux") })
local runtimes_preset = {
{
name = "J2SE-1.5",
path = java_home_preset.java_home_5,
},
{
name = "JavaSE-1.6",
path = java_home_preset.java_home_6,
sources = utils.fs_concat({ java_home_preset.java_home_6, "src.zip" }),
javadoc = "https://docs.oracle.com/javase/6/docs/api",
},
{
name = "JavaSE-1.7",
path = java_home_preset.java_home_7,
sources = utils.fs_concat({ java_home_preset.java_home_7, "src.zip" }),
javadoc = "https://docs.oracle.com/javase/7/docs/api",
},
{
name = "JavaSE-1.8",
path = java_home_preset.java_home_8,
sources = utils.fs_concat({ java_home_preset.java_home_8, "src.zip" }),
javadoc = "https://docs.oracle.com/javase/8/docs/api",
default = true,
},
{
name = "JavaSE-9",
path = java_home_preset.java_home_9,
sources = utils.fs_concat({ java_home_preset.java_home_9, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/9/docs/api",
},
{
name = "JavaSE-10",
path = java_home_preset.java_home_10,
sources = utils.fs_concat({ java_home_preset.java_home_10, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/10/docs/api",
},
{
name = "JavaSE-11",
path = java_home_preset.java_home_11,
sources = utils.fs_concat({ java_home_preset.java_home_11, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/11/docs/api",
},
{
name = "JavaSE-12",
path = java_home_preset.java_home_12,
sources = utils.fs_concat({ java_home_preset.java_home_12, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/12/docs/api",
},
{
name = "JavaSE-13",
path = java_home_preset.java_home_13,
sources = utils.fs_concat({ java_home_preset.java_home_13, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/13/docs/api",
},
{
name = "JavaSE-14",
path = java_home_preset.java_home_14,
sources = utils.fs_concat({ java_home_preset.java_home_14, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/14/docs/api",
},
{
name = "JavaSE-15",
path = java_home_preset.java_home_15,
sources = utils.fs_concat({ java_home_preset.java_home_15, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/15/docs/api",
},
{
name = "JavaSE-16",
path = java_home_preset.java_home_16,
sources = utils.fs_concat({ java_home_preset.java_home_16, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/16/docs/api",
},
{
name = "JavaSE-17",
path = java_home_preset.java_home_17,
sources = utils.fs_concat({ java_home_preset.java_home_17, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/17/docs/api",
},
{
name = "JavaSE-18",
path = java_home_preset.java_home_18,
sources = utils.fs_concat({ java_home_preset.java_home_18, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/18/docs/api",
},
{
name = "JavaSE-19",
path = java_home_preset.java_home_19,
sources = utils.fs_concat({ java_home_preset.java_home_19, "lib", "src.zip" }),
javadoc = "https://docs.oracle.com/javase/19/docs/api",
},
}
local function build_runtimes()
local runtimes = {}
for _, item in pairs(runtimes_preset) do
if item.path ~= nil then
table.insert(runtimes, item)
end
end
return runtimes
end
local M = {}
M.setup = function()
-- 获取工作目录
local root_dir = utils.find_root_dir({
'build.xml', -- Ant
"mvnw", -- Maven
'pom.xml', -- Maven
'settings.gradle', -- Gradle
'settings.gradle.kts', -- Gradle
"gradlew", -- Gradle
})
local is_single_file = root_dir == nil
local workspace_dir = root_dir or vim.fn.expand("%:p")
-- 转义工作目录作为名称
local workspace_name = string.gsub(workspace_dir, utils.fs_separator, "__")
if utils.os_name == "win" then
workspace_name = string.gsub(workspace_name, ":", "++")
end
-- 数据目录
local data_dir = utils.fs_concat({ stdpath_cache, "lsp", "jdtls", workspace_name })
-- jdtls配置
local config = {}
-- jdtls启动命令
config.cmd = {
java,
"-cp", cp,
"-Declipse.application=org.eclipse.jdt.ls.core.id1",
"-Dosgi.bundles.defaultStartLevel=4",
"-Declipse.product=org.eclipse.jdt.ls.core.product",
"-Dlog.protocol=true",
"-Dlog.level=ALL",
"-Xmx4G",
"-javaagent:" .. lombok_jar,
"-jar", launcher_jar,
"-configuration", config_dir,
"-data", data_dir,
"--add-modules=ALL-SYSTEM",
"--add-opens java.base/java.util=ALL-UNNAMED",
"--add-opens java.base/java.lang=ALL-UNNAMED",
}
-- Use an on_attach function to only map the following keys
-- after the language server attaches to the current buffer
config.on_attach = function(client, bufnr)
lsp_utils.on_attach(client, bufnr)
-- jdtls特性
jdtls.setup_dap({ hotcodereplace = "auto" })
jdtls.setup.add_commands()
-- https://github.com/mfussenegger/nvim-jdtls#nvim-dap-configuration
-- 注册用于调试的主类,如果是新增的main方法需要使用:JdtRefreshDebugConfigs命令刷新
if not is_single_file then
require("jdtls.dap").setup_dap_main_class_configs()
end
-- 注册调试命令
vim.cmd([[
command! JdtTestClass lua require'jdtls'.test_class()
command! JdtTestMethod lua require'jdtls'.test_nearest_method()
command! JdtRemoteDebug lua require'user.ide.jdtls.command'.remote_debug_by_input()
command! JdtDebug lua require'user.ide.jdtls.command'.debug()
command! JdtA lua require'user.ide.jdtls.command'.test()
]])
-- 设置jdt的扩展快捷键
vim.keymap.set("n", "gp", "<Cmd>lua require'jdtls'.super_implementation()<CR>", { noremap = true, silent = true, buffer = bufnr, desc = "Lsp Super Implementation" })
end
config.capabilities = lsp_utils.make_capabilities()
config.root_dir = workspace_dir
config.settings = {
java = {
signatureHelp = { enabled = true },
contentProvider = { preferred = "fernflower" },
completion = {
-- 这些包使用静态成员
favoriteStaticMembers = {
"org.hamcrest.MatcherAssert.assertThat",
"org.hamcrest.Matchers.*",
"org.hamcrest.CoreMatchers.*",
"org.junit.jupiter.api.Assertions.*",
"java.util.Objects.requireNonNull",
"java.util.Objects.requireNonNullElse",
"org.mockito.Mockito.*"
}
},
sources = {
-- 不在import中使用*
organizeImports = {
starThreshold = 999,
staticStarThreshold = 999,
},
},
configuration = {
-- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
-- And search for `interface RuntimeOption`
-- The `name` is NOT arbitrary, but must match one of the elements from `enum ExecutionEnvironment` in the link above
runtimes = build_runtimes(),
},
},
}
-- See https://github.com/mfussenegger/nvim-jdtls#java-debug-installation
-- debug插件
local bundles = {
vim.fn.glob(utils.fs_concat({ stdpath_config, "lua", "user", "ide", "jdtls", "plugins", "java-debug", "com.microsoft.java.debug.plugin-*.jar"})),
}
-- 单元测试插件
vim.list_extend(bundles, vim.split(vim.fn.glob(utils.fs_concat({ stdpath_config, "lua", "user", "ide", "jdtls", "plugins", "vscode-java-test", "*.jar" })), "\n"))
local extendedClientCapabilities = jdtls.extendedClientCapabilities
extendedClientCapabilities.resolveAdditionalTextEditsSupport = true
config.init_options = {
bundles = bundles,
extendedClientCapabilities = extendedClientCapabilities,
}
-- This starts a new client & server,
-- or attaches to an existing client & server depending on the `root_dir`.
require("jdtls").start_or_attach(config)
end
return M
Based on your description, I checked the api again, should I use vim.lsp.buf_get_clients({bufnr=0})
instead of vim.lsp.buf_get_clients()
?
get_active_clients({filter}) *vim.lsp.get_active_clients()*
Get active clients.
Parameters:
{filter} (table|nil) A table with key-value pairs used to filter the
returned clients. The available keys are:
• id (number): Only return clients with the given id
• bufnr (number): Only return clients attached to this
buffer
• name (string): Only return clients with the given name
Return:
(table) List of |vim.lsp.client| objects
Based on your description, I checked the api again, should I use
vim.lsp.buf_get_clients({bufnr=0})
instead ofvim.lsp.buf_get_clients()
?
Yes I think something like this should also work as fix:
diff --git a/lua/jdtls.lua b/lua/jdtls.lua
index 97c2328..6323b90 100644
--- a/lua/jdtls.lua
+++ b/lua/jdtls.lua
@@ -36,11 +36,11 @@ function M.start_or_attach(config)
end
local request = function(bufnr, method, params, handler)
local client = nil
- for _, c in pairs(vim.lsp.get_active_clients()) do
+ for _, c in pairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do
if c.name == 'jdtls' then
client = c
break
end
end
Yes, I modified the commit and it works fine.
Thanks, merged.
support multi clients