Skip to content

Commit 39687fe

Browse files
authored
lsp-imenu: support categorizing by :kind? (#2313)
* `lsp-imenu': support categorizing by :kind? * `lsp-imenu': use `lsp-symbol-kinds' Rename `lsp--symbol-kind' to `lsp-symbol-kinds', making the former an alias for the latter and the latter a `defcustom'. In it, rename "Enum Member" to "Enumerator" since that looks better. Instead of creating another constant for that does the same as `lsp-symbol-kinds', use the latter. Refactor: simplify `lsp--get-symbol-type' to use `alist-get' instead of a complex threading sequence, simplifying it to a single s-expression. * `lsp-imenu': use a separate SymbolKinds alist * PR review: nit: use `lsp:document-symbol-kind' * Terms: use "Enum Member" instead of "Enumerator" The latter is specific to C/C++, and not used by the Java language specification, for example. Use "Enum Member" instead, as it is used in the LSP-specification * Docstring: spelling: remove double "the" * `lsp-imenu-index-function': fix customization Remove extraneous sharp quotes, causing errors when using one of the selected customization entries. * `lsp-imenu-create-uncategorized-index': docstring Clarify that the index created corresponds to the server's result directly. * Add a new categorized `imenu' index `lsp-imenu-create-categorized-index' becomes `lsp-imenu-create-top-level-categorized-index', with the former now now creating a "traditional" `imenu' index where all Functions are under a `Functions' section, all Variables under `Variables', etc. It works with both `SymbolInformation' and `DocumentSymbol's (tested with `pyright' and `iph', respectively). Currently there is no way to jump to a symbol if it has children; this defect is shared by `org-imenu', because `imenu' entries with children cannot encode a position. * Include symbols with children in `imenu' * Handle unknown symbol types: use ? as type * Fix defcustom `const': point to right function * `lsp-imenu': `defcustom': fix old function `lsp-imenu-create-top-levelcategorized-index' was renamed to `lsp-imenu-create-top-level-categorized-index'.
1 parent 3ca25e6 commit 39687fe

File tree

1 file changed

+149
-14
lines changed

1 file changed

+149
-14
lines changed

lsp-mode.el

Lines changed: 149 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5386,7 +5386,7 @@ A reference is highlighted only if it is visible in a window."
53865386
wins-visible-pos))
53875387
highlights)))
53885388

5389-
(defconst lsp--symbol-kind
5389+
(defcustom lsp-symbol-kinds
53905390
'((1 . "File")
53915391
(2 . "Module")
53925392
(3 . "Namespace")
@@ -5412,14 +5412,23 @@ A reference is highlighted only if it is visible in a window."
54125412
(23 . "Struct")
54135413
(24 . "Event")
54145414
(25 . "Operator")
5415-
(26 . "Type Parameter")))
5415+
(26 . "Type Parameter"))
5416+
"Alist mapping SymbolKinds to human-readable strings.
5417+
Various Symbol objects in the LSP protocol have an integral type,
5418+
specifying what they are. This alist maps such type integrals to
5419+
readable representations of them. See
5420+
`https://microsoft.github.io/language-server-protocol/specifications/specification-current/',
5421+
namespace SymbolKind."
5422+
:group 'lsp-mode
5423+
:type '(alist :key-type integer :value-type string))
5424+
(defalias 'lsp--symbol-kind 'lsp-symbol-kinds)
54165425

54175426
(lsp-defun lsp--symbol-information-to-xref
54185427
((&SymbolInformation :kind :name
54195428
:location (&Location :uri :range (&Range :start
54205429
(&Position :line :character)))))
54215430
"Return a `xref-item' from SYMBOL information."
5422-
(xref-make (format "[%s] %s" (alist-get kind lsp--symbol-kind) name)
5431+
(xref-make (format "[%s] %s" (alist-get kind lsp-symbol-kinds) name)
54235432
(xref-make-file-location (lsp--uri-to-path uri)
54245433
line
54255434
character)))
@@ -6246,10 +6255,7 @@ an alist
62466255

62476256
(lsp-defun lsp--get-symbol-type ((&SymbolInformation :kind))
62486257
"The string name of the kind of SYM."
6249-
(-> kind
6250-
(assoc lsp--symbol-kind)
6251-
(cl-rest)
6252-
(or "Other")))
6258+
(alist-get kind lsp-symbol-kinds "Other"))
62536259

62546260
(defun lsp--get-line-and-col (sym)
62556261
"Obtain the line and column corresponding to SYM."
@@ -6297,15 +6303,144 @@ representation to point representation."
62976303
(< c1 c2))
62986304
(c1 t)))))
62996305

6300-
(defun lsp--imenu-create-index ()
6301-
"Create imenu index from document symbols."
6302-
(let* ((filtered-symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols)))
6303-
(lsp--line-col-to-point-hash-table (-> filtered-symbols
6306+
(defun lsp-imenu-create-uncategorized-index (symbols)
6307+
"Create imenu index from document SYMBOLS.
6308+
This function, unlike `lsp-imenu-create-categorized-index', does
6309+
not categorize by type, but instead returns an `imenu' index
6310+
corresponding to the symbol hierarchy returned by the server
6311+
directly."
6312+
(let* ((lsp--line-col-to-point-hash-table (-> symbols
63046313
lsp--collect-lines-and-cols
63056314
lsp--convert-line-col-to-points-batch)))
6306-
(if (lsp--imenu-hierarchical-p filtered-symbols)
6307-
(lsp--imenu-create-hierarchical-index filtered-symbols)
6308-
(lsp--imenu-create-non-hierarchical-index filtered-symbols))))
6315+
(if (lsp--imenu-hierarchical-p symbols)
6316+
(lsp--imenu-create-hierarchical-index symbols)
6317+
(lsp--imenu-create-non-hierarchical-index symbols))))
6318+
6319+
(defcustom lsp-imenu-symbol-kinds
6320+
'((1 . "Files")
6321+
(2 . "Modules")
6322+
(3 . "Namespaces")
6323+
(4 . "Packages")
6324+
(5 . "Classes")
6325+
(6 . "Methods")
6326+
(7 . "Properties")
6327+
(8 . "Fields")
6328+
(9 . "Constructors")
6329+
(10 . "Enums")
6330+
(11 . "Interfaces")
6331+
(12 . "Functions")
6332+
(13 . "Variables")
6333+
(14 . "Constants")
6334+
(15 . "Strings")
6335+
(16 . "Numbers")
6336+
(17 . "Booleans")
6337+
(18 . "Arrays")
6338+
(19 . "Objects")
6339+
(20 . "Keys")
6340+
(21 . "Nulls")
6341+
(22 . "Enum Members")
6342+
(23 . "Structs")
6343+
(24 . "Events")
6344+
(25 . "Operators")
6345+
(26 . "Type Parameters"))
6346+
"`lsp-symbol-kinds', but only used by `imenu'.
6347+
A new variable is needed, as it is `imenu' convention to use
6348+
pluralized categories, which `lsp-symbol-kinds' doesn't. If the
6349+
non-pluralized names are preferred, this can be set to
6350+
`lsp-symbol-kinds'."
6351+
:type '(alist :key-type integer :value-type string))
6352+
6353+
(defun lsp--imenu-kind->name (kind)
6354+
(alist-get kind lsp-imenu-symbol-kinds "?"))
6355+
6356+
(defun lsp-imenu-create-top-level-categorized-index (symbols)
6357+
"Create an `imenu' index categorizing SYMBOLS by type.
6358+
Only root symbols are categorized.
6359+
6360+
See `lsp-symbol-kinds' to customize the category naming. SYMBOLS
6361+
shall be a list of DocumentSymbols or SymbolInformation."
6362+
(mapcan
6363+
(-lambda ((type . symbols))
6364+
(let ((cat (lsp--imenu-kind->name type))
6365+
(symbols (lsp-imenu-create-uncategorized-index symbols)))
6366+
;; If there is no :kind (this is being defensive), or we couldn't look it
6367+
;; up, just display the symbols inline, without categories.
6368+
(if cat (list (cons cat symbols)) symbols)))
6369+
(sort (seq-group-by #'lsp:document-symbol-kind symbols)
6370+
(-lambda ((kinda) (kindb)) (< kinda kindb)))))
6371+
6372+
(lsp-defun lsp--symbol->imenu ((sym &as &DocumentSymbol :selection-range (&RangeToPoint :start)))
6373+
"Convert an `&DocumentSymbol' to an `imenu' entry."
6374+
(cons (lsp-render-symbol sym lsp-imenu-detailed-outline) start))
6375+
6376+
(defun lsp--imenu-create-categorized-index-1 (symbols)
6377+
"Returns an `imenu' index from SYMBOLS categorized by type.
6378+
The result looks like this: ((\"Variables\" . (...)))."
6379+
(->>
6380+
symbols
6381+
(mapcan
6382+
(-lambda ((sym &as &DocumentSymbol :kind :children?))
6383+
(if (seq-empty-p children?)
6384+
(list (list kind (lsp--symbol->imenu sym)))
6385+
(let ((parent (lsp-render-symbol sym lsp-imenu-detailed-outline)))
6386+
(cons
6387+
(list kind (lsp--symbol->imenu sym))
6388+
(mapcar (-lambda ((type . imenu-items))
6389+
(list type (cons parent (mapcan #'cdr imenu-items))))
6390+
(-group-by #'car (lsp--imenu-create-categorized-index-1 children?))))))))
6391+
(-group-by #'car)
6392+
(mapcar
6393+
(-lambda ((kind . syms))
6394+
(cons kind (mapcan #'cdr syms))))))
6395+
6396+
(defun lsp--imenu-create-categorized-index (symbols)
6397+
(let ((syms (lsp--imenu-create-categorized-index-1 symbols)))
6398+
(dolist (sym syms)
6399+
(setcar sym (lsp--imenu-kind->name (car sym))))
6400+
syms))
6401+
6402+
(lsp-defun lsp--symbol-information->imenu ((sym &as &SymbolInformation :location (&Location :range (&RangeToPoint :start))))
6403+
(cons (lsp-render-symbol-information sym nil) start))
6404+
6405+
(defun lsp--imenu-create-categorized-index-flat (symbols)
6406+
"Create a kind-categorized index for SymbolInformation."
6407+
(mapcar (-lambda ((kind . syms))
6408+
(cons (lsp--imenu-kind->name kind)
6409+
(mapcan (-lambda ((parent . children))
6410+
(let ((children (mapcar #'lsp--symbol-information->imenu children)))
6411+
(if parent (list (cons parent children)) children)))
6412+
(-group-by #'lsp:symbol-information-container-name? syms))))
6413+
(seq-group-by #'lsp:symbol-information-kind symbols)))
6414+
6415+
(defun lsp-imenu-create-categorized-index (symbols)
6416+
(if (lsp--imenu-hierarchical-p symbols)
6417+
(lsp--imenu-create-categorized-index symbols)
6418+
(lsp--imenu-create-categorized-index-flat symbols)))
6419+
6420+
(defcustom lsp-imenu-index-function #'lsp-imenu-create-uncategorized-index
6421+
"Function that should create an `imenu' index.
6422+
It will be called with a list of SymbolInformation or
6423+
DocumentSymbols, whose first level is already filtered. It shall
6424+
then return an appropriate `imenu' index (see
6425+
`imenu-create-index-function').
6426+
6427+
Note that this interface is not stable, and subject to change any
6428+
time."
6429+
:group 'lsp-imenu
6430+
:type '(radio
6431+
(const :tag "Categorize by type"
6432+
lsp-imenu-create-categorized-index)
6433+
(const :tag "Categorize root symbols by type"
6434+
lsp-imenu-create-top-level-categorized-index)
6435+
(const :tag "Uncategorized, inline entries"
6436+
lsp-imenu-create-uncategorized-index)
6437+
(function :tag "Custom function")))
6438+
6439+
(defun lsp--imenu-create-index ()
6440+
"Create an `imenu' index based on the language server.
6441+
Respects `lsp-imenu-index-function'."
6442+
(let ((symbols (lsp--imenu-filter-symbols (lsp--get-document-symbols))))
6443+
(funcall lsp-imenu-index-function symbols)))
63096444

63106445
(defun lsp--imenu-filter-symbols (symbols)
63116446
"Filter out unsupported symbols from SYMBOLS."

0 commit comments

Comments
 (0)