Neovim and Java with LazyVim, part 2: IDE mechanisms

This is the second part of a few tutorials on Java development with Neovim using the LazyVim setup.

This post assumes you already read and applied all the steps of the first part.

You need Java installed (possibly 21), while Maven is optional.

I will use this Maven example during the tutorial: https://github.com/LorenzoBettini/maven-bank-example, part of my TDD book.

The final result of this series of tutorials can be found here: https://github.com/LorenzoBettini/lazyvim-java. The “main” branch always points to the latest blog post.

The end of the first part is still the branch “first-blog-post” (this blog post does not touch the configuration files).

In the first part, we saw how to enable Java LSP in the LazyVim distribution. We also saw a few interesting features for programming in Java in Neovim with such a configuration. In this post, we’ll see a few of the IDE mechanisms we get from the Java LSP in LazyVim. Let’s continue from where we left off (remember, I’ll use the above-mentioned Maven project).

Code completion and snippets

Let’s instead create a new Java file inside a source folder of this Maven project (e.g., starting from “src/main/java”), either with “:e <path>” or with the “Neotree” file explorer that comes installed and configured with LazyVim: toggle the explorer with “<leader> e”. I’ll create an Example.java inside the “app” subfolder: get to that folder, press “a” (for add), write the name “Hello.java”, and press ENTER to confirm:

Pressing ENTER again will open the empty file in a new buffer.

We’ll use snippets configured by default instead of writing all the contents, e.g., the correct package and the class implementation. Let’s enter INSERT mode and press Ctrl+SPACE. Start typing “cl”, we should get to the snippet we need (see the preview on the right): the one with all the correct contents:

Press ENTER to accept the snippet, and we get a valid Java class, with the cursor positioned inside the class’ body:

Let’s keep on using snippets to create a “main” method:

And a “System.out.println” statement:

Till you can write the “Hello World” string as an argument!

Snippets come from various sources (including the “friendly-snippets” plugin that LazyVim configures by default; (see https://github.com/rafamadriz/friendly-snippets/blob/main/snippets/java/java.json for the available templates). However, the best ones are those from Jdtls (like the “class” snippet we used above).

You might want to explore other useful snippets, like creating a public method:

Once the snippet has been selected, you have placeholders to edit the return type, the name, etc. Start typing to insert the return type, press TAB to get to the name, change the name, and press TAB to get inside the parenthesis to insert the parameters possibly. The final TAB will get you inside the method body. Shift TAB goes in the other direction.

Code actions

Let’s look at a few other features provided by LazyVim Java Extra. Note that these LSP-related features are not strictly related to the Java LSP but to the general configurations for LSPs. The same keybindings and mechanisms are meant to work with other LSPs, though not all LSPs implement all such capabilities.

You have code actions, using “<leader> c a”, which depend on the context you’re in the source file:

This menu is also handy for having the IDE fix errors for you (handy when you apply TDD); in Eclipse, we’d call them “quick fixes”, but in this context, “quick fix” is meant for quickly seeing all the diagnostics. For example, I intentionally refer to a non-existing symbol, and I use “Code actions” to have the IDE create that field for me (remember to exit insert mode to get the diagnostics; by the way, such dialogs can be filtered starting typing something to access the desired item quickly):

As another example, let’s change the package name in the “Hello.java” file we created above:

As expected, this makes the file invalid, as reported in the editor (and also in the explorer). Let’s use the code actions:

We use the first one because we want the file to be moved to the correct source directory:

Let’s do the opposite, press “c” in the Explorer to cut it:

move to the “app” folder and “p” to paste it:

The file is invalid again; this time, we use the second code action to change the package name in the file to respect the current source directory (remember that you must be in the buffer for code actions, not in the Neotree; you can use Ctrl + L to move to the right window):

Navigating through symbols

You might explore other features like showing symbols; there are many ways of achieving that in LazyVim and its default plug-ins. You can use “<leader> s k” to search for keybindings and start typing “Symbol”. Here are some examples.

“<leader> s s” gives you symbols with Telescope:

“<leader> s S” gives you symbols with Telescope in the entire workspace; the workspace is an LSP concept, but for the Java LSP, it can be seen as the Eclipse workspace that is being used under the hood: you get access to all the Java source files and all the types in the dependencies of the project. Start typing (be patient if you have a workspace/project with tons of dependencies), and you should see the classes of your sources and dependencies. In this example, we can access all the types starting with “Assert” from the JDK and the dependencies (in this project, JUnit, AssertJ, Log4J).

Selecting one will open it in the editor. If it comes from a dependency, the LSP will download the sources of the corresponding Maven artifact and show them in a read-only buffer.

“<leader> c s” gives you the “Trouble” symbol window, which is similar to the Eclipse Outline (note that it is synchronized with the editor; I changed the color theme to make selections more visible):

“<leader> c S” (capital “S”) gives you the “Trouble” window with references and calls to the currently focused element (again, the window is kept synchronized):

Then you have all the navigation capabilities, typically starting with “g,” like goto definition or goto references (opening a telescope picker for all the references in the workspace). You can also explore jump operations like “[c”, “]c”, “[f”, “]f” to navigate around a source code (class definitions, method definitions, etc.)

You also have some refactorings, like “Rename” (of course, updating references throughout the project’s files) and “Extract” (variable, constant, methods). You can experiment with them by searching for them with “<leader> s k”  or using “<leader> c” and then waiting for the available commands (provided by the Neovim plugin “which-ley”, automatically configured by LazyVim).

That’s all for this post; stay tuned for future blog posts, where we’ll cover:

  • dealing with Maven dependencies (with the current configuration, editing POM files is not yet ideal);
  • how to run/debug Java programs (with the current configuration, this is not yet possible);
  • how to run tests
  • maybe other features

One thought on “Neovim and Java with LazyVim, part 2: IDE mechanisms

  1. Pingback: Neovim and Java with LazyVim, part 1: initial configuration | Lorenzo Bettini

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.