Subject: bug#24880: 26.0.50; buff-menu: Command to unmark all buffers

From: Tino Calancha <[email-masked]>
Date: Sun, 06 Nov 2016 21:21:29 +0900

Eli Zaretskii <[email-masked]> writes:

>> From: Tino Calancha <[email-masked]>
>> Date: Sat, 05 Nov 2016 19:04:15 +0900
>> 
>> I miss in this package a command to unmark all buffers bound to 'U'.
>> How about the following?
>
> No objections from me, but please add suitable changes to NEWS and the
> user manual.
>
> Also, wrt this:
>
>> * lisp/buff-menu.el (Buffer-menu-unmark-all):
>> New command; cancel all requested operations on buffers.
>                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> I'd suggest to say "remove all flags from all the lines" instead, as
> it describes better what is being done.
Done.  Thank you!
I realized that `bs.el' and `ebuff-menu.el' can also benefit for
a command unmarking all; so i have included them in the patch.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
>From a84c5962f1300c8d858087ebf8fe356beb7586fb Mon Sep 17 00:00:00 2001
From: Tino Calancha <[email-masked]>
Date: Sun, 6 Nov 2016 21:18:17 +0900
Subject: [PATCH] buff-menu: Add command to unmark all buffers

Bind 'U' in buff-menu, bs and electric-buff-menu to commands
to unmark all buffers (Bug#24880).
* lisp/emacs-lisp/tabulated-list.el (tabulated-list-header-overlay-p):
New predicate; return non-nil if tabulated-list has a fake header.
* lisp/buff-menu.el (Buffer-menu-unmark-all):
New command; remove all flags from all the lines.  Bind it to 'U'.
(Buffer-menu-mode-map): Update menus.
(Buffer-menu-mode): Update mode doc.
* lisp/bs.el (bs-unmark-all, bs-unmark-previous): New commands.
(bs-mode-map): Bind them to 'U' and '<backspace>' respectively.
(bs-mode): Update mode doc.
* lisp/ebuff-menu.el (electric-buffer-menu-mode-map):
Bind Buffer-menu-unmark-all to 'U'.
(electric-buffer-list): Update mode doc.
* doc/emacs/buffers.texi (Several Buffers): Mention Buffer-menu-unmark-all.
; * etc/NEWS: Add an entry per each new feature.
---
 doc/emacs/buffers.texi            |  6 ++++++
 etc/NEWS                          | 16 ++++++++++++++++
 lisp/bs.el                        | 33 +++++++++++++++++++++++++++++++++
 lisp/buff-menu.el                 | 17 +++++++++++++++++
 lisp/ebuff-menu.el                |  2 ++
 lisp/emacs-lisp/tabulated-list.el |  6 ++++++
 6 files changed, 80 insertions(+)

diff --git a/doc/emacs/buffers.texi b/doc/emacs/buffers.texi
index 2eb837f..beb23df 100644
--- a/doc/emacs/buffers.texi
+++ b/doc/emacs/buffers.texi
@@ -413,6 +413,12 @@ Several Buffers
 (@code{Buffer-menu-backup-unmark}).
 @end table
 
+@item U
+@findex Buffer-menu-unmark-all
+@kindex U @r{(Buffer Menu)}
+Remove all flags from all the lines
+(@code{Buffer-menu-unmark-all}).
+
 @noindent
 The commands for adding or removing flags, @kbd{d}, @kbd{C-d}, @kbd{s}
 and @kbd{u}, all accept a numeric argument as a repeat count.
diff --git a/etc/NEWS b/etc/NEWS
index 4bf5332..6c9bca0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -259,6 +259,22 @@ the file's actual content before prompting the user.
 
 * Changes in Specialized Modes and Packages in Emacs 26.1
 
+** Electric-Buffer-menu
+
+---
+*** Key 'U' is bound to 'Buffer-menu-unmark-all'.
+
+** bs
+
+---
+*** Two new commands 'bs-unmark-all', bound to 'U', and
+'bs-unmark-previous', bound to <backspace>.
+
+** Buffer-menu
+
+---
+*** A new command 'Buffer-menu-unmark-all'; bound to 'U'.
+
 ** Ibuffer
 
 ---
diff --git a/lisp/bs.el b/lisp/bs.el
index 8351169..f5f4436 100644
--- a/lisp/bs.el
+++ b/lisp/bs.el
@@ -491,6 +491,8 @@ bs-mode-map
     (define-key map "t"       'bs-visit-tags-table)
     (define-key map "m"       'bs-mark-current)
     (define-key map "u"       'bs-unmark-current)
+    (define-key map "U"       'bs-unmark-all)
+    (define-key map "\177"    'bs-unmark-previous)
     (define-key map ">"       'scroll-right)
     (define-key map "<"       'scroll-left)
     (define-key map "?"       'bs-help)
@@ -635,6 +637,8 @@ bs-mode
 \\[bs-clear-modified] -- clear modified-flag on that buffer.
 \\[bs-mark-current] -- mark current line's buffer to be displayed.
 \\[bs-unmark-current] -- unmark current line's buffer to be displayed.
+\\[bs-unmark-all] -- unmark all buffer lines.
+\\[bs-unmark-previous] -- unmark previous line's buffer to be displayed.
 \\[bs-show-sorted] -- display buffer list sorted by next sort aspect.
 \\[bs-set-configuration-and-refresh] -- ask user for a configuration and \
 apply selected configuration.
@@ -882,6 +886,35 @@ bs-unmark-current
 		   (lambda (buf)
 		     (setq bs--marked-buffers (delq buf bs--marked-buffers)))))
 
+(defun bs-unmark-previous (count)
+  "Unmark previous COUNT buffers.
+Move cursor vertically up COUNT lines."
+  (interactive "p")
+  (when (natnump count)
+    (setq count (- count)))
+  (forward-line count)
+  (save-excursion
+    (bs-unmark-current (- count))))
+
+(defun bs-unmark-all ()
+  "Unmark all buffers."
+  (interactive)
+  (let ((marked (string-to-char bs-string-marked))
+        (current (string-to-char bs-string-current))
+        (marked-cur (string-to-char bs-string-current-marked))
+        (unmarked (string-to-char bs-string-show-normally))
+        (inhibit-read-only t))
+    (save-excursion
+      (goto-char (point-min))
+      (forward-line 2)
+      (while (not (eobp))
+        (if (eq (char-after) marked)
+            (subst-char-in-region (point) (1+ (point)) marked unmarked)
+          (when (eq (char-after) marked-cur)
+            (subst-char-in-region (point) (1+ (point)) marked-cur current)))
+        (forward-line 1))
+      (setq bs--marked-buffers nil))))
+
 (defun bs--show-config-message (what)
   "Show message indicating the new showing status WHAT.
 WHAT is a value of nil, `never', or `always'."
diff --git a/lisp/buff-menu.el b/lisp/buff-menu.el
index 4742628..87aab71 100644
--- a/lisp/buff-menu.el
+++ b/lisp/buff-menu.el
@@ -121,6 +121,7 @@ Buffer-menu-mode-map
     (define-key map "\177" 'Buffer-menu-backup-unmark)
     (define-key map "~" 'Buffer-menu-not-modified)
     (define-key map "u" 'Buffer-menu-unmark)
+    (define-key map "U" 'Buffer-menu-unmark-all)
     (define-key map "m" 'Buffer-menu-mark)
     (define-key map "t" 'Buffer-menu-visit-tags-table)
     (define-key map "%" 'Buffer-menu-toggle-read-only)
@@ -197,6 +198,9 @@ Buffer-menu-mode-map
     (bindings--define-key menu-map [umk]
       '(menu-item "Unmark" Buffer-menu-unmark
 		 :help "Cancel all requested operations on buffer on this line and move down"))
+    (bindings--define-key menu-map [umka]
+      '(menu-item "Unmark all" Buffer-menu-unmark-all
+                  :help "Cancel all requested operations on buffers"))
     (bindings--define-key menu-map [mk]
       '(menu-item "Mark" Buffer-menu-mark
 		 :help "Mark buffer on this line for being displayed by v command"))
@@ -239,6 +243,7 @@ Buffer-menu-mode
 \\[Buffer-menu-execute]    Delete or save marked buffers.
 \\[Buffer-menu-unmark]    Remove all marks from current line.
      With prefix argument, also move up one line.
+\\[Buffer-menu-unmark-all]    Remove all marks from all lines.
 \\[Buffer-menu-backup-unmark]  Back up a line and remove marks.
 \\[Buffer-menu-toggle-read-only]    Toggle read-only status of buffer on this line.
 \\[revert-buffer]    Update the list of buffers.
@@ -356,6 +361,18 @@ Buffer-menu-unmark
   (Buffer-menu--unmark)
   (forward-line (if backup -1 1)))
 
+(defun Buffer-menu-unmark-all ()
+  "Cancel all requested operations on buffers."
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (when (tabulated-list-header-overlay-p)
+      (forward-line))
+    (while (not (eobp))
+      (unless (string= "." (aref (tabulated-list-get-entry) 0))
+        (tabulated-list-set-col 0 " " t))
+      (forward-line))))
+
 (defun Buffer-menu-backup-unmark ()
   "Move up and cancel all requested operations on buffer on line above."
   (interactive)
diff --git a/lisp/ebuff-menu.el b/lisp/ebuff-menu.el
index 5536f94..3b8e12d 100644
--- a/lisp/ebuff-menu.el
+++ b/lisp/ebuff-menu.el
@@ -55,6 +55,7 @@ electric-buffer-menu-mode-map
     (define-key map "\177" 'Buffer-menu-backup-unmark)
     (define-key map "~" 'Buffer-menu-not-modified)
     (define-key map "u" 'Buffer-menu-unmark)
+    (define-key map "U" 'Buffer-menu-unmark-all)
     (let ((i ?0))
       (while (<= i ?9)
 	(define-key map (char-to-string i) 'digit-argument)
@@ -114,6 +115,7 @@ electric-buffer-list
 \\[Buffer-menu-save] -- mark that buffer to be saved.
 \\[Buffer-menu-delete] or \\[Buffer-menu-delete-backwards] -- mark that buffer to be deleted.
 \\[Buffer-menu-unmark] -- remove all kinds of marks from current line.
+\\[Buffer-menu-unmark-all] -- remove all kinds of marks from all lines.
 \\[Electric-buffer-menu-mode-view-buffer] -- view buffer, returning when done.
 \\[Buffer-menu-backup-unmark] -- back up a line and remove marks."
   (interactive "P")
diff --git a/lisp/emacs-lisp/tabulated-list.el b/lisp/emacs-lisp/tabulated-list.el
index 00b029d..2757feb 100644
--- a/lisp/emacs-lisp/tabulated-list.el
+++ b/lisp/emacs-lisp/tabulated-list.el
@@ -257,6 +257,12 @@ tabulated-list-print-fake-header
                     (make-overlay (point-min) (point))))
       (overlay-put tabulated-list--header-overlay 'face 'underline))))
 
+(defsubst tabulated-list-header-overlay-p (&optional pos)
+  "Return non-nil if there is a fake header.
+Optional arg POS is a buffer position where to look for a fake header;
+defaults to `point-min'."
+  (overlays-at (or pos (point-min))))
+
 (defun tabulated-list-revert (&rest ignored)
   "The `revert-buffer-function' for `tabulated-list-mode'.
 It runs `tabulated-list-revert-hook', then calls `tabulated-list-print'."
-- 
2.10.1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
In GNU Emacs 26.0.50.2 (x86_64-pc-linux-gnu, GTK+ Version 3.22.2)
 of 2016-11-05 built on calancha-pc
Repository revision: 3424c27abb7c8ea64c32eaf54cf53fc58323e5ce