]> git.eshelyaron.com Git - emacs.git/commit
Eglot: implement inlay hints (bug#61412, bug#61066)
authorJoão Távora <joaotavora@gmail.com>
Tue, 21 Feb 2023 14:14:05 +0000 (14:14 +0000)
committerJoão Távora <joaotavora@gmail.com>
Wed, 22 Feb 2023 19:19:12 +0000 (19:19 +0000)
commit1841299a11dfcd875bdbdb75d1fc56d996a727f7
treedc07d02b4cc768e135be07c6c0067826304b6cf8
parent28ed0d1840f94ba52b8b60bfbf222493fee2a3ea
Eglot: implement inlay hints (bug#61412, bug#61066)

Inlay hints are small text annotations to specific parts of the whole
buffer, not unlike diagnostics, but designed to help readability
instead of indicating problems.  For example, a C++ LSP server can
serve hints about positional parameter names in function calls and a
variable's automatically deduced type.  Emacs can display these hints
in many little 0-length overlays with an 'before-string property, thus
helping the user remember those types and parameter names.

Since inlay hints are potentially a large amount of data to request
from the LSP server, the implementation strives to be as parsimonious
as possible with these requests.

So, by default, inlay hints are only requested for the visible
portions of the buffer across windows showing this buffer.  This is
done by leveraging the 'window-scroll-functions' variable, making for
a reasonably complex implementation involving per-window timers.  When
scrolling a window, it may take a short amount of time for inlay hints
to "pop in".  The new user variable 'eglot-lazy-inlay-hints' can be
used to exert some control over this.

Specifically, if the variable's value is set to 'nil', then inlay
hints are greedily fetched for the whole buffer every time a change
occurs.  This is a much simpler mode of operation which may avoid
problems, but is also likely much slower in large buffers.

Also, because the inlay feature is probably visually suprising to
some, it is turned OFF by default, which is not the usual practice of
Eglot (at least not when the necessary infrastructure is present).
This decision may be changed soon.  Here's a good one-liner for
enabling it by default in every Eglot-managed buffer:

   (add-hook 'eglot-managed-mode-hook #'eglot-inlay-hints-mode)

I haven't tested inlay hints extensively across many LSP servers, so I
would appreciate any testing, both for functional edge cases and
regarding performance.  There are possibly more optimization
oportunities in the "lazy" mode of operation, like more aggressively
deleting buffer overlays that are not in visible parts of the buffer.

Though I ended up writing this one from scratch, I want to thank
Dimitry Bolopopsky <dimitri@belopopsky.com> and Chinmay Dala
<dalal.chinmay.0101@gmail.com> for suggestions and early patches.

* lisp/progmodes/eglot.el (eglot--lsp-interface-alist): Define
InlayHint.
(eglot-client-capabilities): Announce 'inlayHint' capability.
(eglot-ignored-server-capabilities): Add :inlayHintProvider.
(eglot--document-changed-hook): New helper hook.
(eglot--after-change): Use it.
(eglot-inlay-hint-face, eglot-type-hint-face)
(eglot-parameter-hint-face): New faces.
(eglot--update-hints-1, eglot--inlay-hints-after-scroll)
(eglot--inlay-hints-fully, eglot--inlay-hints-lazily): New helpers.
(eglot-lazy-inlay-hints): New user variable.
(eglot-inlay-hints-mode): New minor mode.
(eglot--maybe-activate-editing-mode): Try to activate
eglot-inlay-hints-mode.
(eglot--before-change): Remove overlays immediately in the
area being changed.
(eglot--managed-mode-off): Remove overlays.

* doc/misc/eglot.texi (Eglot Features): Mention inlay hints.
(Eglot Variables): Mention eglot-lazy-inlay-hints.
doc/misc/eglot.texi
lisp/progmodes/eglot.el