Skip to content

Commit 8a8c20b

Browse files
committed
feat: drop nvim-jdtls dependency, native protocol implementations
All remaining nvim-jdtls-backed commands are now implemented in jc directly over the jdtls protocol: - update_project_config: send java/projectConfigurationUpdate as a notification (it is a JsonNotification in jdt.ls; nvim-jdtls sent a request whose success was silent) and give user feedback - jc.refactor: extract variable/constant/method via java/inferSelection + java/getRefactorEdit, with vim.ui.select between candidates and follow-up command support - jc.tools: classpath-aware javap/jshell/jol using java.project.getClasspaths (test/runtime scope) and vscode.java.resolveJavaExecutable with PATH fallback; terminal via jobstart{term=true} on 0.11+ - jol jar resolved lazily from ~/.m2; when missing, JCutilJol offers to download it via mvn dependency:get - treesitter.get_class_name() for decompiled jdt:// buffers - plugin/jc.vim: commands no longer gated behind nvim-jdtls presence; 'jre normal-mode mapping moved onto keys_prefix
1 parent 21674d7 commit 8a8c20b

10 files changed

Lines changed: 361 additions & 80 deletions

File tree

README.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ Requirements:
3131
- for debug attach: the [java-debug](https://github.com/microsoft/java-debug)
3232
bundle loaded into jdtls (nvim-java bundles it; with nvim-jdtls add it to
3333
`init_options.bundles`);
34-
- optional: [nvim-jdtls](https://github.com/mfussenegger/nvim-jdtls) for
35-
extract refactorings and `JCutil*` commands.
34+
- `JCutilJol` looks for the jol-cli jar in `~/.m2` and offers to download
35+
it via maven when missing (or set `require("jc.tools").jol_path`).
3636

3737
Minimal setup using `lazy.nvim` (jdtls managed by nvim-java):
3838

@@ -43,7 +43,6 @@ return {
4343
ft = { "java" },
4444
dependencies = {
4545
"nvim-java/nvim-java",
46-
"mfussenegger/nvim-jdtls", -- optional, refactorings and JCutil*
4746
},
4847
opts = {
4948
keys_prefix = "'j",
@@ -80,7 +79,8 @@ fallback when the corresponding option is not passed to `setup`.
8079
| Organize imports | code action | smart mode remembering preferred classes per project |
8180
| Debug attach | manual dap config | `JCdebugAttach` with per-project host/port memory, dap or vimspector |
8281
| Class creation from templates || `JCgenerateClass` prompt DSL |
83-
| Extract refactorings | yes | reused from nvim-jdtls when installed |
82+
| Extract refactorings | yes | built-in (`java/inferSelection` + `java/getRefactorEdit`) |
83+
| Classpath-aware javap/jshell/jol | yes | built-in |
8484

8585
`:checkhealth jc` verifies the setup; `:help jc` for full docs.
8686

@@ -103,15 +103,12 @@ fallback when the corresponding option is not passed to `setup`.
103103
- `JCgenerateAbstractMethods` – generate abstract methods;
104104
- `JCgenerateClass` – start class generation user input prompt;
105105
- `JCtoggleAutoformat` – enable/disable autoformat file on save;
106-
107-
Using `nvim-jdtls`:
108-
106+
- `JCutilUpdateConfig` – re-read project configuration (pom/gradle);
109107
- `JCrefactorExtractVar` – extract variable;
110-
- `JCrefactorExtractMethod` – extract method;
111-
- `JCutilJshell` – execute java shell;
112-
- `JCutilBytecode` – extract bytecode for class;
113-
- `JCutilJol` – analyze object layout scheme using `jol.jar`;
114-
- `JCutilUpdateConfig` – update current project's configuration.
108+
- `JCrefactorExtractMethod` – extract method (visual range);
109+
- `JCutilJshell` – execute java shell with project classpath;
110+
- `JCutilBytecode` – extract bytecode for class (javap);
111+
- `JCutilJol` – analyze object layout scheme using `jol.jar`.
115112

116113
## Default mappings
117114

@@ -134,8 +131,8 @@ Installed on jdtls attach when `default_mappings` is enabled. `<p>` is
134131
| n | `<p>m`, i `<C-j>m` | generate abstract methods |
135132
| n | `<p>n` | new class prompt |
136133
| n | `<p>da` / `<p>dl` | debug attach / launch |
137-
| v | `<p>re` / `<p>rm` | extract variable / method (nvim-jdtls) |
138-
| n | `<leader>jre` | extract variable (nvim-jdtls) |
134+
| v | `<p>re` / `<p>rm` | extract variable / method (selection) |
135+
| n | `<p>re` | extract variable (inferred at cursor) |
139136

140137
## Class creation
141138

doc/jc.txt

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ nvim-lspconfig) and provides:
4343
- for debug attach/launch: the java-debug bundle loaded into jdtls
4444
(nvim-java bundles it; with nvim-jdtls add it to
4545
`init_options.bundles`) and nvim-dap or vimspector installed;
46-
- optional nvim-jdtls for extract refactorings and `JCutil*` commands.
46+
- `:JCutilJol` looks for the jol-cli jar in `~/.m2` and offers to
47+
download it via maven when missing (or set
48+
`require("jc.tools").jol_path`).
4749

4850
==============================================================================
4951
3. SETUP *jc-setup*
@@ -90,15 +92,12 @@ with defaults.
9092
:JCdapAttach attach via nvim-dap explicitly
9193
:JCvimspectorAttach attach via vimspector explicitly
9294
:JCdebugWithConfig choose a vimspector configuration
93-
94-
With nvim-jdtls installed:
95-
95+
:JCutilUpdateConfig re-read project configuration (pom/gradle)
9696
:JCrefactorExtractVar extract variable
97-
:JCrefactorExtractMethod extract method
98-
:JCutilJshell run java shell
99-
:JCutilBytecode show bytecode for current class
97+
:JCrefactorExtractMethod extract method (visual range)
98+
:JCutilJshell run java shell with project classpath
99+
:JCutilBytecode show bytecode for current class (javap)
100100
:JCutilJol analyze object layout (jol)
101-
:JCutilUpdateConfig update project configuration
102101

103102
==============================================================================
104103
5. MAPPINGS *jc-mappings*
@@ -123,11 +122,8 @@ Installed on jdtls attach when `default_mappings` is enabled. `<p>` is
123122
n <p>n new class prompt
124123
n <p>da debug attach
125124
n <p>dl debug launch
126-
127-
With nvim-jdtls:
128-
129-
v <p>re extract variable
130-
n <leader>jre extract variable
125+
v <p>re extract variable (selection)
126+
n <p>re extract variable (inferred at cursor)
131127
v <p>rm extract method
132128

133129
==============================================================================
@@ -162,8 +158,8 @@ Prompt scheme: >
162158
8. HEALTH *jc-health*
163159

164160
Run `:checkhealth jc` to verify: neovim version, attached jdtls client,
165-
organize-imports and java-debug availability, optional integrations
166-
(nvim-jdtls, nvim-dap/vimspector), treesitter java parser and data dir
161+
organize-imports and java-debug availability, debug backends
162+
(nvim-dap/vimspector), jol jar, treesitter java parser and data dir
167163
permissions.
168164

169165
vim:tw=78:ts=8:noet:ft=help:norl:

lua/jc.lua

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,6 @@ M.config = vim.deepcopy(default_config)
2121

2222
local did_setup = false
2323

24-
-- jol path for nvim-jdtls' :JCutilJol — best-effort lookup in the local
25-
-- maven repository, no installation attempted
26-
local function resolve_jol_path()
27-
local ok, jdtls = pcall(require, "jdtls")
28-
if not ok or jdtls.jol_path then
29-
return
30-
end
31-
local jol = vim.fn.glob(vim.fn.expand("~/.m2/repository/org/openjdk/jol/jol-cli/*/jol-cli-*-full.jar"))
32-
if jol ~= "" then
33-
jdtls.jol_path = vim.split(jol, "\n")[1]
34-
end
35-
end
36-
3724
local function on_jdtls_attach(client, bufnr)
3825
lsp.on_attach(M.config, client, bufnr)
3926
user_on_attach(client, bufnr)
@@ -58,8 +45,6 @@ function M.setup(args)
5845
end
5946
M.config = vim.tbl_deep_extend("keep", args, default_config)
6047

61-
resolve_jol_path()
62-
6348
local group = vim.api.nvim_create_augroup("jc_nvim_attach", { clear = true })
6449
vim.api.nvim_create_autocmd("LspAttach", {
6550
group = group,

lua/jc/config/mappings.lua

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,23 +95,27 @@ function M.install_mappings(conf, bufnr)
9595
opts
9696
)
9797

98-
if pcall(require, "jdtls") then
99-
vim.api.nvim_buf_set_keymap(
100-
bufnr,
101-
"v",
102-
prefix .. "re",
103-
"<Esc><Cmd>lua require('jdtls').extract_variable(true)<CR>",
104-
opts
105-
)
106-
vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>jre", "<Cmd>lua require('jdtls').extract_variable()<CR>", opts)
107-
vim.api.nvim_buf_set_keymap(
108-
bufnr,
109-
"v",
110-
prefix .. "rm",
111-
"<Esc><Cmd>lua require('jdtls').extract_method(true)<CR>",
112-
opts
113-
)
114-
end
98+
vim.api.nvim_buf_set_keymap(
99+
bufnr,
100+
"v",
101+
prefix .. "re",
102+
"<Esc><Cmd>lua require('jc.refactor').extract_variable(true)<CR>",
103+
opts
104+
)
105+
vim.api.nvim_buf_set_keymap(
106+
bufnr,
107+
"n",
108+
prefix .. "re",
109+
"<Cmd>lua require('jc.refactor').extract_variable()<CR>",
110+
opts
111+
)
112+
vim.api.nvim_buf_set_keymap(
113+
bufnr,
114+
"v",
115+
prefix .. "rm",
116+
"<Esc><Cmd>lua require('jc.refactor').extract_method(true)<CR>",
117+
opts
118+
)
115119
end
116120

117121
return M

lua/jc/health.lua

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,6 @@ function M.check()
5757
end
5858

5959
-- optional integrations
60-
if pcall(require, "jdtls") then
61-
health.ok("nvim-jdtls found (extract refactorings, JCutil* commands)")
62-
else
63-
health.info("nvim-jdtls not installed — extract refactorings and JCutil* commands unavailable")
64-
end
65-
6660
local backend = require("jc.debug").backend()
6761
local has_dap = pcall(require, "dap")
6862
local has_vimspector = vim.fn.exists(":VimspectorReset") == 1
@@ -80,6 +74,12 @@ function M.check()
8074
health.warn("neither nvim-dap nor vimspector installed — JCdebug* commands won't work")
8175
end
8276

77+
if require("jc.tools").resolve_jol() then
78+
health.ok("jol jar: " .. require("jc.tools").jol_path)
79+
else
80+
health.info("jol jar not found in ~/.m2 — :JCutilJol will offer to download it via maven")
81+
end
82+
8383
-- java treesitter parser (class generator uses it to resolve packages)
8484
if pcall(vim.treesitter.language.add, "java") then
8585
health.ok("treesitter java parser available")

lua/jc/jdtls.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,19 @@ function M.organize_imports(bn, smart)
259259
vim.lsp.buf_request(bn, "java/organizeImports", make_range_params(), apply_edit)
260260
end
261261

262+
-- jdt.ls declares java/projectConfigurationUpdate as a JsonNotification:
263+
-- there is never a response, so send a notification (nvim-jdtls sends a
264+
-- request and its success therefore looks like "nothing happened")
265+
function M.update_project_config()
266+
local client = lsp.get_jdtls_client()
267+
if not client then
268+
vim.notify("jc: no jdtls client attached", vim.log.levels.ERROR)
269+
return
270+
end
271+
client:notify("java/projectConfigurationUpdate", { uri = vim.uri_from_bufnr(0) })
272+
vim.notify("jc: project configuration update requested", vim.log.levels.INFO)
273+
end
274+
262275
function M.read_class_content(uri)
263276
local client = lsp.get_jdtls_client()
264277
if not client then

lua/jc/refactor.lua

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
-- extract refactorings over the jdtls protocol (java/inferSelection +
2+
-- java/getRefactorEdit); nvim-jdtls is not required
3+
local lsp = require("jc.lsp")
4+
5+
local M = {}
6+
7+
local function code_action_params(client, visual)
8+
local encoding = client.offset_encoding
9+
local params
10+
if visual then
11+
params = vim.lsp.util.make_given_range_params(nil, nil, 0, encoding)
12+
else
13+
params = vim.lsp.util.make_range_params(0, encoding)
14+
end
15+
params.context = { diagnostics = {} }
16+
return params
17+
end
18+
19+
local function apply_refactor_edit(err, result, ctx)
20+
if err then
21+
vim.notify("jc: refactor failed: " .. err.message, vim.log.levels.ERROR)
22+
return
23+
end
24+
if not result then
25+
return
26+
end
27+
if result.edit then
28+
local client = ctx and vim.lsp.get_client_by_id(ctx.client_id)
29+
vim.lsp.util.apply_workspace_edit(result.edit, client and client.offset_encoding or "utf-16")
30+
end
31+
-- jdtls may ask for a follow-up command (e.g. rename of the new symbol)
32+
if result.command then
33+
lsp.executeCommand(result.command, function() end)
34+
end
35+
end
36+
37+
local function refactor(cmd, visual)
38+
local client = lsp.get_jdtls_client()
39+
if not client then
40+
vim.notify("jc: no jdtls client attached", vim.log.levels.ERROR)
41+
return
42+
end
43+
local action_params = code_action_params(client, visual)
44+
local params = {
45+
command = cmd,
46+
context = action_params,
47+
options = {
48+
tabSize = vim.lsp.util.get_effective_tabstop(),
49+
insertSpaces = vim.bo.expandtab,
50+
},
51+
}
52+
local bufnr = vim.api.nvim_get_current_buf()
53+
local range = action_params.range
54+
local has_selection = range.start.character ~= range["end"].character or range.start.line ~= range["end"].line
55+
if has_selection then
56+
client:request("java/getRefactorEdit", params, apply_refactor_edit, bufnr)
57+
return
58+
end
59+
-- cursor position only: let jdtls infer what can be extracted here
60+
client:request("java/inferSelection", params, function(err, selections)
61+
if err or not selections or #selections == 0 then
62+
vim.notify("jc: nothing to extract at cursor", vim.log.levels.WARN)
63+
return
64+
end
65+
local function run(selection)
66+
params.commandArguments = { selection }
67+
client:request("java/getRefactorEdit", params, apply_refactor_edit, bufnr)
68+
end
69+
if #selections == 1 then
70+
run(selections[1])
71+
else
72+
vim.ui.select(selections, {
73+
prompt = "Extract:",
74+
format_item = function(s)
75+
return s.name
76+
end,
77+
}, function(selection)
78+
if selection then
79+
run(selection)
80+
end
81+
end)
82+
end
83+
end, bufnr)
84+
end
85+
86+
function M.extract_variable(visual)
87+
refactor("extractVariable", visual)
88+
end
89+
90+
function M.extract_variable_all(visual)
91+
refactor("extractVariableAllOccurrence", visual)
92+
end
93+
94+
function M.extract_constant(visual)
95+
refactor("extractConstant", visual)
96+
end
97+
98+
function M.extract_method(visual)
99+
refactor("extractMethod", visual)
100+
end
101+
102+
return M

0 commit comments

Comments
 (0)