mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-15 10:30:25 -08:00
Eglot: rework semtok user options and face calculation
* lisp/progmodes/eglot.el (eglot-semantic-tokens-faces) (eglot-semantic-tokens-modifier-faces): Delete. (eglot--semtok-types, eglot--semtok-modifiers): Rename from eglot-semantic-tokens-faces and eglot-semantic-tokens-modifier-faces. (eglot-client-capabilities): Tweak. (eglot--lsp-interface-alist): Add SemanticTokensLegend. (eglot--connect): Don't initialize a server. (eglot--semtok-define-things): New helper. (eglot-lsp-server): Just one slot needed. (eglot--semtok-token-faces): Rework.
This commit is contained in:
parent
2589dfc300
commit
b248acba1d
1 changed files with 92 additions and 118 deletions
|
|
@ -629,71 +629,6 @@ Note additionally:
|
|||
(string :tag "Specify your own")))
|
||||
:package-version '(Eglot . "1.19"))
|
||||
|
||||
(defcustom eglot-semantic-tokens-faces
|
||||
'(("namespace" . font-lock-keyword-face)
|
||||
("type" . font-lock-type-face)
|
||||
("class" . font-lock-type-face)
|
||||
("enum" . font-lock-type-face)
|
||||
("interface" . font-lock-type-face)
|
||||
("struct" . font-lock-type-face)
|
||||
("typeParameter" . font-lock-type-face)
|
||||
("parameter" . font-lock-variable-name-face)
|
||||
("variable" . font-lock-variable-name-face)
|
||||
("property" . font-lock-property-use-face)
|
||||
("enumMember" . font-lock-constant-face)
|
||||
("event" . font-lock-variable-name-face)
|
||||
("function" . font-lock-function-name-face)
|
||||
("method" . font-lock-function-name-face)
|
||||
("macro" . font-lock-preprocessor-face)
|
||||
("keyword" . font-lock-keyword-face)
|
||||
("modifier" . font-lock-function-name-face)
|
||||
("comment" . font-lock-comment-face)
|
||||
("string" . font-lock-string-face)
|
||||
("number" . font-lock-constant-face)
|
||||
("regexp" . font-lock-string-face)
|
||||
("operator" . font-lock-function-name-face)
|
||||
("decorator" . font-lock-type-face))
|
||||
"Alist of faces to use to highlight semantic tokens.
|
||||
Each element is a cons cell whose car is a token type name and cdr is
|
||||
the face to use."
|
||||
:type `(alist :key-type (string :tag "Token name")
|
||||
:value-type (choice (face :tag "Face")
|
||||
(plist :tag "Face Attributes"
|
||||
:key-type
|
||||
(choice
|
||||
,@(mapcar
|
||||
(lambda (cell)
|
||||
`(const :tag ,(capitalize
|
||||
(cdr cell))
|
||||
,(car cell)))
|
||||
face-attribute-name-alist))))))
|
||||
|
||||
(defcustom eglot-semantic-tokens-modifier-faces
|
||||
'(("declaration" . font-lock-function-name-face)
|
||||
("definition" . font-lock-function-name-face)
|
||||
("readonly" . font-lock-constant-face)
|
||||
("static" . font-lock-keyword-face)
|
||||
("deprecated" . eglot-diagnostic-tag-deprecated-face)
|
||||
("abstract" . font-lock-keyword-face)
|
||||
("async" . font-lock-preprocessor-face)
|
||||
("modification" . font-lock-function-name-face)
|
||||
("documentation" . font-lock-doc-face)
|
||||
("defaultLibrary" . font-lock-builtin-face))
|
||||
"List of face to use to highlight tokens with modifiers.
|
||||
Each element is a cons cell whose car is a modifier name and cdr is
|
||||
the face to use."
|
||||
:type `(alist :key-type (string :tag "Token name")
|
||||
:value-type (choice (face :tag "Face")
|
||||
(plist :tag "Face Attributes"
|
||||
:key-type
|
||||
(choice
|
||||
,@(mapcar
|
||||
(lambda (cell)
|
||||
`(const :tag ,(capitalize
|
||||
(cdr cell))
|
||||
,(car cell)))
|
||||
face-attribute-name-alist))))))
|
||||
|
||||
(defvar eglot-withhold-process-id nil
|
||||
"If non-nil, Eglot will not send the Emacs process id to the language server.
|
||||
This can be useful when using docker to run a language server.")
|
||||
|
|
@ -726,6 +661,47 @@ This can be useful when using docker to run a language server.")
|
|||
`((1 . eglot-diagnostic-tag-unnecessary-face)
|
||||
(2 . eglot-diagnostic-tag-deprecated-face)))
|
||||
|
||||
(eval-when-compile
|
||||
(defconst eglot--semtok-types
|
||||
'(("namespace" . font-lock-keyword-face)
|
||||
("type" . font-lock-type-face)
|
||||
("class" . font-lock-type-face)
|
||||
("enum" . font-lock-type-face)
|
||||
("interface" . font-lock-type-face)
|
||||
("struct" . font-lock-type-face)
|
||||
("typeParameter" . font-lock-type-face)
|
||||
("parameter" . font-lock-variable-name-face)
|
||||
("variable" . font-lock-variable-name-face)
|
||||
("property" . font-lock-property-use-face)
|
||||
("enumMember" . font-lock-constant-face)
|
||||
("event" . font-lock-variable-name-face)
|
||||
("function" . font-lock-function-name-face)
|
||||
("method" . font-lock-function-name-face)
|
||||
("macro" . font-lock-preprocessor-face)
|
||||
("keyword" . font-lock-keyword-face)
|
||||
("modifier" . font-lock-function-name-face)
|
||||
("comment" . font-lock-comment-face)
|
||||
("string" . font-lock-string-face)
|
||||
("number" . font-lock-constant-face)
|
||||
("regexp" . font-lock-string-face)
|
||||
("operator" . font-lock-function-name-face)
|
||||
("decorator" . font-lock-type-face)))
|
||||
|
||||
(defconst eglot--semtok-modifiers
|
||||
'(("declaration" . font-lock-function-name-face)
|
||||
("definition" . font-lock-function-name-face)
|
||||
("readonly" . font-lock-constant-face)
|
||||
("static" . font-lock-keyword-face)
|
||||
("deprecated" . eglot-diagnostic-tag-deprecated-face)
|
||||
("abstract" . font-lock-keyword-face)
|
||||
("async" . font-lock-preprocessor-face)
|
||||
("modification" . font-lock-function-name-face)
|
||||
("documentation" . font-lock-doc-face)
|
||||
("defaultLibrary" . font-lock-builtin-face))))
|
||||
|
||||
(defvar eglot-semantic-token-types) ;; forward-declare
|
||||
(defvar eglot-semantic-token-modifiers) ;; forward-declare
|
||||
|
||||
(defvaralias 'eglot-{} 'eglot--{})
|
||||
|
||||
(defconst eglot--{} (make-hash-table :size 0) "The empty JSON object.")
|
||||
|
|
@ -777,6 +753,7 @@ This can be useful when using docker to run a language server.")
|
|||
(ResponseError (:code :message) (:data))
|
||||
(ShowMessageParams (:type :message))
|
||||
(ShowMessageRequestParams (:type :message) (:actions))
|
||||
(SemanticTokensLegend (:tokenTypes :tokenModifiers))
|
||||
(SignatureHelp (:signatures) (:activeSignature :activeParameter))
|
||||
(SignatureInformation (:label) (:documentation :parameters :activeParameter))
|
||||
(SymbolInformation (:name :kind :location)
|
||||
|
|
@ -1149,10 +1126,10 @@ object."
|
|||
:rename `(:dynamicRegistration :json-false)
|
||||
:semanticTokens `(:dynamicRegistration :json-false
|
||||
:requests '(:range t :full (:delta t))
|
||||
:tokenModifiers [,@(mapcar #'car eglot-semantic-tokens-modifier-faces)]
|
||||
:overlappingTokenSupport t
|
||||
:multilineTokenSupport t
|
||||
:tokenTypes [,@(mapcar #'car eglot-semantic-tokens-faces)]
|
||||
:tokenTypes [,@eglot-semantic-token-types]
|
||||
:tokenModifiers [,@eglot-semantic-token-modifiers]
|
||||
:formats ["relative"])
|
||||
:inlayHint `(:dynamicRegistration :json-false)
|
||||
:callHierarchy `(:dynamicRegistration :json-false)
|
||||
|
|
@ -1225,15 +1202,9 @@ object."
|
|||
(saved-initargs
|
||||
:documentation "Saved initargs for reconnection purposes."
|
||||
:accessor eglot--saved-initargs)
|
||||
(semtok-faces
|
||||
:initform nil
|
||||
:documentation "Semantic tokens faces.")
|
||||
(semtok-modifier-faces
|
||||
:initform nil
|
||||
:documentation "Semantic tokens modifier faces.")
|
||||
(semtok-modifier-cache
|
||||
:initform (make-hash-table)
|
||||
:documentation "Map LSP modifier values to the selected faces."))
|
||||
(semtok-cache
|
||||
:initform (make-hash-table :test #'equal)
|
||||
:documentation "Map LSP token conses to face names."))
|
||||
:documentation
|
||||
"Represents a server. Wraps a process for LSP communication.")
|
||||
|
||||
|
|
@ -1820,7 +1791,6 @@ This docstring appeases checkdoc, that's all."
|
|||
(gethash project eglot--servers-by-project))
|
||||
(setf (eglot--capabilities server) capabilities)
|
||||
(setf (eglot--server-info server) serverInfo)
|
||||
(eglot--semtok-initialize server)
|
||||
(jsonrpc-notify server :initialized eglot--{})
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
|
|
@ -4586,22 +4556,52 @@ If NOERROR, return predicate, else erroring function."
|
|||
|
||||
|
||||
;;; Semantic tokens
|
||||
(defmacro eglot--semtok-define-things ()
|
||||
(cl-flet ((def-it (name def)
|
||||
`(defface ,(intern (format "eglot-semantic-%s-face" name))
|
||||
'((t (:inherit ,def)))
|
||||
,(format "Face for painting a `%s' LSP semantic token" name)
|
||||
:group 'eglot-semantic-fontification)))
|
||||
(let ((types (mapcar #'car eglot--semtok-types))
|
||||
(modifiers (mapcar #'car eglot--semtok-modifiers)))
|
||||
`(progn
|
||||
(defgroup eglot-semantic-faces nil
|
||||
"Faces and options for LSP semantic fontification." :group 'eglot)
|
||||
,@(cl-loop for (n . d) in eglot--semtok-types collect (def-it n d))
|
||||
,@(cl-loop for (n . d) in eglot--semtok-modifiers collect (def-it n d))
|
||||
(defcustom eglot-semantic-token-types
|
||||
',types "LSP-supplied semantic types Eglot should consider."
|
||||
:type '(set ,@(mapcar (lambda (o) `(const ,o)) types))
|
||||
:group 'eglot-semantic-fontification)
|
||||
(defcustom eglot-semantic-token-modifiers
|
||||
',modifiers "LSP-supplied semantic modifiers Eglot should consider."
|
||||
:type '(set ,@(mapcar (lambda (o) `(const ,o)) modifiers))
|
||||
:group 'eglot-semantic-fontification)))))
|
||||
|
||||
(eglot--semtok-define-things)
|
||||
|
||||
(defun eglot--semtok-token-faces (tok)
|
||||
(with-slots (semtok-faces
|
||||
(modifier-faces semtok-modifier-faces)
|
||||
(modifier-cache semtok-modifier-cache))
|
||||
(eglot-current-server)
|
||||
(let* ((code (cdr tok))
|
||||
(mods (gethash code modifier-cache 'not-found)))
|
||||
(when (eq mods 'not-found)
|
||||
(setq mods (cl-loop for j from 0 below (length modifier-faces)
|
||||
if (> (logand code (ash 1 j)) 0)
|
||||
if (aref modifier-faces j)
|
||||
collect (aref modifier-faces j)))
|
||||
(puthash code mods modifier-cache))
|
||||
(if-let* ((main (aref semtok-faces (car tok))))
|
||||
(cons main mods)
|
||||
mods))))
|
||||
(with-slots (semtok-cache capabilities)
|
||||
(eglot--current-server-or-lose)
|
||||
(let ((probe (gethash tok semtok-cache :missing))
|
||||
tname)
|
||||
(if (eq probe :missing)
|
||||
(puthash
|
||||
tok
|
||||
(eglot--dbind ((SemanticTokensLegend) tokenTypes tokenModifiers)
|
||||
(plist-get (plist-get capabilities :semanticTokensProvider) :legend)
|
||||
(setq tname (aref tokenTypes (car tok)))
|
||||
(when (member tname eglot-semantic-token-types)
|
||||
(cl-loop
|
||||
for j from 0 for m across tokenModifiers
|
||||
unless (or (zerop (logand (cdr tok) (ash 1 j)))
|
||||
(not (member m eglot-semantic-token-modifiers)))
|
||||
collect (intern (format "eglot-semantic-%s-face" m)) into mfaces
|
||||
finally (cl-return
|
||||
(cons (intern (format "eglot-semantic-%s-face" tname))
|
||||
mfaces)))))
|
||||
semtok-cache)
|
||||
probe))))
|
||||
|
||||
(defvar-local eglot--semtok-idle-timer nil
|
||||
"Idle timer to request full semantic tokens.")
|
||||
|
|
@ -4613,32 +4613,6 @@ If NOERROR, return predicate, else erroring function."
|
|||
(eglot--when-live-buffer buffer
|
||||
(unless (zerop eglot--versioned-identifier) (font-lock-flush)))))
|
||||
|
||||
(defun eglot--semtok-build-face-map (identifiers faces category varname)
|
||||
"Build map of FACES for IDENTIFIERS using CATEGORY and VARNAME."
|
||||
(vconcat
|
||||
(mapcar (lambda (id)
|
||||
(let ((maybe-face (cdr (assoc id faces))))
|
||||
(when (not maybe-face)
|
||||
(eglot--warn "No face has been associated to the %s `%s': consider adding a corresponding definition to %s"
|
||||
category id varname))
|
||||
maybe-face))
|
||||
identifiers)))
|
||||
|
||||
(defun eglot--semtok-initialize (server)
|
||||
"Initialize SERVER for semantic tokens."
|
||||
(with-slots (semtok-faces semtok-modifier-faces capabilities) server
|
||||
;; FIXME: eglot-dbind
|
||||
(cl-destructuring-bind (&key tokenTypes tokenModifiers &allow-other-keys)
|
||||
(plist-get (plist-get capabilities :semanticTokensProvider) :legend)
|
||||
(setq semtok-faces
|
||||
(eglot--semtok-build-face-map
|
||||
tokenTypes eglot-semantic-tokens-faces
|
||||
"semantic token" 'eglot-semantic-tokens-faces)
|
||||
semtok-modifier-faces
|
||||
(eglot--semtok-build-face-map
|
||||
tokenModifiers eglot-semantic-tokens-modifier-faces
|
||||
"semantic token modifier" 'eglot-semantic-tokens-modifier-faces)))))
|
||||
|
||||
(define-minor-mode eglot-semantic-tokens-mode
|
||||
"Minor mode for fontifying buffer with LSP server's semantic tokens."
|
||||
:global nil
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue