Git Product home page Git Product logo

nvim-jdtls's Introduction

nvim-jdtls

Extensions for the built-in Language Server Protocol support in Neovim (>= 0.5) for eclipse.jdt.ls.

Extensions

  • organize_imports function to organize imports
  • extract_variable function to introduce a local variable
  • extract_constant function to extract a constant
  • extract_method function to extract a block of code into a method
  • Open class file contents
  • Code action extensions
    • Generate constructors
    • Generate toString function
    • hashCode and equals generation.
    • Extract variables or methods
    • Generate delegate methods
    • Move package, instance method, static method or type
  • javap command to show bytecode of current file
  • jol command to show memory usage of current file (jol_path must be set)
  • jshell command to open up jshell with classpath from project set
  • Debugger support via nvim-dap

Take a look at a demo to see some of the functionality in action.

Plugin Installation

  • Requires Neovim (>= 0.5)
  • nvim-jdtls is a plugin. Install it like any other Vim plugin:
    • If using vim-plug: Plug 'mfussenegger/nvim-jdtls'
    • If using packer.nvim: use 'mfussenegger/nvim-jdtls'

Language Server Installation

Install eclipse.jdt.ls using one of the three options:

  1. Install eclipse.jdt.ls via your package manager (Assuming a package is available).
  2. Download a pre-built milestone or snapshot and extract the contents of the package.
  3. Build eclipse.jdt.ls from source:
    • Switch to a folder of your choice.
    • git clone https://github.com/eclipse/eclipse.jdt.ls.git
    • cd eclipse.jdt.ls
    • ./mvnw clean install -DskipTests (Set JAVA_HOME to Java 11 before you run this)

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.
  • /usr/lib/jvm/java-11-openjdk/bin/java needs to be changed to point to your Java installation. At least Java 11 is required to run the language server.
#!/usr/bin/env bash

# NOTE:
# This doesn't work as is on Windows. You'll need to create an equivalent `.bat` file instead
#
# NOTE:
# If you're not using Linux you'll need to adjust the `-configuration` option
# to point to the `config_mac' or `config_win` folders depending on your system.

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-11-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 "${1:-$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')
  augroup lsp
    au!
    au FileType java lua require('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 init_options or settings. See the eclipse.jdt.ls Wiki for an overview of available options.

You can also find more complete configuration examples in the Wiki.

root_dir configuration

For the language server to work correctly it is important that the root_dir in the config is set correctly. By default start_or_attach sets the root_dir automatically by looking for marker files relative to each file you're opening. The markers default to .git, mvnw and gradlew. If no parent directory contains any of the markers, it will fallback to the current working directory.

nvim-jdtls contains a find_root function which you could use to customize the root_dir:

-- find_root looks for parent directories relative to the current buffer containing one of the given arguments.
require('jdtls').start_or_attach({cmd = {'java-lsp.sh'}, root_dir = require('jdtls.setup').find_root({'gradle.build', 'pom.xml'})})

data directory configuration

eclipse.jdt.ls stores project specific data within the folder set via the -data flag in the java-lsp.sh script. If you're using eclipse.jdt.ls with multiple different projects you should use a dedicated data directory per project. You can do that by adding a second argument to the cmd property of the config passed to start_or_attach. An example:

start_or_attach({cmd = {'java-lsp.sh', '/home/user/workspace/' .. vim.fn.fnamemodify(vim.fn.getcwd(), ':p:h:t')}})

lspconfig

Warning: You must NOT execute require'nvim_lsp'.jdtls.setup{} from nvim-lspconfig if you use nvim-jdtls.

You can use nvim-lspconfig for other language servers without problems.

UI picker customization

Tip: You can get a better UI for code-actions and other functions by overriding the jdtls.ui picker. See UI Extensions.

Usage

nvim-jdtls extends the capabilities of the built-in LSP support in Neovim, so all the functions mentioned in :help lsp will work.

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>

nnoremap <A-o> <Cmd>lua require'jdtls'.organize_imports()<CR>
nnoremap crv <Cmd>lua require('jdtls').extract_variable()<CR>
vnoremap crv <Esc><Cmd>lua require('jdtls').extract_variable(true)<CR>
nnoremap crc <Cmd>lua require('jdtls').extract_constant()<CR>
vnoremap crc <Esc><Cmd>lua require('jdtls').extract_constant(true)<CR>
vnoremap crm <Esc><Cmd>lua require('jdtls').extract_method(true)<CR>


-- If using nvim-dap
-- This requires java-debug and vscode-java-test bundles, see install steps in this README further below.
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 handler that can be set on the config table which is passed to start_or_attach. If you use jdtls together with nvim-dap, call add_commands after setup_dap to ensure it includes debugging related commands. (More about this is in the debugger setup section further below)

command! -buffer JdtCompile lua require('jdtls').compile()
command! -buffer JdtUpdateConfig lua require('jdtls').update_project_config()
command! -buffer JdtJol lua require('jdtls').jol()
command! -buffer JdtBytecode lua require('jdtls').javap()
command! -buffer JdtJshell lua require('jdtls').jshell()

Debugger (via nvim-dap)

nvim-jdtls provides integration with nvim-dap.

Once setup correctly, it enables the following additional functionality:

  1. Debug applications via explicit configurations
  2. Debug automatically discovered main classes
  3. Debug junit tests. Either whole classes or individual test methods

For 1 & 2 to work, eclipse.jdt.ls needs to load the java-debug extension. For 3 to work, it also needs to load the vscode-java-test extension.

For usage instructions once installed, read the nvim-dap help. Debugging junit test classes or methods will be possible via these two functions:

require'jdtls'.test_class()
require'jdtls'.test_nearest_method()

java-debug installation

  • Clone java-debug
  • Navigate into the cloned repository (cd java-debug)
  • Run ./mvnw clean install
  • Extend the initializationOptions with which you start eclipse.jdt.ls as follows:
config['init_options'] = {
  bundles = {
    vim.fn.glob("path/to/java-debug/com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin-*.jar")
  };
}

nvim-dap setup

You also need to call require('jdtls').setup_dap() to have it register a java adapter.

To do that, extend the configuration for nvim-jdtls with:

config['on_attach'] = function(client, bufnr)
  -- With `hotcodereplace = 'auto' the debug adapter will try to apply code changes
  -- you make during a debug session immediately.
  -- Remove the option if you do not want that.
  require('jdtls').setup_dap({ hotcodereplace = 'auto' })
end

If you also want to discover main classes and create configuration entries for them, you have to call require('jdtls.dap').setup_dap_main_class_configs() or use the JdtRefreshDebugConfigs command which is added as part of add_commands() which is mentioned in the Usage section.

Note that eclipse.jdt.ls needs to have loaded your project before it can discover all main classes and that may take some time. It is best to trigger this deferred or ad-hoc when first required.

See the nvim-dap Adapter Installation Wiki for example configurations in case you're not going to use the main-class discovery functionality of nvim-jdtls.

vscode-java-test installation

To be able to debug junit tests, it is necessary to install the bundles from vscode-java-test:

  • Clone the repository
  • Navigate into the folder (cd vscode-java-test)
  • Run npm install
  • Run npm run build-plugin
  • Extend the bundles in the nvim-jdtls config:
-- This bundles definition is the same as in the previous section (java-debug installation)
local bundles = {
  vim.fn.glob("path/to/java-debug/com.microsoft.java.debug.plugin/target/com.microsoft.java.debug.plugin-*.jar"),
};

-- This is the new part
vim.list_extend(bundles, vim.split(vim.fn.glob("/path/to/microsoft/vscode-java-test/server/*.jar"), "\n"))
config['init_options'] = {
  bundles = bundles;
}

Troubleshooting

vim.lsp.buf functions don't do anything

This can have two reasons:

  1. The client and server aren't starting up correctly.

You can check if the client is running with :lua print(vim.inspect(vim.lsp.buf_get_clients())), it should output a lot of information. If it doesn't, verify:

  • That the language server can be started standalone. (Run the java-lsp.sh in a terminal)
  • That there are no configuration errors. (Run :set ft=java and :messages after opening a Java file)
  • Check the log files (:lua print(vim.fn.stdpath('cache')) lists the path, there should be a lsp.log)
  1. Eclipse.jdt.ls can't compile your project or it cannot load your project and resolve the class paths.
  • Run :JdtCompile for incremental compilation or :JdtCompile full for full compilation. If there are any errors in the project, it will open the quickfix list with the errors.

  • Check the log files (:lua print(vim.fn.stdpath('cache')) lists the path, there should be a lsp.log)

  • If there is nothing, try changing the log level. See :help vim.lsp.set_log_level()

Diagnostics and completion suggestions are slow

Completion requests can be quite expensive on big projects. If you're using some kind of auto-completion plugin that triggers completion requests automatically, consider deactivating it or tuning it so it is less aggressive. Triggering a completion request on each typed character is likely overloading eclipse.jdt.ls.

Newly added dependencies are not found

You can try running :JdtUpdateConfig to refresh the configuration. If that doesn't work you'll need to restart the language server.

Language server doesn't find classes that should be there

The language server has its own mental model of which files exists based on what the client tells it. If you modify files outside of neovim, then the language server won't be notified of the changes. This can happen for example if you switch to a different branch with git.

If the language server doesn't get a notification about a new file, you might get errors, telling you that a class cannot be resolved. If that is the case, open the file and save it. Then the language server will be notified about the new file and it should start to recognize the classes within the file.

After updating eclipse.jdt.ls it doesn't work properly anymore

Try wiping your workspace folder and restart Neovim and the language server.

(the workspace folder is the path you used as argument to -data in your java-lsp.sh).

nvim-jdtls's People

Contributors

mfussenegger avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.