Saturday, October 17, 2020

Setup Neovim for Java Development Part 2

This is an updated version of an older article about setting up Neovim for Java development.

In the previous article I wrote about why I wanted to move away from IntelliJ, about features that I consider essential, how I set it up and the parts that were missing.

Since then, my setup changed significantly.

Neovim 0.5 with built-in LSP

Neovim 0.5 is still in development and one of the features being added is a built-in LSP client. Although some parts are still a work in progress, the basic functionality is there and works pretty well.

One of the main features that distinguishes the built-in LSP support from other plugins implementing an LSP client is that the built-in client was designed to be extensible.

For example, there is a plugin that allows users to further customize how Neovim displays diagnostic information. Another plugin extends the completion support.

The go to language server for Java, contains many extensions that are not part of the standard language server protocol specification. Almost no client other than the Visual Studio Code extensions expose that functionality.

The extensible design of the built-in LSP client in Neovim, enabled me to write a plugin, called nvim-jdtls that exposes these extensions as well.

This plugin, together with the built-in LSP client is now my go-to solution for Java development and replaced the LanguageClient-Neovim plugin that I used previously.


One of the features I missed with my previous setup was a debugger.

There is the vimspector plugin that implements the debug adapter protocol for vim and Neovim. This plugin allows you to use any debugger for which a debug adapter exists.

For Java, there is an extension to that implements the debug adapter protocol. So theoretically it would have been possible to use that with vimspector to debug Java applications.

But at the time vimspector mostly focused on vim support and Neovim support was not a priority. Furthermore, I wanted to dynamically create configurations - to be able to debug individual junit test methods. I may have been wrong, but it seemed as if the APIs of vimspector weren’t laid out to support such a use case. Vimspector aims to provide a IDE like, complete debugging solution. I wasn’t sure if that was what I was looking for. I wanted some smaller core that is extensible and heavily customizable.

Around that time Neovim started treating Lua as first class citizen. Putting all this together motivated me to write nvim-dap. A debug adapter protocol client implementation written in Lua that targets Neovim and is inspired by the extensibility of the built-in LSP client in Neovim.

Having written nvim-dap, I was able to add support for it to nvim-jdtls. nvim-jdtls contains functionality to automatically discover main classes and create debug configurations for them, and it provides methods to debug individual junit tests.

Wrap up

With this new setup I’ve been able to use Neovim instead of IntelliJ as my daily driver for quite a while now. Every now and then I still fire up IntelliJ for some functionality that I miss. Examples are some of the refactoring methods, like the one that allows to shift the order of arguments, or code inspections - for example the one to find unused declarations. is not perfect. The code completion often feels slow and in some cases doesn’t work as well, but a lot of the functionality of Neovim makes up for it.