From 671d3117bd9a810616dd5bbdfffa18e91a4d9896 Mon Sep 17 00:00:00 2001 From: dannyfreeman Date: Mon, 31 Oct 2022 10:01:21 -0400 Subject: [PATCH] Do not parse jar type URIs provided by lsp servers in eglot Clojure-lsp and possibly other lsp servers for JVM languages, can provide file locations as jar scheme URIs for dependencies located inside of jar archives. These URIs contain nested URIs that Emacs out of the box does not know how to work with. Example: jar:file:///path/to/lib.jar!/path/inside/jar/source.ext Even if they are parsed twice, the locations are of the format /path/to/lib.jar!/path/inside/jar/source.ext which Emacs also does not know how to handle. This commit introduces a var `eglot-preserve-jar-uri` that, when true, will pass along jar URIs without parsing them. This allows other packages to handle them through the `file-name-handler-alist` mechanism. Additionally, any jars sent BACK to the lsp server by eglot will also remain in the `jar` format, which at least clojure-lsp knows how to interpret. Given the correct file-name-handler-alist implementation, this change allows xref to navigate to a file within a jar. When `eglot-extend-to-xref` option is enabled, eglot can correctly connect to the previous lsp server and continue to work in the newly opened file. To ensure clojure-lsp servers always use the "jar" URI scheme for these types of dependencies, an initialization option is set. The default option, the "zipfile" scheme, is very similar to the jar scheme. However, the "zipfile" scheme is something that the clojure-lsp maintainers want to move away from and eventually remove. --- lisp/progmodes/eglot.el | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index c587061837..b8f50e3cd8 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -231,7 +231,7 @@ eglot-server-programs (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio")))) (dockerfile-mode . ("docker-langserver" "--stdio")) ((clojure-mode clojurescript-mode clojurec-mode) - . ("clojure-lsp")) + . ("clojure-lsp" :initializationOptions (:dependency-scheme "jar"))) (csharp-mode . ("omnisharp" "-lsp")) (purescript-mode . ("purescript-language-server" "--stdio")) (perl-mode . ("perl" "-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run")) @@ -1492,25 +1492,38 @@ eglot--uri-path-allowed-chars vec) "Like `url-path-allows-chars' but more restrictive.") +(defvar eglot-preserve-jar-uri nil + "If non-nil, jar: type URIs will not be converted to paths. +This means they will be provided to xref as URIs and not file paths.") + (defun eglot--path-to-uri (path) "URIfy PATH." - (let ((truepath (file-truename path))) - (concat "file://" - ;; Add a leading "/" for local MS Windows-style paths. - (if (and (eq system-type 'windows-nt) - (not (file-remote-p truepath))) - "/") - (url-hexify-string - ;; Again watch out for trampy paths. - (directory-file-name (file-local-name truepath)) - eglot--uri-path-allowed-chars)))) + (if (and eglot-preserve-jar-uri (equal "jar" (url-type (url-generic-parse-url path)))) + path + (let ((truepath (file-truename path))) + (concat "file://" + ;; Add a leading "/" for local MS Windows-style paths. + (if (and (eq system-type 'windows-nt) + (not (file-remote-p truepath))) + "/") + (url-hexify-string + ;; Again watch out for trampy paths. + (directory-file-name (file-local-name truepath)) + eglot--uri-path-allowed-chars))))) + +(defun eglot--parse-uri (uri) + "Try to parse a URI." + (let ((url (url-generic-parse-url uri))) + (if (and eglot-preserve-jar-uri (string= "jar" (url-type url))) + uri + (url-unhex-string (url-filename url))))) (defun eglot--uri-to-path (uri) "Convert URI to file path, helped by `eglot--current-server'." (when (keywordp uri) (setq uri (substring (symbol-name uri) 1))) (let* ((server (eglot-current-server)) (remote-prefix (and server (eglot--trampish-p server))) - (retval (url-unhex-string (url-filename (url-generic-parse-url uri)))) + (retval (eglot--parse-uri uri)) ;; Remove the leading "/" for local MS Windows-style paths. (normalized (if (and (not remote-prefix) (eq system-type 'windows-nt) -- 2.37.3