eglot-java
eglot-java copied to clipboard
Java extension for the eglot LSP client
#+TITLE: README
[[https://github.com/yveszoundi/eglot-java/blob/main/LICENSE][file:http://img.shields.io/badge/license-GNU%20GPLv3-blue.svg]] [[https://melpa.org/#/eglot-java][file:https://melpa.org/packages/eglot-java-badge.svg]] [[https://stable.melpa.org/#/eglot-java][file: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 [[https://github.com/joaotavora/eglot][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 [[https://github.com/eclipse/eclipse.jdt.ls][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
|------------------------------------+-----------------------------------------------------------------------------------------+-----------| | Function | Description | Auto-load | |------------------------------------+-----------------------------------------------------------------------------------------+-----------| | =eglot-java-project-new= | Create new projects with a wizard ([[https://start.spring.io/][Spring]], [[https://micronaut.io/][Micronaut]], [[https://quarkus.io/][Quarkus]], [[https://vertx.io/][Vert.x]], [[https://maven.apache.org/][Maven]] or [[https://gradle.org/][Gradle]]) | YES | | =eglot-java-project-build-refresh= | Rebuild the current project | NO | | =eglot-java-project-build-task= | Run a build task using [[https://gradle.org/][Gradle]] or [[https://maven.apache.org/][Maven]] for the current project | NO | | =eglot-java-file-new= | Wizard for creating new Java files | NO | | =eglot-java-run-main= | Run the current main class | NO | | =eglot-java-run-test= | Run the current test ([[https://junit.org/junit5/][JUnit]] only, unless you run Maven or Gradle tasks) | NO | | =eglot-java-upgrade-lsp-server= | Upgrade the LSP server installation | YES | | =eglot-java-upgrade-junit-jar= | Upgrade the JUnit jar installation | YES | |------------------------------------+-----------------------------------------------------------------------------------------+-----------|
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 https://github.com/yveszoundi/eglot-java/issues/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 [[https://github.com/joaotavora/eglot][eglot]], assuming you're running a recent Emacs version.
- The [[https://projects.eclipse.org/projects/eclipse.jdt.ls/downloads][Eclipse JDT Language server]] along with the [[https://mvnrepository.com/artifact/org.junit.platform/junit-platform-console-standalone][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 [[https://github.com/eclipse-jdtls/eclipse.jdt.ls#features][several features]], and it's recommended that you use a recent enough version of the Java Development Toolkit ([[https://www.oracle.com/java/technologies/downloads/][JDK]]).
- This emacs package has been tested for few years (roughly since mid-2018).
- Please also configure your [[https://www.java.com/en/download/help/path.html][PATH]] environment variable [[https://www.tutorialspoint.com/maven/maven_environment_setup.htm][for Maven]] and/or [[https://docs.gradle.org/current/userguide/installation.html][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 [[https://melpa.org/#/getting-started][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.
#+begin_src elisp (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)) #+end_src
- Other notes
** LSP server startup errors
Usually this is due to starting the =LSP= server with an old Java version (see issue [[https://github.com/yveszoundi/eglot-java/issues/29][#29]]).
- Please ensure that your =JVM= version meets the [[https://github.com/eclipse-jdtls/eclipse.jdt.ls#requirements][compatibility requirements]] for the [[https://projects.eclipse.org/projects/eclipse.jdt.ls][Eclipse JDT.LS server]].
- As of early 2024, you need to start the =LSP= server with =JDK 17= or later.
** 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 [[https://github.com/joaotavora/eglot/issues/68][#68]] and [[https://github.com/joaotavora/eglot/issues/1342][#1342]]
** Gradle projects
If you have issues with [[https://gradle.org/][Gradle projects]] (code completion not working), then it's likely due to [[https://docs.gradle.org/current/userguide/compatibility.html][version incompatibilities]] (JDK and bundled Gradle Eclipse versions):
- The safe approach is to always rely on the Gradle wrapper, accordingly to the [[https://docs.gradle.org/current/userguide/compatibility.html][Gradle compatibility matrix]]
- As of December 2022, the latest Eclipse JDT.LS would include the [[https://github.com/eclipse/eclipse.jdt.ls/blob/master/org.eclipse.jdt.ls.target/org.eclipse.jdt.ls.tp.target#L14][buildship plugin version 3.17.x]] for Gradle support
- For version =3.17.x= that [[https://github.com/eclipse/buildship/blob/master/org.gradle.toolingapi/META-INF/MANIFEST.MF][translates into Gradle 7.4.2 or later]]
- =Gradle 7.4.2= would not be compatible with let's say =JDK 19=, the user would need [[https://docs.gradle.org/current/userguide/compatibility.html][Gradle 7.6]], thus why it's best to always use/generate the Gradle wrapper for peace of mind
** 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 [[https://github.com/yveszoundi/eglot-java/issues/15][#15]] and [[https://github.com/yveszoundi/eglot-java/issues/16][#16]] for reference).
** LSP server initialization options
Sometimes you may want to add/modify [[https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request][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 [[https://www.gnu.org/software/emacs/manual/html_node/eglot/User_002dspecific-configuration.html][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 [[https://www.gnu.org/software/emacs/manual/html_node/elisp/Property-Lists.html][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=.
#+begin_src emacs-lisp (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))))) #+end_src
The list of LSP server settings is available in the [[https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki][Eclipse JDT.LS wiki]] on GitHub:
- [[https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Language-Server-Settings-&-Capabilities][General summary and list of extended capabilities]]
- [[https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request][Available settings (names and data types)]]
Per general Eclipse JDT.LS documentation, a basic skeleton of an initialization customization could look as follow:
#+begin_src emacs-lisp '(: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)) #+end_src
** Debugging support
Please first setup the LSP =:bundles= in custom LSP initializing settings (per previous example)
- You can download the latest version of the [[https://github.com/microsoft/java-debug][Microsoft Debug Adapter Protocol (DAP)]] jar from [[https://repo1.maven.org/maven2/com/microsoft/java/com.microsoft.java.debug.plugin/][Maven central]]
- I then recommend installing [[https://github.com/svaante/dape][dape]]
- The package is available on [[https://elpa.gnu.org/packages/dape.html][GNU ELPA]] (=M-x package-install=)
- Utility functions developed by [[https://github.com/MagielBruntink/dape/blob/jdtls-extension/dape-jdtls.el][MagielBruntink]] can be useful
** Systems such as NixOS or Gnu Guix
While =eglot-java= offers few auto-configuration settings for user convenience, this can be challenging for [[https://nixos.org/][NixOS]] or [[https://guix.gnu.org/][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 [[https://github.com/yveszoundi/eglot-java/issues/46#issuecomment-2016032963][issue #46]].