Skip to content

Commit bdde213

Browse files
committed
feat: JCutilWipeWorkspace — delete jdtls index and restart server
The usual fix for a corrupted eclipse workspace state. Resolves the -data directory through a fallback chain: cmd table argument (lspconfig, nvim-jdtls) -> child process cmdline (/proc on linux, ps on mac/bsd, CIM on windows; needed because nvim-java builds cmd with a function the core never sees) -> nvim-java's deterministic cache path helper. Confirms with the resolved path, stops the client (force after 3s), deletes the workspace and restarts with the same config.
1 parent 8a8c20b commit bdde213

4 files changed

Lines changed: 118 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ fallback when the corresponding option is not passed to `setup`.
104104
- `JCgenerateClass` – start class generation user input prompt;
105105
- `JCtoggleAutoformat` – enable/disable autoformat file on save;
106106
- `JCutilUpdateConfig` – re-read project configuration (pom/gradle);
107+
- `JCutilWipeWorkspace` – delete the jdtls workspace (eclipse index) and restart the server;
107108
- `JCrefactorExtractVar` – extract variable;
108109
- `JCrefactorExtractMethod` – extract method (visual range);
109110
- `JCutilJshell` – execute java shell with project classpath;

doc/jc.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ with defaults.
9393
:JCvimspectorAttach attach via vimspector explicitly
9494
:JCdebugWithConfig choose a vimspector configuration
9595
:JCutilUpdateConfig re-read project configuration (pom/gradle)
96+
:JCutilWipeWorkspace delete jdtls workspace (index), restart LSP
9697
:JCrefactorExtractVar extract variable
9798
:JCrefactorExtractMethod extract method (visual range)
9899
:JCutilJshell run java shell with project classpath

lua/jc/jdtls.lua

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,121 @@ function M.update_project_config()
272272
vim.notify("jc: project configuration update requested", vim.log.levels.INFO)
273273
end
274274

275+
local function data_dir_from_args(args)
276+
for i, arg in ipairs(args) do
277+
if arg == "-data" then
278+
return args[i + 1]
279+
end
280+
end
281+
return nil
282+
end
283+
284+
local function data_dir_from_cmdline_string(s)
285+
return s:match('%-data%s+"([^"]+)"') or s:match("%-data%s+(%S+)")
286+
end
287+
288+
-- jdtls runs as a child process of nvim; its cmdline contains -data even
289+
-- when the client config builds the command with a function (nvim-java)
290+
local function data_dir_from_proc()
291+
local my_pid = tostring(vim.fn.getpid())
292+
if vim.fn.has("linux") == 1 then
293+
-- /proc cmdline is NUL-separated, lossless even with spaces in paths
294+
for _, pid in ipairs(vim.fn.systemlist({ "pgrep", "-P", my_pid })) do
295+
local f = io.open("/proc/" .. pid .. "/cmdline", "r")
296+
if f then
297+
local cmdline = f:read("*a")
298+
f:close()
299+
local data_dir = data_dir_from_args(vim.split(cmdline, "\0"))
300+
if data_dir then
301+
return data_dir
302+
end
303+
end
304+
end
305+
elseif vim.fn.has("mac") == 1 or vim.fn.has("bsd") == 1 then
306+
for _, pid in ipairs(vim.fn.systemlist({ "pgrep", "-P", my_pid })) do
307+
local data_dir = data_dir_from_cmdline_string(vim.fn.system({ "ps", "-o", "command=", "-p", pid }))
308+
if data_dir then
309+
return data_dir
310+
end
311+
end
312+
elseif vim.fn.has("win32") == 1 then
313+
local out = vim.fn.system({
314+
"powershell",
315+
"-NoProfile",
316+
"-Command",
317+
"(Get-CimInstance Win32_Process -Filter 'ParentProcessId=" .. my_pid .. "').CommandLine",
318+
})
319+
return data_dir_from_cmdline_string(out)
320+
end
321+
return nil
322+
end
323+
324+
-- nvim-java derives the workspace path from cwd deterministically; ask its
325+
-- util as a portable last resort (approximate: assumes cwd didn't change
326+
-- since the server started)
327+
local function data_dir_from_nvim_java()
328+
local ok, java_lsp = pcall(require, "java-core.utils.lsp")
329+
if ok and java_lsp.get_jdtls_cache_data_path then
330+
return java_lsp.get_jdtls_cache_data_path(vim.fn.getcwd())
331+
end
332+
return nil
333+
end
334+
335+
-- delete the jdtls workspace (-data dir, i.e. the eclipse index — the
336+
-- usual fix for a corrupted project state) and restart the server with
337+
-- the same configuration, whoever owns it (nvim-java, lspconfig, ...)
338+
function M.wipe_workspace()
339+
local client = lsp.get_jdtls_client()
340+
if not client then
341+
vim.notify("jc: no jdtls client attached", vim.log.levels.ERROR)
342+
return
343+
end
344+
local data_dir
345+
if type(client.config.cmd) == "table" then
346+
data_dir = data_dir_from_args(client.config.cmd)
347+
end
348+
data_dir = data_dir or data_dir_from_proc() or data_dir_from_nvim_java()
349+
if not data_dir or vim.fn.isdirectory(data_dir) ~= 1 then
350+
vim.notify("jc: couldn't determine jdtls workspace (-data) directory", vim.log.levels.ERROR)
351+
return
352+
end
353+
vim.ui.select({ "Yes", "No" }, {
354+
prompt = "Delete jdtls workspace " .. data_dir .. " and restart LSP?",
355+
}, function(choice)
356+
if choice ~= "Yes" then
357+
return
358+
end
359+
local config = client.config
360+
local client_id = client.id
361+
local bufnr = vim.api.nvim_get_current_buf()
362+
client:stop()
363+
local tries = 0
364+
local timer = vim.uv.new_timer()
365+
timer:start(
366+
100,
367+
100,
368+
vim.schedule_wrap(function()
369+
tries = tries + 1
370+
if not vim.lsp.get_client_by_id(client_id) then
371+
timer:stop()
372+
timer:close()
373+
vim.fn.delete(data_dir, "rf")
374+
vim.api.nvim_buf_call(bufnr, function()
375+
vim.lsp.start(config)
376+
end)
377+
vim.notify("jc: workspace wiped, jdtls restarted", vim.log.levels.INFO)
378+
elseif tries == 30 then
379+
client:stop(true) -- graceful shutdown is taking too long
380+
elseif tries > 50 then
381+
timer:stop()
382+
timer:close()
383+
vim.notify("jc: jdtls didn't stop, workspace not deleted", vim.log.levels.ERROR)
384+
end
385+
end)
386+
)
387+
end)
388+
end
389+
275390
function M.read_class_content(uri)
276391
local client = lsp.get_jdtls_client()
277392
if not client then

plugin/jc.vim

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ command! JCgenerateAbstractMethods lua require('jc.jdtls').generate_abstractMeth
2828
command! JCgenerateClass lua require('jc.class_generator').generate_class()
2929
command! JCtoggleAutoformat call jc#toggleAutoformat()
3030
command! JCutilUpdateConfig lua require('jc.jdtls').update_project_config()
31+
command! JCutilWipeWorkspace lua require('jc.jdtls').wipe_workspace()
3132
command! JCrefactorExtractVar lua require('jc.refactor').extract_variable()
3233
command! -range JCrefactorExtractMethod lua require('jc.refactor').extract_method(true)
3334
command! JCutilJol lua require('jc.tools').jol()

0 commit comments

Comments
 (0)