# java-language-server: Java Completion Powered by the Compiler API

> This MIT licensed language server implements Java features through the Java compiler API rather than by parsing source with a hand-written grammar, and it ships a VS Code extension alongside build instructions for Vim, Emacs, Kate and Sublime. It suits developers who want accurate Java navigation without a heavyweight IDE.

**georgewfraser/java-language-server** — Java language server using the Java compiler API

- Repository: https://github.com/georgewfraser/java-language-server
- Stars: 816 · Forks: 161
- Language: Java
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/georgewfraser-java-language-server

## One server, four editors, a plugin manager not required

The project is a Java language server based on version 3.0 of the language server protocol, and it is reachable from more editors than the VS Code extension suggests. The VS Code route is the marketplace extension. Vim uses the natebosch/vim-lsc plugin, Emacs uses Eglot, which is built into Emacs 29 and later and otherwise installable with `M-x package-install eglot`, Kate has an LSP client page under its settings, and Sublime 3 uses the LSP package from Package Control. That spread is the practical argument for the project: it is a single server binary that speaks the protocol, so the editor side is configuration rather than a port. One caveat is stated plainly, that the tool is not compatible with vim-lsp because it only supports LSP 2.0.

## Building the server for a non VS Code editor

The non VS Code route is the same four steps everywhere: check out the repository, download a JDK for your platform, link it, and build with Maven. The scripts in the `scripts/` directory take the platform as part of the name.

```bash
./scripts/download_{linux|mac|windows}.sh
./scripts/link_{linux|mac|windows}.sh
mvn package -DskipTests
```

Skipping the tests during the package step keeps the first build quick, which is reasonable for a tool you are installing rather than developing. The result is a launcher script per platform at `dist/lang_server_{linux|mac|windows}.sh` inside the `java-language-server` directory, and each editor's configuration points at that script. The repository root also holds `dist/`, `lib/`, `src/`, `scripts/`, `snippets/`, `git_hooks/` and `configure`, plus `pom.xml` for the Java build and `package.json` for the extension side, so the Java server and the VS Code extension are built from the same tree.

## Pointing Vim, Emacs and Kate at the launcher

Each editor needs the path to the launcher script, and the documentation shows the exact snippet. Vim with vim-lsc uses a server command map.

```vimrc
let g:lsc_server_commands = {'java': '<path-to-java-language-server>/java-language-server/dist/lang_server_{linux|mac|windows}.sh'}
```

Emacs with Eglot needs a hook in the init file.

```emacs-lisp
(add-hook 'java-mode-hook #'eglot-ensure)
```

For Eglot versions at or below 1.12.29, the server program has to be listed explicitly as well.

```emacs-lisp
(add-to-list 'eglot-server-programs '(java-mode . ("<path-to-java-language-server-dir>/dist/lang_server_{linux|mac|windows}.sh")))
```

For newer versions, from 1.19 onwards, the documentation says it is enough to have a script called `java-language-server` on the PATH that launches the real server, and it shows how to create one in `/usr/local/bin` on Linux. Kate instead wants a JSON block in the LSP Client user server settings, with a command array, a URL and a highlighting mode regex.

## Dependency resolution is the part that needs your build tool

The server provides autocomplete over .java files anywhere in the workspace, over the Java platform classes, and over external dependencies, and the last of those is where configuration enters. Dependencies can be specified through `pom.xml`, Bazel, or explicit settings, and the automatic detection covers what the build tool declares. When it does not detect them, you can list them in `.vscode/settings.json` under `java.externalDependencies`, in either Maven format or Gradle-style format.

```json
{
    "java.externalDependencies": [
        "junit:junit:jar:4.12:test", // Maven format
        "junit:junit:4.12" // Gradle-style format is also allowed
    ]
}
```

When even that fails, the class path and the source jar locations can be given by hand under `java.classPath` and `java.docPath`. The server looks for what you list in the Maven and Gradle caches at `~/.m2` and `~/.gradle`, so the build tool has to actually download both the binaries and the sources.

## Source jars, or the Javadoc popup stays empty

The documentation is direct about a step people skip. You should use your build tool to download the library and the source jars of all your dependencies, so the server can find them. For Maven that means `mvn dependency:resolve` for compilation and autocomplete, and `mvn dependency:resolve -Dclassifier=sources` for inline Javadoc help. For Gradle it means `gradle dependencies` for compilation and autocomplete, and including `classifier: sources` in the build file for the inline Javadoc.

```json
dependencies {
    testCompile group: 'junit', name: 'junit', version: '4.+'
    testCompile group: 'junit', name: 'junit', version: '4.+', classifier: 'sources'
}
```

The `java.docPath` setting is what points the server at those source jars. Without them, autocomplete still works, but the hover Javadoc and the inline documentation have nothing to show, which is a confusing half-broken state. The same split applies to the class path jars, which is why the manual `java.classPath` and `java.docPath` arrays are the fallback rather than the first choice.

## What the server actually implements

The feature list is documented with screenshots per capability rather than prose. Javadoc is rendered from the source jars, and signature help shows the parameter list at the call site. Autocomplete covers both symbols and members, and the symbol completion includes auto-import, which is the feature that saves the most typing in Java because it writes the import statement for you. Go-to-definition and find references work across the workspace, and there are two flavours of find symbols, workspace symbols and document symbols. Hover gives type information, and lint highlights errors in the buffer. There is also a debug feature, which is the one area where a language server built on the compiler API is unusual, since debugging normally needs more than type resolution.

## Why the compiler API rather than a parser

The design section explains the core choice: the Java language server uses the Java compiler API to implement language features like linting, autocomplete and smart navigation, and the language server protocol to talk to editors. This matters more than an implementation detail, because the compiler API is the same machinery javac uses, so type resolution, overload selection and generics are handled by the platform rather than by a reimplementation. The trade-off is that the server inherits the compiler's behaviour and its costs. The incremental updates section starts to describe this, noting that the compiler API provides incremental compilation at the level of files, so a long-lived compiler instance can be reused as the user edits rather than restarted for every keystroke.

## Maintenance, packaging and what is not in the readme

The repository is not archived and the last push was on 2026-10-02, so the code is being worked on, but it publishes no GitHub releases, so there is no tag to pin and you build from the source tree. The extension side has its own version, 0.2.49 in the package manifest, and the extension requires VS Code `^1.96.0` or newer. The package manifest also shows what the extension contributes beyond the server: a grammar for Java sourced from `lib/java.tmLanguage.json`, snippets from `snippets/java.json`, a language configuration file, and activation events on `onLanguage:java` and `onDebug`, which is how the debug feature starts. There is a `.vscode-test.mjs` file for extension tests, `TODOS.md`, an `AGENTS.md` and a set of `git_hooks/`. The licence is MIT. The repository root has no separate homepage field, and the documentation lives in the readme itself.

## Conclusion

java-language-server fits a developer who wants Java autocomplete, go-to-definition and linting that agree with javac, and who wants to use it in an editor other than a full IDE. It is the wrong choice if you need the debugging features of a commercial Java tool, though its automatic dependency detection is oriented around Maven and Gradle, with Bazel supported and manual settings as a fallback. Before configuring it, run your build tool's dependency resolve command so the server finds both the jars and the source jars in the local caches.

## FAQ

### Which editors can use the java-language-server?

VS Code through the marketplace extension, Vim with the natebosch/vim-lsc plugin, Emacs with Eglot, KDE Kate through its LSP client settings, and Sublime 3 with the LSP package. It is not compatible with vim-lsp because it only supports LSP 2.0.

### How do I build the server outside VS Code?

Check out the repository, run the platform script for downloading a JDK and the linking script, then build with mvn package -DskipTests. The launcher scripts end up at dist/lang_server_{linux|mac|windows}.sh in the java-language-server directory.

### How does the server find external dependencies?

It reads what your build tool declares in pom.xml or Bazel, and it looks in the Maven and Gradle caches at ~/.m2 and ~/.gradle. You can also list dependencies explicitly under java.externalDependencies in .vscode/settings.json, in Maven or Gradle format, or set java.classPath and java.docPath by hand.

### Why is my inline Javadoc empty?

Because the source jars are missing. For Maven you need mvn dependency:resolve for compilation and autocomplete and mvn dependency:resolve -Dclassifier=sources for the inline Javadoc, and for Gradle you include classifier: sources in the build file.

### What language features does the server provide?

Javadoc, signature help, symbol and member autocomplete with auto-import, go-to-definition, find references, workspace and document symbol search, lint error highlighting, type information on hover, and a debug feature.

## Sources

- [georgewfraser/java-language-server on GitHub](https://github.com/georgewfraser/java-language-server)
- [Issues](https://github.com/georgewfraser/java-language-server/issues)
- [License: MIT](https://github.com/georgewfraser/java-language-server/blob/master/LICENSE)
- [README](https://github.com/georgewfraser/java-language-server/blob/master/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/georgewfraser-java-language-server
