summaryrefslogtreecommitdiff
path: root/content/guide/neovim-as-a-java-ide.md
blob: ae97c17aa5272a59a81beeb9b29c387831f422fb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
---
title: Neovim as a Java IDE
date: 2026-02-21
icon: neovim.png
---

## 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.

```sh
% nvim --version
NVIM v0.11.6
Build type: RelWithDebInfo
LuaJIT 2.1.1767980792
```

```sh
# jdtls
jdt-language-server-1.55.0-202601131729.tar.gz
```

```sh
% python3 --version
Python 3.14.3
```

```sh
% 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 :

```sh
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](https://github.com/mason-org/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.

```sh
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 -fLO $baseurl/$ver/$file
```

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

Next create those folders :

```sh
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`.

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

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

```sh
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](https://github.com/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.

```lua
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.