branch: externals/eglot commit 1ac06d0bcc247fb19df6eceb57e2ea3d534806ec Author: Michael Livshin <r...@cmm.kakpryg.net> Commit: João Távora <joaotav...@gmail.com>
Fix #76: manage cross-referenced files outside project in same server Close #686, Close #695. Co-authored-by: João Távora <joaotav...@gmail.com> * eglot.el (eglot-extend-to-xref): new defcustom, default to nil. (eglot--servers-by-xrefed-file): new hash table, mapping file names to servers. (eglot--managed-mode): use eglot-current-server, instead of eglot--cached-server directly. (eglot--current-server-or-lose): ditto. (eglot--maybe-activate-editing-mode): ditto. (eglot-current-server): move all cached-server update logic here -- if eglot--cached-server is nil, try to find it using current project or (optionally) xref location. (eglot--xref-make-match): record the xref location. * README.md (Customization): Mention new defcustom. * NEWS.md: Mention new feature --- NEWS.md | 10 ++++++++++ README.md | 6 ++++++ eglot.el | 32 +++++++++++++++++++++----------- 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/NEWS.md b/NEWS.md index d060ad4..03a22bc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,6 +7,13 @@ Thanks to Brian Cully for the minimalist approach. (also thanks to Felipe Lema who conducted many early experiments in [#463][github#463]) +##### Manage cross-referenced files outside project ([#76][github#76], ([#686][github#686], ([#695][github#695]) + +This is activated by a new customization option +`eglot-extend-to-xref`, which defaults to nil. + +Thanks to Michael Livshin for the investigation an elegant solution. + ##### Code action shortcuts ([#411][github#411]) `M-x eglot-code-actions` accepts an optional `action-kind` argument, @@ -214,6 +221,7 @@ and now said bunch of references--> [github#68]: https://github.com/joaotavora/eglot/issues/68 [github#73]: https://github.com/joaotavora/eglot/issues/73 [github#74]: https://github.com/joaotavora/eglot/issues/74 +[github#76]: https://github.com/joaotavora/eglot/issues/76 [github#80]: https://github.com/joaotavora/eglot/issues/80 [github#81]: https://github.com/joaotavora/eglot/issues/81 [github#82]: https://github.com/joaotavora/eglot/issues/82 @@ -263,3 +271,5 @@ and now said bunch of references--> [github#494]: https://github.com/joaotavora/eglot/issues/494 [github#637]: https://github.com/joaotavora/eglot/issues/637 [github#643]: https://github.com/joaotavora/eglot/issues/643 +[github#686]: https://github.com/joaotavora/eglot/issues/686 +[github#695]: https://github.com/joaotavora/eglot/issues/695 diff --git a/README.md b/README.md index 0ad6d46..302c7f6 100644 --- a/README.md +++ b/README.md @@ -319,6 +319,12 @@ lisp: - `eglot-stay-out-of`: List of Emacs features that Eglot shouldn't automatically try to manage on users' behalf. Useful when you need non-LSP Flymake or Company backends. See docstring for examples. + +- `eglot-extend-to-xref`: If non-nil and `xref-find-definitions` lands + you in a file outside your project -- like a system-installed + library or header file -- transiently consider it managed by the + same LSP server. That file is still outside your project + (i.e. `project-find-file` won't find it). # How does Eglot work? diff --git a/eglot.el b/eglot.el index f17e795..db468d8 100644 --- a/eglot.el +++ b/eglot.el @@ -237,6 +237,10 @@ let the buffer grow forever." :type '(choice (const :tag "Don't show confirmation prompt" nil) (symbol :tag "Show confirmation prompt" 'confirm))) +(defcustom eglot-extend-to-xref nil + "If non-nil, activate Eglot in cross-referenced non-project files." + :type 'boolean) + ;; Customizable via `completion-category-overrides'. (when (assoc 'flex completion-styles-alist) (add-to-list 'completion-category-defaults '(eglot (styles flex basic)))) @@ -832,6 +836,9 @@ be guessed." (put 'eglot-lsp-context 'variable-documentation "Dynamically non-nil when searching for projects in LSP context.") +(defvar eglot--servers-by-xrefed-file + (make-hash-table :test 'equal :weakness 'value)) + (defun eglot--current-project () "Return a project object for Eglot's LSP purposes. This relies on `project-current' and thus on @@ -1495,7 +1502,7 @@ Use `eglot-managed-p' to determine if current buffer is managed.") #'eglot-imenu)) (flymake-mode 1) (eldoc-mode 1) - (cl-pushnew (current-buffer) (eglot--managed-buffers eglot--cached-server))) + (cl-pushnew (current-buffer) (eglot--managed-buffers (eglot-current-server)))) (t (remove-hook 'after-change-functions 'eglot--after-change t) (remove-hook 'before-change-functions 'eglot--before-change t) @@ -1533,11 +1540,19 @@ Use `eglot-managed-p' to determine if current buffer is managed.") (defun eglot-current-server () "Return logical EGLOT server for current buffer, nil if none." - eglot--cached-server) + (setq eglot--cached-server + (or eglot--cached-server + (cl-find major-mode + (gethash (eglot--current-project) eglot--servers-by-project) + :key #'eglot--major-mode) + (and eglot-extend-to-xref + buffer-file-name + (gethash (expand-file-name buffer-file-name) + eglot--servers-by-xrefed-file))))) (defun eglot--current-server-or-lose () "Return current logical EGLOT server connection or error." - (or eglot--cached-server + (or (eglot-current-server) (jsonrpc-error "No current JSON-RPC connection"))) (defvar-local eglot--unreported-diagnostics nil @@ -1555,14 +1570,7 @@ If it is activated, also signal textDocument/didOpen." (unless eglot--managed-mode ;; Called when `revert-buffer-in-progress-p' is t but ;; `revert-buffer-preserve-modes' is nil. - (when (and buffer-file-name - (or - eglot--cached-server - (setq eglot--cached-server - (cl-find major-mode - (gethash (eglot--current-project) - eglot--servers-by-project) - :key #'eglot--major-mode)))) + (when (and buffer-file-name (eglot-current-server)) (setq eglot--unreported-diagnostics `(:just-opened . nil)) (eglot--managed-mode) (eglot--signal-textDocument/didOpen)))) @@ -2101,6 +2109,8 @@ Try to visit the target file for a richer summary line." (start-pos (cl-getf start :character)) (end-pos (cl-getf (cl-getf range :end) :character))) (list name line start-pos (- end-pos start-pos))))))) + (setf (gethash (expand-file-name file) eglot--servers-by-xrefed-file) + (eglot--current-server-or-lose)) (xref-make-match summary (xref-make-file-location file line column) length))) (cl-defmethod xref-backend-identifier-completion-table ((_backend (eql eglot)))