@@ -272,6 +272,121 @@ function M.update_project_config()
272272 vim .notify (" jc: project configuration update requested" , vim .log .levels .INFO )
273273end
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+
275390function M .read_class_content (uri )
276391 local client = lsp .get_jdtls_client ()
277392 if not client then
0 commit comments