branch: externals/eglot
commit d40f9ac8af850b95868bf810dd67b3c5009bf598
Author: João Távora <[email protected]>
Commit: João Távora <[email protected]>
Half-decent imenu support via textDocument/documentSymbol
* README.md: Update capability
* eglot.el (eglot--lsp-position-to-point): New function.
(eglot--managed-mode): Handle imenu-create-index-function.
(eglot--server-textDocument/publishDiagnostics): Use
eglot--lsp-position-to-point.
(eglot-imenu): New function.
(eglot--client-capabilities): Capable of documentSymbol.
---
README.md | 2 +-
eglot.el | 96 ++++++++++++++++++++++++++++++++++++++++-----------------------
2 files changed, 62 insertions(+), 36 deletions(-)
diff --git a/README.md b/README.md
index 611839b..fef2d16 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ server. To skip the guess and always be prompted use `C-u M-x
eglot`.
- [ ] completionItem/resolve
- [x] textDocument/definition
- [ ] textDocument/documentHighlight
-- [ ] textDocument/documentSymbol
+- [x] textDocument/documentSymbol
- [ ] textDocument/executeCommand
- [ ] textDocument/format
- [x] textDocument/hover
diff --git a/eglot.el b/eglot.el
index 7cc3756..d7ea329 100644
--- a/eglot.el
+++ b/eglot.el
@@ -193,10 +193,11 @@ CONTACT is as `eglot--contact'. Returns a process
object."
:willSave t
:willSaveWaitUntil :json-false
:didSave t)
- :completion `(:dynamicRegistration :json-false)
- :hover `(:dynamicRegistration :json-false)
- :references `(:dynamicRegistration :json-false)
- :definition `(:dynamicRegistration :json-false)
+ :completion `(:dynamicRegistration :json-false)
+ :hover `(:dynamicRegistration :json-false)
+ :references `(:dynamicRegistration :json-false)
+ :definition `(:dynamicRegistration :json-false)
+ :documentSymbol `(:dynamicRegistration :json-false)
:publishDiagnostics `(:relatedInformation :json-false))
:experimental (eglot--obj)))
@@ -703,6 +704,17 @@ Meaning only return locally if successful, otherwise exit
non-locally."
(- (goto-char (or pos (point)))
(line-beginning-position)))))
+(defun eglot--lsp-position-to-point (pos-plist)
+ "Convert LSP position POS-PLIST to Emacs point."
+ (save-excursion (goto-char (point-min))
+ (forward-line (plist-get pos-plist :line))
+ (forward-char
+ (min (plist-get pos-plist :character)
+ (- (line-end-position)
+ (line-beginning-position))))
+ (point)))
+
+
(defun eglot--mapply (fun seq)
"Apply FUN to every element of SEQ."
(mapcar (lambda (e) (apply fun e)) seq))
@@ -766,6 +778,7 @@ Meaning only return locally if successful, otherwise exit
non-locally."
(add-hook 'completion-at-point-functions #'eglot-completion-at-point nil t)
(add-function :before-until (local 'eldoc-documentation-function)
#'eglot-eldoc-function)
+ (advice-add imenu-create-index-function :around #'eglot-imenu)
(flymake-mode 1)
(eldoc-mode 1))
(t
@@ -779,7 +792,8 @@ Meaning only return locally if successful, otherwise exit
non-locally."
(remove-hook 'xref-backend-functions 'eglot-xref-backend t)
(remove-hook 'completion-at-point-functions #'eglot-completion-at-point t)
(remove-function (local 'eldoc-documentation-function)
- #'eglot-eldoc-function))))
+ #'eglot-eldoc-function)
+ (advice-remove imenu-create-index-function #'eglot-imenu))))
(define-minor-mode eglot-mode
"Minor mode for all buffers managed by EGLOT in some way." nil
@@ -956,36 +970,28 @@ running. INTERACTIVE is t if called interactively."
(cond
(buffer
(with-current-buffer buffer
- (cl-flet ((pos-at (pos-plist)
- (save-excursion (goto-char (point-min))
- (forward-line (plist-get pos-plist
:line))
- (forward-char
- (min (plist-get pos-plist
:character)
- (- (line-end-position)
- (line-beginning-position))))
- (point))))
- (cl-loop for diag-spec across diagnostics
- collect (cl-destructuring-bind (&key range severity _group
- _code source message)
- diag-spec
- (cl-destructuring-bind (&key start end)
- range
- (let* ((begin-pos (pos-at start))
- (end-pos (pos-at end)))
- (flymake-make-diagnostic
- (current-buffer)
- begin-pos end-pos
- (cond ((<= severity 1) :error)
- ((= severity 2) :warning)
- (t :note))
- (concat source ": " message)))))
- into diags
- finally
- (if eglot--current-flymake-report-fn
- (funcall eglot--current-flymake-report-fn
- diags)
- (setq eglot--unreported-diagnostics
- diags))))))
+ (cl-loop
+ for diag-spec across diagnostics
+ collect (cl-destructuring-bind (&key range severity _group
+ _code source message)
+ diag-spec
+ (cl-destructuring-bind (&key start end)
+ range
+ (let* ((begin-pos (eglot--lsp-position-to-point start))
+ (end-pos (eglot--lsp-position-to-point end)))
+ (flymake-make-diagnostic
+ (current-buffer)
+ begin-pos end-pos
+ (cond ((<= severity 1) :error)
+ ((= severity 2) :warning)
+ (t :note))
+ (concat source ": " message)))))
+ into diags
+ finally (if eglot--current-flymake-report-fn
+ (funcall eglot--current-flymake-report-fn
+ diags)
+ (setq eglot--unreported-diagnostics
+ diags)))))
(t
(eglot--message "OK so %s isn't visited" filename)))))
@@ -1321,6 +1327,26 @@ DUMMY is ignored"
"\n")))))
nil)
+(defun eglot-imenu (oldfun)
+ "EGLOT's `imenu-create-index-function' overriding OLDFUN."
+ (if (eglot--server-capable :documentSymbolProvider)
+ (let ((entries
+ (eglot--mapply
+ (eglot--lambda (&key name kind location _containerName)
+ (cons (propertize name :kind (cdr (assoc kind
eglot--kind-names)))
+ (eglot--lsp-position-to-point
+ (plist-get (plist-get location :range) :start))))
+ (eglot--sync-request
+ (eglot--current-process-or-lose)
+ :textDocument/documentSymbol
+ (eglot--obj
+ :textDocument
(eglot--current-buffer-TextDocumentIdentifier))))))
+ (append
+ (seq-group-by (lambda (e) (get-text-property 0 :kind (car e)))
+ entries)
+ entries))
+ (funcall oldfun)))
+
;;; Dynamic registration
;;;