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
next prev parent 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).