* 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