mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-05-31 01:32:00 -07:00
Eglot: fix eglot--sig-info with non-UTF-32 positionEncoding
Github-reference: https://github.com/joaotavora/eglot/discussions/1588 When the server negotiates positionEncoding utf-8 or utf-16, ParameterInformation.label vector offsets are byte/code-unit counts into the signature label, not character counts. Using them raw caused wrong highlights and crashes on Unicode-rich signatures. * lisp/progmodes/eglot.el (eglot--sig-info): Mostly rewrite. (eglot-move-to-utf-8-linepos-function): Tweak docstring. (eglot-move-to-utf-8-linepos, eglot-move-to-utf-16-linepos): Return position moved to.
This commit is contained in:
parent
543d8a7a9d
commit
56f27dd9f0
1 changed files with 58 additions and 56 deletions
|
|
@ -2152,19 +2152,18 @@ LBP defaults to `eglot--bol'."
|
|||
(funcall eglot-current-linepos-function)))))
|
||||
|
||||
(defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos
|
||||
"Function to move to a position within a line reported by the LSP server.
|
||||
"Move point to LSP-reported position within a line.
|
||||
|
||||
Per the LSP spec, character offsets in LSP Position objects count
|
||||
UTF-16 code units, not actual code points. So when LSP says
|
||||
position 3 of a line containing just \"aXbc\", where X is a funny
|
||||
looking character in the UTF-16 \"supplementary plane\", it
|
||||
actually means `b', not `c'. The default value
|
||||
`eglot-move-to-utf-16-linepos' accounts for this.
|
||||
Per the LSP spec, character offsets in LSP Position objects count UTF-16
|
||||
code units, not actual code points. So when LSP says position 3 of a
|
||||
line containing just \"aXbc\", where X is a funny looking character in
|
||||
the UTF-16 \"supplementary plane\", it actually means `b', not `c'. The
|
||||
default value `eglot-move-to-utf-16-linepos' accounts for this.
|
||||
|
||||
This variable can also be set to `eglot-move-to-utf-8-linepos' or
|
||||
`eglot-move-to-utf-32-linepos' for servers not closely following
|
||||
the spec. Also, since LSP 3.17 server and client may agree on an
|
||||
encoding and Eglot will set this variable automatically.")
|
||||
`eglot-move-to-utf-32-linepos' for servers not closely following the
|
||||
spec. Also, since LSP 3.17 server and client may agree on an encoding
|
||||
and Eglot will set this variable automatically.")
|
||||
|
||||
(defun eglot-move-to-utf-8-linepos (n)
|
||||
"Move to line's Nth byte as computed by LSP's UTF-8 criterion."
|
||||
|
|
@ -2175,7 +2174,8 @@ encoding and Eglot will set this variable automatically.")
|
|||
(while (and (< (position-bytes (point)) goal-byte) (< (point) eol))
|
||||
;; raw bytes take 2 bytes in the buffer
|
||||
(when (>= (char-after) #x3fff80) (setq goal-byte (1+ goal-byte)))
|
||||
(forward-char 1))))
|
||||
(forward-char 1))
|
||||
(point)))
|
||||
|
||||
(defun eglot-move-to-utf-16-linepos (n)
|
||||
"Move to line's Nth code unit as computed by LSP's UTF-16 criterion."
|
||||
|
|
@ -2186,7 +2186,8 @@ encoding and Eglot will set this variable automatically.")
|
|||
(while (and (< (point) goal-char) (< (point) eol))
|
||||
;; code points in the "supplementary place" use two code units
|
||||
(when (<= #x010000 (char-after) #x10ffff) (setq goal-char (1- goal-char)))
|
||||
(forward-char 1))))
|
||||
(forward-char 1))
|
||||
(point)))
|
||||
|
||||
(defun eglot-move-to-utf-32-linepos (n)
|
||||
"Move to line's Nth codepoint as computed by LSP's UTF-32 criterion."
|
||||
|
|
@ -4108,66 +4109,67 @@ for which LSP on-type-formatting should be requested."
|
|||
(mapconcat #'eglot--format-markup
|
||||
(if (vectorp contents) contents (list contents)) "\n"))
|
||||
|
||||
(defun eglot--sig-info (sig &optional sig-active briefp)
|
||||
(cl-defun eglot--sig-info (sig &optional sig-active briefp
|
||||
&aux (move-fn eglot-move-to-linepos-function)
|
||||
first-parlabel
|
||||
fpardoc)
|
||||
(eglot--dbind ((SignatureInformation)
|
||||
((:label siglabel))
|
||||
((:documentation sigdoc)) parameters activeParameter)
|
||||
sig
|
||||
(with-temp-buffer
|
||||
(insert siglabel)
|
||||
;; Add documentation, indented so we can distinguish multiple signatures
|
||||
(when-let* ((doc (and (not briefp) sigdoc (eglot--format-markup sigdoc))))
|
||||
(goto-char (point-max))
|
||||
(insert "\n" (replace-regexp-in-string "^" " " doc)))
|
||||
;; Try to highlight function name only
|
||||
(let (first-parlabel)
|
||||
(cond ((and (cl-plusp (length parameters))
|
||||
(vectorp (setq first-parlabel
|
||||
(plist-get (aref parameters 0) :label))))
|
||||
(save-excursion
|
||||
(goto-char (elt first-parlabel 0))
|
||||
(skip-syntax-backward "^w")
|
||||
(add-face-text-property (point-min) (point)
|
||||
'font-lock-function-name-face)))
|
||||
((save-excursion
|
||||
(goto-char (point-min))
|
||||
(looking-at "\\([^(]*\\)([^)]*)"))
|
||||
(add-face-text-property (match-beginning 1) (match-end 1)
|
||||
'font-lock-function-name-face))))
|
||||
(save-excursion
|
||||
;; Insert main siglabel line
|
||||
(insert siglabel)
|
||||
;; Add function documentation to end on a new line, indented so
|
||||
;; we can distinguish multiple signatures
|
||||
(when-let* ((doc (and (not briefp) sigdoc (eglot--format-markup sigdoc))))
|
||||
(goto-char (point-max))
|
||||
(insert "\n" (replace-regexp-in-string "^" " " doc))))
|
||||
;; Back to point-min: try to highlight function name only
|
||||
(cond ((and (cl-plusp (length parameters))
|
||||
(vectorp (setq first-parlabel
|
||||
(plist-get (aref parameters 0) :label))))
|
||||
(funcall move-fn (elt first-parlabel 0))
|
||||
(skip-syntax-backward "^w")
|
||||
(add-face-text-property (point-min) (point)
|
||||
'font-lock-function-name-face))
|
||||
((looking-at "\\([^(]*\\)([^)]*)")
|
||||
(add-face-text-property (match-beginning 1) (match-end 1)
|
||||
'font-lock-function-name-face)))
|
||||
;; Now to the parameters
|
||||
(cl-loop
|
||||
with active-param = (or activeParameter sig-active)
|
||||
with case-fold-search = nil
|
||||
for i from 0 for parameter across parameters do
|
||||
(eglot--dbind ((ParameterInformation)
|
||||
((:label parlabel))
|
||||
((:documentation pardoc)))
|
||||
parameter
|
||||
;; ...perhaps highlight it in the formals list
|
||||
(when (eq i active-param)
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(pcase-let
|
||||
((`(,beg ,end)
|
||||
(if (stringp parlabel)
|
||||
(let ((case-fold-search nil))
|
||||
(and (search-forward parlabel (line-end-position) t)
|
||||
(list (match-beginning 0) (match-end 0))))
|
||||
(list (1+ (aref parlabel 0)) (1+ (aref parlabel 1))))))
|
||||
(if (and beg end)
|
||||
(add-face-text-property
|
||||
beg end
|
||||
'eldoc-highlight-function-argument)))))
|
||||
;; ...and/or maybe add its doc on a line by its own.
|
||||
(let (fpardoc)
|
||||
(cl-flet ((parlabel-bounds ()
|
||||
(cond ((stringp parlabel)
|
||||
(and (search-forward parlabel (line-end-position) t)
|
||||
(match-data)))
|
||||
(t (mapcar move-fn parlabel)))))
|
||||
;; ...perhaps highlight it in the formals list
|
||||
(when-let* ((b (and (eq i active-param)
|
||||
(parlabel-bounds))))
|
||||
(add-face-text-property
|
||||
(car b) (cadr b)
|
||||
'eldoc-highlight-function-argument))
|
||||
;; ...and/or maybe add its doc on a line by its own.
|
||||
(when (and pardoc (not briefp)
|
||||
(not (string-empty-p
|
||||
(setq fpardoc (eglot--format-markup pardoc)))))
|
||||
(insert "\n "
|
||||
(propertize
|
||||
(if (stringp parlabel) parlabel
|
||||
(substring siglabel (aref parlabel 0) (aref parlabel 1)))
|
||||
'face (and (eq i active-param) 'eldoc-highlight-function-argument))
|
||||
": " fpardoc)))))
|
||||
(unless (stringp parlabel)
|
||||
(setq parlabel (apply #'buffer-substring (parlabel-bounds))))
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
(insert "\n "
|
||||
(propertize
|
||||
parlabel
|
||||
'face (and (eq i active-param) 'eldoc-highlight-function-argument))
|
||||
": " fpardoc))))))
|
||||
(buffer-string))))
|
||||
|
||||
(defun eglot-signature-eldoc-function (cb &rest _ignored)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue