]> git.eshelyaron.com Git - emacs.git/commitdiff
Eglot: allow customizing the mode line (bug#71823)
authorJoão Távora <joaotavora@gmail.com>
Tue, 28 Jan 2025 10:39:25 +0000 (10:39 +0000)
committerEshel Yaron <me@eshelyaron.com>
Thu, 30 Jan 2025 18:11:44 +0000 (19:11 +0100)
* lisp/progmodes/eglot.el (eglot-code-action-indications): Adjust docstring.
(eglot--mode-line-props): Tweak.
(eglot--mode-line-format): Delete.
(eglot-mode-line-format): New defcustom.
(eglot-mode-line-menu, eglot-mode-line-session)
(eglot-mode-line-error, eglot-mode-line-pending-requests)
(eglot-mode-line-progress, eglot-mode-line-action-suggestion): New
constants.
(mode-line-misc-info): Adjust setting

* etc/EGLOT-NEWS: Mention new feature.

* doc/misc/eglot.texi (Customization Variables): Mention new variable.

(cherry picked from commit b4e9115f4fcfc174a9c3b974bf414ae4af060ae7)

doc/misc/eglot.texi
etc/EGLOT-NEWS
lisp/progmodes/eglot.el

index 93675210c59d68575e507563907b1c87a1e5532d..722766843ec1bbf7882cde47f231c6744460982b 100644 (file)
@@ -576,7 +576,7 @@ buffer, where @var{server} is the name of the server and @var{project}
 identifies the project by its root directory.  Clicking the mouse on the
 Eglot mode-line indication activates a menu with server-specific items.
 The layout of the Eglot mode-line can be changed by customizing
-@code{eglot-mode-line-format} (@pxref{Customizing Eglot}).
+@code{eglot-mode-line-format} (@pxref{Customization Variables}).
 
 @item
 For each buffer in which Eglot is active, it notifies the language
@@ -958,6 +958,28 @@ ElDoc will not hint about at-point actions.
 @item eglot-code-action-indicator
 This variable is a string determining what the special indicator looks
 like.
+
+@item eglot-mode-line-format
+This variables controls the shape and look of Eglot's mode line.  Its
+value is a mode line ``construct'', generally a list of symbols and
+strings.  The following symbols are meaningful and useful for Eglot's
+mode line:
+@itemize @minus
+@item
+@code{eglot-mode-line-menu}: access Eglot's main menu.  See also
+@code{eglot-menu-string};
+@item
+@code{eglot-mode-line-session}: indicate current project and access
+current session's menu;
+@item
+@code{eglot-mode-line-error}: an indication of recent LSP errors;
+@item
+@code{eglot-mode-line-pending-requests}: number of pending LSP requests;
+@item
+@code{eglot-mode-line-progress}: progress reporter widgets;
+@item
+@code{eglot-mode-line-action-suggestion}: LSP code actions at point.
+@end itemize
 @end vtable
 
 @node Other Variables
index 18a4e9e2d9e985e1329ad11a8031ebe88dced788..02355e25f934be99e304efcf17869b086127f436 100644 (file)
@@ -34,6 +34,12 @@ execute them quickly via the usual 'eglot-code-actions' command.
 Customize with 'eglot-code-action-indications' and
 'eglot-code-action-indicator'.
 
+** Mode line is customizable
+
+The composition of Eglot's mode line can be fully customized by adding
+or removing symbols and strings from the customizable variable
+'eglot-mode-line-format'
+
 \f
 * Changes in Eglot 1.18 (20/1/2025)
 
index 0125abe729e4f03bf82640cc058696fd2e676c50..0d60cb972efcd004379b7c524a5f650b145caa00 100644 (file)
@@ -523,10 +523,6 @@ ACTION is the default value for commands not in the alist."
   :type 'boolean
   :package-version '(Eglot . "1.17.30"))
 
-(defcustom eglot-menu-string "eglot"
-  "String displayed in mode line when Eglot is active."
-  :type 'string)
-
 (defcustom eglot-report-progress t
   "If non-nil, show progress of long running LSP server work.
 If set to `messages', use *Messages* buffer, else use Eglot's
@@ -582,18 +578,24 @@ servers."
   "Face used for code action suggestions.")
 
 (defcustom eglot-code-action-indications
-  '(eldoc-hint mode-line margin)
+  '(eldoc-hint margin)
   "How Eglot indicates there's are code actions available at point.
 Value is a list of symbols, more than one can be specified:
 
-- `eldoc-hint': ElDoc is used to hint about at-point actions.
-- `margin': A special indicator appears in the margin.
-- `nearby': A special indicator appears near point.
+- `eldoc-hint': ElDoc is used to hint about at-point actions;
+- `margin': A special indicator appears in the margin;
+- `nearby': A special indicator appears near point;
 - `mode-line': A special indicator appears in the mode-line.
 
-`margin' and `nearby' are incompatible.  `margin's indicator is not
-interactive.  If the list is empty, Eglot will not hint about code
-actions at point."
+If the list is empty, Eglot will not hint about code actions at point.
+
+Note additionally:
+
+- `margin' and `nearby' are incompatible.  If both are specified,
+  the latter takes priority;
+- `margin's indicator is not interactive;
+- `mode-line' only works if `eglot-mode-line-action-suggestion' exists in
+  `eglot-mode-line-format' (which see)."
   :type '(set
           :tag "Tick the ones you're interested in"
           (const :tag "ElDoc textual hint" eldoc-hint)
@@ -2204,12 +2206,8 @@ If it is activated, also signal textDocument/didOpen."
   (setf (jsonrpc-last-error server) nil))
 
 \f
-;;; Mode-line, menu and other sugar
+;;; Menu and other sugar
 ;;;
-(defvar eglot--mode-line-format `(:eval (eglot--mode-line-format)))
-
-(put 'eglot--mode-line-format 'risky-local-variable t)
-
 (defun eglot--mouse-call (what &optional update-mode-line)
   "Make an interactive lambda for calling WHAT with the mouse."
   (lambda (event)
@@ -2288,6 +2286,44 @@ If it is activated, also signal textDocument/didOpen."
        (interactive)
        (customize-variable 'eglot-events-buffer-size))]))
 
+\f
+;;; Mode-line
+;;;
+(defcustom eglot-mode-line-format
+  '(eglot-mode-line-menu
+    eglot-mode-line-session
+    eglot-mode-line-error
+    eglot-mode-line-pending-requests
+    eglot-mode-line-progress
+    eglot-mode-line-action-suggestion)
+  "Mode line construct for customizing Eglot information.
+Meaningful symbols in this construct include:
+
+- `eglot-mode-line-menu': access Eglot's main menu.  See also
+`eglot-menu-string';
+
+- `eglot-mode-line-session': indicate current project and access current
+ session's menu;
+
+- `eglot-mode-line-error': an indication of recent LSP errors;
+
+- `eglot-mode-line-pending-requests': number of pending LSP requests;
+
+- `eglot-mode-line-progress': progress reporter widgets;
+
+- `eglot-mode-line-action-suggestion': LSP code action at point.
+"
+  :type '(repeat (choice string symbol))
+  :package-version '(Eglot . "1.19"))
+
+(put 'eglot-mode-line-format 'risky-local-variable t)
+(put 'eglot-mode-line-menu 'risky-local-variable t)
+(put 'eglot-mode-line-session 'risky-local-variable t)
+(put 'eglot-mode-line-error 'risky-local-variable t)
+(put 'eglot-mode-line-pending-requests 'risky-local-variable t)
+(put 'eglot-mode-line-progress 'risky-local-variable t)
+(put 'eglot-mode-line-action-suggestion 'risky-local-variable t)
+
 (defun eglot--mode-line-props (thing face defs &optional prepend)
   "Helper for function `eglot--mode-line-format'.
 Uses THING, FACE, DEFS and PREPEND."
@@ -2297,10 +2333,11 @@ Uses THING, FACE, DEFS and PREPEND."
            do (define-key map `[mode-line ,key] (eglot--mouse-call def t))
            concat (format "%s: %s" key help) into blurb
            when rest concat "\n" into blurb
-           finally (return `(:propertize ,thing
-                                         face ,face
-                                         keymap ,map help-echo ,(concat prepend blurb)
-                                         mouse-face mode-line-highlight))))
+           finally (return (propertize
+                            thing
+                            'face face
+                            'keymap map 'help-echo (concat prepend blurb)
+                            'mouse-face 'mode-line-highlight))))
 
 (defconst eglot--main-menu-map
   (let ((map (make-sparse-keymap)))
@@ -2312,55 +2349,92 @@ Uses THING, FACE, DEFS and PREPEND."
     (define-key map [mode-line down-mouse-1] eglot-server-menu)
     map))
 
-(defun eglot--mode-line-format ()
-  "Compose Eglot's mode-line."
-  (let* ((server (eglot-current-server))
-         (nick (and server (eglot-project-nickname server)))
-         (pending (and server (jsonrpc-continuation-count server)))
-         (last-error (and server (jsonrpc-last-error server))))
-    (append
-     `(,(propertize
-         eglot-menu-string
-         'face 'eglot-mode-line
-         'mouse-face 'mode-line-highlight
-         'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu"
-         'keymap eglot--main-menu-map))
-     (when nick
-       `(":"
-         ,(propertize
-           nick
-           'face 'eglot-mode-line
-           'mouse-face 'mode-line-highlight
-           'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" nick)
-           'keymap eglot--server-menu-map)
-         ,@(when last-error
-             `("/" ,(eglot--mode-line-props
-                     "error" 'compilation-mode-line-fail
-                     '((mouse-3 eglot-clear-status  "Clear this status"))
-                     (format "An error occurred: %s\n" (plist-get last-error
-                                                                  :message)))))
-         ,@(when (cl-plusp pending)
-             `("/" ,(eglot--mode-line-props
-                     (format "%d" pending) 'warning
-                     '((mouse-3 eglot-forget-pending-continuations
-                                "Forget pending continuations"))
-                     "Number of outgoing, \
-still unanswered LSP requests to the server\n")))
-         ,@(cl-loop for pr hash-values of (eglot--progress-reporters server)
-                    when (eq (car pr)  'eglot--mode-line-reporter)
-                    append `("/" ,(eglot--mode-line-props
-                                   (format "%s%%%%" (or (nth 4 pr) "?"))
-                                   'eglot-mode-line
-                                   nil
-                                   (format "(%s) %s %s" (nth 1 pr)
-                                           (nth 2 pr) (nth 3 pr)))))
-         ,@(when (and
-                  (memq 'mode-line eglot-code-action-indications)
-                  (overlay-buffer eglot--suggestion-overlay))
-             `("/" ,(overlay-get eglot--suggestion-overlay 'eglot--suggestion-tooltip))))))))
-
-(add-to-list 'mode-line-misc-info
-             `(eglot--managed-mode (" [" eglot--mode-line-format "] ")))
+(defcustom eglot-menu-string "eglot"
+  "String displayed in mode line when Eglot is active."
+  :type 'string)
+
+(defconst eglot-mode-line-menu
+  (propertize
+   eglot-menu-string
+   'face 'eglot-mode-line
+   'mouse-face 'mode-line-highlight
+   'help-echo "Eglot: Emacs LSP client\nmouse-1: Display minor mode menu"
+   'keymap eglot--main-menu-map)
+  "Eglot mode line construct for Eglot's main menu.")
+
+(defconst  eglot-mode-line-session
+  '(:eval (when-let* ((server (eglot-current-server))
+                      (nick (eglot-project-nickname server)))
+            (propertize
+             nick
+             'face 'eglot-mode-line
+             'mouse-face 'mode-line-highlight
+             'help-echo (format "Project '%s'\nmouse-1: LSP server control menu" nick)
+             'keymap eglot--server-menu-map)))
+  "Eglot mode line construct for project/LSP session.")
+
+(defconst eglot-mode-line-error
+  '(:eval (when-let* ((server (eglot-current-server))
+                      (last-error (and server (jsonrpc-last-error server))))
+            (eglot--mode-line-props
+             "error" 'compilation-mode-line-fail
+             '((mouse-3 eglot-clear-status  "Clear this status"))
+             (format "An error occurred: %s\n" (plist-get last-error
+                                                          :message)))))
+  "Eglot mode line construct for LSP errors.")
+
+(defconst eglot-mode-line-pending-requests
+  '(:eval (when-let* ((server (eglot-current-server))
+                      (pending (jsonrpc-continuation-count server)))
+            (when (cl-plusp pending)
+              (eglot--mode-line-props
+               (format "%d" pending) 'warning
+               '((mouse-3 eglot-forget-pending-continuations
+                          "Forget pending continuations"))
+               "Number of outgoing, \
+still unanswered LSP requests to the server\n"))))
+  "Eglot mode line construct for number of pending LSP requests.")
+
+(defconst eglot-mode-line-progress
+  '(:eval
+    (when-let ((server (eglot-current-server)))
+      (cl-loop
+       for pr hash-values of (eglot--progress-reporters server)
+       when (eq (car pr) 'eglot--mode-line-reporter)
+       collect (eglot--mode-line-props
+                (format "%s%%%%" (or (nth 4 pr) "?"))
+                'eglot-mode-line
+                nil
+                (format "(%s) %s %s" (nth 1 pr)
+                        (nth 2 pr) (nth 3 pr)))
+       into reports
+       finally (return (mapconcat #'identity reports " /")))))
+  "Eglot mode line construct for LSP progress reports.")
+
+(defconst eglot-mode-line-action-suggestion
+  '(:eval
+    (when (and (memq 'mode-line eglot-code-action-indications)
+               (overlay-buffer eglot--suggestion-overlay))
+      (overlay-get eglot--suggestion-overlay 'eglot--suggestion-tooltip)))
+  "Eglot mode line construct for at-point code actions.")
+
+(add-to-list
+ 'mode-line-misc-info
+ `(eglot--managed-mode
+   (" ["
+    (:eval
+     (cl-loop for e in eglot-mode-line-format
+              for render = (format-mode-line e)
+              unless (eq render "")
+              collect (cons render
+                            (eq e 'eglot-mode-line-menu))
+              into rendered
+              finally
+              (return (cl-loop for (rspec . rest) on rendered
+                               for (r . titlep) = rspec
+                               concat r
+                               when rest concat (if titlep ":" "/")))))
+    "] ")))
 
 \f
 ;;; Flymake customization