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

Applying code completion within lambda does not work in all cases #427

Closed juhaku closed 1 year ago

juhaku commented 1 year ago

LSP client configuration

local bundles = {
    vim.fn.glob(
        home
            .. "/.config/nvim/lib/java-debug/com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin-*.jar"
    ),
}
vim.list_extend(bundles, vim.split(vim.fn.glob(home .. "/.config/nvim/lib/vscode-java-test/server/*.jar"), "\n"))

local jdtls = require("jdtls")

local extendedClientCapabilities = jdtls.extendedClientCapabilities
extendedClientCapabilities.resolveAdditionalTextEditsSupport = true

-- use 2 tabs for Java
local options = vim.opt

options.tabstop = 2
options.shiftwidth = 2
options.softtabstop = 2

local function get_jdks()
    local jdk_paths = vim.fn.systemlist("fd java " .. java_location .. " -d 1")
    local jdks = {}

    for _, path in ipairs(jdk_paths) do
        local version = string.gsub(path, "[a-zA-Z/-]*", "")
        if tonumber(version) < 9 and tonumber(version) > 5 then
            version = "1." .. version
        end
        table.insert(jdks, {
            name = "JavaSE-" .. version,
            version = version,
            path = path,
        })
    end

    table.sort(jdks, function(a, b)
        return a.name < b.name
    end)

    return jdks
end

local function get_runtimes()
    local javas = get_jdks()

    local jdks = {}
    for _, jdk in ipairs(javas) do
        table.insert(jdks, {
            name = jdk.name,
            path = jdk.path,
        })
    end

    return jdks
end

local function find_latest_java()
    local jdks = get_jdks()
    return jdks[#jdks].path
end

local config = {
    cmd = {
        -- use java 17 or never to run
        -- "/usr/lib/jvm/java-18-openjdk/bin/java",
        find_latest_java() .. "/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",
        "-Xms4G",
        "-javaagent:" .. home .. "/.config/nvim/lib/lombok.jar",
        "--add-modules=ALL-SYSTEM",
        "--add-opens",
        "java.base/java.util=ALL-UNNAMED",
        "--add-opens",
        "java.base/java.lang=ALL-UNNAMED",

        "-jar",
        vim.fn.glob(jdtls_path .. "/plugins/" .. jdtls_launcher),

        "-configuration",
        jdtls_path .. "/config_linux",
        "-data",
        workspace_dir,
    },

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

    on_attach = function(client, bufnr)
        require("jdtls").setup_dap({ hotcodereplace = "auto" })
        require("jdtls.dap").setup_dap_main_class_configs()
        require("jdtls.setup").add_commands()
        -- local dap = require("dap")
        -- -- add java debug attach config
        -- vim.fn.list_extend(dap.configurations.java, {
        --  {
        --      type = "java",
        --      request = "attach",
        --      name = "Debug (Attach) - Remote",
        --      hostName = "127.0.0.1",
        --      port = 5005,
        --  },
        -- })

        lsp_config.on_attach(client, bufnr)
    end,

    capabilities = lsp_config.capabilities,

    handlers = lsp_config.handlers,
    -- Here you can configure eclipse.jdt.ls specific settings
    -- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
    -- for a list of options
    settings = {
        java = {
            import = {
                saveActions = {
                    organizeImports = true,
                },
            },
            maven = {
                downloadSources = true,
            },
            signatureHelp = {
                enabled = true,
            },
            configuration = {
                runtimes = get_runtimes(),
            },
            completion = {
                matchCase = "off",
                maxResults = 999,
            }
        },
    },

    init_options = {
        bundles = bundles,
        extendedClientCapabilities = extendedClientCapabilities,
    },
}
-- This starts a new client & server,
-- or attaches to an existing client & server depending on the `root_dir`.
jdtls.start_or_attach(config)

-- lsp config on_attach()
local on_attach = function(client, bufnr)
    if client.server_capabilities.documentSymbolProvider then
        navic.attach(client, bufnr)
    end

    require("illuminate").on_attach(client)

    -- Enable completion triggered by <c-x><c-o>
    vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")

    -- Mappings.
    -- See `:help vim.lsp.*` for documentation on any of the below functions
    local bufopts = { noremap = true, silent = true, buffer = bufnr }
    vim.keymap.set("n", "gD", vim.lsp.buf.declaration, bufopts)
    vim.keymap.set("n", "gd", vim.lsp.buf.definition, bufopts)
    vim.keymap.set("n", "K", vim.lsp.buf.hover, bufopts)
    vim.keymap.set("n", "gi", vim.lsp.buf.implementation, bufopts)
    vim.keymap.set({ "n", "i" }, "<C-k>", vim.lsp.buf.signature_help, bufopts)
    vim.keymap.set("n", "<leader>wa", vim.lsp.buf.add_workspace_folder, bufopts)
    vim.keymap.set("n", "<leader>wr", vim.lsp.buf.remove_workspace_folder, bufopts)
    vim.keymap.set("n", "<leader>wl", function()
        print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
    end, bufopts)
    vim.keymap.set("n", "td", vim.lsp.buf.type_definition, bufopts)
    vim.keymap.set("n", "gs", vim.lsp.buf.document_symbol, bufopts)
    vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, bufopts)
    vim.keymap.set("n", "<A-CR>", vim.lsp.buf.code_action, bufopts)
    vim.keymap.set("v", "<A-CR>", vim.lsp.buf.code_action, bufopts)
    vim.keymap.set("n", "gr", vim.lsp.buf.references, bufopts)

    vim.keymap.set("n", "<leader>f", function()
        vim.lsp.buf.format({
            filter = function(c)
                return c.name ~= "tsserver" or c.name ~= "gopls" or c.name ~= "lua_ls" or c.name ~= "eslint"
            end,
            async = true,
        })
    end, bufopts)
end

Eclipse.jdt.ls version

1.19.0 / 1.20.0

Steps to Reproduce

Open a code base and try to apply completion within a lambda body as seen in the picture. The completion does not show up in cases where the lambda body is an method argument which expects a lambda body. This seems to be an issue with lambdas that are returning something. Consumers does seem to work since they will match the method argument signature immediately. But the Suppliers break for some reason. Perhaps because they do not match to the method signature. image

If the lambda was to be extracted from the method argument to a separate method or a local variable, the auto completion starts working again as normally.

Expected Result

The completion should open as in the picture: image image

Actual Result

Completion does not open: image

mfussenegger commented 1 year ago

Thanks for the detailed report but this is not a client issue but a problem with the language server, or in this case probably even with eclipse.jdt.core. You'll have to report this upstream

juhaku commented 1 year ago

Yeah, this seems to have been an issue for a long time in eclipse jdt language server based on these threads: https://github.com/redhat-developer/vscode-java/issues/2324 https://github.com/eclipse/eclipse.jdt.ls/issues/2237 :facepalm: Just when I thought I was able to rely on neovim to write some Java, but it seems that I have to fall back to IntelliJ.