You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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的语义令牌处理逻辑中,通常是因为两个核心问题:

  1. 你的jdtls版本过旧,与Neovim 0.12新增的LSP语义令牌API不兼容
  2. 手动配置的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,
})

后续验证

  1. 按照上述修改保存配置后,重启Neovim打开Java文件,观察是否还会触发语义令牌错误
  2. 如果基础LSP功能(如跳转、诊断)正常工作,再尝试升级jdtls到最新版,然后逐步启用语义令牌功能
  3. 若问题依然存在,可开启Neovim的LSP日志排查细节:
    -- 在init.lua中添加
    vim.lsp.set_log_level("debug")
    
    日志文件路径:~/.local/state/nvim/lsp.log,搜索semantic_tokens关键词查看具体请求异常信息

火山引擎 最新活动