Subject: Re: bug#32731: 26.1.50; Ibuffer filter by mode: Handle >1 mode names

From: Tino Calancha <[email-masked]>
Date: Fri, 14 Sep 2018 05:38:39 +0900

Noam Postavsky <[email-masked]> writes:

> This will use completion only for the first mode, right? Perhaps
> completing-read-multiple would make more sense here.
This is very nice suggestion!  I never used this nice defun before!
(`completing-read-multiple')

> Maybe it's simpler to construct the filter directly? As in:
>
>   (push `(or ,@(mapcar (lambda (m) `(used-mode . ,m)) modes))
>         ibuffer-filtering-qualifiers)
>   (ibuffer-update nil t)
I tried this way and I see 2 nits:
1) With just one mode you still get superflous `or'
   [OR [major mode in use: mode1]]

2) Also, with just one mode, we miss the printout message
   with the description:
"Filter by major mode in use added:  mode1"

Less important but `define-ibuffer-filter' performs some checks
(there is a `condition-case').

Following is a patch on top of previous patch with all your suggestions:
--8<-----------------------------cut here---------------start------------->8---
commit 5a0657d2d06dd495090d49fae477dbc624b1c85b
Author: Tino Calancha <[email-masked]>
Date:   Fri Sep 14 05:22:32 2018 +0900

    Use completing-read-multiple
    
    * lisp/ibuf-ext.el (ibuffer-filter-by-used-modes): Construct
    teh filter by hand, instead of callig `completing-read-multiple'.

diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el
index b7c3ae8030..f2f9ce8bce 100644
--- a/lisp/ibuf-ext.el
+++ b/lisp/ibuf-ext.el
@@ -1277,21 +1277,16 @@ ibuffer-filter-by-used-modes
                                      'major-mode buf)))))
      (list
       (mapcar #'intern
-              (split-string
-               (completing-read
-                (if default
-                    (format "Filter by major mode (default %s): " default)
-                  "Filter by major mode: ")
-                (ibuffer-list-buffer-modes)
-                nil nil nil nil default)
-               "\\s-*,\\s-*" t)))))
+              (completing-read-multiple
+               (if default
+                   (format "Filter by major mode (default %s): " default)
+                 "Filter by major mode: ")
+               (ibuffer-list-buffer-modes)
+               nil t nil nil nil default)))))
   (setq modes (delete-dups modes))
-  (mapc #'ibuffer-filter-by-used-mode modes)
-  (let ((nb_modes (length modes)))
-    (when (>= nb_modes 2)
-      (cl-loop repeat (- nb_modes 2) do (ibuffer-or-filter))
-      (message nil)
-      (ibuffer-or-filter))))
+  (push `(or ,@(mapcar (lambda (m) `(used-mode . ,m)) modes))
+        ibuffer-filtering-qualifiers)
+  (ibuffer-update nil t))
 
 ;;;###autoload (autoload 'ibuffer-filter-by-derived-mode "ibuf-ext")
 (define-ibuffer-filter derived-mode
--8<-----------------------------cut here---------------end--------------->8---

Next one just use `completing-read-multiple' (keeps calling
`'ibuffer-filter-by-used-mode'); I prefer this one:
--8<-----------------------------cut here---------------start------------->8---
commit af9471040740d351208da7bc929219b291674d67
Author: Tino Calancha <[email-masked]>
Date:   Fri Sep 14 05:36:35 2018 +0900

    Ibuffer filter by modes: Accept several mode names
    
    Extend mode filters so that we can input more than 1 mode.
    For instance, if we want to filter all buffers in
    C or C++ mode, then we can call the filter interactively
    with input: 'c-mode,c++-mode'.
    
    * lisp/ibuf-ext.el (ibuffer-filter-by-used-modes):
    New command.  Accept a list of mode names (symbols).
    Interactively, accept a comma separated list of mode names.
    * lisp/ibuffer.el(ibuffer-mode-map): Rebind '/ m' to
    the new command.
    * etc/NEWS(Ibuffer): Announce this change.
    
    Co-authored-by: Noam Postavsky <[email-masked]>

diff --git a/etc/NEWS b/etc/NEWS
index a54ac2db43..6ef6f20b05 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -48,6 +48,11 @@ often cause crashes.  Set it to nil if you really need those fonts.
 ** Ibuffer
 
 ---
+*** The new command 'ibuffer-filter-by-used-modes', bound
+to '/ m', extends 'ibuffer-filter-by-used-mode'; it makes easy
+filtering buffers by a list of major modes.
+
+---
 *** New toggle 'ibuffer-do-toggle-lock', bound to 'L'.
 
 ** Imenu
diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el
index a3143e5e29..048d0bc126 100644
--- a/lisp/ibuf-ext.el
+++ b/lisp/ibuf-ext.el
@@ -1265,6 +1265,32 @@ used-mode
        (ibuffer-list-buffer-modes) nil t nil nil default))))
   (eq qualifier (buffer-local-value 'major-mode buf)))
 
+;;;###autoload
+(defun ibuffer-filter-by-used-modes(modes)
+  "Limit current view to buffers with major mode in MODES.
+MODES is a list of mode names (symbols).
+When called interactively, accept the mode names separated by commas."
+  (interactive
+   (let* ((buf (ibuffer-current-buffer))
+          (default (if (and buf (buffer-live-p buf))
+                       (symbol-name (buffer-local-value
+                                     'major-mode buf)))))
+     (list
+      (mapcar #'intern
+              (completing-read-multiple
+               (if default
+                   (format "Filter by major mode (default %s): " default)
+                 "Filter by major mode: ")
+               (ibuffer-list-buffer-modes)
+               nil t nil nil nil default)))))
+  (setq modes (delete-dups modes))
+  (mapc #'ibuffer-filter-by-used-mode modes)
+  (let ((nb_modes (length modes)))
+    (when (>= nb_modes 2)
+      (cl-loop repeat (- nb_modes 2) do (ibuffer-or-filter))
+      (message nil)
+      (ibuffer-or-filter))))
+
 ;;;###autoload (autoload 'ibuffer-filter-by-derived-mode "ibuf-ext")
 (define-ibuffer-filter derived-mode
     "Limit current view to buffers whose major mode inherits from QUALIFIER."
diff --git a/lisp/ibuffer.el b/lisp/ibuffer.el
index 08b0801cb5..f7c20d0da3 100644
--- a/lisp/ibuffer.el
+++ b/lisp/ibuffer.el
@@ -519,7 +519,7 @@ ibuffer-mode-map
     (define-key map (kbd "s m") 'ibuffer-do-sort-by-major-mode)
 
     (define-key map (kbd "/ RET") 'ibuffer-filter-by-mode)
-    (define-key map (kbd "/ m") 'ibuffer-filter-by-used-mode)
+    (define-key map (kbd "/ m") 'ibuffer-filter-by-used-modes)
     (define-key map (kbd "/ M") 'ibuffer-filter-by-derived-mode)
     (define-key map (kbd "/ n") 'ibuffer-filter-by-name)
     (define-key map (kbd "/ *") 'ibuffer-filter-by-starred-name)

--8<-----------------------------cut here---------------end--------------->8---