mirror of
https://github.com/mfussenegger/nvim-jdtls
synced 2024-09-16 14:34:13 +02:00
Make setup easier and outline eclipse.jdt.ls installation
This commit is contained in:
parent
7b524f8da7
commit
bc1e54f819
4 changed files with 230 additions and 45 deletions
130
README.md
130
README.md
|
@ -27,51 +27,104 @@ Take a look at [a demo](https://github.com/mfussenegger/nvim-jdtls/issues/3) to
|
|||
see some of the functionality in action.
|
||||
|
||||
|
||||
## Installation
|
||||
## Plugin Installation
|
||||
|
||||
- Requires [Neovim HEAD/nightly][4]
|
||||
- nvim-jdtls is a plugin. Install it like any other Vim plugin.
|
||||
- Call `:packadd nvim-jdtls` if you install `nvim-jdtls` to `'packpath'`.
|
||||
|
||||
|
||||
## LSP Installation
|
||||
|
||||
For ``nvim-jdtls`` to work, [eclipse.jdt.ls][3] needs to be installed.
|
||||
|
||||
To build eclipse.jdt.ls from source, switch to a folder of your choice and run:
|
||||
|
||||
|
||||
```bash
|
||||
git clone https://github.com/eclipse/eclipse.jdt.ls.git
|
||||
cd eclipse.jdt.ls
|
||||
./mvnw clean verify
|
||||
```
|
||||
|
||||
Create a launch script with the following contents. **But don't forget to adapt
|
||||
the paths**.
|
||||
|
||||
- `$HOME/dev/eclipse` needs to be changed to the folder where you cloned the
|
||||
repository.
|
||||
- `$HOME/workspace` needs to be changed to where you want eclipse.jdt.ls to save
|
||||
settings for workspaces.
|
||||
- `/usr/lib/jvm/java-14-openjdk/bin/java` needs to be changed to point to your
|
||||
Java installation.
|
||||
|
||||
If you're using Java < 9, remove the `add-modules` and `-add-opens` options.
|
||||
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
JAR="$HOME/dev/eclipse/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository/plugins/org.eclipse.equinox.launcher_*.jar"
|
||||
GRADLE_HOME=$HOME/gradle /usr/lib/jvm/java-14-openjdk/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 \
|
||||
-Xms1g \
|
||||
-Xmx2G \
|
||||
-jar $(echo "$JAR") \
|
||||
-configuration "$HOME/dev/eclipse/eclipse.jdt.ls/org.eclipse.jdt.ls.product/target/repository/config_linux" \
|
||||
-data "$HOME/workspace" \
|
||||
--add-modules=ALL-SYSTEM \
|
||||
--add-opens java.base/java.util=ALL-UNNAMED \
|
||||
--add-opens java.base/java.lang=ALL-UNNAMED
|
||||
```
|
||||
|
||||
The script must be placed in a folder that is part of `$PATH`. To verify that
|
||||
the installation worked, launch it in a shell. You should get the following
|
||||
output:
|
||||
|
||||
```
|
||||
Content-Length: 126
|
||||
|
||||
{"jsonrpc":"2.0","method":"window/logMessage","params":{"type":3,"message":"Sep 16, 2020, 8:10:53 PM Main thread is waiting"}}
|
||||
```
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
To use `nvim-jdtls`, you need to setup a LSP client. In your `init.vim` add the
|
||||
following:
|
||||
|
||||
```
|
||||
if has('nvim-0.5')
|
||||
packadd nvim-jdtls
|
||||
lua jdtls = require('jdtls')
|
||||
augroup lsp
|
||||
au!
|
||||
au FileType java lua jdtls.start_or_attach({cmd={'java-lsp.sh'}})
|
||||
augroup end
|
||||
endif
|
||||
```
|
||||
|
||||
`java-lsp.sh` needs to be changed to the name of the shell script created earlier.
|
||||
|
||||
The argument passed to `start_or_attach` is the same `config` mentioned in
|
||||
`:help vim.lsp.start_client`. You may want to configure some settings via the `init_options`. See the [eclipse.jdt.ls Wiki][8] for an overview of available options.
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
`nvim-jdtls` doesn't contain logic to spawn a LSP client for [eclipse.jdt.ls][3], see `:help lsp` for information on how to launch a LSP client.
|
||||
`nvim-jdtls` extends the capabilities of the built-in LSP support in
|
||||
Neovim, so all the functions mentioned in `:help lsp` will work.
|
||||
|
||||
To make use of all the functionality `nvim-jdtls` provides, you need to set some extra capabilities and set a couple of initialization options.
|
||||
|
||||
Additional capabilities:
|
||||
|
||||
```lua
|
||||
local capabilities = vim.lsp.protocol.make_client_capabilities()
|
||||
capabilities.textDocument.codeAction = {
|
||||
dynamicRegistration = false;
|
||||
codeActionLiteralSupport = {
|
||||
codeActionKind = {
|
||||
valueSet = {
|
||||
"source.generate.toString",
|
||||
"source.generate.hashCodeEquals"
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Initialization options:
|
||||
|
||||
|
||||
```lua
|
||||
config['init_options'] = {
|
||||
extendedClientCapabilities = require('jdtls').extendedClientCapabilities;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
You may want to create some mappings and commands to make the functionality of `nvim-jdtls` accessible:
|
||||
`nvim-jdtls` provides some extras, for those you'll want to create additional
|
||||
mappings:
|
||||
|
||||
```
|
||||
-- `code_action` is a superset of vim.lsp.buf.code_action and you'll be able to
|
||||
-- use this mapping also with other language servers
|
||||
nnoremap <A-CR> <Cmd>lua require('jdtls').code_action()<CR>
|
||||
vnoremap <A-CR> <Esc><Cmd>lua require('jdtls').code_action(true)<CR>
|
||||
nnoremap <leader>r <Cmd>lua require('jdtls').code_action(false, 'refactor')<CR>
|
||||
|
@ -82,11 +135,17 @@ vnoremap crv <Esc><Cmd>lua require('jdtls').extract_variable(true)<CR>
|
|||
vnoremap crm <Esc><Cmd>lua require('jdtls').extract_method(true)<CR>
|
||||
|
||||
|
||||
-- For nvim-dap
|
||||
-- If using nvim-dap
|
||||
nnoremap <leader>df <Cmd>lua require'jdtls'.test_class()<CR>
|
||||
nnoremap <leader>dn <Cmd>lua require'jdtls'.test_nearest_method()<CR>
|
||||
```
|
||||
|
||||
|
||||
Some methods are better exposed via commands. As a shortcut you can also call
|
||||
`:lua require('jdtls.setup').add_commands()` to declare these. It's recommended to call `add_commands` within the `on_attach` callback that can be set on the `config` table which is passed to `start_or_attach`.
|
||||
|
||||
|
||||
```
|
||||
command! -buffer JdtCompile lua require('jdtls').compile()
|
||||
command! -buffer JdtUpdateConfig lua require('jdtls').update_project_config()
|
||||
command! -buffer JdtJol lua require('jdtls').jol()
|
||||
|
@ -147,3 +206,4 @@ config['init_options'] = {
|
|||
[5]: https://github.com/mfussenegger/nvim-dap
|
||||
[6]: https://github.com/microsoft/java-debug
|
||||
[7]: https://github.com/microsoft/vscode-java-test
|
||||
[8]: https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line
|
||||
|
|
|
@ -873,15 +873,9 @@ function M.setup_dap()
|
|||
end
|
||||
|
||||
|
||||
M.extendedClientCapabilities = {
|
||||
classFileContentsSupport = true;
|
||||
generateToStringPromptSupport = true;
|
||||
hashCodeEqualsPromptSupport = true;
|
||||
advancedExtractRefactoringSupport = true;
|
||||
advancedOrganizeImportsSupport = true;
|
||||
generateConstructorsPromptSupport = true;
|
||||
generateDelegateMethodsPromptSupport = true;
|
||||
};
|
||||
|
||||
local setup = require('jdtls.setup')
|
||||
M.extendedClientCapabilities = setup.extendedClientCapabilities
|
||||
M.start_or_attach = setup.start_or_attach
|
||||
M.setup = setup
|
||||
|
||||
return M
|
||||
|
|
22
lua/jdtls/path.lua
Normal file
22
lua/jdtls/path.lua
Normal file
|
@ -0,0 +1,22 @@
|
|||
local M = {}
|
||||
|
||||
local is_windows = vim.loop.os_uname().version:match('Windows')
|
||||
|
||||
M.sep = is_windows and '\\' or '/'
|
||||
|
||||
if is_windows then
|
||||
M.is_fs_root = function(path)
|
||||
return path:match('^%a:$')
|
||||
end
|
||||
else
|
||||
M.is_fs_root = function(path)
|
||||
return path == '/'
|
||||
end
|
||||
end
|
||||
|
||||
function M.join(...)
|
||||
local result = table.concat(vim.tbl_flatten {...}, M.sep):gsub(M.sep .. '+', M.sep)
|
||||
return result
|
||||
end
|
||||
|
||||
return M
|
109
lua/jdtls/setup.lua
Normal file
109
lua/jdtls/setup.lua
Normal file
|
@ -0,0 +1,109 @@
|
|||
local api = vim.api
|
||||
local lsp = vim.lsp
|
||||
local uv = vim.loop
|
||||
local path = require('jdtls.path')
|
||||
|
||||
local lsps = {}
|
||||
local status_callback = vim.schedule_wrap(function(_, _, result)
|
||||
api.nvim_command(string.format(':echohl Function | echo "%s" | echohl None', result.message))
|
||||
end)
|
||||
|
||||
local function attach_to_active_buf(bufnr, client_name)
|
||||
for _, buf in pairs(vim.fn.getbufinfo({bufloaded=true})) do
|
||||
if api.nvim_buf_get_option(buf.bufnr, 'filetype') == 'java' then
|
||||
local clients = lsp.buf_get_clients(buf.bufnr)
|
||||
for _, client in ipairs(clients) do
|
||||
if client.config.name == client_name then
|
||||
lsp.buf_attach_client(bufnr, client.id)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
print('No active LSP client found to use for jdt:// document')
|
||||
return false
|
||||
end
|
||||
|
||||
local function find_root(bufname, markers)
|
||||
local dirname = vim.fn.fnamemodify(bufname, ':p:h')
|
||||
while not path.is_fs_root(dirname) do
|
||||
for _, marker in ipairs(markers) do
|
||||
if uv.fs_stat(path.join(dirname, marker)) then
|
||||
return dirname
|
||||
end
|
||||
end
|
||||
dirname = vim.fn.fnamemodify(dirname, ':h')
|
||||
end
|
||||
end
|
||||
|
||||
local function start_or_attach(config)
|
||||
assert(config, 'config is required')
|
||||
assert(
|
||||
config.cmd and type(config.cmd) == 'table',
|
||||
'Config must have a `cmd` property and that must be a table. Got: '
|
||||
.. table.concat(config.cmd, ' ') or 'nil'
|
||||
)
|
||||
assert(
|
||||
tonumber(vim.fn.executable(config.cmd[1])) == 1,
|
||||
'LSP cmd must be an executable: ' .. config.cmd[1]
|
||||
)
|
||||
config.name = config.name or 'jdt.ls'
|
||||
|
||||
local bufnr = api.nvim_get_current_buf()
|
||||
local bufname = api.nvim_buf_get_name(bufnr)
|
||||
-- Won't be able to get the correct root path for jdt:// URIs
|
||||
-- So need to connect to an existing client
|
||||
if vim.startswith(bufname, 'jdt://') then
|
||||
if attach_to_active_buf(bufnr, config.name) then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
config.root_dir = config.root_dir or find_root(bufname, {'.git', 'gradlew', 'mvnw'})
|
||||
config.callbacks = config.callbacks or {}
|
||||
config.callbacks['language/status'] = config.callbacks['language/status'] or status_callback
|
||||
config.capabilities = config.capabilities or lsp.protocol.make_client_capabilities()
|
||||
config.capabilities.textDocument.codeAction = {
|
||||
dynamicRegistration = false;
|
||||
codeActionLiteralSupport = {
|
||||
codeActionKind = {
|
||||
valueSet = {
|
||||
"source.generate.toString",
|
||||
"source.generate.hashCodeEquals",
|
||||
"source.organizeImports",
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
local client_id = lsps[config.root_dir]
|
||||
if not client_id then
|
||||
client_id = lsp.start_client(config)
|
||||
lsps[config.root_dir] = client_id
|
||||
end
|
||||
lsp.buf_attach_client(bufnr, client_id)
|
||||
end
|
||||
|
||||
local extendedClientCapabilities = {
|
||||
classFileContentsSupport = true;
|
||||
generateToStringPromptSupport = true;
|
||||
hashCodeEqualsPromptSupport = true;
|
||||
advancedExtractRefactoringSupport = true;
|
||||
advancedOrganizeImportsSupport = true;
|
||||
generateConstructorsPromptSupport = true;
|
||||
generateDelegateMethodsPromptSupport = true;
|
||||
};
|
||||
|
||||
|
||||
local function add_commands()
|
||||
api.nvim_command [[command! -buffer -nargs=? JdtCompile lua require('jdtls').compile(<f-args>)]]
|
||||
api.nvim_command [[command! -buffer JdtUpdateConfig lua require('jdtls').update_project_config()]]
|
||||
api.nvim_command [[command! -buffer -nargs=* JdtJol lua require('jdtls').jol(<f-args>)]]
|
||||
api.nvim_command [[command! -buffer JdtBytecode lua require('jdtls').javap()]]
|
||||
api.nvim_command [[command! -buffer JdtJshell lua require('jdtls').jshell()]]
|
||||
end
|
||||
|
||||
return {
|
||||
start_or_attach = start_or_attach;
|
||||
extendedClientCapabilities = extendedClientCapabilities;
|
||||
add_commands = add_commands;
|
||||
}
|
Loading…
Reference in a new issue