unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: Filippo Argiolas <filippo.argiolas@gmail.com>
To: "João Távora" <joaotavora@gmail.com>
Cc: 65418@debbugs.gnu.org, Philip Kaludercic <philipk@posteo.net>,
	Felician Nemeth <felician.nemeth@gmail.com>
Subject: bug#65418: 29.1; Eglot: support clangd inactiveRegions extension
Date: Thu, 31 Aug 2023 19:28:45 +0200	[thread overview]
Message-ID: <CAOdrLGKowEmxfULUPFpGGyMic4pxp2vn9_4W3T7mXV_G3dUc0A@mail.gmail.com> (raw)
In-Reply-To: <873504d1oe.fsf@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 1070 bytes --]

On Sun, Aug 27, 2023 at 3:58 PM João Távora <joaotavora@gmail.com> wrote:
> Glad to help.  You're lucky I'm not some kind of wine connoisseur ;-)
> Make a patch for that (and remember to also include the exportation of
> the '--' symbols).
>

Many thanks again! That code definitely made this work week more enjoyable ;-)

Sorry for the delay with the patch, little time left these days for
side projects or even trivial contributions like this.
See if the attached patches could do!

> > One thing about UI, all the themes I tried seem to render shadow as
> > grey-ish but it was my impression reading the docs that it would be a
> > dim version of the current face, so it would still have syntax
> > highlighting. Is it just a theme limitation (probably because shadow
> > wasn't used for something like this before) or it's not technically
> > possible?
>
> I'm fairly sure it's technically possible, even if perhaps not easy.
> You can investigate or ask this on emacs-devel.

Agreed, will move the discussion about this there.

Filippo

[-- Attachment #2: 0002-Promote-eglot-methods-used-in-examples-to-public.patch --]
[-- Type: application/octet-stream, Size: 8948 bytes --]

From 18340104293ef004c38305ac99839e5ca9faedc5 Mon Sep 17 00:00:00 2001
From: Filippo Argiolas <filippo.argiolas@gmail.com>
Date: Thu, 31 Aug 2023 19:09:47 +0200
Subject: [PATCH 2/2] Promote eglot methods used in examples to public

* lisp/progmodes/eglot.el: promote eglot--uri-to-path and
eglot--range-region to public API as they're being used in eglot docs
as example about extending eglot functionality

* doc/misc/eglot.texi: update Extending Eglot example with new public methods
---
 doc/misc/eglot.texi     |  2 +-
 lisp/progmodes/eglot.el | 32 ++++++++++++++++----------------
 2 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index ed1456725ae..dc668ae7960 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -1316,7 +1316,7 @@ Extending Eglot
 (cl-defmethod eglot-handle-notification
   (_server (_method (eql textDocument/inactiveRegions))
            &key regions textDocument &allow-other-keys)
-  (if-let* ((path (expand-file-name (eglot--uri-to-path
+  (if-let* ((path (expand-file-name (eglot-uri-to-path
                                      (cl-getf textDocument :uri))))
             (buffer (find-buffer-visiting path)))
       (with-current-buffer buffer
diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el
index 65daa0941d5..3190a9fe762 100644
--- a/lisp/progmodes/eglot.el
+++ b/lisp/progmodes/eglot.el
@@ -1671,7 +1671,7 @@ eglot--path-to-uri
                eglot--uri-path-allowed-chars)))))
 
 (declare-function w32-long-file-name "w32proc.c" (fn))
-(defun eglot--uri-to-path (uri)
+(defun eglot-uri-to-path (uri)
   "Convert URI to file path, helped by `eglot--current-server'."
   (when (keywordp uri) (setq uri (substring (symbol-name uri) 1)))
   (let* ((server (eglot-current-server))
@@ -1780,7 +1780,7 @@ eglot--server-capable-or-lose
                     (mapconcat #'symbol-name feats " ")))
     retval))
 
-(defun eglot--range-region (range &optional markers)
+(defun eglot-range-region (range &optional markers)
   "Return region (BEG . END) that represents LSP RANGE.
 If optional MARKERS, make markers."
   (let* ((st (plist-get range :start))
@@ -2291,7 +2291,7 @@ eglot-handle-notification
                     (t          'eglot-note)))
             (mess (source code message)
               (concat source (and code (format " [%s]" code)) ": " message)))
-    (if-let* ((path (expand-file-name (eglot--uri-to-path uri)))
+    (if-let* ((path (expand-file-name (eglot-uri-to-path uri)))
               (buffer (find-buffer-visiting path)))
         (with-current-buffer buffer
           (cl-loop
@@ -2303,7 +2303,7 @@ eglot-handle-notification
                        diag-spec
                      (setq message (mess source code message))
                      (pcase-let
-                         ((`(,beg . ,end) (eglot--range-region range)))
+                         ((`(,beg . ,end) (eglot-range-region range)))
                        ;; Fallback to `flymake-diag-region' if server
                        ;; botched the range
                        (when (= beg end)
@@ -2395,7 +2395,7 @@ eglot-handle-request
         (filename))
     (cond
      ((eq external t) (browse-url uri))
-     ((file-readable-p (setq filename (eglot--uri-to-path uri)))
+     ((file-readable-p (setq filename (eglot-uri-to-path uri)))
       ;; Use run-with-timer to avoid nested client requests like the
       ;; "synchronous imenu" floated in bug#62116 presumably caused by
       ;; which-func-mode.
@@ -2408,7 +2408,7 @@ eglot-handle-request
                   (select-frame-set-input-focus (selected-frame)))
                  ((display-buffer (current-buffer))))
            (when selection
-             (pcase-let ((`(,beg . ,end) (eglot--range-region selection)))
+             (pcase-let ((`(,beg . ,end) (eglot-range-region selection)))
                ;; FIXME: it is very naughty to use someone else's `--'
                ;; function, but `xref--goto-char' happens to have
                ;; exactly the semantics we want vis-a-vis widening.
@@ -2643,7 +2643,7 @@ eglot-handle-request
          (mapcar
           (eglot--lambda ((ConfigurationItem) scopeUri section)
             (cl-loop
-             with scope-uri-path = (and scopeUri (eglot--uri-to-path scopeUri))
+             with scope-uri-path = (and scopeUri (eglot-uri-to-path scopeUri))
              for (wsection o)
              on (eglot--workspace-configuration-plist server scope-uri-path)
              by #'cddr
@@ -2774,12 +2774,12 @@ eglot--xref-make-match
   "Like `xref-make-match' but with LSP's NAME, URI and RANGE.
 Try to visit the target file for a richer summary line."
   (pcase-let*
-      ((file (eglot--uri-to-path uri))
+      ((file (eglot-uri-to-path uri))
        (visiting (or (find-buffer-visiting file)
                      (gethash uri eglot--temp-location-buffers)))
        (collect (lambda ()
                   (eglot--widening
-                   (pcase-let* ((`(,beg . ,end) (eglot--range-region range))
+                   (pcase-let* ((`(,beg . ,end) (eglot-range-region range))
                                 (bol (progn (goto-char beg) (eglot--bol)))
                                 (substring (buffer-substring bol (line-end-position)))
                                 (hi-beg (- beg bol))
@@ -3190,7 +3190,7 @@ eglot-completion-at-point
                         (delete-region orig-pos (point))
                         (eglot--dbind ((TextEdit) range newText) textEdit
                           (pcase-let ((`(,beg . ,end)
-                                       (eglot--range-region range)))
+                                       (eglot-range-region range)))
                             (delete-region beg end)
                             (goto-char beg)
                             (funcall (or snippet-fn #'insert) newText))))
@@ -3331,7 +3331,7 @@ eglot--highlight-piggyback
                  (mapcar
                   (eglot--lambda ((DocumentHighlight) range)
                     (pcase-let ((`(,beg . ,end)
-                                 (eglot--range-region range)))
+                                 (eglot-range-region range)))
                       (let ((ov (make-overlay beg end)))
                         (overlay-put ov 'face 'eglot-highlight-symbol-face)
                         (overlay-put ov 'modification-hooks
@@ -3351,7 +3351,7 @@ eglot--imenu-SymbolInformation
        (pcase-lambda (`(,container . ,objs))
          (let ((elems (mapcar
                        (eglot--lambda ((SymbolInformation) kind name location)
-                         (let ((reg (eglot--range-region
+                         (let ((reg (eglot-range-region
                                      (plist-get location :range)))
                                (kind (alist-get kind eglot--symbol-kind-names)))
                            (cons (propertize name
@@ -3367,7 +3367,7 @@ eglot--imenu-SymbolInformation
 (defun eglot--imenu-DocumentSymbol (res)
   "Compute `imenu--index-alist' for RES vector of DocumentSymbol."
   (cl-labels ((dfs (&key name children range kind &allow-other-keys)
-                (let* ((reg (eglot--range-region range))
+                (let* ((reg (eglot-range-region range))
                        (kind (alist-get kind eglot--symbol-kind-names))
                        (name (propertize name
                                          'breadcrumb-region reg
@@ -3423,7 +3423,7 @@ eglot--apply-text-edits
                       (when reporter
                         (eglot--reporter-update reporter (cl-incf done))))))))
             (mapcar (eglot--lambda ((TextEdit) range newText)
-                      (cons newText (eglot--range-region range 'markers)))
+                      (cons newText (eglot-range-region range 'markers)))
                     (reverse edits)))
       (undo-amalgamate-change-group change-group)
       (when reporter
@@ -3436,14 +3436,14 @@ eglot--apply-workspace-edit
            (mapcar (eglot--lambda ((TextDocumentEdit) textDocument edits)
                      (eglot--dbind ((VersionedTextDocumentIdentifier) uri version)
                          textDocument
-                       (list (eglot--uri-to-path uri) edits version)))
+                       (list (eglot-uri-to-path uri) edits version)))
                    documentChanges)))
       (unless (and changes documentChanges)
         ;; We don't want double edits, and some servers send both
         ;; changes and documentChanges.  This unless ensures that we
         ;; prefer documentChanges over changes.
         (cl-loop for (uri edits) on changes by #'cddr
-                 do (push (list (eglot--uri-to-path uri) edits) prepared)))
+                 do (push (list (eglot-uri-to-path uri) edits) prepared)))
       (if (or confirm
               (cl-notevery #'find-buffer-visiting
                            (mapcar #'car prepared)))
-- 
2.41.0


[-- Attachment #3: 0001-Add-documentation-about-extending-Eglot.patch --]
[-- Type: application/octet-stream, Size: 4061 bytes --]

From e860bab2ac22fb9ea99e12bb095f72ef4c9b774a Mon Sep 17 00:00:00 2001
From: Filippo Argiolas <filippo.argiolas@gmail.com>
Date: Thu, 31 Aug 2023 07:22:05 +0200
Subject: [PATCH 1/2] Add documentation about extending Eglot
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* doc/misc/eglot.texi: add a simple example about extending Eglot
implementing its generic methods. The example, from João Távora, adds
support to inactiveRegions clangd protocol extension rendering
inactive code regions as shadowed in a LSP aware way (see discussion
in bug#65418)
---
 doc/misc/eglot.texi | 70 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi
index 6eb212ca841..ed1456725ae 100644
--- a/doc/misc/eglot.texi
+++ b/doc/misc/eglot.texi
@@ -985,6 +985,7 @@ Advanced server configuration
 * Project-specific configuration::
 * User-specific configuration::
 * JSONRPC objects in Elisp::
+* Extending Eglot::
 @end menu
 
 It's important to note that not all servers allow both kinds of
@@ -1262,6 +1263,75 @@ JSONRPC objects in Elisp
 @}
 @end example
 
+@node Extending Eglot
+@section Extending Eglot
+Sometimes it may be useful to extend existing Eglot functionality
+using its public methods.  A good example of when this need may arise
+is adding support for a custom LSP protocol extension only implemented
+by a specific server.
+
+The best source of documentation for this is probably Eglot source
+code itself. Most of the functionality is implemented with generic
+methods that can be easily extended or overridden and the code itself
+shows many examples about how to do this.
+
+Here's a simple example that adds support for clangd
+@code{inactiveRegions} extension introduced in clangd 17.
+
+When @code{inactiveRegions} capability is enabled the server will send
+a @code{textDocument/inactiveRegions} notification with a list of
+inactive code (e.g. code under ifdef macros) regions to the client.
+
+The first method extends @code{eglot-client-capabilities} using a
+simple heuristic to detect if current server is @command{clangd} and
+enables the @code{inactiveRegion} capability.
+
+The second method implements @code{eglot-handle-notification} to
+process the correspondent server notification and for each region
+received it creates an overlay applying the @code{shadow} face to the
+region. Overlays are recreated every time a new notification is
+received.
+
+Result will be that all the inactive code in the buffer will be nicely
+greyed out using the LSP server knowledge about current compile time
+preprocessor defines.
+
+@lisp
+;;; eglot-clangd-inactive-region.el -*- lexical-binding: t; -*-
+
+(require 'eglot)
+(require 'cl-lib)
+
+(cl-defmethod eglot-client-capabilities :around (server)
+  (let ((base (cl-call-next-method)))
+    (when (cl-find "clangd" (process-command (jsonrpc--process server))
+                   :test #'string-match)
+      (setf (cl-getf (cl-getf base :textDocument)
+                     :inactiveRegionsCapabilities)
+            '(:inactiveRegions t)))
+    base))
+
+(defvar-local eglot-clangd-inactive-region-overlays '())
+
+(cl-defmethod eglot-handle-notification
+  (_server (_method (eql textDocument/inactiveRegions))
+           &key regions textDocument &allow-other-keys)
+  (if-let* ((path (expand-file-name (eglot--uri-to-path
+                                     (cl-getf textDocument :uri))))
+            (buffer (find-buffer-visiting path)))
+      (with-current-buffer buffer
+        (mapc #'delete-overlay eglot-clangd-inactive-region-overlays)
+        (cl-loop
+         for r across regions
+         for (beg . end) = (eglot--range-region r)
+         for ov = (make-overlay beg end)
+         do
+         (overlay-put ov 'face 'shadow)
+         (push ov eglot-clangd-inactive-region-overlays)))))
+
+(provide 'eglot-clangd-inactive-region)
+@end lisp
+
 @node Troubleshooting Eglot
 @chapter Troubleshooting Eglot
 @cindex troubleshooting Eglot
-- 
2.41.0


  reply	other threads:[~2023-08-31 17:28 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-21  8:41 bug#65418: 29.1; Eglot: support clangd inactiveRegions extension Filippo Argiolas
2023-08-21 16:57 ` Philip Kaludercic
2023-08-21 19:04 ` Felician Nemeth
2023-08-22  7:09   ` Filippo Argiolas
2023-08-22  8:56     ` João Távora
2023-08-22 11:02       ` Filippo Argiolas
2023-08-25 12:18         ` João Távora
2023-08-27 10:52           ` Filippo Argiolas
2023-08-27 14:01             ` João Távora
2023-08-31 17:28               ` Filippo Argiolas [this message]
2023-09-04  1:05                 ` João Távora
2023-09-04  1:08                   ` João Távora
2023-09-04  3:59                     ` Filippo Argiolas
2023-09-04  4:09                       ` Filippo Argiolas
2023-09-04 10:51                         ` João Távora
2023-09-04 12:44                           ` Eli Zaretskii
2023-09-04 12:49                             ` João Távora
2023-09-04 16:17                               ` Eli Zaretskii
2023-09-04 20:37                                 ` João Távora
2023-09-04 11:41                     ` Eli Zaretskii
2023-09-02  8:14               ` Filippo Argiolas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAOdrLGKowEmxfULUPFpGGyMic4pxp2vn9_4W3T7mXV_G3dUc0A@mail.gmail.com \
    --to=filippo.argiolas@gmail.com \
    --cc=65418@debbugs.gnu.org \
    --cc=felician.nemeth@gmail.com \
    --cc=joaotavora@gmail.com \
    --cc=philipk@posteo.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).