From f7dd1adac40a266e6945911a4212b1bf107fab9b Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Mon, 4 Sep 2023 02:08:58 +0100 Subject: [PATCH] Revert "Eglot: add new chapter about Elisp extensions to Eglot manual" This reverts commit 2a66334bada5173f351e452f40d4289b5e06f04c. One of the co-authors doesn't yet have a FSF copyright assignment. --- doc/misc/eglot.texi | 149 -------------------------------------------- 1 file changed, 149 deletions(-) diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi index 89813a17944..3338756c63c 100644 --- a/doc/misc/eglot.texi +++ b/doc/misc/eglot.texi @@ -99,7 +99,6 @@ This manual documents how to configure, use, and customize Eglot. * 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:: @@ -1265,154 +1264,6 @@ is serialized by Eglot to the following JSON text: @} @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 -- 2.39.2