]> git.eshelyaron.com Git - emacs.git/commitdiff
Eglot: rework eglot-imenu
authorJoão Távora <joaotavora@gmail.com>
Fri, 7 Apr 2023 11:54:39 +0000 (12:54 +0100)
committerJoão Távora <joaotavora@gmail.com>
Fri, 7 Apr 2023 11:57:59 +0000 (12:57 +0100)
Most newer servers return a vector of 'DocumentSymbol' as a response
to 'textDocument/documentSymbol'.  It's not worth trying to dumb this
down to imenu format of 'SymbolInformation' vectors.

This lays groundwork for the forthcoming "breadcrumb" feature of
bug#58431.

* lisp/progmodes/eglot.el
(eglot--imenu-SymbolInformation, eglot--imenu-DocumentSymbol): New
helpers.
(eglot-imenu): Rework.

lisp/electric.el
lisp/progmodes/eglot.el

index bac3f5a2b3ca36a8654e794179575ddefaeb6d1d..cef5326852ce340794a35d08e42c8083ac2ec21c 100644 (file)
@@ -409,9 +409,7 @@ If multiple rules match, only first one is executed.")
                                 (goto-char pos)
                                 (funcall probe last-command-event))))
                          (when res (throw 'done res))))))))))
-    (when (and rule
-               ;; Not in a string or comment.
-               (not (nth 8 (save-excursion (syntax-ppss pos)))))
+    (when rule
       (goto-char pos)
       (when (functionp rule) (setq rule (funcall rule)))
       (dolist (sym (if (symbolp rule) (list rule) rule))
index f3b806e56136e96dc93cf06928369e6d202b491a..db57b122a94ee7d146507425ecb17d04b1ea68ec 100644 (file)
@@ -486,9 +486,7 @@ This can be useful when using docker to run a language server.")
       (SymbolInformation (:name :kind :location)
                          (:deprecated :containerName))
       (DocumentSymbol (:name :range :selectionRange :kind)
-                      ;; `:containerName' isn't really allowed , but
-                      ;; it simplifies the impl of `eglot-imenu'.
-                      (:detail :deprecated :children :containerName))
+                      (:detail :deprecated :children))
       (TextDocumentEdit (:textDocument :edits) ())
       (TextEdit (:range :newText))
       (VersionedTextDocumentIdentifier (:uri :version) ())
@@ -3235,49 +3233,53 @@ for which LSP on-type-formatting should be requested."
        :deferred :textDocument/documentHighlight)
       nil)))
 
+(defun eglot--imenu-SymbolInformation (res)
+  "Compute `imenu--index-alist' for RES vector of SymbolInformation."
+  (mapcar
+   (pcase-lambda (`(,kind . ,objs))
+     (cons
+      (alist-get kind eglot--symbol-kind-names "Unknown")
+      (mapcan
+       (pcase-lambda (`(,container . ,objs))
+         (let ((elems (mapcar
+                       (eglot--lambda ((SymbolInformation) kind name location)
+                         (let ((reg (eglot--range-region
+                                     (plist-get location :range))))
+                           (cons (propertize name
+                                             'breadcrumb-region reg
+                                             'breadcrumb-kind kind)
+                                 (car reg))))
+                       objs)))
+           (if container (list (cons container elems)) elems)))
+       (seq-group-by
+        (eglot--lambda ((SymbolInformation) containerName) containerName) objs))))
+   (seq-group-by (eglot--lambda ((SymbolInformation) kind) kind) res)))
+
+(defun eglot--imenu-DocumentSymbol (res)
+  "Compute `imenu--index-alist' for RES vector of DocumentSymbol."
+  (cl-labels ((dfs (&key name children range kind &allow-other-keys)
+                (let* ((reg (eglot--range-region range))
+                       (name (propertize name
+                                         'breadcrumb-region reg
+                                         'breadcrumb-kind kind)))
+                  (if children
+                      (cons name
+                            (mapcar (lambda (c) (apply #'dfs c)) children))
+                    (cons name (car reg))))))
+    (mapcar (lambda (s) (apply #'dfs s)) res)))
+
 (defun eglot-imenu ()
   "Eglot's `imenu-create-index-function'.
 Returns a list as described in docstring of `imenu--index-alist'."
-  (cl-labels
-      ((unfurl (obj)
-         (eglot--dcase obj
-           (((SymbolInformation)) (list obj))
-           (((DocumentSymbol) name children)
-            (cons obj
-                  (mapcar
-                   (lambda (c)
-                     (plist-put
-                      c :containerName
-                      (let ((existing (plist-get c :containerName)))
-                        (if existing (format "%s::%s" name existing)
-                          name))))
-                   (mapcan #'unfurl children)))))))
-    (mapcar
-     (pcase-lambda (`(,kind . ,objs))
-       (cons
-        (alist-get kind eglot--symbol-kind-names "Unknown")
-        (mapcan (pcase-lambda (`(,container . ,objs))
-                  (let ((elems (mapcar
-                                (lambda (obj)
-                                  (cons (plist-get obj :name)
-                                        (car (eglot--range-region
-                                              (eglot--dcase obj
-                                                (((SymbolInformation) location)
-                                                 (plist-get location :range))
-                                                (((DocumentSymbol) selectionRange)
-                                                 selectionRange))))))
-                                objs)))
-                    (if container (list (cons container elems)) elems)))
-                (seq-group-by
-                 (lambda (e) (plist-get e :containerName)) objs))))
-     (seq-group-by
-      (lambda (obj) (plist-get obj :kind))
-      (mapcan #'unfurl
-              (eglot--request (eglot--current-server-or-lose)
+  (let* ((res (eglot--request (eglot--current-server-or-lose)
                               :textDocument/documentSymbol
                               `(:textDocument
                                 ,(eglot--TextDocumentIdentifier))
-                              :cancel-on-input non-essential))))))
+                              :cancel-on-input non-essential))
+         (head (and res (elt res 0))))
+    (eglot--dcase head
+      (((SymbolInformation)) (eglot--imenu-SymbolInformation res))
+      (((DocumentSymbol)) (eglot--imenu-DocumentSymbol res)))))
 
 (cl-defun eglot--apply-text-edits (edits &optional version)
   "Apply EDITS for current buffer if at VERSION, or if it's nil."