Neovim 0.12无插件配置jdtls时出现语义令牌nil值错误
Neovim 0.12无插件配置jdtls时出现语义令牌nil值错误
我明白你在尝试不依赖nvim-jdtls插件,直接在Neovim 0.12-dev中配置jdtls时遇到了这个语义令牌相关的nil错误——虽然checkhealth显示LSP已连接运行,但实际打开Java项目时还是触发了LSP客户端的内部错误。下面我们一步步排查和解决这个问题:
错误原因分析
这个错误attempt to index local 'request' (a nil value)出现在Neovim的语义令牌处理逻辑中,通常是因为两个核心问题:
- 你的jdtls版本过旧,与Neovim 0.12新增的LSP语义令牌API不兼容
- 手动配置的LSP客户端缺少必要的能力声明或初始化参数,导致语义令牌请求的构建过程出现异常
分步解决方法
1. 先规避语义令牌错误(快速验证)
我们可以先暂时禁用Neovim的语义令牌支持,确认是否能正常启动jdtls并使用基础功能:
-- 在你的FileType autocmd回调中,添加以下内容 local capabilities = vim.lsp.protocol.make_client_capabilities() -- 显式禁用语义令牌支持,规避nil错误 capabilities.textDocument.semanticTokens = nil -- 或者直接全局禁用语义令牌渲染 vim.g.semantic_tokens_enabled = false
2. 修复配置中的路径和根目录问题
你的脚本中存在两处workspace_dir重复定义,且路径拼接缺少分隔符,可能导致jdtls无法正确创建工作区,间接引发LSP请求异常:
-- 修复workspace路径拼接(添加斜杠避免路径混乱) local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t") local workspace_dir = home .. "/jdtls_index/" .. project_name -- 这里的斜杠很重要 -- 完善项目根目录查找逻辑,同时支持文件和目录类型的根标记 local function find_root(path) -- 先找文件类型的根标记(如mvnw、pom.xml) local root_file = vim.fs.find(root_files, { upward = true, path = path, type = "file" })[1] if root_file then return vim.fs.dirname(root_file) end -- 再找目录类型的根标记(如.git) local root_dir = vim.fs.find(root_files, { upward = true, path = path, type = "directory" })[1] if root_dir then return root_dir end -- 都找不到则用当前目录 return vim.fn.getcwd() end
3. 升级jdtls到兼容版本
Neovim 0.12的LSP客户端对语义令牌的支持基于较新的LSP 3.16+规范,如果你使用的jdtls版本过旧(比如你当前的org.eclipse.equinox.launcher是2021年的版本),很可能会出现协议不兼容问题。建议:
- 下载最新稳定版的jdtls替换现有安装
- 确保jdtls的
config_linux目录与jar文件匹配(从同一份下载包中提取)
4. 完善LSP启动配置
修改vim.lsp.start的调用,补充必要的初始化参数和能力声明:
vim.lsp.start(vim.lsp.config('jdtls', { cmd = cmd1, root_dir = root_dir, capabilities = capabilities, -- 用之前定义的capabilities init_options = { -- 可选:指定JAVA_HOME,帮助jdtls定位JDK java.home = os.getenv("JAVA_HOME"), workspace = workspace_dir, -- 可选:启用jdtls的额外功能(如代码补全、重构) extendedClientCapabilities = { classFileContentsSupport = true, generateToStringPromptSupport = true } } }))
完整修复后的配置脚本
vim.lsp.enable("jdtls") -- 无插件启动jdtls的完整配置 vim.api.nvim_create_autocmd("FileType", { pattern = "java", callback = function() -- 暂时禁用语义令牌规避错误 vim.g.semantic_tokens_enabled = false local home = os.getenv("HOME") local jdtls_root = "/home/pp/lsp" local jar_file = "/home/pp/lsp/plugins/org.eclipse.equinox.launcher_1.6.400.v20210924-0641.jar" -- 每个项目独立的工作区目录 local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t") local workspace_dir = home .. "/jdtls_index/" .. project_name -- 项目根目录查找规则 local root_files = { "mvnw", "gradlew", "pom.xml", "build.gradle", ".git" } local function find_root(path) local root_file = vim.fs.find(root_files, { upward = true, path = path, type = "file" })[1] if root_file then return vim.fs.dirname(root_file) end local root_dir = vim.fs.find(root_files, { upward = true, path = path, type = "directory" })[1] if root_dir then return root_dir end return vim.fn.getcwd() end local root_dir = find_root(vim.api.nvim_buf_get_name(0)) -- jdtls启动命令 local cmd1 = { "java", "-Declipse.application=org.eclipse.jdt.ls.core.id1", "-Dosgi.bundles.defaultStartLevel=4", "-Declipse.product=org.eclipse.jdt.ls.core.product", "-Dlog.level=ALL", "-Xms1g", "--add-modules=ALL-SYSTEM", "--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "-jar", jar_file, "-configuration", jdtls_root .. "/config_linux", "-data", workspace_dir, } -- 配置LSP客户端能力 local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.semanticTokens = nil -- 启动jdtls vim.lsp.start(vim.lsp.config('jdtls', { cmd = cmd1, root_dir = root_dir, capabilities = capabilities, init_options = { java.home = os.getenv("JAVA_HOME"), workspace = workspace_dir, extendedClientCapabilities = { classFileContentsSupport = true } } })) end, })
后续验证
- 按照上述修改保存配置后,重启Neovim打开Java文件,观察是否还会触发语义令牌错误
- 如果基础LSP功能(如跳转、诊断)正常工作,再尝试升级jdtls到最新版,然后逐步启用语义令牌功能
- 若问题依然存在,可开启Neovim的LSP日志排查细节:
日志文件路径:-- 在init.lua中添加 vim.lsp.set_log_level("debug")~/.local/state/nvim/lsp.log,搜索semantic_tokens关键词查看具体请求异常信息




