Ibuffer filter by modes: Accept several mode names

Extend all mode filters so that they handle >1 mode.
For instance, if the users want to filter all buffers in
C or C++ mode, then they can call the filter interactively
with input: 'c-mode,c++-mode' (Bug#32731).

* lisp/ibuf-macs.el(define-ibuffer-filter): Add key :accept-list.
If the value of this key is non-nil, then the filter accepts
either a single qualifier or a list of them; in the latter case,
the resultant filter is the `or' composition of the individual ones.

* lisp/ibuf-ext.el (ibuffer-filter-by-used-mode)
(ibuffer-filter-by-mode, ibuffer-filter-by-derived-mode)
Set :accept-list value non-nil.
Interactively, accept a comma separated list of mode names.

* etc/NEWS(Ibuffer): Announce this change.

Co-authored-by: Noam Postavsky <npostavs@gmail.com>
This commit is contained in:
Tino Calancha 2018-09-29 18:40:46 +09:00
parent 3bbe9e6091
commit 2296bf188f
3 changed files with 73 additions and 42 deletions

View file

@ -61,6 +61,11 @@ to reduce differences between developer and production builds.
** Ibuffer
---
*** All mode filters can now accept a list of symbols.
This means you can now easily filter several major modes, as well
as a single mode.
---
*** New toggle 'ibuffer-do-toggle-lock', bound to 'L'.

View file

@ -1228,28 +1228,33 @@ If INCLUDE-PARENTS is non-nil then include parent modes."
;;;###autoload (autoload 'ibuffer-filter-by-mode "ibuf-ext")
(define-ibuffer-filter mode
"Limit current view to buffers with major mode QUALIFIER."
"Limit current view to buffers with major mode(s) specified by QUALIFIER.
QUALIFIER is the mode name as a symbol or a list of symbols.
Called interactively, accept a comma separated list of mode names."
(:description "major mode"
:reader
(let* ((buf (ibuffer-current-buffer))
(default (if (and buf (buffer-live-p buf))
(symbol-name (buffer-local-value
'major-mode buf)))))
(intern
(completing-read
(mapcar #'intern
(completing-read-multiple
(if default
(format "Filter by major mode (default %s): " default)
"Filter by major mode: ")
obarray
#'(lambda (e)
(string-match "-mode\\'" (symbol-name e)))
t nil nil default))))
(lambda (e)
(string-match "-mode\\'" (if (symbolp e) (symbol-name e) e)))
t nil nil default)))
:accept-list t)
(eq qualifier (buffer-local-value 'major-mode buf)))
;;;###autoload (autoload 'ibuffer-filter-by-used-mode "ibuf-ext")
(define-ibuffer-filter used-mode
"Limit current view to buffers with major mode QUALIFIER.
Called interactively, this function allows selection of modes
"Limit current view to buffers with major mode(s) specified by QUALIFIER.
QUALIFIER is the mode name as a symbol or a list of symbols.
Called interactively, accept a comma separated list of mode names
currently used by buffers."
(:description "major mode in use"
:reader
@ -1257,23 +1262,29 @@ currently used by buffers."
(default (if (and buf (buffer-live-p buf))
(symbol-name (buffer-local-value
'major-mode buf)))))
(intern
(completing-read
(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 default))))
(ibuffer-list-buffer-modes) nil t nil nil default)))
:accept-list t)
(eq qualifier (buffer-local-value 'major-mode buf)))
;;;###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."
"Limit current view to buffers with major mode(s) specified by QUALIFIER.
QUALIFIER is the mode name as a symbol or a list of symbols.
Restrict the view to buffers whose major mode derivates
from modes specified by QUALIFIER.
Called interactively, accept a comma separated list of mode names."
(:description "derived mode"
:reader
(intern
(completing-read "Filter by derived mode: "
(ibuffer-list-buffer-modes t)
nil t)))
:reader
(mapcar #'intern
(completing-read-multiple "Filter by derived mode: "
(ibuffer-list-buffer-modes t)
nil t))
:accept-list t)
(with-current-buffer buf (derived-mode-p qualifier)))
;;;###autoload (autoload 'ibuffer-filter-by-name "ibuf-ext")

View file

@ -280,14 +280,18 @@ buffer object.
;;;###autoload
(cl-defmacro define-ibuffer-filter (name documentation
(&key
reader
description)
&rest body)
(&key
reader
description
accept-list)
&rest body)
"Define a filter named NAME.
DOCUMENTATION is the documentation of the function.
READER is a form which should read a qualifier from the user.
DESCRIPTION is a short string describing the filter.
ACCEPT-LIST is a boolean; if non-nil, the filter accepts either
a single condition or a list of them; in the latter
case the filter is the `or' composition of the conditions.
BODY should contain forms which will be evaluated to test whether or
not a particular buffer should be displayed or not. The forms in BODY
@ -296,30 +300,41 @@ bound to the current value of the filter.
\(fn NAME DOCUMENTATION (&key READER DESCRIPTION) &rest BODY)"
(declare (indent 2) (doc-string 2))
(let ((fn-name (intern (concat "ibuffer-filter-by-" (symbol-name name)))))
(let ((fn-name (intern (concat "ibuffer-filter-by-" (symbol-name name))))
(filter (make-symbol "ibuffer-filter"))
(qualifier-str (make-symbol "ibuffer-qualifier-str")))
`(progn
(defun ,fn-name (qualifier)
,(or documentation "This filter is not documented.")
(interactive (list ,reader))
(if (null (ibuffer-push-filter (cons ',name qualifier)))
(message "%s"
(format ,(concat (format "Filter by %s already applied: " description)
" %s")
qualifier))
(message "%s"
(format ,(concat (format "Filter by %s added: " description)
" %s")
qualifier))
(ibuffer-update nil t)))
,(or documentation "This filter is not documented.")
(interactive (list ,reader))
(let ((,filter (cons ',name qualifier))
(,qualifier-str qualifier))
,(when accept-list
`(progn
(unless (listp qualifier) (setq qualifier (list qualifier)))
;; Reject equivalent filters: (or f1 f2) is same as (or f2 f1).
(setq qualifier (sort (delete-dups qualifier) #'string-lessp))
(setq ,filter (cons ',name (car qualifier)))
(setq ,qualifier-str
(mapconcat (lambda (m) (if (symbolp m) (symbol-name m) m))
qualifier ","))
(when (cdr qualifier) ; Compose individual filters with `or'.
(setq ,filter `(or ,@(mapcar (lambda (m) (cons ',name m)) qualifier))))))
(if (null (ibuffer-push-filter ,filter))
(message ,(format "Filter by %s already applied: %%s" description)
,qualifier-str)
(message ,(format "Filter by %s added: %%s" description)
,qualifier-str)
(ibuffer-update nil t))))
(push (list ',name ,description
(lambda (buf qualifier)
(condition-case nil
(progn ,@body)
(error (ibuffer-pop-filter)
(when (eq ',name 'predicate)
(error "Wrong filter predicate: %S"
qualifier))))))
ibuffer-filtering-alist)
(lambda (buf qualifier)
(condition-case nil
(progn ,@body)
(error (ibuffer-pop-filter)
(when (eq ',name 'predicate)
(error "Wrong filter predicate: %S"
qualifier))))))
ibuffer-filtering-alist)
:autoload-end)))
(provide 'ibuf-macs)