Monthly Archives: November 2024

Neovim and Java with LazyVim, part 1: initial configuration

This is the first part of a few tutorials on Java development with Neovim using the LazyVim setup. I highly recommend LazyVim because it has many cool plugins configured with nice defaults. Moreover, as we see in this tutorial, it lets you quickly have the Java LSP up and running in Neovim.

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, which is part of my TDD book.

You can follow this step-by-step tutorial by implementing the steps starting from a fresh LazyVim installation (see http://www.lazyvim.org/installation). If you already use LazyVim, you might want to do the steps in this tutorial on a fresh, separate Neovim configuration directory before applying all the configurations in your daily-driver Neovim configuration.

For example, if you start with the starter project provided by LazyVim, you can clone it into a separate configuration directory:

And then you start Neovim pointing to that configuration:

This way, your existing Neovim configuration won’t be touched, and you’ll be free to experiment with this new configuration.

In these tutorials, the path is always intended relative to the Neovim configuration path when referring to a configuration file. If you follow the above commands, for example, it will be relative to “~/.config/lazyvim-java”.

I suggest you try to follow the tutorials by manually executing the configuration steps in such a new separate Neovim configuration.

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 the branch “first-blog-post”.

A few initial notes

LazyVim comes with autoformatting on save. If you don’t want to disable that in configuration, at least you can use these commands to toggle it during the current session:

  • “<leader> u f” Toggle Auto Format (Global)
  • “<leader> u F” Toggle Auto Format (Buffer)

Moreover, by default, LazyVim is configured to replace tabs with spaces. I don’t like such a behavior globally, so I disable it by adding this line to the existing file “config/options.lua”

To make the tutorial more readable, I will use a few different “light” themes in Neovim instead of the default dark one. Remember that you can temporarily switch the theme with “leader uC”.

Let’s start

If you cloned the above Maven Java example, you can try opening one of its Java files in Neovim:

Without the Java LSP, you only have basic syntax highlighting but no other IDE tooling.

Let’s install the LazyVim “Java Extra”. A LazyVim extra is meant to configure several plugins and configurations with a single mechanism. In this case, it will configure the Java LSP (“jdtls”, the Java LSP provided by Eclipse, implementing all the excellent Eclipse IDE Java development features) and configure other related plugins and keybindings.

You have some alternatives to enable an “Extra”, which is documented for each available “Extra”; for Java, see http://www.lazyvim.org/extras/lang/java. I prefer to use the UI: Type “:LazyExtras” to show the LazyVim Extras UI:

The LazyExtras UI proposes some recommended extras based on the files you’ve just opened, so it should recommend “lang.java”; if not, search for “lang.java” and when you’re on that line, press “x” to install the extra.

You get the feedback about restarting Neovim:

Specifying an extra as we’ve just done creates a file in the configuration, “lazyvim.json”, with this content (the numbers for news and version might be different depending on when you follow this tutorial):

The idea is to keep this file in the Git repository for your configuration as part of your dotfiles for Neovim.

You can now try and open a Java file from this project.

It will not work the first time because Mason has to install the LSP. So, it would try to start the Java LSP (“jdtls”) before it is fully installed. After you see that Mason has successfully installed “jdtls”, you can restart Neovim.

Of course, you could force the installation of “jdtls” by using the Mason UI, “:Mason”: select “jdtls” and install it.

Alternatively, if you want a fully automated solution that, besides installing all the Neovim plugins, also automatically installs the Java LSP, you can add this Lua specification in the Neovim configuration folder (the name does not matter; I call it “extend-lsp” to stress that it extends the Mason configuration of LazyVim, https://github.com/LazyVim/LazyVim/blob/main/lua/lazyvim/plugins/lsp/init.lua) “lua/plugins/extend-lsp.lua” with this content:

Now, when you start Neovim, “jdtls” will automatically install. Remember that “jdtls” is a big Java JAR, so you might want to check whether Mason has successfully downloaded and installed it or is still installing it through the Mason UI.

It works best if you start Neovim from the root directory of the Maven project (the one containing the “pom.xml”). If you open a Java file from another directory, the configured LSP will still search for a “pom.xml” in the parent directories until it finds one (other valid files are the Gradle and Ant specification files if the project is not based on Maven); the root of a Git repository is also a valid root directory.

The highlighting looks much better, with many more highlighted parts (thanks to Treesitter, which the LazyVim Java Extra includes), and you can see the messages from the Java LSP doing its checks and tasks (including downloading Maven dependencies if they are not already cached):

This is a Maven project, so the Java LSP has to resolve the Maven dependencies. If this is the first time you open this project, you should see messages about downloading Maven dependencies.

When all dependencies have been resolved, and the Java LSP has correctly compiled the sources, you can also see some editor decorations (inlay hints), like the parameter names, when calling a method (of course, those parts are NOT part of the source, but only visual content in the editor):

You can enable/disable inlay hints with “<leader> u h”.

With “<leader> c C” you can also refresh “code lens”; for Java, you get the references to classes and methods:

Hovering support (“K”) allows you to see the Javadoc of the current element, e.g., for String:

Pressing any key will make the pop-up window go away. If you want to scroll the pop-up window, use “Ctrl w w” (switch window), and you can scroll the Javadoc.

You’re ready to develop your Java program using typical IDE tooling, like content assist.

Exiting the “insert mode” automatically triggers Java validation, possibly showing warnings and error markers in the editor.

NOTE: The Jdtls LSP (i.e., how LazyVim configures such an LSP by default) creates an Eclipse workspace in the directory “~/.cache/lazyvim-java/jdtls/” in a subdirectory named after the Maven project, e.g., in this example, “~/.cache/lazyvim-java/jdtls/maven-bank-example”. This is just information since you don’t need to care about the created workspaces, which are meant to be ephemeral.

The example used in this post is an Eclipse project. The LSP detects that and possibly updates the Eclipse metadata files accordingly, e.g., “.classpath”, “.settings” directory, “.project”, etc.). If you start with a Maven project (not Eclipse) since the Java LSP is the one from Eclipse, it will create the above Eclipse metadata files automatically for you.

Interestingly, you could use a plain existing Eclipse project, and the Java LSP would still work in Neovim. For example, this is an Eclipse project (not Maven), again part of my TDD book: https://github.com/LorenzoBettini/assertj-log4j-bank-example. It contains the JARs for Assertj and Log4J, and the “.classpath” refers to the JARs that are included. It works seamlessly in Neovim!

Everything works also with multi-module Maven projects; try, e.g., https://github.com/LorenzoBettini/maven-bank-multimodule-example, again part of my TDD book.

You can explore such IDE tooling by modifying one of the existing files.

Otherwise, stay tuned for future blog posts, where we’ll cover:

  • IDE mechanisms provided by this configuration;
  • 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

That’s all for the first part; stay tuned! 🙂

Sway WM: Notifications with Mako

Let’s continue the Sway window manager (in Arch Linux) series I started in my previous post.

In this post, we deal with system notifications. (This post is similar to the corresponding one about Hyprland.)

We use mako, a lightweight notification daemon for Wayland, one of the optional software suggested when we installed Sway. We also need “inotify-tools”:

The nice thing about mako is that you don’t need to start it manually as a service: the first time a notification is emitted, mako will run automatically.

Let’s try to run the notification command:

We see the pop-up in the screen’s top right corner by default.

You can have a look at mako’s manual (5) about its configuration file and where it is searched for:

An example configuration, usable as a starting point, can be found here: https://github.com/emersion/mako/wiki/Example-configuration.

Each time you modify the configuration, you must reload mako by using one of the following commands:

or

With that example configuration, we can emit a few notifications with different “urgencies”, and see the different colors and positions of the boxes:

If you use EndeavourOS, you will get notifications about new updates and when a reboot is required after a system update (the latter is a “critical” notification):

That’s all for this post!

Stay tuned for another post about Sway WM.

Speeding up AUR package installations in Arch Linux

This post can be used for Arch Linux and EndeavourOS.

Recently, a change was committed that highly compresses AUR packages before the actual installation. This takes a lot of time: even on a fast machine, installing something like Chrome or Visual Studio (AUR packages) takes even minutes just for the compression phase. Since I typically remove the generated packages in the AUR cache after installation (e.g., “~/.cache/yay”), the compression is entirely useless to me.

To disable compression altogether, you can edit the file “/etc/makepkg.conf” (requires sudo, of course) and change the line:

into:

And you’re done! Now, even if the string “Compressing package…” is printed on the screen when installing an AUR package, that is actually a no-op.

If you don’t want to change the system file “/etc/makepkg.conf”, you can create this file in your user home, “~/.config/pacman/makepkg.conf”, whose contents take precedence over the ones of the system file. Just put the line:

And you’re done!

Java, Maven and Gitpod, part 4: Maven and Dependencies

This is the 4th post about using Java in Gitpod.

It assumes you have already read the first, second, and third posts.

Let’s now use the POM editor to update the version of JUnit (the Maven archetype is based on an old version of JUnit). When we open the “pom.xml” in the editor, we get a pop-up suggesting installing another extension for analyzing dependencies.

Let’s accept that, as done before, without synching. After the extension is installed, we can visit the corresponding entry in the extensions tab, and, as done before, we use the gear icon to add the extension to our “.gitpod.yml”.

We can then click on the bottom status bar’s button corresponding to the extension (see the one in the left corner in the screenshot) to have an analysis report:

Once it’s finished, we can look at the report. For this simple example, we don’t have any vulnerabilities in our dependencies:

Let’s go back to editing the “pom.xml”; by using the code completion, we can select the newer version of JUnit 4:

Let’s select the latest version (at the time of writing, it is 4.13.2) and let the Java LSP rebuild the project. When we update a dependency in the POM, the analyzer seen above automatically performs another analysis (you can see the feedback in the bottom status bar).

Let’s now explore the “Maven” outline:

We can run lifecycle phases (“compile” in the screenshot above) or single plug-in goals (something I don’t find in the Eclipse Maven support). We can also see the dependencies and their transitive dependencies (“hamcrest” is a transitive dependency of JUnit).

The “Dependencies” entry provides a “+” icon to add a dependency by using the UI. A pop-up will appear where you type a part of the dependency, press ENTER, and see some completions. For example, for log4j:

Upon selection, the POM will be updated. We intentionally selected an old version of LOG4J with a known vulnerability issue to show that the Dependency Analyzer we previously installed detects that:

We can undo the modification we made only for demonstration or use a recent version of log4j (version 2).

We can also create new “Favorites” commands (by pressing the “+” it appears when hovering on “Favorites”). This will open a pop-up for letting us insert a Maven command we want to run often.

For example, let’s insert “package -DskipTests=true”. The favorite will be saved into the Git repository in the file “.vscode/settings.json”. We can also manually tweak it, giving it an alias, e.g., “Create JAR without running tests” (note that we can use content assist in the JSON file):

Now, we have the favorite with the given alias that we can run by using the corresponding triangle:

Speaking of language support for the “settings.json” file, I prefer to disable “autosave”, especially with LSP running in the background, which will continuously ask to synchronize the Java project each time you start modifying the POM. Using the content assist, it is easy to find the corresponding entry and disable autosave:

Stay tuned for the fifth part!