mfussenegger / nvim-jdtls

Extensions for the built-in LSP support in Neovim for eclipse.jdt.ls
GNU General Public License v3.0
1.09k stars 62 forks source link

LSP not starting up in new workspace directory when root is changed #417

Closed sgoudham closed 1 year ago

sgoudham commented 1 year ago

LSP client configuration

local HOME = os.getenv("HOME")
local SDKMAN_DIR = os.getenv("SDKMAN_DIR")
local lsp = require("lsp")
local jdtls = require("jdtls")

local root_markers = { "gradlew", "mvnw", ".git", "pom.xml", "build.gradle" }
local root_dir = require("jdtls.setup").find_root(root_markers)

-- eclipse.jdt.ls stores project specific data within a folder. If you are working
-- with multiple different projects, each project must use a dedicated data directory.
-- This variable is used to configure eclipse to use the directory name of the
-- current project found using the root_marker as the folder for project specific data.
local workspace_folder = HOME .. "/.local/share/eclipse/" .. vim.fn.fnamemodify(root_dir, ":p:h:t")

local bundles = {}
local mason_path = vim.fn.glob(vim.fn.stdpath("data") .. "/mason/")
vim.list_extend(bundles, vim.split(vim.fn.glob(mason_path .. "packages/java-test/extension/server/*.jar"), "\n"))
vim.list_extend(
  bundles,
  vim.split(
    vim.fn.glob(mason_path .. "packages/java-debug-adapter/extension/server/com.microsoft.java.debug.plugin-*.jar"),
    "\n"
  )
)

local function nnoremap(rhs, lhs, bufopts, desc)
  bufopts.desc = desc
  vim.keymap.set("n", rhs, lhs, bufopts)
end

local on_attach = function(client, bufnr)
  require("jdtls").setup_dap({ hotcodereplace = "auto" })
  require("jdtls.dap").setup_dap_main_class_configs()
  require("jdtls.setup").add_commands()
  lsp.default_config.on_attach(client, bufnr)

  -- Regular Neovim LSP client keymappings
  local bufopts = { noremap = true, silent = true, buffer = bufnr }
  nnoremap("<leader>lwa", vim.lsp.buf.add_workspace_folder, bufopts, "Add workspace folder")
  nnoremap("<leader>lwr", vim.lsp.buf.remove_workspace_folder, bufopts, "Remove workspace folder")
  nnoremap("<leader>lwl", function()
    print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
  end, bufopts, "List workspace folders")

  -- Java extensions provided by jdtls
  nnoremap("<leader>lo", jdtls.organize_imports, bufopts, "Organize imports")
  nnoremap("<leader>le", jdtls.extract_variable, bufopts, "Extract variable")
  nnoremap("<leader>lc", jdtls.extract_constant, bufopts, "Extract constant")
  vim.keymap.set(
    "v",
    "<leader>lm",
    [[<ESC><CMD>lua require('jdtls').extract_method(true)<CR>]],
    { noremap = true, silent = true, buffer = bufnr, desc = "Extract method" }
  )
  nnoremap("<leader>tc", jdtls.test_class, bufopts, "Test class (DAP)")
  nnoremap("<leader>tm", jdtls.test_nearest_method, bufopts, "Test method (DAP)")
end

local config = {
  flags = {
    debounce_text_changes = 80,
    allow_incremental_sync = true,
  },
  on_attach = on_attach,
  root_dir = root_dir,
  init_options = {
    bundles = bundles,
  },

  -- https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
  settings = {
    java = {
      format = {
        settings = {
          -- https://github.com/google/styleguide/blob/gh-pages/eclipse-java-google-style.xml
          url = "/.local/share/eclipse/eclipse-java-google-style.xml",
          profile = "GoogleStyle",
        },
      },
      contentProvider = { preferred = "fernflower" }, -- Use fernflower to decompile library code
      -- Specify any completion options
      completion = {
        maxResults = 30,
        postfix = { enabled = true },
        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.*",
        },
        filteredTypes = {
          "com.sun.*",
          "io.micrometer.shaded.*",
          "java.awt.*",
          "jdk.*",
          "sun.*",
        },
      },
      -- LSP Related
      implementationsCodeLens = { enabled = true },
      referenceCodeLens = { enabled = true },
      signatureHelp = { enabled = true },
      inlayHints = {
        parameterNames = { enabled = true },
      },
      -- Specify any options for organizing imports
      sources = {
        organizeImports = {
          starThreshold = 9999,
          staticStarThreshold = 9999,
        },
      },
      maven = {
        downloadSources = true,
        updateSnapshots = true,
      },
      -- On Save Cleanup
      cleanup = {
        actionsOnSave = {
          "addOverride",
        },
      },
      -- How code generation should act
      codeGeneration = {
        toString = {
          template = "${object.className}{${member.name()}=${member.value}, ${otherMembers}}",
        },
        hashCodeEquals = {
          useJava7Objects = true,
        },
        useBlocks = true,
      },
      -- https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
      -- `interface RuntimeOption`
      configuration = {
        maven = {
          userSettings = HOME .. "/.m2/settings.xml",
        },
        runtimes = {
          {
            name = "JavaSE-17",
            path = SDKMAN_DIR .. "/candidates/java/17.0.5-amzn",
          },
          {
            name = "JavaSE-11",
            path = SDKMAN_DIR .. "/candidates/java/11.0.18-amzn",
          },
          {
            name = "JavaSE-1.8",
            path = SDKMAN_DIR .. "/candidates/java/8.0.362-amzn",
          },
        },
      },
    },
  },
  -- Note that eclipse.jdt.ls must be started with a Java version of 17 or higher
  -- See: https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line
  cmd = {
    SDKMAN_DIR .. "/candidates/java/17.0.5-amzn/bin/java",
    "-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",
    "--add-modules=ALL-SYSTEM",
    "--add-opens",
    "java.base/java.util=ALL-UNNAMED",
    "--add-opens",
    "java.base/java.lang=ALL-UNNAMED",
    -- If you use lombok, download the lombok jar and place it in ~/.local/share/eclipse
    -- "-javaagent:"
    --   .. HOME
    --   .. "/.local/share/eclipse/lombok.jar",

    "-jar",
    vim.fn.glob(HOME .. "/.local/share/eclipse.jdt.ls/plugins/org.eclipse.equinox.launcher_*.jar"),

    "-configuration",
    HOME .. "/.local/share/eclipse.jdt.ls/config_linux",

    "-data",
    workspace_folder,
  },
}

Eclipse.jdt.ls version

1.10.0

Steps to Reproduce

  1. Open projectA and observe Java LSP starting
  2. cd to projectB from within NeoVim (I am using project.nvim for convenience)
  3. Observe Java LSP starting up in single-file-mode
  4. Run JdtRestart
  5. Observe Java LSP starting up with still projectA workspace directory configured instead of projectB

Expected Result

When switching root directories from within neovim, I would expect the Java LSP to automatically detect and refresh the workspace directory to ensure that the correct workspace directory is passed to the LSP.

Actual Result

lsp.log

[START][2023-02-09 06:31:07] LSP logging initiated [ERROR][2023-02-09 06:31:07] .../vim/lsp/rpc.lua:734 "rpc" "/home/hammy/.sdkman/candidates/java/17.0.5-amzn/bin/java" "stderr" "WARNING: Using incubator modules: jdk.incubator.foreign, jdk.incubator.vector\n" [ERROR][2023-02-09 06:31:08] ...lsp/handlers.lua:513 "9 Feb 2023, 06:31:08 Failed to load extension bundles \nLoad bundle list\norg.eclipse.core.runtime.CoreException: Load bundle list\n\tat org.eclipse.jdt.ls.core.internal.handlers.BundleUtils.loadBundles(BundleUtils.java:169)\n\tat org.eclipse.jdt.ls.core.internal.handlers.InitHandler.handleInitializationOptions(InitHandler.java:92)\n\tat org.eclipse.jdt.ls.core.internal.handlers.BaseInitHandler.initialize(BaseInitHandler.java:64)\n\tat org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.initialize(JDTLanguageServer.java:248)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$null$0(GenericEndpoint.java:65)\n\tat org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.request(GenericEndpoint.java:120)\n\tat org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleRequest(RemoteEndpoint.java:261)\n\tat org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:190)\n\tat org.eclipse.jdt.ls.core.internal.ParentProcessWatcher.lambda$1(ParentProcessWatcher.java:144)\n\tat org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:194)\n\tat org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:94)\n\tat org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)\n\tat java.base/java.lang.Thread.run(Thread.java:833)\nContains: Failed to get bundleInfo for bundle from /home/hammy/.local/share/nvim/mason/packages/java-test/extension/server/com.microsoft.java.test.runner-jar-with-dependencies.jar\n" [WARN][2023-02-09 06:31:09] .../lua/vim/lsp.lua:1072 "server_request: no handler found for" "workspace/inlayHint/refresh" [WARN][2023-02-09 06:31:12] ...lsp/handlers.lua:128 "The language server jdtls triggers a registerCapability handler despite dynamicRegistration set to false. Report upstream, this warning is harmless" [ERROR][2023-02-09 06:31:12] ...lsp/handlers.lua:513 "9 Feb 2023, 06:31:12 Command _java.reloadBundles.command not supported on client" [ERROR][2023-02-09 06:31:33] .../vim/lsp/rpc.lua:734 "rpc" "/home/hammy/.sdkman/candidates/java/17.0.5-amzn/bin/java" "stderr" "WARNING: Using incubator modules: jdk.incubator.vector, jdk.incubator.foreign\n" [ERROR][2023-02-09 06:31:34] ...lsp/handlers.lua:513 "9 Feb 2023, 06:31:34 Failed to load extension bundles \nLoad bundle list\norg.eclipse.core.runtime.CoreException: Load bundle list\n\tat org.eclipse.jdt.ls.core.internal.handlers.BundleUtils.loadBundles(BundleUtils.java:169)\n\tat org.eclipse.jdt.ls.core.internal.handlers.InitHandler.handleInitializationOptions(InitHandler.java:92)\n\tat org.eclipse.jdt.ls.core.internal.handlers.BaseInitHandler.initialize(BaseInitHandler.java:64)\n\tat org.eclipse.jdt.ls.core.internal.handlers.JDTLanguageServer.initialize(JDTLanguageServer.java:248)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n\tat java.base/java.lang.reflect.Method.invoke(Method.java:568)\n\tat org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$null$0(GenericEndpoint.java:65)\n\tat org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.request(GenericEndpoint.java:120)\n\tat org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleRequest(RemoteEndpoint.java:261)\n\tat org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:190)\n\tat org.eclipse.jdt.ls.core.internal.ParentProcessWatcher.lambda$1(ParentProcessWatcher.java:144)\n\tat org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:194)\n\tat org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:94)\n\tat org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113)\n\tat java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)\n\tat java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)\n\tat java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)\n\tat java.base/java.lang.Thread.run(Thread.java:833)\nContains: Failed to get bundleInfo for bundle from /home/hammy/.local/share/nvim/mason/packages/java-test/extension/server/com.microsoft.java.test.runner-jar-with-dependencies.jar\n" [WARN][2023-02-09 06:31:35] .../lua/vim/lsp.lua:1072 "server_request: no handler found for" "workspace/inlayHint/refresh" [WARN][2023-02-09 06:31:38] ...lsp/handlers.lua:128 "The language server jdtls triggers a registerCapability handler despite dynamicRegistration set to false. Report upstream, this warning is harmless" [ERROR][2023-02-09 06:31:38] ...lsp/handlers.lua:513 "9 Feb 2023, 06:31:38 Command _java.reloadBundles.command not supported on client"

:lua require('jdtls').compile('full')

Compile Successful

mfussenegger commented 1 year ago

You are responsible for setting the root_dir appropriately. That's intentionally left up to the user to give more flexibility.

The find_root function traverses upwards from the current buffer until it finds the markers. Make sure you run that on each buffer you open, not only once.

sgoudham commented 1 year ago

Ah okay that makes sense. Thanks for the speedy response!

Just one clarification: My config that I've listed above is run within the context of ftplugin/java.lua, I was under the impression that the require("jdtls.setup").find_root(root_markers) would get ran on each java buffer? Is that not the case there?

sgoudham commented 1 year ago

Ah nevermind, figured it out! Turns out I wasn't calling the function in the correct place. For any future travellers: https://github.com/sgoudham/dotfiles/blob/83924bfa130fb83da6c7066eb5a58bf2e7ef7bbc/dot_config/nvim/ftplugin/java.lua

Thanks so much again