/eglot-java

Java extension for the eglot LSP client

Primary LanguageEmacs LispGNU General Public License v3.0GPL-3.0

README

http://img.shields.io/badge/license-GNU%20GPLv3-blue.svg https://melpa.org/packages/eglot-java-badge.svg https://stable.melpa.org/packages/eglot-java-badge.svg

All the bugs reports are welcome and appreciated.

  • Please note that the best way to contribute is via pull requests.
  • I may not notice immediately when something is broken, as I don’t really program professionally anymore.

Overview

This package provides additional Java programming language support for eglot.

  • Few convenience functions are available for common operations such as creating a new project or class.
  • There are couple of customization options, including passing custom JVM arguments to the Java language server.
  • The Eclipse JDT language server is automatically installed (if needed) when you open a Java file:
    • This downloads the latest known milestone release
    • This gives you code completion, refactoring capabilities and other features
FunctionDescriptionAuto-load
eglot-java-project-newCreate new projects with a wizard (Spring, Micronaut, Quarkus, Vert.x, Maven or Gradle)YES
eglot-java-project-build-refreshRebuild the current projectNO
eglot-java-project-build-taskRun a build task using Gradle or Maven for the current projectNO
eglot-java-file-newWizard for creating new Java filesNO
eglot-java-run-mainRun the current main classNO
eglot-java-run-testRun the current test (JUnit only, unless you run Maven or Gradle tasks)NO
eglot-java-upgrade-lsp-serverUpgrade the LSP server installationYES
eglot-java-upgrade-junit-jarUpgrade the JUnit jar installationYES

Note: eglot-java dynamically modifies the eglot-server-programs variable, you can change that behavior with the variable eglot-java-eglot-server-programs-manual-updates (See #44).

  • You may prefer using the eglot defaults (jdtls Python script), eglot-java doesn’t use that
  • eglot-java calls the relevant Java command directly, both for historical reasons and for potentially avoiding any Python dependency (Windows, Mac OS)

Dependencies

  • The only direct Emacs package dependency is eglot, assuming you’re running a recent Emacs version.
  • The Eclipse JDT Language server along with the JUnit console runner are configured to be automatically installed when not found at default locations (see M-x customize-group eglot-java).
    • The Eclipse JDT.LS server has several features, and it’s recommended that you use a recent enough version of the Java Development Toolkit (JDK).
    • This emacs package has been tested for few years (roughly since mid-2018).
  • Please also configure your PATH environment variable for Maven and/or for Gradle. Gradle support isn’t that mature compared to Maven overall (Maven has been around much longer…).

Installation

The eglot-java package is available on MELPA.

Once the MELPA repository is configured, please run M-x package-install and type eglot-java.

Usage

Customization

You can configure few settings to reflect your preferences via M-x customize-group (eglot-java):

  • You can set the default LSP server installation folder, etc.
  • You can specify JVM arguments for the LSP server (eglot-java-eclipse-jdt-args variable).
  • You can control eglot initialization of LSP server settings through the eglot-java-user-init-options variable. Its value will be merged with defaults (e.g., specifying code formatting profiles, etc.).
  • etc.

Configuration

Initialization

Below is a sample minimal configuration, without any fancy library such as use-package or similar.

(add-hook 'java-mode-hook 'eglot-java-mode)
(with-eval-after-load 'eglot-java
  (define-key eglot-java-mode-map (kbd "C-c l n") #'eglot-java-file-new)
  (define-key eglot-java-mode-map (kbd "C-c l x") #'eglot-java-run-main)
  (define-key eglot-java-mode-map (kbd "C-c l t") #'eglot-java-run-test)
  (define-key eglot-java-mode-map (kbd "C-c l N") #'eglot-java-project-new)
  (define-key eglot-java-mode-map (kbd "C-c l T") #'eglot-java-project-build-task)
  (define-key eglot-java-mode-map (kbd "C-c l R") #'eglot-java-project-build-refresh))

Other notes

LSP server startup errors

Usually this is due to starting the LSP server with an old Java version (see issue #29).

Intermittent eglot timeout errors

You might want to set the value of eglot-sync-connect or eglot-connect-timeout.

  • Please consult inspect the relevant variable documentation documentation with C-h v.
  • See eglot issues #68 and #1342

Gradle projects

If you have issues with Gradle projects (code completion not working), then it’s likely due to version incompatibilities (JDK and bundled Gradle Eclipse versions):

Class file navigation

The classFileContentsSupport capability is registered with some known limitations. After visiting an initial “class contents buffer”, further type definition navigation is not supported. This can be mitigated by the following workflow:

  • Go back to the previous Java buffer
  • Call M-x xref-find-apropos with the name of the class to lookup (fully qualified name or simple class name)
    • Sometimes the fully qualified class name gives you good results
    • However, if you don’t see the class name in question, please type the simple class name instead

LSP server upgrades

In earlier versions of eglot-java, the LSP server installation was reflecting the latest available snapshot.

As of eglot-java 1.11 (December 2023), only milestones releases will be installed in order to mitigate challenges with buggy snapshot versions (See issues #15 and #16 for reference).

LSP server initialization options

Sometimes you may want to add/modify LSP server initialization settings. There are tons of them…

  • For basic flexibility, you can control the settings node of the LSP server configuration via the variable eglot-workspace-configuration. This is best suited for project-specific configuration.
  • For greater flexibility, you can leverage the eglot-java-user-init-opts-fn variable
    • You’ll need to bind the value of eglot-java-user-init-opts-fn with your own callback function
    • You’ll need to return a property list of valid JDT.LS settings (merged with defaults):
      • In eglot keys (property names) are keywords symbols (e.g., :key)
      • Instead of curly braces in JSON, you use nested parenthesis (e.g., (:java (:home "/usr/share/jdk21")))
      • For boolean values, use t for “true” and :json-false for “false”

In the example below, the Google style of formatting is configured for later invocation via M-x eglot-format.

(setq eglot-java-user-init-opts-fn 'custom-eglot-java-init-opts)
(defun custom-eglot-java-init-opts (server eglot-java-eclipse-jdt)
  "Custom options that will be merged with any default settings."
  '(:settings
    (:java
     (:format
      (:settings
       (:url "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml")
       :enabled t)))))

The list of LSP server settings is available in the Eclipse JDT.LS wiki on GitHub:

Per general Eclipse JDT.LS documentation, a basic skeleton of an initialization customization could look as follow:

'(:bundles: ["/home/me/.emacs.d/lsp-bundles/com.microsoft.java.debug.plugin-0.50.0.jar"]
  :workspaceFolders: ["file:///home/me/Projects/mavenproject"]
  :settings: (:java (:home "/usr/local/jdk21"))
  :extendedClientCapabilities (:classFileContentsSupport t))

Debugging support

Please first setup the LSP :bundles in custom LSP initializing settings (per previous example)

Systems such as NixOS or Gnu Guix

While eglot-java offers few auto-configuration settings for user convenience, this can be challenging for NixOS or Gnu Guix users.

There are couple of variables that you can customize (M-x customize-variable):

  • You can set a fixed location for the LSP installation folder (eglot-java-server-install-dir)
  • You can define additional JVM parameters to avoid read-only file system errors (eglot-java-eclipse-jdt-args)
  • You can configure the folder containing the server config.ini file (eglot-java-eclipse-jdt-config-directory)

Please also see issue #46.