Subject: bug#77361: closed (Re: bug#77361: [PATCH] New user option to hide minor mode lighters)

From: [email-masked] (GNU bug Tracking System)
Date: Sun, 13 Apr 2025 08:48:02 +0000

Your bug report

#77361: [PATCH] New user option to hide minor mode lighters

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
77361: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=77361
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#77361: [PATCH] New user option to hide minor mode lighters
From: Eli Zaretskii <[email-masked]>
To: Pengji Zhang <[email-masked]>
Cc: [email-masked]
Date: Sun, 13 Apr 2025 11:46:33 +0300
List-Id: <debbugs-submit.debbugs.gnu.org>

> From: Pengji Zhang <[email-masked]>
> Cc: [email-masked]
> Date: Sun, 06 Apr 2025 16:05:00 +0800
> 
> Eli Zaretskii <[email-masked]> writes:
> 
> > Please in future submissions of the patch mention the bug number in
> > the log message.
> 
> Thanks for the review! Bug number added in the updated patch.
> 
> >> --- a/doc/lispref/modes.texi
> >> +++ b/doc/lispref/modes.texi
> >> @@ -2163,6 +2163,18 @@ Mode Line Basics
> >>  variable can be buffer-local to only compress mode-lines in certain
> >>  buffers.
> >>  
> >> +@vindex mode-line-collapse-minor-modes
> >> +  To further ``compress'' the mode line, you may customize the
> >> +@code{mode-line-collapse-minor-modes} option to a non-@code{nil} value,
> >> +and Emacs will hide some minor mode indicators on the mode line by
> >> +collapsing them into a single clickable button.  The option can also be
> >> +a list of symbols to select minor modes indicators to hide or show.  If
> >> +the list starts with the symbol @code{not}, it specifies minor modes to
> >> +show, otherwise it means minor modes to hide.  For example, setting it
> >> +to @code{(not flymake-mode)} makes only the indicator for Flymake mode
> >> +shown, and setting it to @code{(eldoc-mode)} hides only the indicator
> >> +for ElDoc mode.
> >
> > I think this text and the preceding paragraph should be moved to the
> > Emacs user manual, into the "Optional Mode Line" node.  These are
> > user-facing features, so their place is not in the ELisp Reference
> > manual.
> >
> > For the user manual, the above description of the possible values of
> > mode-line-collapse-minor-modes is too detailed.  I suggest to have
> > there only the first sentence, and then refer to the doc string for
> > the various alternative forms of the value.
> 
> I have moved the paragraph for 'mode-line-compact' to the "Optional Mode
> Line" node, and added the first sentence for
> 'mode-line-collapse-minor-modes' to that paragraph.
> 
> >> ++++
> >> +*** New user option 'mode-line-collapse-minor-modes'.
> >> +If non-nil, minor mode lighters on the mode line are collapsed into a
> >> +single button.  It could also be a list to specify minor mode lighters
> >                    ^^^^^^^^^^^^^^^^^^^^^^^
> > "The value could also be a list..."
> 
> Fixed.
> 
> >> +(defcustom mode-line-collapse-minor-modes nil
> >> +  "Minor modes for which mode line lighters are hidden.
> >> +Hidden lighters are collapsed into one.
> >> +
> >> +The value could be a list (MODES ...) which means to collapse lighters
> >> +only for MODES, or a list (not MODES ...) which means to collapse all
> >> +lighters for minor modes not in MODES.  Other non-nil values make all
> >> +lighters hidden."
> >> +  :type '(choice (const :tag "No modes" nil)
> >> +                 (repeat :tag "Modes" symbol)
> >> +                 (cons :tag "All modes except"
> >> +                       (const not) (repeat symbol))
> >> +                 (const :tag "All modes" t))
> >> +  :group 'mode-line)
> >
> > The :version tag is missing.
> 
> Added.

Thanks, now installed on the master branch, and closing the bug.

Subject: [PATCH] New user option to hide minor mode lighters
From: Pengji Zhang <[email-masked]>
To: [email-masked]
Date: Sat, 29 Mar 2025 19:07:40 +0800
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello,

The attached patch adds a new user option to hide some minor mode
lighters on the mode line (by collapsing them into a menu), which is to
shorten the mode line and prioritize important information when many
minor modes are on.

There exist a few packages that solve the problem. To name a few:

- diminish[1] and delight[2]. Both offer a way to change the minor mode
  lighters by modifying 'minor-mode-alist'. To hide a minor mode
  lighter, one may change it to nil.

- minions[3], which partially inspired this patch. It replaces minor
  mode lighters by a menu to toggle all minor modes.

This patch is different from those solutions in two aspects:

- Unlike diminish or delight, one can still see the lighters by clicking
  the button, instead of hiding them permanently. Besides, this patch is
  compatible with those two packages.

- Unlike minions, this patch focuses on *lighters* for *enabled* minor
  modes. The menu contains only lighters, making it a more space
  efficient replacement for lighters on mode line, instead of a way to
  manage minor modes like minions.

So I hope this patch is still useful given the existing similar
solutions.

Please let me know what you think. Thanks!

Pengji

[1] https://github.com/myrjola/diminish.el
[2] https://elpa.gnu.org/packages/delight.html
[3] https://github.com/tarsius/minions

>From c1e27606247761c31cec3d363714875c74b30277 Mon Sep 17 00:00:00 2001
From: Pengji Zhang <[email-masked]>
Date: Sat, 29 Mar 2025 19:04:58 +0800
Subject: [PATCH] New user option to hide minor mode lighters

* lisp/bindings.el (mode-line-collapse-minor-modes): New user
option.
(mode-line-minor-modes): New variable to hold mode line
constructs for minor modes.
(mode-line-modes): Use the new variable 'mode-line-minor-modes',
and adjust the order of elements so the indicator for hidden
minor modes is shown towards the end.
(mode-line--make-lighter-menu): New helper function to generate
the menu for hidden minor modes.
(mode-line--minor-modes): New helper function to computer mode
line constructs for minor mode lighters.

* doc/lispref/modes.texi (Mode Line Basics): Document the new
user option.
* etc/NEWS (Note): Annouce the new user option.
---
 doc/lispref/modes.texi |  12 ++++
 etc/NEWS               |   9 +++
 lisp/bindings.el       | 122 ++++++++++++++++++++++++++++++++++++++---
 3 files changed, 136 insertions(+), 7 deletions(-)

diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi
index 788d98fdf20..23c363be12c 100644
--- a/doc/lispref/modes.texi
+++ b/doc/lispref/modes.texi
@@ -2163,6 +2163,18 @@ Mode Line Basics
 variable can be buffer-local to only compress mode-lines in certain
 buffers.
 
+@vindex mode-line-collapse-minor-modes
+  To further ``compress'' the mode line, you may customize the
+@code{mode-line-collapse-minor-modes} option to a non-@code{nil} value,
+and Emacs will hide some minor mode indicators on the mode line by
+collapsing them into a single clickable button.  The option can also be
+a list of symbols to select minor modes indicators to hide or show.  If
+the list starts with the symbol @code{not}, it specifies minor modes to
+show, otherwise it means minor modes to hide.  For example, setting it
+to @code{(not flymake-mode)} makes only the indicator for Flymake mode
+shown, and setting it to @code{(eldoc-mode)} hides only the indicator
+for ElDoc mode.
+
 @node Mode Line Data
 @subsection The Data Structure of the Mode Line
 @cindex mode line construct
diff --git a/etc/NEWS b/etc/NEWS
index 1bd2fd6d486..8b7dfa0ee04 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -316,6 +316,15 @@ This will inhibit implied resizing while a new frame is made and can be
 useful on tiling window managers where the initial frame size should be
 specified by external means.
 
+** Mode Line
+
++++
+*** New user option 'mode-line-collapse-minor-modes'.
+If non-nil, minor mode lighters on the mode line are collapsed into a
+single button.  It could also be a list to specify minor mode lighters
+to hide or show.  The default value is nil, which retains the previous
+behavior of showing all minor mode lighters.
+
 ** Tab Bars and Tab Lines
 
 ---
diff --git a/lisp/bindings.el b/lisp/bindings.el
index 9707ce4b474..a32e3d58dc7 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -429,6 +429,120 @@ bindings--sort-menu-keymap
                            (bindings--menu-item-string (cdr-safe b))))))
     (nconc (make-sparse-keymap prompt) bindings)))
 
+(defcustom mode-line-collapse-minor-modes nil
+  "Minor modes for which mode line lighters are hidden.
+Hidden lighters are collapsed into one.
+
+The value could be a list (MODES ...) which means to collapse lighters
+only for MODES, or a list (not MODES ...) which means to collapse all
+lighters for minor modes not in MODES.  Other non-nil values make all
+lighters hidden."
+  :type '(choice (const :tag "No modes" nil)
+                 (repeat :tag "Modes" symbol)
+                 (cons :tag "All modes except"
+                       (const not) (repeat symbol))
+                 (const :tag "All modes" t))
+  :group 'mode-line)
+
+(defvar mode-line-minor-modes '(:eval (mode-line--minor-modes))
+  "Mode line construct for minor mode lighters.")
+;;;###autoload
+(put 'mode-line-minor-modes 'risky-local-variable t)
+
+(defun mode-line--make-lighter-menu (alist)
+  "Return a menu keymap for minor mode lighters in ALIST.
+ALIST should be in the same format as `minor-mode-alist'.
+
+Return nil if no lighters in ALIST should be visible, for example, there
+are no active minor modes or non-empty lighters."
+  (let ((menu (make-sparse-keymap "Minor Modes"))
+        (empty t))
+    (dolist (item alist)
+      (when-let* ((variable (car item))
+                  ((and (boundp variable)
+                        (symbol-value variable)))
+                  (lighter (format-mode-line `("" ,@(cdr-safe item))))
+                  ((not (string= lighter "")))
+                  (toggle (or (get variable :minor-mode-function) variable))
+                  ;; Follow the format in `mouse-minor-mode-menu'
+                  (name (format "%s - %s" lighter
+                                (capitalize
+                                 (string-replace
+                                  "-" " " (symbol-name toggle))))))
+        (when (eq ?  (aref name 0))
+          (setq name (substring name 1)))
+        (let* ((map (cdr-safe (assq variable minor-mode-map-alist)))
+               (mm-menu (and (keymapp map)
+                             (keymap-lookup map "<menu-bar>"))))
+          (setq mm-menu
+                (cond (mm-menu (mouse-menu-non-singleton mm-menu))
+                      ((fboundp toggle)
+                       (define-keymap :name name
+                         "<help>" (list 'menu-item
+                                        "Help for minor mode"
+                                        (lambda () (interactive)
+                                          (describe-function toggle)))
+                         "<turn-off>" (list 'menu-item
+                                            "Turn off minor mode"
+                                            toggle)))
+                      ;; No menu and not a minor mode function, so just
+                      ;; display the label without a sub-menu.
+                      (t nil)))
+          (keymap-set menu (format "<%s>" toggle)
+                      (list 'menu-item name mm-menu))
+          (setq empty nil))))
+    (and (not empty) menu)))
+
+(defun mode-line--minor-modes ()
+  "Compute mode line constructs for minor mode lighters."
+  (let (visible hidden)
+    (cond
+     ((not mode-line-collapse-minor-modes)
+      (setq visible minor-mode-alist
+            hidden nil))
+     ((eq 'not (car-safe mode-line-collapse-minor-modes))
+      (let ((modes (cdr mode-line-collapse-minor-modes)))
+        (dolist (item minor-mode-alist)
+          (if (memq (car item) modes)
+              (push item visible)
+            (push item hidden)))
+        (setq visible (nreverse visible)
+              hidden (nreverse hidden))))
+     ((listp mode-line-collapse-minor-modes)
+      (let ((modes mode-line-collapse-minor-modes))
+        (dolist (item minor-mode-alist)
+          (if (memq (car item) modes)
+              (push item hidden)
+            (push item visible)))
+        (setq visible (nreverse visible)
+              hidden (nreverse hidden))))
+     (t (setq visible nil
+              hidden minor-mode-alist)))
+    (list ""
+          `(:propertize ("" ,visible)
+                        mouse-face mode-line-highlight
+                        help-echo "Minor mode\n\
+mouse-1: Display minor mode menu\n\
+mouse-2: Show help for minor mode\n\
+mouse-3: Toggle minor modes"
+                        local-map ,mode-line-minor-mode-keymap)
+          (when-let* ((menu (mode-line--make-lighter-menu hidden))
+                      (menu-binding (list 'menu-item "Display" menu
+                                          :filter #'bindings--sort-menu-keymap))
+                      (toggle-binding (list 'menu-item "Toggle" mode-line-mode-menu
+                                            :fitler #'bindings--sort-menu-keymap))
+                      (keymap (define-keymap
+                                "<mode-line> <down-mouse-1>" menu-binding
+                                "<mode-line> <mouse-2>" #'describe-mode
+                                "<mode-line> <down-mouse-3>" toggle-binding)))
+            `(:propertize ,(if (char-displayable-p ?…) " …" " ...")
+                          mouse-face mode-line-highlight
+                          help-echo "Hidden minor modes\n\
+mouse-1: Display hidden minor modes\n\
+mouse-2: Show help for enabled minor modes\n\
+mouse-3: Toggle minor modes"
+                          local-map ,keymap)))))
+
 (defvar mode-line-major-mode-keymap
   (let ((map (make-sparse-keymap)))
     (define-key map [mode-line down-mouse-1]
@@ -466,17 +580,11 @@ mode-line-modes
 			mouse-face mode-line-highlight
 			local-map ,mode-line-major-mode-keymap)
 	  '("" mode-line-process)
-	  `(:propertize ("" minor-mode-alist)
-			mouse-face mode-line-highlight
-			help-echo "Minor mode\n\
-mouse-1: Display minor mode menu\n\
-mouse-2: Show help for minor mode\n\
-mouse-3: Toggle minor modes"
-			local-map ,mode-line-minor-mode-keymap)
 	  (propertize "%n" 'help-echo "mouse-2: Remove narrowing from buffer"
 		      'mouse-face 'mode-line-highlight
 		      'local-map (make-mode-line-mouse-map
 				  'mouse-2 #'mode-line-widen))
+	  '("" mode-line-minor-modes)
 	  ")"
 	  (propertize "%]" 'help-echo recursive-edit-help-echo)
 	  " "))
-- 
2.49.0