Skip to content

Commit 9eab80b

Browse files
committed
fix(spring-boot): register classpath listener command globally
The "vscode-spring-boot.ls.start" command which starts the classpath handshake between the editor and the Spring Boot LS is sent by the jdtls extension, not by the Spring Boot LS itself. Where a client sends "workspace/executeClientCommand", the handler resolves commands against that same client's "commands" and the global table, so a per-client registration on the "spring-boot" client is never consulted for it. Move the command into the global vim.lsp.commands from a new M.register_client_commands(), call it and M.init_lsp_commands() from M.setup() so both exist before jdtls imports a Spring Boot project, and drop the now-dead "commands" block from lsp/spring-boot.lua. Also expand the comments explaining the split between per-client and global handlers.
1 parent 79de8ba commit 9eab80b

2 files changed

Lines changed: 37 additions & 11 deletions

File tree

‎lsp/spring-boot.lua‎

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,11 @@ return {
8282
end,
8383

8484
handlers = {
85-
-- The server asking the editor to run a command, e.g. to enable classpath
86-
-- listening. Handled on this client, so no global handler is needed.
85+
-- The server asking the editor to run a command by id. The commands
86+
-- themselves live in |vim.lsp.commands|, registered by
87+
-- |spring_boot.setup()|: the one the Spring Boot LS depends on is sent by
88+
-- the *jdtls* extension, so it never reaches this client. See
89+
-- |spring_boot.register_client_commands()|.
8790
["workspace/executeClientCommand"] = function(err, result, ctx, config)
8891
return require("spring_boot").execute_client_command(err, result, ctx, config)
8992
end,
@@ -105,10 +108,4 @@ return {
105108
["sts/javaCodeComplete"] = handler("java_data", "sts/javaCodeComplete"),
106109
["sts/project/gav"] = handler("java_data", "sts/project/gav"),
107110
},
108-
109-
commands = {
110-
["vscode-spring-boot.ls.start"] = function()
111-
require("spring_boot.util").boot_execute_command("sts.vscode-spring-boot.enableClasspathListening", { true })
112-
end,
113-
},
114111
}

‎lua/spring_boot.lua‎

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ local M = {}
2525
--- a command, possibly on another server. The command is resolved through the
2626
--- client that sent the request, so per-client `commands` win.
2727
---
28-
--- `lsp/spring-boot.lua` registers this on the `spring-boot` client, which is
29-
--- enough on its own. See |spring_boot.init_lsp_commands()| for the global
30-
--- variant.
28+
--- `lsp/spring-boot.lua` registers this on the `spring-boot` client, which
29+
--- covers the Spring Boot LS itself. The commands jdtls sends to its own client
30+
--- need the global variant installed by |spring_boot.init_lsp_commands()|,
31+
--- unless nvim-jdtls already provided one.
3132
--- see https://github.com/mfussenegger/nvim-jdtls/blob/29255ea26dfb51ef0213f7572bff410f1afb002d/lua/jdtls.lua#L819
3233
---@type lsp.Handler
3334
M.execute_client_command = function(_, params, ctx)
@@ -67,6 +68,28 @@ M.init_lsp_commands = function()
6768
vim.lsp.handlers["workspace/executeClientCommand"] = M.execute_client_command -- luacheck: ignore 122
6869
end
6970

71+
--- Registers the commands a server asks the editor to run through
72+
--- `workspace/executeClientCommand`.
73+
---
74+
--- These belong in the global |vim.lsp.commands| and not in the `spring-boot`
75+
--- config's `commands`, because the one that matters here is sent by the *jdtls*
76+
--- extension, once jdtls has imported a Spring Boot project — so it is
77+
--- dispatched on the `jdtls` client, whose handler resolves commands against
78+
--- that client and the global table only. A `commands` entry on the
79+
--- `spring-boot` client is never consulted for it.
80+
---
81+
--- This is the start of the classpath handshake: the command makes the editor
82+
--- ask the language server to listen for classpath changes
83+
--- (`sts.vscode-spring-boot.enableClasspathListening`), and without it the
84+
--- server never learns the project's classpath — no beans, no endpoints, no
85+
--- `application.properties` / `.yml` properties.
86+
--- see https://github.com/spring-projects/sts4/blob/cfd10f0b53be0bfe107ca91ae0ad3df3038b1af2/headless-services/jdt-ls-extension/org.springframework.tooling.jdt.ls.extension/src/org/springframework/tooling/jdt/ls/extension/JdtLsExtensionPlugin.java
87+
M.register_client_commands = function()
88+
vim.lsp.commands["vscode-spring-boot.ls.start"] = function()
89+
require("spring_boot.util").boot_execute_command("sts.vscode-spring-boot.enableClasspathListening", { true })
90+
end
91+
end
92+
7093
M.get_ls_from_mason = function()
7194
local result = M.get_from_mason_registry("vscode-spring-boot-tools", "language-server.jar")
7295
if #result > 0 then
@@ -162,6 +185,12 @@ M.setup = function(opts)
162185
return config
163186
end
164187

188+
-- The classpath handshake starts on jdtls' side, so both the handler and the
189+
-- command have to be registered globally, before jdtls imports a Spring Boot
190+
-- project. See |spring_boot.register_client_commands()|.
191+
M.register_client_commands()
192+
M.init_lsp_commands()
193+
165194
-- Options may have changed, so workspaces are re-judged by `project_filter`.
166195
require("spring_boot.launch").clear_project_filter_cache()
167196

0 commit comments

Comments
 (0)