emacs-lsp / lsp-java

lsp-mode :heart: java
https://emacs-lsp.github.io/lsp-java
GNU General Public License v3.0
650 stars 90 forks source link

Feature Request/Proposal: Customize `jdtls` Jar Location #487

Open rectangular-zone opened 1 month ago

rectangular-zone commented 1 month ago

I've recently had cause to install jdtls myself, rather than using lsp-mode's auto-install faculty. Unfortunately, this can get a little messy. The issue at hand is the function lsp-java--locate-server-jar, and the directory layout of jdtls when it's installed by alternative tools. Specifically, lsp-mode's installation creates a structure like this:

# ~.emacs.d/.cache/lsp/eclipse.jdt.ls/
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 bin                                                                                       drwxr-xr-x@ - rdonaldson 19 Sep 16:03 boot-server                                                                               drwxr-xr-x@ - rdonaldson 19 Sep 16:03 bundles
drwxr-xr-x@ - rdonaldson 19 Sep 16:15 config_linux
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 config_mac
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 config_ss_linux                                                                           drwxr-xr-x@ - rdonaldson 19 Sep 16:03 config_ss_mac                                                                             drwxr-xr-x@ - rdonaldson 19 Sep 16:03 config_ss_win
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 config_win
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 features
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 java-decompiler
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 java-test
drwxr-xr-x@ - rdonaldson 19 Sep 16:03 plugins    

Tools I'm using are creating layouts like so:

├── bin
│  ├── jdtls
│  └── jdtls.py
└── share
   └── java
      └── jdtls
         ├── config_linux
         ├── features
         └── plugins

While this installation is valid, it breaks JAR detection, so LSP won't start -- even though it can find the "server command".

I can see a couple very reasonable solutions here, and I'm happy to implement whichever solution y'all prefer.

  1. Right now, jar location searches in lsp-java-server-install-dir; we could add a new defcustom for lsp-java-server-jar-location and check there first if non-nil.
  2. We could add a new defcustom for the path prefix of the JAR location within the install dir -- something like (defcustom lsp-java-server-jar-subpath "plugins").
  3. We could make a new defcustom for the JAR location function itself, setting the current function as the default implementation.

Thanks so much for the excellent tools, y'all.

LuigiPiucco commented 1 month ago

I recently ran into this, it happens for instance when installing via nix (by the structure listed, I'm guessing that's your use-case too?), a temporary workaround I found is to add :around advice to lsp-java--locate-server-jar, with a simple function such as:

(defun java-server-subdir-for-jar (orig &rest args)
      "Add nix subdir to `lsp-java-server-install-dir' so that the lsp test
succeeds."
      (let ((lsp-java-server-install-dir
             (expand-file-name "./share/java/jdtls/" lsp-java-server-install-dir)))
        (apply orig args)))
(advice-add 'lsp-java--locate-server-jar :around #'java-server-subdir-for-jar)

Additionally, when talking about a more permanent solution, I see a different (maybe complementary) possibility to add to the list. Currently, there is lsp-java-jdt-ls-prefer-native-command to choose between using the jar invocation or the executable invocation; if that is t, it should probably not check for the presence of the jar at all. Adding a separate variable for the jar is still a good idea though.