Improve ergonomics of Eglot's inlay hints

Instead of deleting inlay hints instantly as soon as the affected
changes, make hint overlays span one character (instead of being
length 0).  Give the overlays an "evaporate" property.

Given an inlay hints at position POS, its attached to [POS, POS+1] if
it's kind=1 (usually type hints) and [POS-1, POS] otherwise.  For
kind=1 hints, the 'cursor position of the first such overlay is also
tweaked, so that's it's less akward to edit around it.

* lisp/progmodes/eglot.el (eglot--before-change): Don't delete hints
  here.
  (eglot--update-hints-1): Rework.
This commit is contained in:
João Távora 2023-03-19 00:47:55 +00:00
parent c3a543123a
commit 5bbbd70f56

View file

@ -2356,7 +2356,6 @@ THINGS are either registrations or unregisterations (sic)."
(defun eglot--before-change (beg end) (defun eglot--before-change (beg end)
"Hook onto `before-change-functions' with BEG and END." "Hook onto `before-change-functions' with BEG and END."
(remove-overlays beg end 'eglot--overlay t)
(when (listp eglot--recent-changes) (when (listp eglot--recent-changes)
;; Records BEG and END, crucially convert them into LSP ;; Records BEG and END, crucially convert them into LSP
;; (line/char) positions before that information is lost (because ;; (line/char) positions before that information is lost (because
@ -3612,31 +3611,41 @@ If NOERROR, return predicate, else erroring function."
(eglot--lambda ((InlayHint) position kind label paddingLeft paddingRight) (eglot--lambda ((InlayHint) position kind label paddingLeft paddingRight)
(goto-char (eglot--lsp-position-to-point position)) (goto-char (eglot--lsp-position-to-point position))
(when (or (> (point) to) (< (point) from)) (cl-return)) (when (or (> (point) to) (< (point) from)) (cl-return))
(let ((left-pad (and paddingLeft (let* ((left-pad (and paddingLeft
(not (eq paddingLeft :json-false)) (not (eq paddingLeft :json-false))
(not (memq (char-before) '(32 9))) " ")) (not (memq (char-before) '(32 9))) " "))
(right-pad (and paddingRight (right-pad (and paddingRight
(not (eq paddingRight :json-false)) (not (eq paddingRight :json-false))
(not (memq (char-after) '(32 9))) " "))) (not (memq (char-after) '(32 9))) " "))
(cl-flet (peg-after-p (eql kind 1)))
((do-it (text lpad rpad) (cl-labels
(let ((ov (make-overlay (point) (point)))) ((make-ov ()
(overlay-put ov 'before-string (if peg-after-p
(make-overlay (point) (1+ (point)) nil t)
(make-overlay (1- (point)) (point) nil nil nil)))
(do-it (label lpad rpad firstp)
(let* ((tweak-cursor-p (and firstp peg-after-p))
(ov (make-ov))
(text (concat lpad label rpad)))
(when tweak-cursor-p (put-text-property 0 1 'cursor 1 text))
(overlay-put ov (if peg-after-p 'before-string 'after-string)
(propertize (propertize
(concat lpad text rpad) text
'face (pcase kind 'face (pcase kind
(1 'eglot-type-hint-face) (1 'eglot-type-hint-face)
(2 'eglot-parameter-hint-face) (2 'eglot-parameter-hint-face)
(_ 'eglot-inlay-hint-face)))) (_ 'eglot-inlay-hint-face))))
(overlay-put ov 'eglot--inlay-hint t) (overlay-put ov 'eglot--inlay-hint t)
(overlay-put ov 'evaporate t)
(overlay-put ov 'eglot--overlay t)))) (overlay-put ov 'eglot--overlay t))))
(if (stringp label) (do-it label left-pad right-pad) (if (stringp label) (do-it label left-pad right-pad t)
(cl-loop (cl-loop
for i from 0 for ldetail across label for i from 0 for ldetail across label
do (eglot--dbind ((InlayHintLabelPart) value) ldetail do (eglot--dbind ((InlayHintLabelPart) value) ldetail
(do-it value (do-it value
(and (zerop i) left-pad) (and (zerop i) left-pad)
(and (= i (1- (length label))) right-pad)))))))))) (and (= i (1- (length label))) right-pad)
(zerop i))))))))))
(jsonrpc-async-request (jsonrpc-async-request
(eglot--current-server-or-lose) (eglot--current-server-or-lose)
:textDocument/inlayHint :textDocument/inlayHint