summaryrefslogtreecommitdiff
path: root/lisp/progmodes/eglot.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/progmodes/eglot.el')
-rw-r--r--lisp/progmodes/eglot.el220
1 files changed, 126 insertions, 94 deletions
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index d330e6e23cb..7d2f1a55165 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -2,12 +2,12 @@
;; Copyright (C) 2018-2024 Free Software Foundation, Inc.
-;; Version: 1.16
+;; Version: 1.17
;; Author: João Távora <joaotavora@gmail.com>
;; Maintainer: João Távora <joaotavora@gmail.com>
;; URL: https://github.com/joaotavora/eglot
;; Keywords: convenience, languages
-;; Package-Requires: ((emacs "26.3") (jsonrpc "1.0.23") (flymake "1.2.1") (project "0.9.8") (xref "1.6.2") (eldoc "1.14.0") (seq "2.23") (external-completion "0.1"))
+;; Package-Requires: ((emacs "26.3") (jsonrpc "1.0.24") (flymake "1.2.1") (project "0.9.8") (xref "1.6.2") (eldoc "1.14.0") (seq "2.23") (external-completion "0.1"))
;; This is a GNU ELPA :core package. Avoid adding functionality
;; that is not available in the version of Emacs recorded above or any
@@ -226,90 +226,108 @@ automatically)."
when probe return (cons probe args)
finally (funcall err)))))))
-(defvar eglot-server-programs `(((rust-ts-mode rust-mode) . ("rust-analyzer"))
- ((cmake-mode cmake-ts-mode) . ("cmake-language-server"))
- (vimrc-mode . ("vim-language-server" "--stdio"))
- ((python-mode python-ts-mode)
- . ,(eglot-alternatives
- '("pylsp" "pyls" ("pyright-langserver" "--stdio") "jedi-language-server" "ruff-lsp")))
- ((js-json-mode json-mode json-ts-mode)
- . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio")
- ("vscode-json-languageserver" "--stdio")
- ("json-languageserver" "--stdio"))))
- (((js-mode :language-id "javascript")
- (js-ts-mode :language-id "javascript")
- (tsx-ts-mode :language-id "typescriptreact")
- (typescript-ts-mode :language-id "typescript")
- (typescript-mode :language-id "typescript"))
- . ("typescript-language-server" "--stdio"))
- ((bash-ts-mode sh-mode) . ("bash-language-server" "start"))
- ((php-mode phps-mode)
- . ,(eglot-alternatives
- '(("phpactor" "language-server")
- ("php" "vendor/felixfbecker/language-server/bin/php-language-server.php"))))
- ((c-mode c-ts-mode c++-mode c++-ts-mode objc-mode)
- . ,(eglot-alternatives
- '("clangd" "ccls")))
- (((caml-mode :language-id "ocaml")
- (tuareg-mode :language-id "ocaml") reason-mode)
- . ("ocamllsp"))
- ((ruby-mode ruby-ts-mode)
- . ("solargraph" "socket" "--port" :autoport))
- (haskell-mode
- . ("haskell-language-server-wrapper" "--lsp"))
- (elm-mode . ("elm-language-server"))
- (mint-mode . ("mint" "ls"))
- (kotlin-mode . ("kotlin-language-server"))
- ((go-mode go-dot-mod-mode go-dot-work-mode go-ts-mode go-mod-ts-mode)
- . ("gopls"))
- ((R-mode ess-r-mode) . ("R" "--slave" "-e"
- "languageserver::run()"))
- ((java-mode java-ts-mode) . ("jdtls"))
- ((dart-mode dart-ts-mode)
- . ("dart" "language-server"
- "--client-id" "emacs.eglot-dart"))
- ((elixir-mode elixir-ts-mode heex-ts-mode)
- . ,(if (and (fboundp 'w32-shell-dos-semantics)
- (w32-shell-dos-semantics))
- '("language_server.bat")
- (eglot-alternatives
- '("language_server.sh" "start_lexical.sh"))))
- (ada-mode . ("ada_language_server"))
- (scala-mode . ,(eglot-alternatives
- '("metals" "metals-emacs")))
- (racket-mode . ("racket" "-l" "racket-langserver"))
- ((tex-mode context-mode texinfo-mode bibtex-mode)
- . ,(eglot-alternatives '("digestif" "texlab")))
- (erlang-mode . ("erlang_ls" "--transport" "stdio"))
- ((yaml-ts-mode yaml-mode) . ("yaml-language-server" "--stdio"))
- (nix-mode . ,(eglot-alternatives '("nil" "rnix-lsp" "nixd")))
- (nickel-mode . ("nls"))
- (gdscript-mode . ("localhost" 6008))
- ((fortran-mode f90-mode) . ("fortls"))
- (futhark-mode . ("futhark" "lsp"))
- ((lua-mode lua-ts-mode) . ,(eglot-alternatives
- '("lua-language-server" "lua-lsp")))
- (zig-mode . ("zls"))
- ((css-mode css-ts-mode)
- . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio")
- ("css-languageserver" "--stdio"))))
- (html-mode . ,(eglot-alternatives '(("vscode-html-language-server" "--stdio") ("html-languageserver" "--stdio"))))
- ((dockerfile-mode dockerfile-ts-mode) . ("docker-langserver" "--stdio"))
- ((clojure-mode clojurescript-mode clojurec-mode clojure-ts-mode)
- . ("clojure-lsp"))
- ((csharp-mode csharp-ts-mode)
- . ,(eglot-alternatives
- '(("omnisharp" "-lsp")
- ("csharp-ls"))))
- (purescript-mode . ("purescript-language-server" "--stdio"))
- ((perl-mode cperl-mode) . ("perl" "-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run"))
- (markdown-mode
- . ,(eglot-alternatives
- '(("marksman" "server")
- ("vscode-markdown-language-server" "--stdio"))))
- (graphviz-dot-mode . ("dot-language-server" "--stdio"))
- (terraform-mode . ("terraform-ls" "serve"))
- ((uiua-ts-mode uiua-mode) . ("uiua" "lsp")))
+(defvar eglot-server-programs
+ ;; FIXME: Maybe this info should be distributed into the major modes
+ ;; themselves where they could set a buffer-local `eglot-server-program'
+ ;; instead of keeping this database centralized.
+ ;; FIXME: With `derived-mode-add-parents' in Emacs≥30, some of
+ ;; those entries can be simplified, but we keep them for when
+ ;; `eglot.el' is installed via GNU ELPA in an older Emacs.
+ `(((rust-ts-mode rust-mode) . ("rust-analyzer"))
+ ((cmake-mode cmake-ts-mode) . ("cmake-language-server"))
+ (vimrc-mode . ("vim-language-server" "--stdio"))
+ ((python-mode python-ts-mode)
+ . ,(eglot-alternatives
+ '("pylsp" "pyls" ("basedpyright-langserver" "--stdio")
+ ("pyright-langserver" "--stdio")
+ "jedi-language-server" "ruff-lsp")))
+ ((js-json-mode json-mode json-ts-mode)
+ . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio")
+ ("vscode-json-languageserver" "--stdio")
+ ("json-languageserver" "--stdio"))))
+ (((js-mode :language-id "javascript")
+ (js-ts-mode :language-id "javascript")
+ (tsx-ts-mode :language-id "typescriptreact")
+ (typescript-ts-mode :language-id "typescript")
+ (typescript-mode :language-id "typescript"))
+ . ("typescript-language-server" "--stdio"))
+ ((bash-ts-mode sh-mode) . ("bash-language-server" "start"))
+ ((php-mode phps-mode php-ts-mode)
+ . ,(eglot-alternatives
+ '(("phpactor" "language-server")
+ ("php" "vendor/felixfbecker/language-server/bin/php-language-server.php"))))
+ ((c-mode c-ts-mode c++-mode c++-ts-mode objc-mode)
+ . ,(eglot-alternatives
+ '("clangd" "ccls")))
+ (((caml-mode :language-id "ocaml")
+ (tuareg-mode :language-id "ocaml") reason-mode)
+ . ("ocamllsp"))
+ ((ruby-mode ruby-ts-mode)
+ . ("solargraph" "socket" "--port" :autoport))
+ (haskell-mode
+ . ("haskell-language-server-wrapper" "--lsp"))
+ (elm-mode . ("elm-language-server"))
+ (mint-mode . ("mint" "ls"))
+ ((kotlin-mode kotlin-ts-mode) . ("kotlin-language-server"))
+ ((go-mode go-dot-mod-mode go-dot-work-mode go-ts-mode go-mod-ts-mode)
+ . ("gopls"))
+ ((R-mode ess-r-mode) . ("R" "--slave" "-e"
+ "languageserver::run()"))
+ ((java-mode java-ts-mode) . ("jdtls"))
+ ((dart-mode dart-ts-mode)
+ . ("dart" "language-server"
+ "--client-id" "emacs.eglot-dart"))
+ ((elixir-mode elixir-ts-mode heex-ts-mode)
+ . ,(if (and (fboundp 'w32-shell-dos-semantics)
+ (w32-shell-dos-semantics))
+ '("language_server.bat")
+ (eglot-alternatives
+ '("language_server.sh" "start_lexical.sh"))))
+ (ada-mode . ("ada_language_server"))
+ (scala-mode . ,(eglot-alternatives
+ '("metals" "metals-emacs")))
+ (racket-mode . ("racket" "-l" "racket-langserver"))
+ ((tex-mode context-mode texinfo-mode bibtex-mode)
+ . ,(eglot-alternatives '("digestif" "texlab")))
+ (erlang-mode . ("erlang_ls" "--transport" "stdio"))
+ ((yaml-ts-mode yaml-mode) . ("yaml-language-server" "--stdio"))
+ (nix-mode . ,(eglot-alternatives '("nil" "rnix-lsp" "nixd")))
+ (nickel-mode . ("nls"))
+ ((nushell-mode nushell-ts-mode) . ("nu" "--lsp"))
+ (gdscript-mode . ("localhost" 6008))
+ (fennel-mode . ("fennel-ls"))
+ (move-mode . ("move-analyzer"))
+ ((fortran-mode f90-mode) . ("fortls"))
+ (futhark-mode . ("futhark" "lsp"))
+ ((lua-mode lua-ts-mode) . ,(eglot-alternatives
+ '("lua-language-server" "lua-lsp")))
+ (zig-mode . ("zls"))
+ ((css-mode css-ts-mode)
+ . ,(eglot-alternatives '(("vscode-css-language-server" "--stdio")
+ ("css-languageserver" "--stdio"))))
+ (html-mode . ,(eglot-alternatives
+ '(("vscode-html-language-server" "--stdio")
+ ("html-languageserver" "--stdio"))))
+ ((dockerfile-mode dockerfile-ts-mode) . ("docker-langserver" "--stdio"))
+ ((clojure-mode clojurescript-mode clojurec-mode clojure-ts-mode)
+ . ("clojure-lsp"))
+ ((csharp-mode csharp-ts-mode)
+ . ,(eglot-alternatives
+ '(("omnisharp" "-lsp")
+ ("csharp-ls"))))
+ (purescript-mode . ("purescript-language-server" "--stdio"))
+ ((perl-mode cperl-mode)
+ . ("perl" "-MPerl::LanguageServer" "-e" "Perl::LanguageServer::run"))
+ (markdown-mode
+ . ,(eglot-alternatives
+ '(("marksman" "server")
+ ("vscode-markdown-language-server" "--stdio"))))
+ (graphviz-dot-mode . ("dot-language-server" "--stdio"))
+ (terraform-mode . ("terraform-ls" "serve"))
+ ((uiua-ts-mode uiua-mode) . ("uiua" "lsp"))
+ (sml-mode
+ . ,(lambda (_interactive project)
+ (list "millet-ls" (project-root project)))))
"How the command `eglot' guesses the server to start.
An association list of (MAJOR-MODE . CONTACT) pairs. MAJOR-MODE
identifies the buffers that are to be managed by a specific
@@ -575,7 +593,7 @@ It is nil if Eglot is not byte-complied.")
(defvaralias 'eglot-{} 'eglot--{})
-(defconst eglot--{} (make-hash-table :size 1) "The empty JSON object.")
+(defconst eglot--{} (make-hash-table :size 0) "The empty JSON object.")
(defun eglot--executable-find (command &optional remote)
"Like Emacs 27's `executable-find', ignore REMOTE on Emacs 26."
@@ -590,7 +608,7 @@ It is nil if Eglot is not byte-complied.")
(let ((vec (copy-sequence url-path-allowed-chars)))
(aset vec ?: nil) ;; see github#639
vec)
- "Like `url-path-allows-chars' but more restrictive.")
+ "Like `url-path-allowed-chars' but more restrictive.")
;;; Message verification helpers
@@ -1797,6 +1815,12 @@ If optional MARKER, return a marker instead"
;;; More helpers
+(defconst eglot--uri-path-allowed-chars
+ (let ((vec (copy-sequence url-path-allowed-chars)))
+ (aset vec ?: nil) ;; see github#639
+ vec)
+ "Like `url-path-allowed-chars' but more restrictive.")
+
(defun eglot--snippet-expansion-fn ()
"Compute a function to expand snippets.
Doubles as an indicator of snippet support."
@@ -3054,9 +3078,14 @@ for which LSP on-type-formatting should be requested."
finally (cl-return comp)))
(defun eglot--dumb-allc (pat table pred _point) (funcall table pat pred t))
+(defun eglot--dumb-tryc (pat table pred point)
+ (let ((probe (funcall table pat pred nil)))
+ (cond ((eq probe t) t)
+ (probe (cons probe (length probe)))
+ (t (cons pat point)))))
(add-to-list 'completion-category-defaults '(eglot-capf (styles eglot--dumb-flex)))
-(add-to-list 'completion-styles-alist '(eglot--dumb-flex ignore eglot--dumb-allc))
+(add-to-list 'completion-styles-alist '(eglot--dumb-flex eglot--dumb-tryc eglot--dumb-allc))
(defun eglot-completion-at-point ()
"Eglot's `completion-at-point' function."
@@ -3115,7 +3144,8 @@ for which LSP on-type-formatting should be requested."
items)))
;; (trace-values "Requested" (length proxies) cachep bounds)
(setq eglot--capf-session
- (if cachep (list bounds retval resolved orig-pos) :none))
+ (if cachep (list bounds retval resolved orig-pos
+ bounds-string) :none))
(setq local-cache retval)))))
(resolve-maybe
;; Maybe completion/resolve JSON object `lsp-comp' into
@@ -3135,7 +3165,8 @@ for which LSP on-type-formatting should be requested."
(>= (cdr bounds) (cdr (nth 0 eglot--capf-session))))
(setq local-cache (nth 1 eglot--capf-session)
resolved (nth 2 eglot--capf-session)
- orig-pos (nth 3 eglot--capf-session))
+ orig-pos (nth 3 eglot--capf-session)
+ bounds-string (nth 4 eglot--capf-session))
;; (trace-values "Recalling cache" (length local-cache) bounds orig-pos)
)
(list
@@ -3605,16 +3636,17 @@ edit proposed by the server."
(defun eglot--code-action-bounds ()
"Calculate appropriate bounds depending on region and point."
- (let (diags)
+ (let (diags boftap)
(cond ((use-region-p) `(,(region-beginning) ,(region-end)))
((setq diags (flymake-diagnostics (point)))
(cl-loop for d in diags
minimizing (flymake-diagnostic-beg d) into beg
maximizing (flymake-diagnostic-end d) into end
finally (cl-return (list beg end))))
+ ((setq boftap (bounds-of-thing-at-point 'sexp))
+ (list (car boftap) (cdr boftap)))
(t
- (let ((boftap (bounds-of-thing-at-point 'sexp)))
- (list (car boftap) (cdr boftap)))))))
+ (list (point) (point))))))
(defun eglot-code-actions (beg &optional end action-kind interactive)
"Find LSP code actions of type ACTION-KIND between BEG and END.