unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#61412: [PATCH] Add inlay hints to eglot
@ 2023-02-11  8:13 Chinmay Dalal
  2023-02-11 11:50 ` Chinmay Dalal
                   ` (8 more replies)
  0 siblings, 9 replies; 28+ messages in thread
From: Chinmay Dalal @ 2023-02-11  8:13 UTC (permalink / raw)
  To: 61412; +Cc: Chinmay Dalal

Hello all, I have improved upon the patch from bug#61066.

From the original email:
> - I can't figure out a way to show the hints on a document without causing lags or timeouts from the lsp server
This might have been because they were sending an asynchronous request, waiting
for the response, collecting the hints and displaying them. I have instead used
`jsonrpc-async-request` with a handler function which should remedy this issue.
I have tried this with rust-analyzer and faced no lags.

The original patch also checked the wrong condition (`cond (eglot--managed-mode`)
in `eglot-inlay-mode` and had a superfluous `(run-hooks 'eglot-managed-mode-hook)`
(presumably copy-pasted from `define-minor-mode eglot--managed-mode`), I have
fixed that.

From a reply to the original email:
> AFAIU, inlay hints provide information of the same kind as ElDoc and
> in similar manner from the display and UX POV.  So I think this
> feature should work via ElDoc, not as a separate from-the-scratch
> implementation.
This can't be done via ElDoc because the purpose of inlay hints is to display
variable types and parameter names without moving the cursor to the location.
One can already see this information if they move their cursor to the variable
or function and wait for the "hover" - inlay hints were added to the spec after
this which (IMHO) clearly means something like overlays should be used.

A couple of issues at present:
1. I have not implemented "complex" hints (whose `label` is an array of
`InlayHintLabelPart` instead of a string) because I don't know what to do when
there are multiple labels at the same location.
rust-analyzer uses these hints to show a clickable link at the end of a function
block, which points to the beginning of the function.

2. I need to save the buffer or disable and re-enable `eglot-inlay-mode` to
get hints for the first time after opening a file, even though I call `eglot--update-hints`
once in `eglot-inlay-mode`.

Regards,
Chinmay Dalal
---
 lisp/progmodes/eglot.el | 70 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 69 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 6caf589..0d5e63e 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -404,6 +404,10 @@ This can be useful when using docker to run a language server.")
 (when (assoc 'flex completion-styles-alist)
   (add-to-list 'completion-category-defaults '(eglot (styles flex basic))))
 
+(defcustom eglot-inlay-hints t
+  "If non-nil, enable inlay hints."
+  :type 'boolean)
+
 \f
 ;;; Constants
 ;;;
@@ -1624,7 +1628,8 @@ under cursor."
           (const :tag "Highlight links in document" :documentLinkProvider)
           (const :tag "Decorate color references" :colorProvider)
           (const :tag "Fold regions of buffer" :foldingRangeProvider)
-          (const :tag "Execute custom commands" :executeCommandProvider)))
+          (const :tag "Execute custom commands" :executeCommandProvider)
+          (const :tag "Inlay hints" :inlayHintProvider)))
 
 (defun eglot--server-capable (&rest feats)
   "Determine if current server is capable of FEATS."
@@ -1845,6 +1850,8 @@ If it is activated, also signal textDocument/didOpen."
     (when (and buffer-file-name (eglot-current-server))
       (setq eglot--diagnostics nil)
       (eglot--managed-mode)
+      (unless (not eglot-inlay-hints)
+        (eglot-inlay-mode))
       (eglot--signal-textDocument/didOpen))))
 
 (add-hook 'find-file-hook 'eglot--maybe-activate-editing-mode)
@@ -3456,6 +3463,67 @@ If NOERROR, return predicate, else erroring function."
       (revert-buffer)
       (pop-to-buffer (current-buffer)))))
 
+(defface eglot-inlay-hint
+  '((t (:height 0.8 :inherit shadow)))
+  "Face used for inlay hint overlays.")
+
+(define-minor-mode eglot-inlay-mode
+  "Mode for displaying inlay hints."
+  :lighter " inlay"
+  (if eglot-inlay-mode
+      (progn
+        (add-hook 'after-save-hook 'eglot--update-hints 0 t)
+        (eglot--update-hints))
+      (progn
+        (remove-hook 'after-save-hook 'eglot--update-hints t)
+        (eglot--remove-hints))))
+
+(defun eglot--inlay-handler (buffer hints)
+  "Apply vector of inlay hints HINTS on buffer BUFFER."
+  (seq-doseq (hint hints)
+    (let* ((position (plist-get hint :position))
+           (line (plist-get position :line))
+           (character (plist-get position :character))
+           (label (plist-get hint :label)))
+      (when (stringp label)
+        (with-current-buffer buffer
+          (eglot--widening
+            (goto-char (point-min))
+            (forward-line line)
+            (eglot-move-to-column character)
+            (let ((overlay (make-overlay (point) (point))))
+              (overlay-put overlay 'before-string (propertize
+                                                    (concat (if (plist-get hint :paddingLeft) " " "")
+                                                            label
+                                                            (if (plist-get hint :paddingRight) " " ""))
+                                                    'face 'eglot-inlay-hint))
+              (overlay-put overlay 'is-eglot-inlay-hint t))))))))
+
+(defun eglot--remove-hints ()
+  "Remove inlay hints from the buffer."
+  (remove-overlays nil nil 'is-eglot-inlay-hint t))
+
+(defun eglot--update-hints ()
+  "Request inlay hints for the current buffer and apply them."
+  (unless (eglot--server-capable :inlayHintProvider)
+    (eglot--error "This LSP server isn't an :inlayHintProvider"))
+  ;; Remove existing hints
+  (eglot--remove-hints)
+  (let ((buffer (current-buffer)))
+    (jsonrpc-async-request
+      (eglot--current-server-or-lose)
+      :textDocument/inlayHint
+      (list
+        :textDocument (eglot--TextDocumentIdentifier)
+        :range (list
+                :start (list :line 0 :character 0)
+                :end (list
+                      :line (count-lines (point-min) (point-max))
+                      :character 0)))
+      :success-fn (lambda (hints)
+                    (eglot--inlay-handler buffer hints))
+      :deferred t)))
+
 \f
 ;;; Hacks
 ;;;
-- 
2.39.1






^ permalink raw reply related	[flat|nested] 28+ messages in thread

end of thread, other threads:[~2023-02-25 10:19 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-11  8:13 bug#61412: [PATCH] Add inlay hints to eglot Chinmay Dalal
2023-02-11 11:50 ` Chinmay Dalal
2023-02-11 15:23 ` bug#61412: Question about implementation Chinmay Dalal
2023-02-13 15:35 ` bug#61412: Hover github discussion Chinmay Dalal
2023-02-15 13:58   ` Eli Zaretskii
2023-02-15 15:05     ` João Távora
2023-02-15 15:38       ` Eli Zaretskii
2023-02-15 12:56 ` bug#61412: [PATCH v2] Add inlay hints to eglot Chinmay Dalal
2023-02-15 13:08 ` bug#61412: [PATCH v3] " Chinmay Dalal
2023-02-15 16:24 ` bug#61412: Inlay activation Chinmay Dalal
2023-02-15 18:09   ` Eli Zaretskii
2023-02-15 18:48     ` Chinmay Dalal
2023-02-15 19:01       ` João Távora
2023-02-15 19:17         ` Chinmay Dalal
2023-02-15 19:41       ` Eli Zaretskii
2023-02-15 20:17         ` Chinmay Dalal
2023-02-21 15:13           ` bug#61412: Inlay hints implementation João Távora
2023-02-21 15:21             ` Eli Zaretskii
2023-02-21 18:42             ` Dimitri Belopopsky
2023-02-21 21:26               ` João Távora
2023-02-25  0:21                 ` João Távora
2023-02-25  7:59                   ` Eli Zaretskii
2023-02-25 10:19                     ` João Távora
2023-02-21 15:33 ` Chinmay Dalal
2023-02-21 15:57 ` Chinmay Dalal
2023-02-22 15:26 ` Chinmay Dalal
2023-02-22 16:51   ` Chinmay Dalal
2023-02-22 23:17   ` João Távora

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).