config

Personal configuration.
git clone git://code.dwrz.net/config
Log | Files | Refs

consult-lsp.el (24724B)


      1 ;;; consult-lsp.el --- LSP-mode Consult integration -*- lexical-binding: t; -*-
      2 
      3 ;; Licence: MIT
      4 ;; Keywords: tools, completion, lsp
      5 ;; Author: Gerry Agbobada
      6 ;; Maintainer: Gerry Agbobada
      7 ;; Package-Requires: ((emacs "27.1") (lsp-mode "5.0") (consult "0.16") (f "0.20.0"))
      8 ;; Version: 1.1-dev
      9 ;; Homepage: https://github.com/gagbo/consult-lsp
     10 
     11 ;; Copyright (c) 2021 Gerry Agbobada and contributors
     12 ;;
     13 ;; Permission is hereby granted, free of charge, to any person obtaining a copy
     14 ;; of this software and associated documentation files (the "Software"), to deal
     15 ;; in the Software without restriction, including without limitation the rights
     16 ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     17 ;; copies of the Software, and to permit persons to whom the Software is
     18 ;; furnished to do so, subject to the following conditions:
     19 ;;
     20 ;; The above copyright notice and this permission notice shall be included in all
     21 ;; copies or substantial portions of the Software.
     22 ;;
     23 ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     24 ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     25 ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     26 ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     27 ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     28 ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     29 ;; SOFTWARE.
     30 
     31 ;;; Commentary:
     32 ;; Provides LSP-mode related commands for consult
     33 ;;
     34 ;; The commands are autoloaded so you don't need to require anything to make them
     35 ;; available. Just use M-x and go!
     36 ;;
     37 ;;;; Diagnostics
     38 ;; M-x consult-lsp-diagnostics provides a view of all diagnostics in the current
     39 ;; workspace (or all workspaces if passed a prefix argument).
     40 ;;
     41 ;; You can use prefixes to filter diagnostics per severity, and
     42 ;; previewing/selecting a candidate will go to it directly.
     43 ;;
     44 ;;;; Symbols
     45 ;; M-x consult-lsp-symbols provides a selection/narrowing command to search
     46 ;; and go to any arbitrary symbol in the workspace (or all workspaces if
     47 ;; passed a prefix argument).
     48 ;;
     49 ;; You can use prefixes as well to filter candidates per type, and
     50 ;; previewing/selecting a candidate will go to it.
     51 ;;
     52 ;;;; File symbols
     53 ;; M-x consult-lsp-file-symbols provides a selection/narrowing command to search
     54 ;; and go to any arbitrary symbol in the selected buffer (like imenu)
     55 ;;
     56 ;;;; Contributions
     57 ;; Possible contributions for ameliorations include:
     58 ;; - using a custom format for the propertized candidates
     59 ;;   This should be done with :type 'function custom variables probably.
     60 ;; - checking the properties in LSP to see how diagnostic-sources should be used
     61 ;; - checking the properties in LSP to see how symbol-sources should be used
     62 ;;; Code:
     63 
     64 (eval-when-compile
     65   (require 'subr-x))
     66 (require 'consult)
     67 (require 'lsp)
     68 (require 'f)
     69 (require 'cl-lib)
     70 
     71 ;; Poorman's breaking change detection
     72 ;; The state protocol change happened in https://github.com/minad/consult/commit/3668df6afaa8c188d7c255fa6ae4e62d54cb20c9
     73 ;; which also happened to remove this macro
     74 (when (fboundp 'consult--with-location-upgrade)
     75   (user-error "This version of consult-lsp is unstable with older versions of consult. Please:
     76  - upgrade consult past https://github.com/minad/consult/commit/3668df6afaa8c188d7c255fa6ae4e62d54cb20c9 or
     77  - downgrade consult-lsp to 0.7 tag."))
     78 
     79 (defgroup consult-lsp nil
     80   "Consult commands for `lsp-mode'."
     81   :group 'tools
     82   :prefix "consult-lsp-")
     83 
     84 ;;;; Customization
     85 (defcustom consult-lsp-diagnostics-transformer-function #'consult-lsp--diagnostics--transformer
     86   "Function that transform LSP-mode diagnostic from a (file diag) pair
     87 to a candidate for `consult-lsp-diagnostics'."
     88   :type 'function
     89   :group 'consult-lsp)
     90 
     91 (defcustom consult-lsp-diagnostics-annotate-builder-function #'consult-lsp--diagnostics-annotate-builder
     92   "Annotation function builder for `consult-lsp-diagnostics'."
     93   :type 'function
     94   :group 'consult-lsp)
     95 
     96 (defcustom consult-lsp-symbols-transformer-function #'consult-lsp--symbols--transformer
     97   "Function that transform LSP symbols from symbol-info
     98 to a candidate for `consult-lsp-symbols'."
     99   :type 'function
    100   :group 'consult-lsp)
    101 
    102 (defcustom consult-lsp-symbols-annotate-builder-function #'consult-lsp--symbols-annotate-builder
    103   "Annotation function builder for `consult-lsp-symbols'."
    104   :type 'function
    105   :group 'consult-lsp)
    106 
    107 (defcustom consult-lsp-file-symbols-transformer-function #'consult-lsp--file-symbols--transformer
    108   "Function that transform LSP file symbols from symbol-info
    109  to a candidate for `consult-lsp-file-symbols'."
    110   :type 'function
    111   :group 'consult-lsp)
    112 
    113 (defcustom consult-lsp-file-symbols-annotate-builder-function #'consult-lsp--file-symbols-annotate-builder
    114   "Annotation function builder for `consult-lsp-file-symbols'."
    115   :type 'function
    116   :group 'consult-lsp)
    117 
    118 (defcustom consult-lsp-symbols-narrow
    119   '(
    120     ;; Lowercase classes
    121     (?c . "Class")
    122     (?f . "Field")
    123     (?e . "Enum")
    124     (?i . "Interface")
    125     (?m . "Module")
    126     (?n . "Namespace")
    127     (?p . "Package")
    128     (?s . "Struct")
    129     (?t . "Type Parameter")
    130     (?v . "Variable")
    131 
    132     ;; Uppercase classes
    133     (?A . "Array")
    134     (?B . "Boolean")
    135     (?C . "Constant")
    136     (?E . "Enum Member")
    137     (?F . "Function")
    138     (?M . "Method")
    139     (?N . "Number")
    140     (?O . "Object")
    141     (?P . "Property")
    142     (?S . "String")
    143 
    144     (?o . "Other")
    145     ;; Example types included in "Other" (i.e. the ignored)
    146     ;; (?n . "Null")
    147     ;; (?c . "Constructor")
    148     ;; (?e . "Event")
    149     ;; (?k . "Key")
    150     ;; (?o . "Operator")
    151     )
    152   "Set of narrow keys for `consult-lsp-symbols' and `consult-lsp-file-symbols'.
    153 
    154 It MUST have a \"Other\" category for everything that is not listed."
    155   :group 'consult-lsp
    156   :type '(alist :key-type character :value-type string))
    157 
    158 
    159 
    160 ;;;; Inner functions
    161 ;; These helpers are mostly verbatim copies of consult internal functions that are needed,
    162 ;; but are unstable and would break if relied upon
    163 (defun consult-lsp--marker-from-line-column (buffer line column)
    164   "Get marker in BUFFER from LINE and COLUMN."
    165   (when (buffer-live-p buffer)
    166     (with-current-buffer buffer
    167       (save-restriction
    168         (save-excursion
    169           (widen)
    170           (goto-char (point-min))
    171           ;; Location data might be invalid by now!
    172           (ignore-errors
    173             (forward-line (1- line))
    174             (forward-char column))
    175           (point-marker))))))
    176 
    177 (defun consult-lsp--format-file-line-match (file line &optional match)
    178   "Format string FILE:LINE:MATCH with faces."
    179   (setq line (number-to-string line)
    180         match (concat file ":" line (and match ":") match)
    181         file (length file))
    182   (put-text-property 0 file 'face 'consult-file match)
    183   (put-text-property (1+ file) (+ 1 file (length line)) 'face 'consult-line-number match)
    184   match)
    185 
    186 
    187 ;;;; Diagnostics
    188 
    189 (defun consult-lsp--diagnostics--flatten-diagnostics (transformer &optional current-workspace?)
    190   "Flatten the list of LSP-mode diagnostics to consult candidates.
    191 
    192 TRANSFORMER takes (file diag) and returns a suitable element for
    193 `consult--read'.
    194 CURRENT-WORKSPACE? has the same meaning as in `lsp-diagnostics'."
    195   (sort
    196    (flatten-list
    197     (ht-map
    198      (lambda (file diags)
    199        (mapcar (lambda (diag) (funcall transformer file diag))
    200                diags))
    201      (lsp-diagnostics current-workspace?)))
    202    ;; Sort by ascending severity
    203    (lambda (cand-left cand-right)
    204      (let* ((diag-left (cdr (get-text-property 0 'consult--candidate cand-left)))
    205             (diag-right (cdr (get-text-property 0 'consult--candidate cand-right)))
    206             (sev-left (or (lsp:diagnostic-severity? diag-left) 12))
    207             (sev-right (or (lsp:diagnostic-severity? diag-right) 12)))
    208        (< sev-left sev-right)))))
    209 
    210 (defun consult-lsp--diagnostics--severity-to-level (diag)
    211   "Convert diagnostic severity of DIAG to a string."
    212   (pcase (lsp:diagnostic-severity? diag)
    213     (1 (propertize "error" 'face 'error))
    214     (2 (propertize "warn" 'face 'warning))
    215     (3 (propertize "info" 'face 'success))
    216     (4 (propertize "hint" 'face 'italic))
    217     (_ "unknown")))
    218 
    219 (defconst consult-lsp--diagnostics--narrow
    220   '((?e . "Errors")
    221     (?w . "Warnings")
    222     (?i . "Infos")
    223     (?h . "Hints")
    224     (?u . "Unknown"))
    225   "Set of narrow keys for `consult-lsp-diagnostics'.")
    226 
    227 (defun consult-lsp--diagnostics--severity-to-type (diag)
    228   "Convert diagnostic severity of DIAG to a type for consult--type."
    229   (pcase (lsp:diagnostic-severity? diag)
    230     (1 (car (rassoc "Errors" consult-lsp--diagnostics--narrow)))
    231     (2 (car (rassoc "Warnings" consult-lsp--diagnostics--narrow)))
    232     (3 (car (rassoc "Infos" consult-lsp--diagnostics--narrow)))
    233     (4 (car (rassoc "Hints" consult-lsp--diagnostics--narrow)))
    234     (_ (car (rassoc "Unknown" consult-lsp--diagnostics--narrow)))))
    235 
    236 (defun consult-lsp--diagnostics--source (diag)
    237   "Convert source of DIAG to a propertized string."
    238   (propertize (lsp:diagnostic-source? diag) 'face 'success))
    239 
    240 (defun consult-lsp--diagnostics--diagnostic-marker (file diag)
    241   "Return a marker in FILE at the beginning of DIAG."
    242   (consult-lsp--marker-from-line-column
    243    file
    244    (lsp-translate-line (1+ (lsp:position-line (lsp:range-start (lsp:diagnostic-range diag)))))
    245    (lsp-translate-column (1+ (lsp:position-character (lsp:range-start (lsp:diagnostic-range diag)))))))
    246 
    247 (defun consult-lsp--diagnostics--transformer (file diag)
    248   "Transform LSP-mode diagnostics from a pair FILE DIAG to a candidate."
    249   (propertize
    250    (format "%-60.60s"
    251            (consult-lsp--format-file-line-match
    252             (if-let ((wks (lsp-workspace-root file)))
    253                 (f-relative file wks)
    254               file)
    255             (lsp-translate-line (1+ (lsp-get (lsp-get (lsp-get diag :range) :start) :line)))))
    256    'consult--candidate (cons file diag)
    257    'consult--type (consult-lsp--diagnostics--severity-to-type diag)))
    258 
    259 (defun consult-lsp--diagnostics-annotate-builder ()
    260   "Annotation function for `consult-lsp-diagnostics'.
    261 
    262 See `consult-lsp--diagnostics--transformer' for the usable text-properties
    263 in candidates."
    264   (let* ((width (length (number-to-string (line-number-at-pos
    265                                            (point-max)
    266                                            consult-line-numbers-widen)))))
    267     (lambda (cand)
    268       (let* ((diag (cdr (get-text-property 0 'consult--candidate cand))))
    269         (list cand
    270               (format "%-5s " (consult-lsp--diagnostics--severity-to-level diag))
    271               (concat
    272                (format "%s" (lsp:diagnostic-message diag))
    273                (when-let ((source (consult-lsp--diagnostics--source diag)))
    274                  (propertize (format " - %s" source) 'face 'font-lock-doc-face))))))))
    275 
    276 (defun consult-lsp--diagnostics--state ()
    277   "LSP diagnostic preview."
    278   (let ((open (consult--temporary-files))
    279         (jump (consult--jump-state)))
    280     (lambda (action cand)
    281       (when (eq action 'exit)
    282         (funcall open))
    283       (funcall jump action
    284                (when cand (consult-lsp--marker-from-line-column
    285                            (and (car cand) (funcall (if (eq action 'finish) #'find-file open) (car cand)))
    286                            (lsp-translate-line (1+ (lsp:position-line (lsp:range-start (lsp:diagnostic-range (cdr cand))))))
    287                            (lsp-translate-column (1+ (lsp:position-character (lsp:range-start (lsp:diagnostic-range (cdr cand))))))))))))
    288 
    289 ;;;###autoload
    290 (defun consult-lsp-diagnostics (arg)
    291   "Query LSP-mode diagnostics.
    292 
    293 When ARG is set through prefix, query all workspaces."
    294   (interactive "P")
    295   (let ((all-workspaces? arg))
    296     (consult--read (consult-lsp--diagnostics--flatten-diagnostics consult-lsp-diagnostics-transformer-function (not all-workspaces?))
    297                    :prompt (concat  "LSP Diagnostics " (when arg "(all workspaces) "))
    298                    :annotate (funcall consult-lsp-diagnostics-annotate-builder-function)
    299                    :require-match t
    300                    :history t
    301                    :category 'consult-lsp-diagnostics
    302                    :sort nil
    303                    :group (consult--type-group consult-lsp--diagnostics--narrow)
    304                    :narrow (consult--type-narrow consult-lsp--diagnostics--narrow)
    305                    :state (consult-lsp--diagnostics--state)
    306                    :lookup #'consult--lookup-candidate)))
    307 
    308 
    309 ;;;; Symbols
    310 
    311 (defun consult-lsp--symbols--kind-to-narrow (symbol-info)
    312   "Get the narrow character for SYMBOL-INFO."
    313   (if-let ((pair (rassoc
    314                   (alist-get (lsp:symbol-information-kind symbol-info) lsp-symbol-kinds)
    315                   consult-lsp-symbols-narrow)))
    316       (car pair)
    317     (rassoc "Other" consult-lsp-symbols-narrow)))
    318 
    319 (defun consult-lsp--symbols--state ()
    320   "Return a LSP symbol preview function."
    321   (let ((open (consult--temporary-files))
    322         (jump (consult--jump-state)))
    323     (lambda (action cand)
    324       (when (eq action 'exit)
    325         (funcall open))
    326       (funcall jump action
    327                (when cand (let* ((location (lsp:symbol-information-location cand))
    328                                  (uri (lsp:location-uri location)))
    329                             (consult-lsp--marker-from-line-column
    330                              (and uri (funcall (if (eq action 'finish) #'find-file open) (lsp--uri-to-path uri)))
    331                              (thread-first location
    332                                            (lsp:location-range)
    333                                            (lsp:range-start)
    334                                            (lsp:position-line)
    335                                            (1+)
    336                                            (lsp-translate-line))
    337                              (thread-first location
    338                                            (lsp:location-range)
    339                                            (lsp:range-start)
    340                                            (lsp:position-character)
    341                                            (1+)
    342                                            (lsp-translate-column)))))))))
    343 
    344 ;; It is an async source because some servers, like rust-analyzer, send a
    345 ;; max count of results for queries (120 last time checked). Therefore, in
    346 ;; big projects the first query might not have the target result to filter on.
    347 ;; To avoid this issue, we use an async source that retriggers the request.
    348 (defun consult-lsp--symbols--make-async-source (async workspaces)
    349   "Pipe a `consult--read' compatible async-source ASYNC to search for symbols in WORKSPACES."
    350   (let ((cancel-token :consult-lsp--symbols))
    351     (lambda (action)
    352       (pcase-exhaustive action
    353         ((or 'setup (pred stringp))
    354          (let ((query (if (stringp action) action "")))
    355            (with-lsp-workspaces workspaces
    356              (consult--async-log "consult-lsp-symbols request started for %S\n" action)
    357              (lsp-request-async
    358               "workspace/symbol"
    359               (list :query query)
    360               (lambda (res)
    361                 ;; Flush old candidates list
    362                 (funcall async 'flush)
    363                 (funcall async res))
    364               :mode 'detached
    365               :no-merge nil
    366               :cancel-token cancel-token)))
    367          (funcall async action))
    368         ('destroy
    369          (lsp-cancel-request-by-token cancel-token)
    370          (funcall async action))
    371         (_ (funcall async action))))))
    372 
    373 (defun consult-lsp--symbols--transformer (symbol-info)
    374   "Default transformer to produce a completion candidate from SYMBOL-INFO."
    375   (propertize
    376    ;; We have to add the location in the candidate string for 2 purposes,
    377    ;; in case symbols have the same name:
    378    ;; - being able to narrow using the path
    379    ;; - because it breaks marginalia integration otherwise
    380    ;;   (it uses a cache where candidates are caching keys through `marginalia--cached')
    381    (format "%s — %s"
    382            (lsp:symbol-information-name symbol-info)
    383            (consult-lsp--format-file-line-match
    384             (let ((file
    385                    (lsp--uri-to-path (lsp:location-uri (lsp:symbol-information-location symbol-info)))))
    386               (if-let ((wks (lsp-workspace-root file)))
    387                   (f-relative file wks)
    388                 file))
    389             (thread-first symbol-info
    390                           (lsp:symbol-information-location)
    391                           (lsp:location-range)
    392                           (lsp:range-start)
    393                           (lsp:position-line)
    394                           (1+)
    395                           (lsp-translate-line))))
    396    'consult--type (consult-lsp--symbols--kind-to-narrow symbol-info)
    397    'consult--candidate symbol-info
    398    'consult--details (lsp:document-symbol-detail? symbol-info)))
    399 
    400 (defun consult-lsp--symbols-annotate-builder ()
    401   "Annotation function for `consult-lsp-symbols'.
    402 
    403 See `consult-lsp--symbols--transformer' for the available text-properties
    404 usable in the annotation-function."
    405   (let* ((width (length (number-to-string (line-number-at-pos
    406                                            (point-max)
    407                                            consult-line-numbers-widen))))
    408          (fmt (propertize (format "%%%dd " width) 'face 'consult-line-number-prefix)))
    409     (lambda (cand)
    410       (let* ((symbol-info (get-text-property 0 'consult--candidate cand))
    411              (line (thread-first symbol-info
    412                      (lsp:symbol-information-location)
    413                      (lsp:location-range)
    414                      (lsp:range-start)
    415                      (lsp:position-line)
    416                      (1+)
    417                      (lsp-translate-line))))
    418         (list
    419          cand
    420          (format "%-10s "
    421                  (alist-get (lsp:symbol-information-kind symbol-info) lsp-symbol-kinds))
    422          (concat
    423           (or
    424            (when-let ((details (get-text-property 0 'consult--details cand)))
    425              (propertize (format " — %s" details) 'face 'font-lock-doc-face))
    426            "")))))))
    427 
    428 ;;;###autoload
    429 (defun consult-lsp-symbols (arg)
    430   "Query workspace symbols. When ARG is set through prefix, query all workspaces."
    431   (interactive "P")
    432   (let* ((initial "")
    433          (all-workspaces? arg)
    434          (ws (or (and all-workspaces? (-uniq (-flatten (ht-values (lsp-session-folder->servers (lsp-session))))))
    435                  (lsp-workspaces)
    436                  (lsp-get (lsp-session-folder->servers (lsp-session))
    437                           (lsp-workspace-root default-directory)))))
    438     (unless ws
    439       (user-error "There is no active workspace !"))
    440     (consult--read
    441      (thread-first
    442          (consult--async-sink)
    443        (consult--async-refresh-immediate)
    444        (consult--async-map consult-lsp-symbols-transformer-function)
    445        (consult-lsp--symbols--make-async-source ws)
    446        (consult--async-throttle)
    447        (consult--async-split))
    448      :prompt "LSP Symbols "
    449      :annotate (funcall consult-lsp-symbols-annotate-builder-function)
    450      :require-match t
    451      :history t
    452      :add-history (consult--async-split-thingatpt 'symbol)
    453      :initial (consult--async-split-initial initial)
    454      :category 'consult-lsp-symbols
    455      :lookup #'consult--lookup-candidate
    456      :group (consult--type-group consult-lsp-symbols-narrow)
    457      :narrow (consult--type-narrow consult-lsp-symbols-narrow)
    458      :state (consult-lsp--symbols--state))))
    459 
    460 
    461 ;;;; File symbols
    462 
    463 (defun consult-lsp--flatten-document-symbols (to-flatten)
    464   "Helper function for flattening document symbols TO-FLATTEN to a plain list."
    465   (cl-labels ((rec-helper
    466                (to-flatten accumulator)
    467                (dolist (table to-flatten)
    468                  (push table accumulator)
    469                  (when-let ((children (lsp-get table :children)))
    470                    (setq accumulator (rec-helper
    471                                       (append children nil) ; convert children from vector to list
    472                                       accumulator))))
    473                accumulator))
    474     (nreverse (rec-helper to-flatten nil))))
    475 
    476 (defun consult-lsp--file-symbols--transformer (symbol)
    477   "Default transformer to produce a completion candidate from SYMBOL."
    478   (let ((lbeg (thread-first symbol
    479                             (lsp:document-symbol-selection-range)
    480                             (lsp:range-start)
    481                             (lsp:position-line)
    482                             (lsp-translate-line)))
    483         (lend (thread-first symbol
    484                             (lsp:document-symbol-selection-range)
    485                             (lsp:range-end)
    486                             (lsp:position-line)
    487                             (lsp-translate-line)))
    488         (cbeg (thread-first symbol
    489                             (lsp:document-symbol-selection-range)
    490                             (lsp:range-start)
    491                             (lsp:position-character)
    492                             (lsp-translate-column)))
    493         (cend (thread-first symbol
    494                             (lsp:document-symbol-selection-range)
    495                             (lsp:range-end)
    496                             (lsp:position-character)
    497                             (lsp-translate-column))))
    498     (let ((beg (lsp--line-character-to-point lbeg cbeg))
    499           (end (lsp--line-character-to-point lend cend))
    500           (marker (make-marker)))
    501       (set-marker marker beg)
    502       ;; Pre-condition to respect narrowing
    503       (unless (or (< beg (point-min))
    504                   (> end (point-max)))
    505         ;; NOTE: no need to add anything to the candidate string like
    506         ;; for consult-lsp-symbols because
    507         ;; - we have the line location and there are less hits in this command,
    508         ;; - the candidates are different caching keys because of
    509         ;;   `consult--location-candidate' usage.
    510         ;;
    511         ;; `consult--location-candidate' is unavailable for
    512         ;; `consult-lsp--symbols--transformer'because it needs a marker,
    513         ;; and we cannot create marker for buffers that aren't open.
    514         (consult--location-candidate
    515          (let ((substr (consult--buffer-substring beg end 'fontify))
    516                (symb-info-name (lsp:symbol-information-name symbol)))
    517            (concat substr
    518                    (unless (string= substr symb-info-name)
    519                      (format " (%s)"
    520                              symb-info-name))))
    521          marker
    522          (1+ lbeg)
    523          marker
    524          'consult--type (consult-lsp--symbols--kind-to-narrow symbol)
    525          'consult--name (lsp:symbol-information-name symbol)
    526          'consult--details (lsp:document-symbol-detail? symbol))))))
    527 
    528 (defun consult-lsp--file-symbols-candidates ()
    529   "Returns all candidates for a `consult-lsp-file-symbols' search.
    530 
    531 See the :annotate documentation of `consult--read' for more information."
    532   (consult--forbid-minibuffer)
    533   (let* ((all-symbols (consult-lsp--flatten-document-symbols
    534                        (lsp-request "textDocument/documentSymbol"
    535                                     (lsp-make-document-symbol-params :text-document
    536                                                                      (lsp--text-document-identifier)))))
    537          (candidates (mapcar consult-lsp-file-symbols-transformer-function all-symbols)))
    538     (unless candidates
    539       (user-error "No symbols"))
    540     candidates))
    541 
    542 (defun consult-lsp--file-symbols-annotate-builder ()
    543   "Annotation function for `consult-lsp-file-symbols'."
    544   (let* ((width (length (number-to-string (line-number-at-pos
    545                                            (point-max)
    546                                            consult-line-numbers-widen))))
    547          (fmt (propertize (format "%%%dd " width) 'face 'consult-line-number-prefix)))
    548     (lambda (cand)
    549       (let ((line (cdr (get-text-property 0 'consult-location cand))))
    550         (list cand
    551               (format fmt line)
    552               (concat
    553                (when-let ((details (get-text-property 0 'consult--details cand)))
    554                  (propertize (format " - %s" details) 'face 'font-lock-doc-face))))))))
    555 
    556 ;;;###autoload
    557 (defun consult-lsp-file-symbols (group-results)
    558   "Search symbols defined in current file in a manner similar to `consult-line'.
    559 
    560 If the prefix argument GROUP-RESULTS is specified, symbols are grouped by their
    561 kind; otherwise they are returned in the order that they appear in the file."
    562   (interactive "P")
    563   (consult--read
    564    (consult--with-increased-gc (consult-lsp--file-symbols-candidates))
    565    :prompt "Go to symbol: "
    566    :annotate (funcall consult-lsp-file-symbols-annotate-builder-function)
    567    :require-match t
    568    :sort nil
    569    :history '(:input consult--line-history)
    570    :category 'consult-lsp-file-symbols
    571    :lookup #'consult--line-match
    572    :narrow (consult--type-narrow consult-lsp-symbols-narrow)
    573    :group (when group-results (consult--type-group consult-lsp-symbols-narrow))
    574    :state (consult--jump-state)))
    575 
    576 
    577 
    578 (provide 'consult-lsp)
    579 ;;; consult-lsp.el ends here