mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-06-14 12:31:25 +00: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)))))
|
(funcall eglot-current-linepos-function)))))
|
||||||
|
|
||||||
(defvar eglot-move-to-linepos-function #'eglot-move-to-utf-16-linepos
|
(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
|
Per the LSP spec, character offsets in LSP Position objects count UTF-16
|
||||||
UTF-16 code units, not actual code points. So when LSP says
|
code units, not actual code points. So when LSP says position 3 of a
|
||||||
position 3 of a line containing just \"aXbc\", where X is a funny
|
line containing just \"aXbc\", where X is a funny looking character in
|
||||||
looking character in the UTF-16 \"supplementary plane\", it
|
the UTF-16 \"supplementary plane\", it actually means `b', not `c'. The
|
||||||
actually means `b', not `c'. The default value
|
default value `eglot-move-to-utf-16-linepos' accounts for this.
|
||||||
`eglot-move-to-utf-16-linepos' accounts for this.
|
|
||||||
|
|
||||||
This variable can also be set to `eglot-move-to-utf-8-linepos' or
|
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
|
`eglot-move-to-utf-32-linepos' for servers not closely following the
|
||||||
the spec. Also, since LSP 3.17 server and client may agree on an
|
spec. Also, since LSP 3.17 server and client may agree on an encoding
|
||||||
encoding and Eglot will set this variable automatically.")
|
and Eglot will set this variable automatically.")
|
||||||
|
|
||||||
(defun eglot-move-to-utf-8-linepos (n)
|
(defun eglot-move-to-utf-8-linepos (n)
|
||||||
"Move to line's Nth byte as computed by LSP's UTF-8 criterion."
|
"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))
|
(while (and (< (position-bytes (point)) goal-byte) (< (point) eol))
|
||||||
;; raw bytes take 2 bytes in the buffer
|
;; raw bytes take 2 bytes in the buffer
|
||||||
(when (>= (char-after) #x3fff80) (setq goal-byte (1+ goal-byte)))
|
(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)
|
(defun eglot-move-to-utf-16-linepos (n)
|
||||||
"Move to line's Nth code unit as computed by LSP's UTF-16 criterion."
|
"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))
|
(while (and (< (point) goal-char) (< (point) eol))
|
||||||
;; code points in the "supplementary place" use two code units
|
;; code points in the "supplementary place" use two code units
|
||||||
(when (<= #x010000 (char-after) #x10ffff) (setq goal-char (1- goal-char)))
|
(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)
|
(defun eglot-move-to-utf-32-linepos (n)
|
||||||
"Move to line's Nth codepoint as computed by LSP's UTF-32 criterion."
|
"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
|
(mapconcat #'eglot--format-markup
|
||||||
(if (vectorp contents) contents (list contents)) "\n"))
|
(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)
|
(eglot--dbind ((SignatureInformation)
|
||||||
((:label siglabel))
|
((:label siglabel))
|
||||||
((:documentation sigdoc)) parameters activeParameter)
|
((:documentation sigdoc)) parameters activeParameter)
|
||||||
sig
|
sig
|
||||||
(with-temp-buffer
|
(with-temp-buffer
|
||||||
(insert siglabel)
|
(save-excursion
|
||||||
;; Add documentation, indented so we can distinguish multiple signatures
|
;; Insert main siglabel line
|
||||||
(when-let* ((doc (and (not briefp) sigdoc (eglot--format-markup sigdoc))))
|
(insert siglabel)
|
||||||
(goto-char (point-max))
|
;; Add function documentation to end on a new line, indented so
|
||||||
(insert "\n" (replace-regexp-in-string "^" " " doc)))
|
;; we can distinguish multiple signatures
|
||||||
;; Try to highlight function name only
|
(when-let* ((doc (and (not briefp) sigdoc (eglot--format-markup sigdoc))))
|
||||||
(let (first-parlabel)
|
(goto-char (point-max))
|
||||||
(cond ((and (cl-plusp (length parameters))
|
(insert "\n" (replace-regexp-in-string "^" " " doc))))
|
||||||
(vectorp (setq first-parlabel
|
;; Back to point-min: try to highlight function name only
|
||||||
(plist-get (aref parameters 0) :label))))
|
(cond ((and (cl-plusp (length parameters))
|
||||||
(save-excursion
|
(vectorp (setq first-parlabel
|
||||||
(goto-char (elt first-parlabel 0))
|
(plist-get (aref parameters 0) :label))))
|
||||||
(skip-syntax-backward "^w")
|
(funcall move-fn (elt first-parlabel 0))
|
||||||
(add-face-text-property (point-min) (point)
|
(skip-syntax-backward "^w")
|
||||||
'font-lock-function-name-face)))
|
(add-face-text-property (point-min) (point)
|
||||||
((save-excursion
|
'font-lock-function-name-face))
|
||||||
(goto-char (point-min))
|
((looking-at "\\([^(]*\\)([^)]*)")
|
||||||
(looking-at "\\([^(]*\\)([^)]*)"))
|
(add-face-text-property (match-beginning 1) (match-end 1)
|
||||||
(add-face-text-property (match-beginning 1) (match-end 1)
|
'font-lock-function-name-face)))
|
||||||
'font-lock-function-name-face))))
|
|
||||||
;; Now to the parameters
|
;; Now to the parameters
|
||||||
(cl-loop
|
(cl-loop
|
||||||
with active-param = (or activeParameter sig-active)
|
with active-param = (or activeParameter sig-active)
|
||||||
|
with case-fold-search = nil
|
||||||
for i from 0 for parameter across parameters do
|
for i from 0 for parameter across parameters do
|
||||||
(eglot--dbind ((ParameterInformation)
|
(eglot--dbind ((ParameterInformation)
|
||||||
((:label parlabel))
|
((:label parlabel))
|
||||||
((:documentation pardoc)))
|
((:documentation pardoc)))
|
||||||
parameter
|
parameter
|
||||||
;; ...perhaps highlight it in the formals list
|
(cl-flet ((parlabel-bounds ()
|
||||||
(when (eq i active-param)
|
(cond ((stringp parlabel)
|
||||||
(save-excursion
|
(and (search-forward parlabel (line-end-position) t)
|
||||||
(goto-char (point-min))
|
(match-data)))
|
||||||
(pcase-let
|
(t (mapcar move-fn parlabel)))))
|
||||||
((`(,beg ,end)
|
;; ...perhaps highlight it in the formals list
|
||||||
(if (stringp parlabel)
|
(when-let* ((b (and (eq i active-param)
|
||||||
(let ((case-fold-search nil))
|
(parlabel-bounds))))
|
||||||
(and (search-forward parlabel (line-end-position) t)
|
(add-face-text-property
|
||||||
(list (match-beginning 0) (match-end 0))))
|
(car b) (cadr b)
|
||||||
(list (1+ (aref parlabel 0)) (1+ (aref parlabel 1))))))
|
'eldoc-highlight-function-argument))
|
||||||
(if (and beg end)
|
;; ...and/or maybe add its doc on a line by its own.
|
||||||
(add-face-text-property
|
|
||||||
beg end
|
|
||||||
'eldoc-highlight-function-argument)))))
|
|
||||||
;; ...and/or maybe add its doc on a line by its own.
|
|
||||||
(let (fpardoc)
|
|
||||||
(when (and pardoc (not briefp)
|
(when (and pardoc (not briefp)
|
||||||
(not (string-empty-p
|
(not (string-empty-p
|
||||||
(setq fpardoc (eglot--format-markup pardoc)))))
|
(setq fpardoc (eglot--format-markup pardoc)))))
|
||||||
(insert "\n "
|
(unless (stringp parlabel)
|
||||||
(propertize
|
(setq parlabel (apply #'buffer-substring (parlabel-bounds))))
|
||||||
(if (stringp parlabel) parlabel
|
(save-excursion
|
||||||
(substring siglabel (aref parlabel 0) (aref parlabel 1)))
|
(goto-char (point-max))
|
||||||
'face (and (eq i active-param) 'eldoc-highlight-function-argument))
|
(insert "\n "
|
||||||
": " fpardoc)))))
|
(propertize
|
||||||
|
parlabel
|
||||||
|
'face (and (eq i active-param) 'eldoc-highlight-function-argument))
|
||||||
|
": " fpardoc))))))
|
||||||
(buffer-string))))
|
(buffer-string))))
|
||||||
|
|
||||||
(defun eglot-signature-eldoc-function (cb &rest _ignored)
|
(defun eglot-signature-eldoc-function (cb &rest _ignored)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue