;; These functions should be automatically loaded when called, but you
;; can explicitly (require 'ibuf-ext) in your ~/.emacs to have them
;; preloaded.
+;;
+;; For details on the structure of ibuffer filters and filter groups,
+;; see the documentation for variables `ibuffer-filtering-qualifiers',
+;; `ibuffer-filter-groups', and `ibuffer-saved-filters' in that order.
+;; The variable `ibuffer-filtering-alist' contains names and
+;; descriptions of the currently defined filters; also see the macro
+;; `define-ibuffer-filter'.
;;; Code:
(fixed (mapcar fix-filter filters)))
(cons old-format-detected fixed))))
-(defcustom ibuffer-saved-filters '(("gnus"
+(defcustom ibuffer-saved-filters '(("programming"
+ (or (derived-mode . prog-mode)
+ (mode . ess-mode)
+ (mode . compilation-mode)))
+ ("text document"
+ (and (derived-mode . text-mode)
+ (not (starred-name))))
+ ("TeX"
+ (or (derived-mode . tex-mode)
+ (mode . latex-mode)
+ (mode . context-mode)
+ (mode . ams-tex-mode)
+ (mode . bibtex-mode)))
+ ("web"
+ (or (derived-mode . sgml-mode)
+ (derived-mode . css-mode)
+ (mode . javascript-mode)
+ (mode . js2-mode)
+ (mode . scss-mode)
+ (derived-mode . haml-mode)
+ (mode . sass-mode)))
+ ("gnus"
(or (mode . message-mode)
(mode . mail-mode)
(mode . gnus-group-mode)
(mode . gnus-summary-mode)
- (mode . gnus-article-mode)))
- ("programming"
- (or (mode . emacs-lisp-mode)
- (mode . cperl-mode)
- (mode . c-mode)
- (mode . java-mode)
- (mode . idl-mode)
- (mode . lisp-mode))))
+ (mode . gnus-article-mode))))
"An alist mapping saved filter names to filter specifications.
"))
(defvar ibuffer-filtering-qualifiers nil
- "A list like (SYMBOL . QUALIFIER) which filters the current buffer list.
-See also `ibuffer-filtering-alist'.")
+ "A list specifying the filters currently acting on the buffer list.
+
+If this list is nil, then no filters are currently in
+effect. Otherwise, each element of this list specifies a single
+filter, and all of the specified filters in the list are applied
+successively to the buffer list.
+
+Each filter specification can be of two types: simple or compound.
+
+A simple filter specification has the form (SYMBOL . QUALIFIER),
+where SYMBOL is a key in the alist `ibuffer-filtering-alist' that
+determines the filter function to use and QUALIFIER is the data
+passed to that function (along with the buffer being considered).
+
+A compound filter specification can have one of four forms:
+
+-- (not FILTER-SPEC)
+
+ Represents the logical complement of FILTER-SPEC, which
+ is any single filter specification, simple or compound.
+ The form (not . FILTER-SPEC) is also accepted here.
+
+-- (and FILTER-SPECS...)
+
+ Represents the logical-and of the filters defined by one or
+ more filter specifications FILTER-SPECS..., where each
+ specification can be simple or compound. Note that and is
+ implicitly applied to the filters in the top-level list.
+
+-- (or FILTER-SPECS...)
+
+ Represents the logical-or of the filters defined by one or
+ more filter specifications FILTER-SPECS..., where each
+ specification can be simple or compound.
+
+-- (saved . \"NAME\")
+
+ Represents the filter saved under the string NAME
+ in the alist `ibuffer-saved-filters'. It is an
+ error to name a filter that has not been saved.
+
+This variable is local to each ibuffer buffer.")
;; This is now frobbed by `define-ibuffer-filter'.
(defvar ibuffer-filtering-alist nil
(defvar ibuffer-compiled-filter-formats nil)
(defvar ibuffer-filter-groups nil
- "A list like ((\"NAME\" ((SYMBOL . QUALIFIER) ...) ...) which groups buffers.
-The SYMBOL should be one from `ibuffer-filtering-alist'.
-The QUALIFIER should be the same as QUALIFIER in
-`ibuffer-filtering-qualifiers'.")
+ "An alist giving this buffer's active filter groups, or nil if none.
+
+This alist maps filter group labels to filter specification
+lists. Each element has the form (\"LABEL\" FILTER-SPECS...),
+where FILTER-SPECS... represents one or more filter
+specifications of the same form as allowed as elements of
+`ibuffer-filtering-qualifiers'.
+
+Each filter group is displayed as a separate section in the
+ibuffer list, headed by LABEL and displaying only the buffers
+that pass through all the filters associated with NAME in this
+list.")
(defcustom ibuffer-show-empty-filter-groups t
"If non-nil, then show the names of filter groups which are empty."
(defcustom ibuffer-saved-filter-groups nil
"An alist of filtering groups to switch between.
-This variable should look like ((\"STRING\" QUALIFIERS)
- (\"STRING\" QUALIFIERS) ...), where
-QUALIFIERS is a list of the same form as
-`ibuffer-filtering-qualifiers'.
+Each element is of the form (\"NAME\" . FILTER-GROUP-LIST),
+where NAME is a unique but arbitrary name and FILTER-GROUP-LIST
+is a list of filter groups with the same structure as
+allowed for `ibuffer-filter-groups'.
-See also the variables `ibuffer-filter-groups',
-`ibuffer-filtering-qualifiers', `ibuffer-filtering-alist', and the
-functions `ibuffer-switch-to-saved-filter-groups',
-`ibuffer-save-filter-groups'."
+See also the functions `ibuffer-save-filter-groups' and
+`ibuffer-switch-to-saved-filter-groups' for saving and switching
+between sets of filter groups, and the variable
+`ibuffer-save-with-custom' that affects how this information is
+saved."
:type '(repeat sexp)
:group 'ibuffer)
(defvar ibuffer-hidden-filter-groups nil
- "A list of filtering groups which are currently hidden.")
+ "The list of filter groups that are currently hidden.")
(defvar ibuffer-filter-group-kill-ring nil)
;;;###autoload
(defun ibuffer-included-in-filters-p (buf filters)
+ "Return non-nil if BUF passes all FILTERS.
+
+BUF is a lisp buffer object, and FILTERS is a list of filter
+specifications with the same structure as
+`ibuffer-filtering-qualifiers'."
(not
(memq nil ;; a filter will return nil if it failed
- (mapcar
- ;; filter should be like (TYPE . QUALIFIER), or
- ;; (or (TYPE . QUALIFIER) (TYPE . QUALIFIER) ...)
- #'(lambda (qual)
- (ibuffer-included-in-filter-p buf qual))
- filters))))
+ (mapcar #'(lambda (filter)
+ (ibuffer-included-in-filter-p buf filter))
+ filters))))
+
+(defun ibuffer-unary-operand (filter)
+ "Extracts operand from a unary compound FILTER specification.
+
+FILTER should be a cons cell of either form (f . d) or (f d),
+where operand d is itself a cons cell, or nil. Returns d."
+ (let* ((tail (cdr filter))
+ (maybe-q (car-safe tail)))
+ (if (consp maybe-q) maybe-q tail)))
(defun ibuffer-included-in-filter-p (buf filter)
+ "Return non-nil if BUF pass FILTER.
+
+BUF is a lisp buffer object, and FILTER is a filter
+specification, with the same structure as an element of the list
+`ibuffer-filtering-qualifiers'."
(if (eq (car filter) 'not)
- (not (ibuffer-included-in-filter-p-1 buf (cdr filter)))
+ (let ((inner (ibuffer-unary-operand filter)))
+ ;; Allows (not (not ...)) etc, which may be overkill
+ (if (eq (car inner) 'not)
+ (ibuffer-included-in-filter-p buf (ibuffer-unary-operand inner))
+ (not (ibuffer-included-in-filter-p-1 buf inner))))
(ibuffer-included-in-filter-p-1 buf filter)))
(defun ibuffer-included-in-filter-p-1 (buf filter)
(not
(pcase (car filter)
(`or
+ ;;; ATTN: Short-circuiting alternative with parallel structure w/`and
+ ;;(catch 'has-match
+ ;; (dolist (filter-spec (cdr filter) nil)
+ ;; (when (ibuffer-included-in-filter-p buf filter-spec)
+ ;; (throw 'has-match t))))
(memq t (mapcar #'(lambda (x)
- (ibuffer-included-in-filter-p buf x))
- (cdr filter))))
+ (ibuffer-included-in-filter-p buf x))
+ (cdr filter))))
+ (`and
+ (catch 'no-match
+ (dolist (filter-spec (cdr filter) t)
+ (unless (ibuffer-included-in-filter-p buf filter-spec)
+ (throw 'no-match nil)))))
(`saved
(let ((data (assoc (cdr filter) ibuffer-saved-filters)))
(unless data
(when buf
(ibuffer-jump-to-buffer (buffer-name buf)))))
-(defun ibuffer-push-filter (qualifier)
- "Add QUALIFIER to `ibuffer-filtering-qualifiers'."
- (push qualifier ibuffer-filtering-qualifiers))
+(defun ibuffer-push-filter (filter-specification)
+ "Add FILTER-SPECIFICATION to `ibuffer-filtering-qualifiers'."
+ (push filter-specification ibuffer-filtering-qualifiers))
;;;###autoload
(defun ibuffer-decompose-filter ()
- "Separate the top compound filter (OR, NOT, or SAVED) in this buffer.
+ "Separate this buffer's top compound filter (AND, OR, NOT, or SAVED).
This means that the topmost filter on the filtering stack, which must
be a complex filter like (OR [name: foo] [mode: bar-mode]), will be
-turned into two separate filters [name: foo] and [mode: bar-mode]."
+turned into separate filters, like [name: foo] and [mode: bar-mode]."
(interactive)
(unless ibuffer-filtering-qualifiers
(error "No filters in effect"))
(tail (cdr filters))
(value
(pcase (caar filters)
- (`or (nconc head tail))
+ ((or `or 'and) (nconc head tail))
(`saved
(let ((data (assoc head ibuffer-saved-filters)))
(unless data
(ibuffer-filter-disable)
(error "Unknown saved filter %s" head))
(append (cdr data) tail)))
- (`not (cons head tail))
+ (`not (cons (ibuffer-unary-operand (car filters)) tail))
(_
(error "Filter type %s is not compound" (caar filters))))))
(setq ibuffer-filtering-qualifiers value))
ibuffer-filtering-qualifiers))
(ibuffer-update nil t))
+(defun ibuffer--or-and-filter (op decompose)
+ (if decompose
+ (if (eq op (caar ibuffer-filtering-qualifiers))
+ (ibuffer-decompose-filter)
+ (error "Top filter is not an %s" (upcase (symbol-name op))))
+ (when (< (length ibuffer-filtering-qualifiers) 2)
+ (error "Need two filters to %s" (upcase (symbol-name op))))
+ ;; If either filter is an op, eliminate unnecessary nesting.
+ (let ((first (pop ibuffer-filtering-qualifiers))
+ (second (pop ibuffer-filtering-qualifiers)))
+ (push (nconc (if (eq op (car first)) first (list op first))
+ (if (eq op (car second)) (cdr second) (list second)))
+ ibuffer-filtering-qualifiers)))
+ (ibuffer-update nil t))
+
;;;###autoload
-(defun ibuffer-or-filter (&optional reverse)
+(defun ibuffer-or-filter (&optional decompose)
"Replace the top two filters in this buffer with their logical OR.
-If optional argument REVERSE is non-nil, instead break the top OR
+If optional argument DECOMPOSE is non-nil, instead break the top OR
filter into parts."
(interactive "P")
- (if reverse
- (progn
- (when (or (null ibuffer-filtering-qualifiers)
- (not (eq 'or (caar ibuffer-filtering-qualifiers))))
- (error "Top filter is not an OR"))
- (let ((lim (pop ibuffer-filtering-qualifiers)))
- (setq ibuffer-filtering-qualifiers
- (nconc (cdr lim) ibuffer-filtering-qualifiers))))
- (when (< (length ibuffer-filtering-qualifiers) 2)
- (error "Need two filters to OR"))
- ;; If the second filter is an OR, just add to it.
- (let ((first (pop ibuffer-filtering-qualifiers))
- (second (pop ibuffer-filtering-qualifiers)))
- (if (eq 'or (car second))
- (push (nconc (list 'or first) (cdr second))
- ibuffer-filtering-qualifiers)
- (push (list 'or first second)
- ibuffer-filtering-qualifiers))))
- (ibuffer-update nil t))
+ (ibuffer--or-and-filter 'or decompose))
+
+;;;###autoload
+(defun ibuffer-and-filter (&optional decompose)
+ "Replace the top two filters in this buffer with their logical AND.
+If optional argument DECOMPOSE is non-nil, instead break the top AND
+filter into parts."
+ (interactive "P")
+ (ibuffer--or-and-filter 'and decompose))
(defun ibuffer-maybe-save-stuff ()
(when ibuffer-save-with-custom
(defun ibuffer-format-qualifier (qualifier)
(if (eq (car-safe qualifier) 'not)
- (concat " [NOT" (ibuffer-format-qualifier-1 (cdr qualifier)) "]")
+ (concat " [NOT"
+ (ibuffer-format-qualifier-1 (ibuffer-unary-operand qualifier))
+ "]")
(ibuffer-format-qualifier-1 qualifier)))
(defun ibuffer-format-qualifier-1 (qualifier)
(concat " [filter: " (cdr qualifier) "]"))
(`or
(concat " [OR" (mapconcat #'ibuffer-format-qualifier
- (cdr qualifier) "") "]"))
+ (cdr qualifier) "") "]"))
+ (`and
+ (concat " [AND" (mapconcat #'ibuffer-format-qualifier
+ (cdr qualifier) "") "]"))
(_
(let ((type (assq (car qualifier) ibuffer-filtering-alist)))
(unless qualifier
- (error "Ibuffer: bad qualifier %s" qualifier))
+ (error "Ibuffer: bad qualifier %s" qualifier))
(concat " [" (cadr type) ": " (format "%s]" (cdr qualifier)))))))
-
(defun ibuffer-list-buffer-modes (&optional include-parents)
"Create a completion table of buffer modes currently in use.
If INCLUDE-PARENTS is non-nil then include parent modes."
;;;###autoload (autoload 'ibuffer-filter-by-mode "ibuf-ext")
(define-ibuffer-filter mode
- "Toggle current view to buffers with major mode QUALIFIER."
+ "Limit current view to buffers with major mode QUALIFIER."
(:description "major mode"
:reader
(let* ((buf (ibuffer-current-buffer))
;;;###autoload (autoload 'ibuffer-filter-by-used-mode "ibuf-ext")
(define-ibuffer-filter used-mode
- "Toggle current view to buffers with major mode QUALIFIER.
+ "Limit current view to buffers with major mode QUALIFIER.
Called interactively, this function allows selection of modes
currently used by buffers."
(:description "major mode in use"
;;;###autoload (autoload 'ibuffer-filter-by-derived-mode "ibuf-ext")
(define-ibuffer-filter derived-mode
- "Toggle current view to buffers whose major mode inherits from QUALIFIER."
+ "Limit current view to buffers whose major mode inherits from QUALIFIER."
(:description "derived mode"
:reader
(intern
;;;###autoload (autoload 'ibuffer-filter-by-name "ibuf-ext")
(define-ibuffer-filter name
- "Toggle current view to buffers with name matching QUALIFIER."
+ "Limit current view to buffers with name matching QUALIFIER."
(:description "buffer name"
:reader (read-from-minibuffer "Filter by name (regexp): "))
(string-match qualifier (buffer-name buf)))
+;;;###autoload (autoload 'ibuffer-filter-by-starred-name "ibuf-ext")
+(define-ibuffer-filter starred-name
+ "Limit current view to buffers with name beginning and ending
+with *, along with an optional suffix of the form digits or
+<digits>."
+ (:description "starred buffer name"
+ :reader nil)
+ (string-match "\\`\\*[^*]+\\*\\(?:<[[:digit:]]+>\\)?\\'" (buffer-name buf)))
+
;;;###autoload (autoload 'ibuffer-filter-by-filename "ibuf-ext")
(define-ibuffer-filter filename
- "Toggle current view to buffers with filename matching QUALIFIER."
- (:description "filename"
- :reader (read-from-minibuffer "Filter by filename (regexp): "))
+ "Limit current view to buffers with full file name matching QUALIFIER.
+
+For example, for a buffer associated with file '/a/b/c.d', this
+matches against '/a/b/c.d'."
+ (:description "full file name"
+ :reader (read-from-minibuffer "Filter by full file name (regexp): "))
(ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
(string-match qualifier it)))
+;;;###autoload (autoload 'ibuffer-filter-by-basename "ibuf-ext")
+(define-ibuffer-filter basename
+ "Limit current view to buffers with file basename matching QUALIFIER.
+
+For example, for a buffer associated with file '/a/b/c.d', this
+matches against 'c.d'."
+ (:description "file basename"
+ :reader (read-from-minibuffer
+ "Filter by file name, without directory part (regex): "))
+ (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
+ (string-match qualifier (file-name-nondirectory it))))
+
+;;;###autoload (autoload 'ibuffer-filter-by-file-extension "ibuf-ext")
+(define-ibuffer-filter file-extension
+ "Limit current view to buffers with filename extension matching QUALIFIER.
+
+The separator character (typically `.') is not part of the
+pattern. For example, for a buffer associated with file
+'/a/b/c.d', this matches against 'd'."
+ (:description "filename extension"
+ :reader (read-from-minibuffer
+ "Filter by filename extension without separator (regex): "))
+ (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
+ (string-match qualifier (or (file-name-extension it) ""))))
+
+;;;###autoload (autoload 'ibuffer-filter-by-directory "ibuf-ext")
+(define-ibuffer-filter directory
+ "Limit current view to buffers with directory matching QUALIFIER.
+
+For a buffer associated with file '/a/b/c.d', this matches
+against '/a/b'. For a buffer not associated with a file, this
+matches against the value of `default-directory' in that buffer."
+ (:description "directory name"
+ :reader (read-from-minibuffer "Filter by directory name (regex): "))
+ (ibuffer-aif (with-current-buffer buf (ibuffer-buffer-file-name))
+ (let ((dirname (file-name-directory it)))
+ (when dirname (string-match qualifier dirname)))
+ (when default-directory (string-match qualifier default-directory))))
+
;;;###autoload (autoload 'ibuffer-filter-by-size-gt "ibuf-ext")
(define-ibuffer-filter size-gt
- "Toggle current view to buffers with size greater than QUALIFIER."
+ "Limit current view to buffers with size greater than QUALIFIER."
(:description "size greater than"
:reader
(string-to-number (read-from-minibuffer "Filter by size greater than: ")))
;;;###autoload (autoload 'ibuffer-filter-by-size-lt "ibuf-ext")
(define-ibuffer-filter size-lt
- "Toggle current view to buffers with size less than QUALIFIER."
+ "Limit current view to buffers with size less than QUALIFIER."
(:description "size less than"
:reader
(string-to-number (read-from-minibuffer "Filter by size less than: ")))
(< (with-current-buffer buf (buffer-size))
qualifier))
+;;;###autoload (autoload 'ibuffer-filter-by-modified "ibuf-ext")
+(define-ibuffer-filter modified
+ "Limit current view to buffers that are marked as modified."
+ (:description "modified"
+ :reader nil)
+ (buffer-modified-p buf))
+
+;;;###autoload (autoload 'ibuffer-filter-by-visiting-file "ibuf-ext")
+(define-ibuffer-filter visiting-file
+ "Limit current view to buffers that are visiting a file."
+ (:description "visiting a file"
+ :reader nil)
+ (with-current-buffer buf (buffer-file-name)))
+
;;;###autoload (autoload 'ibuffer-filter-by-content "ibuf-ext")
(define-ibuffer-filter content
- "Toggle current view to buffers whose contents match QUALIFIER."
+ "Limit current view to buffers whose contents match QUALIFIER."
(:description "content"
:reader (read-from-minibuffer "Filter by content (regexp): "))
(with-current-buffer buf
;;;###autoload (autoload 'ibuffer-filter-by-predicate "ibuf-ext")
(define-ibuffer-filter predicate
- "Toggle current view to buffers for which QUALIFIER returns non-nil."
+ "Limit current view to buffers for which QUALIFIER returns non-nil."
(:description "predicate"
:reader (read-minibuffer "Filter by predicate (form): "))
(with-current-buffer buf
(eval qualifier)))
+;;;###autoload (autoload 'ibuffer-filter-chosen-by-completion "ibuf-ext")
+(defun ibuffer-filter-chosen-by-completion ()
+ "Select and apply filter chosen by completion against available filters.
+Indicates corresponding key sequences in echo area after filtering.
+
+The completion matches against the filter description text of
+each filter in `ibuffer-filtering-alist'."
+ (interactive)
+ (let* ((filters (mapcar (lambda (x) (cons (cadr x) (car x)))
+ ibuffer-filtering-alist))
+ (match (completing-read "Filter by: " filters nil t))
+ (filter (cdr (assoc match filters)))
+ (command (intern (concat "ibuffer-filter-by-" (symbol-name filter)))))
+ (call-interactively command)
+ (message "%s can be run with key sequences: %s"
+ command
+ (mapconcat #'key-description
+ (where-is-internal command ibuffer-mode-map nil t)
+ "or "))))
+
+
;;; Sorting
;;;###autoload
(require 'ibuf-macs))
(ert-deftest ibuffer-autoload ()
- "Tests to see whether reftex-auc has been autoloaded"
+ "Tests to see whether ibuffer has been autoloaded"
+ (skip-unless (not (featurep 'ibuf-ext)))
(should
(fboundp 'ibuffer-mark-unsaved-buffers))
(should
(should-not ibuffer-filtering-qualifiers))
(setq ibuffer-filtering-qualifiers filters))))
+;; Test Filter Inclusion
+(let* (test-buffer-list ; accumulated buffers to clean up
+ ;; Utility functions without polluting the environment
+ (set-buffer-mode
+ (lambda (buffer mode)
+ "Set BUFFER's major mode to MODE, a mode function, or fundamental."
+ (with-current-buffer buffer
+ (funcall (or mode #'fundamental-mode)))))
+ (set-buffer-contents
+ (lambda (buffer size include-content)
+ "Add exactly SIZE bytes to BUFFER, including INCLUDE-CONTENT."
+ (when (or size include-content)
+ (let* ((unit "\n")
+ (chunk "ccccccccccccccccccccccccccccccc\n")
+ (chunk-size (length chunk))
+ (size (if (and size include-content (stringp include-content))
+ (- size (length include-content))
+ size)))
+ (unless (or (null size) (> size 0))
+ (error "size argument must be nil or positive"))
+ (with-current-buffer buffer
+ (when include-content
+ (insert include-content))
+ (when size
+ (dotimes (_ (floor size chunk-size))
+ (insert chunk))
+ (dotimes (_ (mod size chunk-size))
+ (insert unit)))
+ ;; prevent query on cleanup
+ (set-buffer-modified-p nil))))))
+ (create-file-buffer
+ (lambda (prefix &rest args-plist)
+ "Create a file and buffer with designated properties.
+ PREFIX is a string giving the beginning of the name, and ARGS-PLIST
+ is a series of keyword-value pairs, with allowed keywords
+ :suffix STRING, :size NUMBER, :mode MODE-FUNC, :include-content STRING.
+ Returns the created buffer."
+ (let* ((suffix (plist-get args-plist :suffix))
+ (size (plist-get args-plist :size))
+ (include (plist-get args-plist :include-content))
+ (mode (plist-get args-plist :mode))
+ (file (make-temp-file prefix nil suffix))
+ (buf (find-file-noselect file t)))
+ (push buf test-buffer-list) ; record for cleanup
+ (funcall set-buffer-mode buf mode)
+ (funcall set-buffer-contents buf size include)
+ buf)))
+ (create-non-file-buffer
+ (lambda (prefix &rest args-plist)
+ "Create a non-file and buffer with designated properties.
+ PREFIX is a string giving the beginning of the name, and ARGS-PLIST
+ is a series of keyword-value pairs, with allowed keywords
+ :size NUMBER, :mode MODE-FUNC, :include-content STRING.
+ Returns the created buffer."
+ (let* ((size (plist-get args-plist :size))
+ (include (plist-get args-plist :include-content))
+ (mode (plist-get args-plist :mode))
+ (buf (generate-new-buffer prefix)))
+ (push buf test-buffer-list) ; record for cleanup
+ (funcall set-buffer-mode buf mode)
+ (funcall set-buffer-contents buf size include)
+ buf)))
+ (clean-up
+ (lambda ()
+ "Restore all emacs state modified during the tests"
+ (while test-buffer-list ; created temporary buffers
+ (let ((buf (pop test-buffer-list)))
+ (with-current-buffer buf (bury-buffer)) ; ensure not selected
+ (kill-buffer buf))))))
+ ;; Tests
+ (ert-deftest ibuffer-filter-inclusion-1 ()
+ "Tests inclusion using basic filter combinators with a single buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((buf
+ (funcall create-file-buffer "ibuf-test-1" :size 100
+ :include-content "One ring to rule them all\n")))
+ (should (ibuffer-included-in-filters-p buf '((size-gt . 99))))
+ (should (ibuffer-included-in-filters-p buf '((size-lt . 101))))
+ (should (ibuffer-included-in-filters-p
+ buf '((mode . fundamental-mode))))
+ (should (ibuffer-included-in-filters-p
+ buf '((content . "ring to rule them all"))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (content . "ring to rule them all")))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (and (content . "ring to rule them all"))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (and (and (content . "ring to rule them all")))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((or (content . "ring to rule them all")))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (not (content . "ring to rule them all"))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (size-gt . 99)
+ (content . "ring to rule them all")
+ (mode . fundamental-mode)
+ (basename . "\\`ibuf-test-1")))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (or (not (size-gt . 99))
+ (not (content . "ring to rule them all"))
+ (not (mode . fundamental-mode))
+ (not (basename . "\\`ibuf-test-1")))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (or (size-gt . 99) (size-lt . 10))
+ (and (content . "ring.*all")
+ (content . "rule")
+ (content . "them all")
+ (content . "One"))
+ (not (mode . text-mode))
+ (basename . "\\`ibuf-test-1"))))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-2 ()
+ "Tests inclusion of basic filters in combination on a single buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((buf
+ (funcall create-file-buffer "ibuf-test-2" :size 200
+ :mode #'text-mode
+ :include-content "and in the darkness find them\n")))
+ (should (ibuffer-included-in-filters-p buf '((size-gt . 199))))
+ (should (ibuffer-included-in-filters-p buf '((size-lt . 201))))
+ (should (ibuffer-included-in-filters-p buf '((not size-gt . 200))))
+ (should (ibuffer-included-in-filters-p buf '((not (size-gt . 200)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (size-gt . 199) (size-lt . 201)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((or (size-gt . 199) (size-gt . 201)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((or (size-gt . 201) (size-gt . 199)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((size-gt . 199) (mode . text-mode)
+ (content . "darkness find them"))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (size-gt . 199) (mode . text-mode)
+ (content . "darkness find them")))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (or (not (size-gt . 199)) (not (mode . text-mode))
+ (not (content . "darkness find them")))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((or (size-gt . 200) (content . "darkness find them")
+ (derived-mode . emacs-lisp-mode)))))
+ (should-not (ibuffer-included-in-filters-p
+ buf '((or (size-gt . 200) (content . "rule them all")
+ (derived-mode . emacs-lisp-mode))))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-3 ()
+ "Tests inclusion with filename filters on specified buffers."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let* ((bufA
+ (funcall create-file-buffer "ibuf-test-3.a" :size 50
+ :mode #'text-mode
+ :include-content "...but a multitude of drops?\n"))
+ (bufB
+ (funcall create-non-file-buffer "ibuf-test-3.b" :size 50
+ :mode #'text-mode
+ :include-content "...but a multitude of drops?\n"))
+ (dirA (with-current-buffer bufA default-directory))
+ (dirB (with-current-buffer bufB default-directory)))
+ (should (ibuffer-included-in-filters-p
+ bufA '((basename . "ibuf-test-3"))))
+ (should (ibuffer-included-in-filters-p
+ bufA '((basename . "test-3\\.a"))))
+ (should (ibuffer-included-in-filters-p
+ bufA '((file-extension . "a"))))
+ (should (ibuffer-included-in-filters-p
+ bufA (list (cons 'directory dirA))))
+ (should-not (ibuffer-included-in-filters-p
+ bufB '((basename . "ibuf-test-3"))))
+ (should-not (ibuffer-included-in-filters-p
+ bufB '((file-extension . "b"))))
+ (should (ibuffer-included-in-filters-p
+ bufB (list (cons 'directory dirB))))
+ (should (ibuffer-included-in-filters-p
+ bufA '((name . "ibuf-test-3"))))
+ (should (ibuffer-included-in-filters-p
+ bufB '((name . "ibuf-test-3")))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-4 ()
+ "Tests inclusion with various filters on a single buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((buf
+ (funcall create-file-buffer "ibuf-test-4"
+ :mode #'emacs-lisp-mode :suffix ".el"
+ :include-content "(message \"--%s--\" 'emacs-rocks)\n")))
+ (should (ibuffer-included-in-filters-p
+ buf '((file-extension . "el"))))
+ (should (ibuffer-included-in-filters-p
+ buf '((derived-mode . prog-mode))))
+ (should (ibuffer-included-in-filters-p
+ buf '((used-mode . emacs-lisp-mode))))
+ (should (ibuffer-included-in-filters-p
+ buf '((mode . emacs-lisp-mode))))
+ (with-current-buffer buf (set-buffer-modified-p t))
+ (should (ibuffer-included-in-filters-p buf '((modified))))
+ (with-current-buffer buf (set-buffer-modified-p nil))
+ (should (ibuffer-included-in-filters-p buf '((not modified))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (file-extension . "el")
+ (derived-mode . prog-mode)
+ (not modified)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((or (file-extension . "tex")
+ (derived-mode . prog-mode)
+ (modified)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((file-extension . "el")
+ (derived-mode . prog-mode)
+ (not modified)))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-5 ()
+ "Tests inclusion with various filters on a single buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((buf
+ (funcall create-non-file-buffer "ibuf-test-5.el"
+ :mode #'emacs-lisp-mode
+ :include-content
+ "(message \"--%s--\" \"It really does!\")\n")))
+ (should-not (ibuffer-included-in-filters-p
+ buf '((file-extension . "el"))))
+ (should (ibuffer-included-in-filters-p
+ buf '((size-gt . 18))))
+ (should (ibuffer-included-in-filters-p
+ buf '((predicate . (lambda ()
+ (> (- (point-max) (point-min)) 18))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (mode . emacs-lisp-mode)
+ (or (starred-name)
+ (size-gt . 18))
+ (and (not (size-gt . 100))
+ (content . "[Ii]t *really does!")
+ (or (name . "test-5")
+ (not (filename . "test-5")))))))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-6 ()
+ "Tests inclusion using saved filters and DeMorgan's laws."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((buf
+ (funcall create-non-file-buffer "*ibuf-test-6*" :size 65
+ :mode #'text-mode))
+ (buf2
+ (funcall create-file-buffer "ibuf-test-6a" :suffix ".html"
+ :mode #'html-mode
+ :include-content
+ "<HTML><BODY><H1>Hello, World!</H1></BODY></HTML>")))
+ (should (ibuffer-included-in-filters-p buf '((starred-name))))
+ (should-not (ibuffer-included-in-filters-p
+ buf '((saved . "text document"))))
+ (should (ibuffer-included-in-filters-p buf2 '((saved . "web"))))
+ (should (ibuffer-included-in-filters-p
+ buf2 '((not (and (not (derived-mode . sgml-mode))
+ (not (derived-mode . css-mode))
+ (not (mode . javascript-mode))
+ (not (mode . js2-mode))
+ (not (mode . scss-mode))
+ (not (derived-mode . haml-mode))
+ (not (mode . sass-mode)))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((and (starred-name)
+ (or (size-gt . 50) (filename . "foo"))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (or (not starred-name)
+ (and (size-lt . 51)
+ (not (filename . "foo")))))))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-7 ()
+ "Tests inclusion with various filters on a single buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((buf
+ (funcall create-non-file-buffer "ibuf-test-7"
+ :mode #'artist-mode)))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (starred-name)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not starred-name))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (not (not starred-name))))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (modified)))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not modified))))
+ (should (ibuffer-included-in-filters-p
+ buf '((not (not (not modified)))))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-filter-inclusion-8 ()
+ "Tests inclusion with various filters."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((bufA
+ (funcall create-non-file-buffer "ibuf-test-8a"
+ :mode #'artist-mode))
+ (bufB (funcall create-non-file-buffer "*ibuf-test-8b*" :size 32))
+ (bufC (funcall create-file-buffer "ibuf-test8c" :suffix "*"
+ :size 64))
+ (bufD (funcall create-file-buffer "*ibuf-test8d" :size 128))
+ (bufE (funcall create-file-buffer "*ibuf-test8e" :suffix "*<2>"
+ :size 16))
+ (bufF (and (funcall create-non-file-buffer "*ibuf-test8f*")
+ (funcall create-non-file-buffer "*ibuf-test8f*"
+ :size 8))))
+ (with-current-buffer bufA (set-buffer-modified-p t))
+ (should (ibuffer-included-in-filters-p
+ bufA '((and (not starred-name)
+ (modified)
+ (name . "test-8")
+ (not (size-gt . 100))
+ (mode . picture-mode)))))
+ (with-current-buffer bufA (set-buffer-modified-p nil))
+ (should-not (ibuffer-included-in-filters-p
+ bufA '((or (starred-name) (visiting-file) (modified)))))
+ (should (ibuffer-included-in-filters-p
+ bufB '((and (starred-name)
+ (name . "test.*8b")
+ (size-gt . 31)
+ (not visiting-file)))))
+ (should (ibuffer-included-in-filters-p
+ bufC '((and (not (starred-name))
+ (visiting-file)
+ (name . "8c[^*]*\\*")
+ (size-lt . 65)))))
+ (should (ibuffer-included-in-filters-p
+ bufD '((and (not (starred-name))
+ (visiting-file)
+ (name . "\\`\\*.*test8d")
+ (size-lt . 129)
+ (size-gt . 127)))))
+ (should (ibuffer-included-in-filters-p
+ bufE '((and (starred-name)
+ (visiting-file)
+ (name . "8e.*?\\*<[[:digit:]]+>")
+ (size-gt . 10)))))
+ (should (ibuffer-included-in-filters-p
+ bufF '((and (starred-name)
+ (not (visiting-file))
+ (name . "8f\\*<[[:digit:]]>")
+ (size-lt . 10))))))
+ (funcall clean-up))))
+
+;; Test Filter Combination and Decomposition
+(let* (ibuffer-to-kill ; if non-nil, kill this buffer at cleanup
+ (ibuffer-already 'check) ; existing ibuffer buffer to use but not kill
+ ;; Utility functions without polluting the environment
+ (get-test-ibuffer
+ (lambda ()
+ "Returns a test ibuffer-mode buffer, creating one if necessary.
+ If a new buffer is created, it is named \"*Test-Ibuffer*\" and is
+ saved to `ibuffer-to-kill' for later cleanup."
+ (when (eq ibuffer-already 'check)
+ (setq ibuffer-already
+ (catch 'found-buf
+ (dolist (buf (buffer-list) nil)
+ (when (with-current-buffer buf
+ (derived-mode-p 'ibuffer-mode))
+ (throw 'found-buf buf))))))
+ (or ibuffer-already
+ ibuffer-to-kill
+ (let ((test-ibuf-name "*Test-Ibuffer*"))
+ (ibuffer nil test-ibuf-name nil t)
+ (setq ibuffer-to-kill (get-buffer test-ibuf-name))))))
+ (clean-up
+ (lambda ()
+ "Restore all emacs state modified during the tests"
+ (when ibuffer-to-kill ; created ibuffer
+ (with-current-buffer ibuffer-to-kill
+ (set-buffer-modified-p nil)
+ (bury-buffer))
+ (kill-buffer ibuffer-to-kill)
+ (setq ibuffer-to-kill nil))
+ (when (and ibuffer-already (not (eq ibuffer-already 'check)))
+ ;; restore existing ibuffer state
+ (ibuffer-update nil t)))))
+ ;; Tests
+ (ert-deftest ibuffer-decompose-filter ()
+ "Tests `ibuffer-decompose-filter' for and, or, not, and saved."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((ibuf (funcall get-test-ibuffer)))
+ (with-current-buffer ibuf
+ (let ((ibuffer-filtering-qualifiers nil)
+ (ibuffer-filter-groups nil)
+ (filters '((size-gt . 100) (not (starred-name))
+ (name . "foo"))))
+ (progn
+ (push (cons 'or filters) ibuffer-filtering-qualifiers)
+ (ibuffer-decompose-filter)
+ (should (equal filters ibuffer-filtering-qualifiers))
+ (setq ibuffer-filtering-qualifiers nil))
+ (progn
+ (push (cons 'and filters) ibuffer-filtering-qualifiers)
+ (ibuffer-decompose-filter)
+ (should (equal filters ibuffer-filtering-qualifiers))
+ (setq ibuffer-filtering-qualifiers nil))
+ (progn
+ (push (list 'not (car filters)) ibuffer-filtering-qualifiers)
+ (ibuffer-decompose-filter)
+ (should (equal (list (car filters))
+ ibuffer-filtering-qualifiers))
+ (setq ibuffer-filtering-qualifiers nil))
+ (progn
+ (push (cons 'not (car filters)) ibuffer-filtering-qualifiers)
+ (ibuffer-decompose-filter)
+ (should (equal (list (car filters))
+ ibuffer-filtering-qualifiers))
+ (setq ibuffer-filtering-qualifiers nil))
+ (let ((gnus (assoc "gnus" ibuffer-saved-filters)))
+ (push '(saved . "gnus") ibuffer-filtering-qualifiers)
+ (ibuffer-decompose-filter)
+ (should (equal (cdr gnus) ibuffer-filtering-qualifiers))
+ (ibuffer-decompose-filter)
+ (should (equal (cdr (cadr gnus)) ibuffer-filtering-qualifiers))
+ (setq ibuffer-filtering-qualifiers nil))
+ (when (not (assoc "__unknown__" ibuffer-saved-filters))
+ (push '(saved . "__uknown__") ibuffer-filtering-qualifiers)
+ (should-error (ibuffer-decompose-filter) :type 'error)
+ (setq ibuffer-filtering-qualifiers nil))
+ (progn
+ (push (car filters) ibuffer-filtering-qualifiers)
+ (should-error (ibuffer-decompose-filter) :type 'error)
+ (setq ibuffer-filtering-qualifiers nil)))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-and-filter ()
+ "Tests `ibuffer-and-filter' in an Ibuffer buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((ibuf (funcall get-test-ibuffer)))
+ (with-current-buffer ibuf
+ (let ((ibuffer-filtering-qualifiers nil)
+ (ibuffer-filter-groups nil)
+ (filters [(size-gt . 100) (not (starred-name))
+ (filename . "A") (mode . text-mode)]))
+ (should-error (ibuffer-and-filter) :type 'error)
+ (progn
+ (push (aref filters 1) ibuffer-filtering-qualifiers)
+ (should-error (ibuffer-and-filter) :type 'error))
+ (should (progn
+ (push (aref filters 0) ibuffer-filtering-qualifiers)
+ (ibuffer-and-filter)
+ (and (equal (list 'and (aref filters 0) (aref filters 1))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (should (progn
+ (ibuffer-and-filter 'decompose)
+ (and (equal (aref filters 0)
+ (pop ibuffer-filtering-qualifiers))
+ (equal (aref filters 1)
+ (pop ibuffer-filtering-qualifiers))
+ (null ibuffer-filtering-qualifiers))))
+ (should (progn
+ (push (list 'and (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'and (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-and-filter)
+ (and (equal (list 'and (aref filters 0) (aref filters 1)
+ (aref filters 2) (aref filters 3))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (pop ibuffer-filtering-qualifiers)
+ (should (progn
+ (push (list 'or (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'and (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-and-filter)
+ (and (equal (list 'and (aref filters 0) (aref filters 1)
+ (list 'or (aref filters 2)
+ (aref filters 3)))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (pop ibuffer-filtering-qualifiers)
+ (should (progn
+ (push (list 'and (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'or (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-and-filter)
+ (and (equal (list 'and (list 'or (aref filters 0)
+ (aref filters 1))
+ (aref filters 2) (aref filters 3))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (pop ibuffer-filtering-qualifiers)
+ (should (progn
+ (push (list 'or (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'or (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-and-filter)
+ (and (equal (list 'and
+ (list 'or (aref filters 0)
+ (aref filters 1))
+ (list 'or (aref filters 2)
+ (aref filters 3)))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers))))))))
+ (funcall clean-up)))
+
+ (ert-deftest ibuffer-or-filter ()
+ "Tests `ibuffer-or-filter' in an Ibuffer buffer."
+ (skip-unless (featurep 'ibuf-ext))
+ (unwind-protect
+ (let ((ibuf (funcall get-test-ibuffer)))
+ (with-current-buffer ibuf
+ (let ((ibuffer-filtering-qualifiers nil)
+ (ibuffer-filter-groups nil)
+ (filters [(size-gt . 100) (not (starred-name))
+ (filename . "A") (mode . text-mode)]))
+ (should-error (ibuffer-or-filter) :type 'error)
+ (progn
+ (push (aref filters 1) ibuffer-filtering-qualifiers)
+ (should-error (ibuffer-or-filter) :type 'error))
+ (should (progn
+ (push (aref filters 0) ibuffer-filtering-qualifiers)
+ (ibuffer-or-filter)
+ (and (equal (list 'or (aref filters 0) (aref filters 1))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (should (progn
+ (ibuffer-or-filter 'decompose)
+ (and (equal (aref filters 0)
+ (pop ibuffer-filtering-qualifiers))
+ (equal (aref filters 1)
+ (pop ibuffer-filtering-qualifiers))
+ (null ibuffer-filtering-qualifiers))))
+ (should (progn
+ (push (list 'or (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'or (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-or-filter)
+ (and (equal (list 'or (aref filters 0) (aref filters 1)
+ (aref filters 2) (aref filters 3))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (pop ibuffer-filtering-qualifiers)
+ (should (progn
+ (push (list 'and (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'or (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-or-filter)
+ (and (equal (list 'or (aref filters 0) (aref filters 1)
+ (list 'and (aref filters 2)
+ (aref filters 3)))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (pop ibuffer-filtering-qualifiers)
+ (should (progn
+ (push (list 'or (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'and (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-or-filter)
+ (and (equal (list 'or (list 'and (aref filters 0)
+ (aref filters 1))
+ (aref filters 2) (aref filters 3))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers)))))
+ (pop ibuffer-filtering-qualifiers)
+ (should (progn
+ (push (list 'and (aref filters 2) (aref filters 3))
+ ibuffer-filtering-qualifiers)
+ (push (list 'and (aref filters 0) (aref filters 1))
+ ibuffer-filtering-qualifiers)
+ (ibuffer-or-filter)
+ (and (equal (list 'or
+ (list 'and (aref filters 0)
+ (aref filters 1))
+ (list 'and (aref filters 2)
+ (aref filters 3)))
+ (car ibuffer-filtering-qualifiers))
+ (null (cdr ibuffer-filtering-qualifiers))))))))
+ (funcall clean-up))))
+
+(ert-deftest ibuffer-format-qualifier ()
+ "Tests string recommendation of filter from `ibuffer-format-qualifier'."
+ (skip-unless (featurep 'ibuf-ext))
+ (let ((test1 '(mode . org-mode))
+ (test2 '(size-lt . 100))
+ (test3 '(derived-mode . prog-mode))
+ (test4 '(or (size-gt . 10000)
+ (and (not (starred-name))
+ (directory . "\\<org\\>"))))
+ (test5 '(or (filename . "scratch")
+ (filename . "bonz")
+ (filename . "temp")))
+ (test6 '(or (mode . emacs-lisp-mode) (file-extension . "elc?")
+ (and (starred-name) (name . "elisp"))
+ (mode . lisp-interaction-mode)))
+ (description (lambda (q)
+ (cadr (assq q ibuffer-filtering-alist))))
+ (tag (lambda (&rest args )
+ (concat " [" (apply #'concat args) "]"))))
+ (should (equal (ibuffer-format-qualifier test1)
+ (funcall tag (funcall description 'mode)
+ ": " "org-mode")))
+ (should (equal (ibuffer-format-qualifier test2)
+ (funcall tag (funcall description 'size-lt)
+ ": " "100")))
+ (should (equal (ibuffer-format-qualifier test3)
+ (funcall tag (funcall description 'derived-mode)
+ ": " "prog-mode")))
+ (should (equal (ibuffer-format-qualifier test4)
+ (funcall tag "OR"
+ (funcall tag (funcall description 'size-gt)
+ ": " (format "%s" 10000))
+ (funcall tag "AND"
+ (funcall tag "NOT"
+ (funcall tag
+ (funcall description
+ 'starred-name)
+ ": " "nil"))
+ (funcall tag
+ (funcall description 'directory)
+ ": " "\\<org\\>")))))
+ (should (equal (ibuffer-format-qualifier test5)
+ (funcall tag "OR"
+ (funcall tag (funcall description 'filename)
+ ": " "scratch")
+ (funcall tag (funcall description 'filename)
+ ": " "bonz")
+ (funcall tag (funcall description 'filename)
+ ": " "temp"))))
+ (should (equal (ibuffer-format-qualifier test6)
+ (funcall tag "OR"
+ (funcall tag (funcall description 'mode)
+ ": " "emacs-lisp-mode")
+ (funcall tag (funcall description 'file-extension)
+ ": " "elc?")
+ (funcall tag "AND"
+ (funcall tag
+ (funcall description 'starred-name)
+ ": " "nil")
+ (funcall tag
+ (funcall description 'name)
+ ": " "elisp"))
+ (funcall tag (funcall description 'mode)
+ ": " "lisp-interaction-mode"))))))
+
+(ert-deftest ibuffer-unary-operand ()
+ "Tests `ibuffer-unary-operand': (not cell) or (not . cell) -> cell."
+ (skip-unless (featurep 'ibuf-ext))
+ (should (equal (ibuffer-unary-operand '(not . (mode "foo")))
+ '(mode "foo")))
+ (should (equal (ibuffer-unary-operand '(not (mode "foo")))
+ '(mode "foo")))
+ (should (equal (ibuffer-unary-operand '(not "cdr"))
+ '("cdr")))
+ (should (equal (ibuffer-unary-operand '(not)) nil))
+ (should (equal (ibuffer-unary-operand '(not . a)) 'a)))
+
(provide 'ibuffer-tests)
;; ibuffer-tests.el ends here