* Using Eglot:: Important Eglot commands and variables.
* Customizing Eglot:: Eglot customization and advanced features.
* Advanced server configuration:: Fine-tune a specific language server
+* Extending Eglot:: Writing Eglot extensions in Elisp
* Troubleshooting Eglot:: Troubleshooting and reporting bugs.
* GNU Free Documentation License:: The license for this manual.
* Index::
@}
@end example
+@node Extending Eglot
+@chapter Extending Eglot
+
+Sometimes it may be useful to extend existing Eglot functionality
+using Elisp 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, particularly the section marked ``API''.
+
+Most of the functionality is implemented with Common-Lisp style
+generic functions (@pxref{Generics,,,eieio,EIEIO}) that can be easily
+extended or overridden. The Eglot code itself is an example on how to
+do this.
+
+The following is a relatively simple example that adds support for the
+@code{inactiveRegions} experimental feature introduced in version 17
+of the @command{clangd} C/C++ language server++.
+
+Summarily, the feature works by first having the server detect the
+Eglot's advertisement of the @code{inactiveRegions} client capability
+during startup, whereupon the language server will report a list of
+regions of inactive code for each buffer. This is usually code
+surrounded by C/C++ @code{#ifdef} macros that the preprocessor removes
+based on compile-time information.
+
+The language server reports the regions by periodically sending a
+@code{textDocument/inactiveRegions} notification for each managed
+buffer (@pxref{Eglot and Buffers}). Normally, unknown server
+notifications are ignored by Eglot, but we're going change that.
+
+Both the announcement of the client capability and the handling of the
+new notification is done by adding methods to generic functions.
+
+@itemize @bullet
+@item
+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.
+
+@lisp
+(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))
+@end lisp
+
+Notice we use an internal function of the @code{jsonrpc.el} library,
+and a regexp search to detect @command{clangd}. An alternative would
+be to define a new EIEIO subclass of @code{eglot-lsp-server}, maybe
+called @code{eglot-clangd}, so that the method would be simplified:
+
+@lisp
+(cl-defmethod eglot-client-capabilities :around ((_s eglot-clangd))
+ (let ((base (cl-call-next-method)))
+ (setf (cl-getf (cl-getf base :textDocument)
+ :inactiveRegionsCapabilities)
+ '(:inactiveRegions t))))
+@end lisp
+
+However, this would require that users tweak
+@code{eglot-server-program} to tell Eglot instantiate such sub-classes
+instead of the generic @code{eglot-lsp-server} (@pxref{Setting Up LSP
+Servers}). For the purposes of this particular demonstration, we're
+going to use the more hacky regexp route which doesn't require that.
+
+Note, however, that detecting server versions before announcing new
+capabilities is generally not needed, as both server and client are
+required by LSP to ignore unknown capabilities advertised by their
+counterparts.
+
+@item
+The second method implements @code{eglot-handle-notification} to
+process the server notification for the LSP method
+@code{textDocument/inactiveRegions}. For each region received it
+creates an overlay applying the @code{shadow} face to the region.
+Overlays are recreated every time a new notification of this kind is
+received.
+
+To learn about how @command{clangd}'s special JSONRPC notification
+message is structured in detail you could consult that server's
+documentation. Another possibility is to evaluate the first
+capability-announcing method, reconnect to the server and peek in the
+events buffer (@pxref{Eglot Commands, eglot-events-buffer}). You
+could find something like:
+
+@lisp
+[server-notification] Mon Sep 4 01:10:04 2023:
+(:jsonrpc "2.0" :method "textDocument/inactiveRegions" :params
+ (:textDocument
+ (:uri "file:///path/to/file.cpp")
+ :regions
+ [(:start (:character 0 :line 18)
+ :end (:character 58 :line 19))
+ (:start (:character 0 :line 36)
+ :end (:character 1 :line 38))]))
+@end lisp
+
+This reveals that the @code{textDocument/inactiveRegions} notification
+contains a @code{:textDocument} property to designate the managed
+buffer and an array of LSP regions under the @code{:regions} property.
+Notice how the message (originally in JSON format), is represented as
+Elisp plists (@pxref{JSONRPC objects in Elisp}).
+
+The Eglot generic function machinery will automatically destructure
+the incoming message, so these two properties can simply be added to
+the new method's lambda list as @code{&key} arguments. Also, the
+@code{eglot-uri-to-path} and@code{eglot-range-region} may be used to
+easily parse the LSP @code{:uri} and @code{:start ... :end ...}
+objects to obtain Emacs objects for file names and positions.
+
+The remainder of the implementation consists of standard Elisp
+techniques to loop over arrays, manage buffers and overlays.
+
+@lisp
+(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)))))
+@end lisp
+
+@end itemize
+
+After evaluating these two additions and reconnecting to the
+@command{clangd} language server (version 17), the result will be that
+all the inactive code in the buffer will be nicely grayed out using
+the LSP server knowledge about current compile time preprocessor
+defines.
+
@node Troubleshooting Eglot
@chapter Troubleshooting Eglot
@cindex troubleshooting Eglot