Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

changeSignature code action throws error after manual input #743

Closed
phernandoe opened this issue Jan 23, 2025 · 2 comments · Fixed by #744
Closed

changeSignature code action throws error after manual input #743

phernandoe opened this issue Jan 23, 2025 · 2 comments · Fixed by #744

Comments

@phernandoe
Copy link

phernandoe commented Jan 23, 2025

LSP client configuration

local java_cmds = vim.api.nvim_create_augroup('java_cmds', { clear = true })
local cache_vars = {}

local root_files = {
    '.git',
    'mvnw',
    'gradlew',
    'pom.xml',
    'build.gradle',
}

local features = {
    -- change this to `true` to enable codelens
    codelens = true,

    -- change this to `true` if you have `nvim-dap`,
    -- `java-test` and `java-debug-adapter` installed
    debugger = false,
}

local function get_jdtls_paths()
    if cache_vars.paths then
        return cache_vars.paths
    end

    local path = {}

    path.data_dir = vim.fn.stdpath('cache') .. '/nvim-jdtls'

    local jdtls_install = require('mason-registry')
        .get_package('jdtls')
        :get_install_path()

    path.java_agent = jdtls_install .. '/lombok.jar'
    path.launcher_jar = vim.fn.glob(jdtls_install .. '/plugins/org.eclipse.equinox.launcher_*.jar')

    if vim.fn.has('mac') == 1 then
        path.platform_config = jdtls_install .. '/config_mac'
    elseif vim.fn.has('unix') == 1 then
        path.platform_config = jdtls_install .. '/config_linux'
    elseif vim.fn.has('win32') == 1 then
        path.platform_config = jdtls_install .. '/config_win'
    end

    path.bundles = {}

    ---
    -- Include java-test bundle if present
    ---
    local java_test_path = require('mason-registry')
        .get_package('java-test')
        :get_install_path()

    local java_test_bundle = vim.split(
        vim.fn.glob(java_test_path .. '/extension/server/*.jar'),
        '\n'
    )

    if java_test_bundle[1] ~= '' then
        vim.list_extend(path.bundles, java_test_bundle)
    end

    ---
    -- Include java-debug-adapter bundle if present
    ---
    local java_debug_path = require('mason-registry')
        .get_package('java-debug-adapter')
        :get_install_path()

    local java_debug_bundle = vim.split(
        vim.fn.glob(java_debug_path .. '/extension/server/com.microsoft.java.debug.plugin-*.jar'),
        '\n'
    )

    if java_debug_bundle[1] ~= '' then
        vim.list_extend(path.bundles, java_debug_bundle)
    end

    ---
    -- Useful if you're starting jdtls with a Java version that's
    -- different from the one the project uses.
    ---
    path.runtimes = {
        -- Note: the field `name` must be a valid `ExecutionEnvironment`,
        -- you can find the list here:
        -- https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
        --
        -- This example assume you are using sdkman: https://sdkman.io
        -- {
        --   name = 'JavaSE-17',
        --   path = vim.fn.expand('~/.sdkman/candidates/java/17.0.6-tem'),
        -- },
        -- {
        --   name = 'JavaSE-18',
        --   path = vim.fn.expand('~/.sdkman/candidates/java/18.0.2-amzn'),
        -- },
    }

    cache_vars.paths = path

    return path
end

local function enable_codelens(bufnr)
    pcall(vim.lsp.codelens.refresh)

    vim.api.nvim_create_autocmd('BufWritePost', {
        buffer = bufnr,
        group = java_cmds,
        desc = 'refresh codelens',
        callback = function()
            pcall(vim.lsp.codelens.refresh)
        end,
    })
end

local function enable_debugger(bufnr)
    require('jdtls').setup_dap({ hotcodereplace = 'auto' })
    require('jdtls.dap').setup_dap_main_class_configs()

    local opts = { buffer = bufnr }
    vim.keymap.set('n', '<leader>df', "<cmd>lua require('jdtls').test_class()<cr>", opts)
    vim.keymap.set('n', '<leader>dn', "<cmd>lua require('jdtls').test_nearest_method()<cr>", opts)
end

local function jdtls_on_attach(client, bufnr)
    if features.debugger then
        enable_debugger(bufnr)
    end

    if features.codelens then
        enable_codelens(bufnr)
    end

    -- The following mappings are based on the suggested usage of nvim-jdtls
    -- https://github.com/mfussenegger/nvim-jdtls#usage

    local opts = { buffer = bufnr }
    vim.keymap.set('n', '<leader>o', "<cmd>lua require('jdtls').organize_imports()<cr>", opts)
    vim.keymap.set('n', 'crv', "<cmd>lua require('jdtls').extract_variable()<cr>", opts)
    vim.keymap.set('x', 'crv', "<esc><cmd>lua require('jdtls').extract_variable(true)<cr>", opts)
    vim.keymap.set('n', 'crc', "<cmd>lua require('jdtls').extract_constant()<cr>", opts)
    vim.keymap.set('x', 'crc', "<esc><cmd>lua require('jdtls').extract_constant(true)<cr>", opts)
    vim.keymap.set('x', 'crm', "<esc><Cmd>lua require('jdtls').extract_method(true)<cr>", opts)
    vim.keymap.set('n', '<leader>bt', "<cmd>lua require('jdtls').javap()<cr>", opts)
end

local function jdtls_setup(event)
    local jdtls = require('jdtls')

    local path = get_jdtls_paths()
    local data_dir = path.data_dir .. '/' .. vim.fn.fnamemodify(vim.fn.getcwd(), ':p:h:t')

    if cache_vars.capabilities == nil then
        jdtls.extendedClientCapabilities.resolveAdditionalTextEditsSupport = true

        local ok_cmp, cmp_lsp = pcall(require, 'cmp_nvim_lsp')
        cache_vars.capabilities = vim.tbl_deep_extend(
            'force',
            vim.lsp.protocol.make_client_capabilities(),
            ok_cmp and cmp_lsp.default_capabilities() or {}
        )
    end

    -- The command that starts the language server
    -- See: https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line
    local cmd = {
        -- 💀
        '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',
        '-javaagent:' .. path.java_agent,
        '-Xms1g',
        '--add-modules=ALL-SYSTEM',
        '--add-opens',
        'java.base/java.util=ALL-UNNAMED',
        '--add-opens',
        'java.base/java.lang=ALL-UNNAMED',

        -- 💀
        '-jar',
        path.launcher_jar,

        -- 💀
        '-configuration',
        path.platform_config,

        -- 💀
        '-data',
        data_dir,
    }

    local lsp_settings = {
        java = {
            -- jdt = {
            --   ls = {
            --     vmargs = "-XX:+UseParallelGC -XX:GCTimeRatio=4 -XX:AdaptiveSizePolicyWeight=90 -Dsun.zip.disableMemoryMapping=true -Xmx1G -Xms100m"
            --   }
            -- },
            eclipse = {
                downloadSources = true,
            },
            configuration = {
                updateBuildConfiguration = 'interactive',
                runtimes = path.runtimes,
            },
            maven = {
                downloadSources = true,
            },
            implementationsCodeLens = {
                enabled = true,
            },
            referencesCodeLens = {
                enabled = true,
            },
            -- inlayHints = {
            --   parameterNames = {
            --     enabled = 'all' -- literals, all, none
            --   }
            -- },
            format = {
                enabled = true,
                -- settings = {
                --   profile = 'asdf'
                -- },
            }
        },
        signatureHelp = {
            enabled = true,
        },
        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.*',
            },
        },
        contentProvider = {
            preferred = 'fernflower',
        },
        extendedClientCapabilities = jdtls.extendedClientCapabilities,
        sources = {
            organizeImports = {
                starThreshold = 9999,
                staticStarThreshold = 9999,
            }
        },
        codeGeneration = {
            toString = {
                template = '${object.className}{${member.name()}=${member.value}, ${otherMembers}}',
            },
            useBlocks = true,
        },
    }

    -- This starts a new client & server,
    -- or attaches to an existing client & server depending on the `root_dir`.
    jdtls.start_or_attach({
        cmd = cmd,
        settings = lsp_settings,
        on_attach = jdtls_on_attach,
        capabilities = cache_vars.capabilities,
        root_dir = jdtls.setup.find_root(root_files),
        flags = {
            allow_incremental_sync = true,
        },
        init_options = {
            bundles = path.bundles,
        },
    })
end

vim.api.nvim_create_autocmd('FileType', {
    group = java_cmds,
    pattern = { 'java' },
    desc = 'Setup jdtls',
    callback = jdtls_setup,
})

Eclipse.jdt.ls version

1.43.9

Steps to Reproduce

  1. Create HelloWorld.java
public class HelloWorld {
    public static void foo() {
        System.out.println("Hello, World!");
    }
    public static void main(String[] args) {
        foo();
    }
}
  1. Move cursor to the public static void foo() { line
  2. Run your LSP code action command.
Code actions:                                                                                                                                                                             
1: Change signature for 'foo'                                                                                                                                                             
2: Move 'foo()'...                                                                                                                                                                        
3: Inline Method                                                                                                                                                                          
4: Add Javadoc comment                                                                                                                                                                    
5: Organize imports                                                                                                                                                                       
6: Generate toString()                                                                                                                                                                    
7: Override/Implement Methods...                                                                                                                                                          
8: Change modifiers to final where possible                                                                                                                                               
Type number and <Enter> or click with the mouse (q or empty cancels): 1 
  1. Choose option 1.

I checked out the java_apply_refactoring_command function and printed out the contents of the command argument where I don't see a command.signature, probably leading to the error.

{                                                                                                                                                                                         
  arguments = { "changeSignature", {                                                                                                                                                      
      context = {                                                                                                                                                                         
        diagnostics = {},                                                                                                                                                                 
        triggerKind = 1                                                                                                                                                                   
      },                                                                                                                                                                                  
      range = {                                                                                                                                                                           
        ["end"] = {                                                                                                                                                                       
          character = 23,                                                                                                                                                                 
          line = 1                                                                                                                                                                        
        },                                                                                                                                                                                
        start = {                                                                                                                                                                         
          character = 23,                                                                                                                                                                 
          line = 1                                                                                                                                                                        
        }                                                                                                                                                                                 
      },                                                                                                                                                                                  
      textDocument = {                                                                                                                                                                    
        uri = "file:///[path]/HelloWorld.java"                                                                                                                                
      }                                                                                                                                                                                   
    } },                                                                                                                                                                                  
  command = "java.action.applyRefactoringCommand",                                                                                                                                        
  title = "Change signature for 'foo'"                                                                                                                                                    
}  

Expected Result

Function signature prompt appears.

Actual Result

Error executing vim.schedule lua callback: ...are/nvim/site/pack/packer/start/nvim-jdtls/lua/jdtls.lua:550: attempt to index local 'signature' (a nil value)                              
stack traceback:                                                                                                                                                                          
        ...are/nvim/site/pack/packer/start/nvim-jdtls/lua/jdtls.lua:550: in function 'fn'                                                                                                 
        .../neovim/0.10.3/share/nvim/runtime/lua/vim/lsp/client.lua:878: in function '_exec_cmd'                                                                                          
        ...lar/neovim/0.10.3/share/nvim/runtime/lua/vim/lsp/buf.lua:757: in function 'apply_action'                                                                                       
        ...lar/neovim/0.10.3/share/nvim/runtime/lua/vim/lsp/buf.lua:796: in function 'handler'                                                                                            
        .../neovim/0.10.3/share/nvim/runtime/lua/vim/lsp/client.lua:687: in function ''                                                                                                   
        vim/_editor.lua: in function <vim/_editor.lua:0>  
@phernandoe
Copy link
Author

I stepped through the stack trace and it might be a problem with neovim's LSP, but I'm not sure.

mfussenegger added a commit that referenced this issue Jan 23, 2025
There were changes to no longer provide the `signature` within the
code-action command arguments but instead require an additional request
from the client.

See:
- eclipse-jdtls/eclipse.jdt.ls#3322
- redhat-developer/vscode-java#3845

Closes: #743
@mfussenegger
Copy link
Owner

Thanks for the report. Looks like eclipse.jdt.ls changed how the change-signature command works.

#744 should fix it.

mfussenegger added a commit that referenced this issue Jan 23, 2025
There were changes to no longer provide the `signature` within the
code-action command arguments but instead require an additional request
from the client.

See:
- eclipse-jdtls/eclipse.jdt.ls#3322
- redhat-developer/vscode-java#3845

Closes: #743
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants