Closed mikehaertl closed 1 year ago
Addition: I'm reporting this here even though it could also be a problem in the LSP implementation of neovim. I just had no idea where to start searching. If it's clearly a problem of neovim I can also report the issue there.
Any pointers or ideas how to further debug this are welcome.
Forgot to add my jdtls config:
-- See `:help vim.lsp.start_client` for an overview of the supported `config` options.
-- nvim data and config path (:h stdpath)
local nvim_data_path = vim.fn.stdpath('data') .. '/'
local nvim_config_path = vim.fn.stdpath('config') .. '/'
-- location where mason installs jdts
local mason_package_path = nvim_data_path .. 'mason/packages/'
local jdtls_path = mason_package_path .. 'jdtls/'
-- root project dir of currently opened file
local project_dir = require('jdtls.setup').find_root({'.git', 'mvnw', 'gradlew'})
-- last part of the project_dir
local project_name = vim.fn.fnamemodify(project_dir, ':p:h:t')
local jdtls = require('jdtls')
local util = require('jdtls.util')
local dap = require('dap')
local dapui = require('dapui')
local widgets = require('dap.ui.widgets')
local whichkey = require('which-key')
local config = {
-- The command that starts the language server
-- See: https://github.com/eclipse/eclipse.jdt.ls#running-from-the-command-line
cmd = {
'/home/mike/.sdkman/candidates/java/17.0.6-tem/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',
'-Xmx1g',
'--add-modules=ALL-SYSTEM',
'--add-opens', 'java.base/java.util=ALL-UNNAMED',
'--add-opens', 'java.base/java.lang=ALL-UNNAMED',
'-javaagent:' .. jdtls_path .. 'lombok.jar',
'-jar', vim.fn.glob(jdtls_path .. 'plugins/org.eclipse.equinox.launcher_*.jar', 1),
'-configuration', (jdtls_path .. 'config_linux'),
-- directory where jdtls stores project related data
'-data', (nvim_data_path .. 'jdtls_data/' .. project_name),
},
-- One dedicated LSP server & client will be started per unique root_dir
root_dir = project_dir,
-- Inject nvim-cmp capabilities, e.g. to support snippets provided by jdtls
capabilities = require('cmp_nvim_lsp').default_capabilities(),
-- 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 = {
autobuild = { enabled = false },
codeGeneration = {
hashCodeEquals = {
useJava7Objects = true,
},
useBlocks = true,
toString = {
template = "${object.className}{${member.name()}=${member.value}, ${otherMembers}}"
},
},
completion = {
-- Static members or types with static members. Content assist will
-- propose those static members even if the import is missing.
favoriteStaticMembers = {
"java.lang.String.*",
"org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestBuilders.*",
"org.springframework.security.test.web.servlet.response.SecurityMockMvcResultHandlers.*",
"org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.*",
"org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.*",
"org.springframework.test.web.client.match.MockRestRequestMatchers.*",
"org.springframework.test.web.client.response.MockRestResponseCreators.*",
"org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*",
"org.springframework.test.web.servlet.result.MockMvcResultMatchers.*",
"org.assertj.core.api.Assertions.*",
"org.awaitility.Awaitility.*",
"org.hamcrest.Matchers.*",
"org.mockito.BDDMockito.*",
"org.mockito.Mockito.*",
"org.mockito.ArgumentMatchers.*",
-- Defaults:
"org.junit.Assert.*",
"org.junit.Assume.*",
"org.junit.jupiter.api.Assertions.*",
"org.junit.jupiter.api.Assumptions.*",
"org.junit.jupiter.api.DynamicContainer.*",
"org.junit.jupiter.api.DynamicTest.*",
},
-- Members or types that should be excluded from completion.
-- Useful for nasty suggestions that are never used and only add noise
-- to suggestions e.g "java.awt.*"
filteredTypes = {
},
},
configuration = {
-- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
-- And search for `interface RuntimeOption`
-- The `name` is NOT arbitrary, but must match one of the elements from `enum ExecutionEnvironment` in the link above
runtimes = {
{
name = "JavaSE-11",
path = "/usr/lib/jvm/java-11-openjdk-amd64/",
},
{
name = "JavaSE-17",
path = "/home/mike/.sdkman/candidates/java/17.0.6-tem/",
},
{
name = "JavaSE-20",
path = "/home/mike/.sdkman/candidates/java/20.0.1-tem/",
},
},
},
contentProvider = {
preferred = 'fernflower'
},
format = {
settings = {
-- Formatter specifications are in an XML format as used by Eclipse [1].
-- Our file is a copy from vscode-java [2] with local modifications.
--
-- [1] https://github.com/redhat-developer/vscode-java/wiki/Formatter-settings
-- [2] https://github.com/redhat-developer/vscode-java/blob/master/formatters/eclipse-formatter.xml
url = nvim_config_path .. 'eclipse-formatter.xml',
profile = 'Eclipse'
},
},
-- Should make gradle use java 11 instead of system default, but has no effect:
-- import = {
-- gradle = {
-- java = {
-- home = '/usr/lib/jvm/java-11-openjdk-amd64/',
-- }
-- }
-- },
signatureHelp = {
description = { enabled = true },
},
sources = {
organizeImports = {
starThreshold = 9999;
staticStarThreshold = 9999;
},
},
},
},
-- Language server `initializationOptions`
-- You need to extend the `bundles` with paths to jar files
-- if you want to use additional eclipse.jdt.ls plugins.
--
-- See https://github.com/mfussenegger/nvim-jdtls#java-debug-installation
init_options = {
bundles = vim.list_extend(
{
-- Required for DAP
vim.fn.glob(mason_package_path .. 'java-debug-adapter/extension/server/com.microsoft.java.debug.plugin-*.jar', 1),
},
-- Required for test_class / test_nearest_method
vim.split(vim.fn.glob(mason_package_path .. 'java-test/extension/server/{junit,org,com.microsoft.java.test.plugin}*.jar', 1), "\n")
)
},
on_attach = function(client, bufnr)
-- With `hotcodereplace = 'auto' the debug adapter will try to apply code changes
-- you make during a debug session immediately.
-- You can use the `JdtHotcodeReplace` command to trigger it manually
jdtls.setup_dap({ hotcodereplace = 'auto' })
jdtls.setup.add_commands()
-- Opens the DAP UI as soon as debugger runs/continues
dap.listeners.after.event_initialized["dapui_config"] = function()
dapui.open()
end
whichkey.register({
['<A-o>'] = { jdtls.organize_imports, 'Java: Organize imports'},
['<F9>'] = { dap.step_over, 'Debugger: step over'},
['<F10>'] = { dap.step_over, 'Debugger: step into'},
['<F12>'] = { dap.step_over, 'Debugger: step out'},
['<space>'] = {
d = {
name = 'debug (DAP)',
a = { '<Cmd>:vsplit | terminal w3m https://docs.oracle.com/javase/8/docs/api/compact3-summary.html<CR>', 'Browse Java 8 API'},
b = { dap.toggle_breakpoint, 'Toggle breakpoint' },
B = { function() dap.set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, 'Set conditional breakpoint' },
c = { dap.continue, 'Continue' },
d = { dap.clear_breakpoints, 'Clear breakpoints' },
e = { jdtls.test_class, 'Debug test class' },
E = { jdtls.test_nearest_method, 'Debug test method' },
f = { function() widgets.centered_float(widgets.frames) end, 'Show frames' },
h = { widgets.hover, 'Show hover', mode = {'n', 'v'} },
i = { function()
require('jdtls.dap').setup_dap_main_class_configs({
verbose = true,
})
end, 'Init autostart main class' },
I = { function()
-- Configures, how to launch a debug adapter for java.
-- We use a callback where we ask the LSP server to start a debug session.
-- The response contains the port that the adapter can connect to,
-- to debug the running application.
dap.adapters.java = function(callback)
util.execute_command(
-- Ask the LSP server to start a debug session
{command = 'vscode.java.startDebugSession'},
-- The response provides the port that the adapter will listen on
function(err0, port)
assert(not err0, vim.inspect(err0))
print("Attaching to debug adapter on port ", port)
callback({
type = 'server';
host = '127.0.0.1';
port = port;
})
end
)
end
-- Configuration for the debug adapter
dap.configurations.java = {
{
type = 'java',
-- attach to a running Java program that was started with --debug-jvm
-- and that is waiting on 5005 for a debug adapter to attach
request = 'attach',
name = "Java attach",
hostName = "127.0.0.1",
port = 5005,
projectName = project_name,
},
}
end, 'Init external (req. --debug-jvm)' },
r = { dap.repl.open, 'Open REPL' },
s = { function() widgets.centered_float(widgets.scopes) end, 'Show scopes' },
t = { dapui.toggle, 'Toggle UI' },
},
},
['<leader>se']= { vim.lsp.buf.format, 'Format code'},
})
end
}
-- This starts a new client & server,
-- or attaches to an existing client & server depending on the `root_dir`.
require('jdtls').start_or_attach(config)
Both neovim core and nvim-jdtls don't cancel any requests automatically.
I suspect it could be the completion plugin. There are some cases where computing the completions can take a while - especially if re-builds are ongoing.
Maybe check if it has some throttle settings.
You could also try setting:
java = {
autobuild = { enabled = false },
...
In the lsp settings. It helps with completion speed but also means that you have to force a build via :JdtCompile before you debug. Otherwise you'd debug an outdated version.
nvim-jdtls btw. takes care of the dap.adapters.java definition. You should be able to remove that from your config
Thanks for the pointers. I did a quick grep for cancelRequest
in my plugin directory and there were no matches in any of the completion related plugins (cmp-nvim-lsp and cmp-nvim-lsp-signature-help are the ones I use).
The only match is in nvim-dap-ui:
nvim-dap-ui/lua/dapui/async/lsp.lua
79: async_request("$/cancelRequest", { requestId = req_id }, bufnr)
But I hardly ever use the debugger. Not sure if it still does something in the background, though. More ideas on what to look for are very welcome.
It helps with completion speed but also means that you have to force a build via :JdtCompile before you debug.
Hmm, I found that even without your suggested setting I already have to :JdtCompile
to not get an error when I run jdtls.test_nearest_method
.
Anyway, as for now it seems like completion is not the problem I leave the autobuild setting away.
They'd be calling client.cancel_request
instead of sending a $/cancelRequest
request directly.
Ah, ok. Now it gets more interesting:
cmp-nvim-lsp/lua/cmp_nvim_lsp/source.lua
130: self.client.cancel_request(self.request_ids[method])
lspsaga.nvim/lua/lspsaga/symbol/init.lua
35: client.cancel_request(id)
I also checked to which request the id
of the cancelRequest
relates to. It always seems to be textDocument/documentSymbol
requests, which again matches lspsaga:
lspsaga.nvim/lua/lspsaga/symbol/init.lua
99: _, request_id = client.request('textDocument/documentSymbol', params, function(err, result, ctx)
Soo ... I think we can close this here and I have to open another issue with lspsaga.
Thanks for your help.
LSP client configuration
When working on Java files from time to time it happens that nvim gets terribly slow up to the point where I can only quit and restart the editor.
Eclipse.jdt.ls version
1.24.0
Steps to Reproduce
I can not provide a clear procedure to reproduce as the effect seems to be random. And the codebase I'm working on is private.
All I can say is:
Expected Result
No slowdown.
Actual Result
The debug log is flooded with requests like this:
The requests come in blocks of growing size: First I have about 5-10 requests in a row. Over the course of some seconds I see about 100 requests in a row. After some minutes it ramps up into blocks of 1000s of requests with incremental id:
In a couple of minutes the debug log grew to 350 MB.
I could provide the debug log on a private chanel if that helps.