Neovim as a Java IDE

Table of Contents

Préambule

Most of the time, you can configure Neovim painlessly with a simple vim.lsp.enable("some_lsp_server"). And boom, autocompletion, go to definition. Everything works out of the box. But that’s not the case with Java, you can’t check the definition of classes from the java, javafx or whatever package that is outside of your project, and most of the time the language server takes forever to load up or simply doesn’t start.

To fix these issues, we will not enable jdtls from the vim.lsp.enable function, and we will use the mfussenegger/nvim-jdtls plugin.

In this tutorial, I assume that you know how to download plugins and edit you init.lua file. You should also already have a completion plugin.

Versions

I think that the version of python, jdtls and nvim might affect the accuracy of this tutorial. So I will give you the versions I use as a reference in case you encounter some bugs.

% nvim --version
NVIM v0.11.6
Build type: RelWithDebInfo
LuaJIT 2.1.1767980792
# jdtls
jdt-language-server-1.55.0-202601131729.tar.gz
% python3 --version
Python 3.14.3
% java --version
openjdk 25.0.2 2026-01-20

Downloading Java and jdtls

Java

First of all, you should install OpenJDK. You should download the latest version (at the time being it’s version 25, but 21 should work).

jdtls with pacman

If you use pacman on your Linux distro it’s really easy. Just build the AUR package :

git clone https://aur.archlinux.org/jdtls.git
cd jdtls/
makepkg -si

jdtls without pacman

On other distros, I do not know how to install jdtls with the ‘official’ way. I know you could download it with the mason.nvim plugin, but it will make you go through additional configuration steps. I prefer downloading jdtls and adding it to my $PATH myself.

So first of all download the latest tarball.

baseurl="https://download.eclipse.org/jdtls/milestones"

ver=$(
  curl -s "$baseurl/" |
  grep -oE '/jdtls/milestones/[0-9]+\.[0-9]+\.[0-9]+' |
  awk -F/ '{print $4}' |
  sort -V |
  tail -n1
)

file=$(
  curl -s "$baseurl/$ver/" |
  grep -oE "jdt-language-server-${ver}-[0-9]+\.tar\.gz" |
  head -n1
)

curl -fL "https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/1.56.0/$file" -o jdtls.tar.gz

You should now have a jdtls.tar.gz file in your current working directory. Do not untar it now.

Next create those folders :

sudo mkdir -pv /usr/local/share/java/jdtls

And you can now extract the content of jdtls.tar.gz into /usr/local/share/java/jdtls.

sudo tar xf jdtls.tar.gz -C /usr/local/share/java/jdtls

Next create a symlink to the jdtls binary in your $PATH :

sudo ln -s --relative /usr/local/share/java/jdtls/bin/jdtls /usr/local/bin/jdtls

You have successfully installed jdtls !

Configuring Neovim

First of all, install the mfussenegger/nvim-jdtls plugin with your neovim plugin manager.

Then, DO NOT enable jdtls with the vim.lsp.enable function.

Instead, append this at the end of your init.lua file.

vim.api.nvim_create_autocmd("FileType", {
	pattern = "java",
	callback = function(args)
		local project_name = vim.fn.fnamemodify(vim.fn.getcwd(), ":p:h:t")
		local workspace_dir = vim.fn.stdpath("data") ..
		    package.config:sub(1, 1) .. "jdtls-workspace" .. package.config:sub(1, 1) .. project_name

		local config = {
			name = "jdtls",
			cmd = {
				"jdtls",
				"-data",
				workspace_dir,
			},

			root_dir = vim.fs.root(0, { ".git", "gradlew", "mvnw" }),

			-- Here you can configure eclipse.jdt.ls specific settings
			-- See https://github.com/eclipse/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request
			-- for a list of options
			settings = {
				java = {
				}
			},


			-- This sets the `initializationOptions` sent to the language server
			-- If you plan on using additional eclipse.jdt.ls plugins like java-debug
			-- you'll need to set the `bundles`
			--
			-- See https://codeberg.org/mfussenegger/nvim-jdtls#java-debug-installation
			--
			-- If you don't plan on any eclipse.jdt.ls plugins you can remove this
			init_options = {
				bundles = {}
			},
		}
		require('jdtls').start_or_attach(config)
	end
})

Now, if you restart Neovim, and open a Java file in a project that has a .git, mvnw, or gradlew file at the top level directory. jdtls should work out of the box and you will be able to use all it’s lsp features.