aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristopher Genovese2016-12-20 17:41:56 +0900
committerTino Calancha2016-12-20 17:41:56 +0900
commit35aaa6b6aa9a2e7b42465603fb32355a009c510f (patch)
tree59e390dc4ab4b10398da125ce1d1e4f2c03b9570
parentf8072cd5c16f855505f6a0ce6a6b30309735705d (diff)
downloademacs-35aaa6b6aa9a2e7b42465603fb32355a009c510f.tar.gz
emacs-35aaa6b6aa9a2e7b42465603fb32355a009c510f.zip
ibuffer: New filters and commands
Add several new filters and improve documentation. See discussion on: https://lists.gnu.org/archive/html/emacs-devel/2016-11/msg00399.html * lisp/ibuf-ext.el: Add paragraph to file commentary. (ibuffer-saved-filters, ibuffer-filtering-qualifiers) (ibuffer-filter-groups): Update doc string. (ibuffer-unary-operand): Add new function that transparently handles 'not' formats for compound filters. (ibuffer-included-in-filter-p): Handle 'not' fully; update doc string. (ibuffer-included-in-filter-p-1): Handle 'and' compound filters. (ibuffer-decompose-filter): Handle 'and' as well, and handle 'not' consistently with other uses. (ibuffer-and-filter): New defun analogous to 'ibuffer-or-filter'. (ibuffer--or-and-filter): New defun. (ibuffer-or-filter, ibuffer-and-filter): Use it. (ibuffer-format-qualifier): Handle 'and' filters as well. (ibuffer-filter-by-basename, ibuffer-filter-by-file-extension) (ibuffer-filter-by-directory, ibuffer-filter-by-starred-name) (ibuffer-filter-by-modified, ibuffer-filter-by-visiting-file): Add new pre-defined filters. (ibuffer-filter-chosen-by-completion): Add new interactive command for easily choosing a filter from the descriptions. * lisp/ibuffer.el (ibuffer-mode-map): Bind ibuffer-filter-by-basename, ibuffer-filter-by-file-extension, ibuffer-filter-by-starred-name, ibuffer-filter-by-modified, ibuffer-filter-by-visiting-file to '/b', '/.', '/*', '/i', '/v' respectively; bind 'ibuffer-or-filter', 'ibuffer-and-filter', 'ibuffer-pop-filter' ,'ibuffer-pop-filter-group' and 'ibuffer-filter-disable' to '/|', '/&', '/<up>', '/S-<up>' and '/ DEL' respectively. * test/lisp/ibuffer-tests.el (ibuffer-autoload): Add appropriate skip specification. Add menu entries for the new filters. (ibuffer-filter-inclusion-1, ibuffer-filter-inclusion-2 ibuffer-filter-inclusion-3, ibuffer-filter-inclusion-4 ibuffer-filter-inclusion-5, ibuffer-filter-inclusion-6 ibuffer-filter-inclusion-7, ibuffer-filter-inclusion-8 ibuffer-decompose-filter, ibuffer-and-filter ibuffer-or-filter): Add new tests; they are skipped unless ibuf-ext is loaded. ; * etc/NEWS: Add entries for new user-facing features.
-rw-r--r--etc/NEWS21
-rw-r--r--lisp/ibuf-ext.el347
-rw-r--r--lisp/ibuffer.el55
-rw-r--r--test/lisp/ibuffer-tests.el667
4 files changed, 1009 insertions, 81 deletions
diff --git a/etc/NEWS b/etc/NEWS
index e15ab79d509..ee74236a528 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -333,6 +333,27 @@ bound to 'Buffer-menu-unmark-all-buffers'.
333** Ibuffer 333** Ibuffer
334 334
335--- 335---
336*** New filter commands `ibuffer-filter-by-basename',
337`ibuffer-filter-by-file-extension', `ibuffer-filter-by-directory',
338`ibuffer-filter-by-starred-name', `ibuffer-filter-by-modified'
339and `ibuffer-filter-by-visiting-file'; bound respectively
340to '/b', '/.', '//', '/*', '/i' and '/v'.
341
342---
343*** Two new commands 'ibuffer-filter-chosen-by-completion'
344and `ibuffer-and-filter', the second bound to '/&'.
345
346---
347*** The commands `ibuffer-pop-filter', `ibuffer-pop-filter-group',
348`ibuffer-or-filter' and `ibuffer-filter-disable' have the alternative
349bindings '/<up>', '/S-<up>', '/|' and '/DEL', respectively.
350
351---
352*** The data format specifying filters has been extended to allow
353explicit logical 'and', and a more flexible form for logical 'not'.
354See 'ibuffer-filtering-qualifiers' doc string for full details.
355
356---
336*** A new command 'ibuffer-copy-buffername-as-kill'; bound 357*** A new command 'ibuffer-copy-buffername-as-kill'; bound
337to 'B'. 358to 'B'.
338 359
diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el
index 9ce7b5a4846..7ebfecd3749 100644
--- a/lisp/ibuf-ext.el
+++ b/lisp/ibuf-ext.el
@@ -28,6 +28,13 @@
28;; These functions should be automatically loaded when called, but you 28;; These functions should be automatically loaded when called, but you
29;; can explicitly (require 'ibuf-ext) in your ~/.emacs to have them 29;; can explicitly (require 'ibuf-ext) in your ~/.emacs to have them
30;; preloaded. 30;; preloaded.
31;;
32;; For details on the structure of ibuffer filters and filter groups,
33;; see the documentation for variables `ibuffer-filtering-qualifiers',
34;; `ibuffer-filter-groups', and `ibuffer-saved-filters' in that order.
35;; The variable `ibuffer-filtering-alist' contains names and
36;; descriptions of the currently defined filters; also see the macro
37;; `define-ibuffer-filter'.
31 38
32;;; Code: 39;;; Code:
33 40
@@ -139,19 +146,33 @@ Returns (OLD-FORMAT-DETECTED . UPDATED-SAVED-FILTERS-LIST)."
139 (fixed (mapcar fix-filter filters))) 146 (fixed (mapcar fix-filter filters)))
140 (cons old-format-detected fixed)))) 147 (cons old-format-detected fixed))))
141 148
142(defcustom ibuffer-saved-filters '(("gnus" 149(defcustom ibuffer-saved-filters '(("programming"
150 (or (derived-mode . prog-mode)
151 (mode . ess-mode)
152 (mode . compilation-mode)))
153 ("text document"
154 (and (derived-mode . text-mode)
155 (not (starred-name))))
156 ("TeX"
157 (or (derived-mode . tex-mode)
158 (mode . latex-mode)
159 (mode . context-mode)
160 (mode . ams-tex-mode)
161 (mode . bibtex-mode)))
162 ("web"
163 (or (derived-mode . sgml-mode)
164 (derived-mode . css-mode)
165 (mode . javascript-mode)
166 (mode . js2-mode)
167 (mode . scss-mode)
168 (derived-mode . haml-mode)
169 (mode . sass-mode)))
170 ("gnus"
143 (or (mode . message-mode) 171 (or (mode . message-mode)
144 (mode . mail-mode) 172 (mode . mail-mode)
145 (mode . gnus-group-mode) 173 (mode . gnus-group-mode)
146 (mode . gnus-summary-mode) 174 (mode . gnus-summary-mode)
147 (mode . gnus-article-mode))) 175 (mode . gnus-article-mode))))
148 ("programming"
149 (or (mode . emacs-lisp-mode)
150 (mode . cperl-mode)
151 (mode . c-mode)
152 (mode . java-mode)
153 (mode . idl-mode)
154 (mode . lisp-mode))))
155 176
156 "An alist mapping saved filter names to filter specifications. 177 "An alist mapping saved filter names to filter specifications.
157 178
@@ -214,8 +235,48 @@ Alternative ways to save the repaired value:
214")) 235"))
215 236
216(defvar ibuffer-filtering-qualifiers nil 237(defvar ibuffer-filtering-qualifiers nil
217 "A list like (SYMBOL . QUALIFIER) which filters the current buffer list. 238 "A list specifying the filters currently acting on the buffer list.
218See also `ibuffer-filtering-alist'.") 239
240If this list is nil, then no filters are currently in
241effect. Otherwise, each element of this list specifies a single
242filter, and all of the specified filters in the list are applied
243successively to the buffer list.
244
245Each filter specification can be of two types: simple or compound.
246
247A simple filter specification has the form (SYMBOL . QUALIFIER),
248where SYMBOL is a key in the alist `ibuffer-filtering-alist' that
249determines the filter function to use and QUALIFIER is the data
250passed to that function (along with the buffer being considered).
251
252A compound filter specification can have one of four forms:
253
254-- (not FILTER-SPEC)
255
256 Represents the logical complement of FILTER-SPEC, which
257 is any single filter specification, simple or compound.
258 The form (not . FILTER-SPEC) is also accepted here.
259
260-- (and FILTER-SPECS...)
261
262 Represents the logical-and of the filters defined by one or
263 more filter specifications FILTER-SPECS..., where each
264 specification can be simple or compound. Note that and is
265 implicitly applied to the filters in the top-level list.
266
267-- (or FILTER-SPECS...)
268
269 Represents the logical-or of the filters defined by one or
270 more filter specifications FILTER-SPECS..., where each
271 specification can be simple or compound.
272
273-- (saved . \"NAME\")
274
275 Represents the filter saved under the string NAME
276 in the alist `ibuffer-saved-filters'. It is an
277 error to name a filter that has not been saved.
278
279This variable is local to each ibuffer buffer.")
219 280
220;; This is now frobbed by `define-ibuffer-filter'. 281;; This is now frobbed by `define-ibuffer-filter'.
221(defvar ibuffer-filtering-alist nil 282(defvar ibuffer-filtering-alist nil
@@ -247,10 +308,18 @@ to this variable."
247(defvar ibuffer-compiled-filter-formats nil) 308(defvar ibuffer-compiled-filter-formats nil)
248 309
249(defvar ibuffer-filter-groups nil 310(defvar ibuffer-filter-groups nil
250 "A list like ((\"NAME\" ((SYMBOL . QUALIFIER) ...) ...) which groups buffers. 311 "An alist giving this buffer's active filter groups, or nil if none.
251The SYMBOL should be one from `ibuffer-filtering-alist'. 312
252The QUALIFIER should be the same as QUALIFIER in 313This alist maps filter group labels to filter specification
253`ibuffer-filtering-qualifiers'.") 314lists. Each element has the form (\"LABEL\" FILTER-SPECS...),
315where FILTER-SPECS... represents one or more filter
316specifications of the same form as allowed as elements of
317`ibuffer-filtering-qualifiers'.
318
319Each filter group is displayed as a separate section in the
320ibuffer list, headed by LABEL and displaying only the buffers
321that pass through all the filters associated with NAME in this
322list.")
254 323
255(defcustom ibuffer-show-empty-filter-groups t 324(defcustom ibuffer-show-empty-filter-groups t
256 "If non-nil, then show the names of filter groups which are empty." 325 "If non-nil, then show the names of filter groups which are empty."
@@ -260,20 +329,21 @@ The QUALIFIER should be the same as QUALIFIER in
260(defcustom ibuffer-saved-filter-groups nil 329(defcustom ibuffer-saved-filter-groups nil
261 "An alist of filtering groups to switch between. 330 "An alist of filtering groups to switch between.
262 331
263This variable should look like ((\"STRING\" QUALIFIERS) 332Each element is of the form (\"NAME\" . FILTER-GROUP-LIST),
264 (\"STRING\" QUALIFIERS) ...), where 333where NAME is a unique but arbitrary name and FILTER-GROUP-LIST
265QUALIFIERS is a list of the same form as 334is a list of filter groups with the same structure as
266`ibuffer-filtering-qualifiers'. 335allowed for `ibuffer-filter-groups'.
267 336
268See also the variables `ibuffer-filter-groups', 337See also the functions `ibuffer-save-filter-groups' and
269`ibuffer-filtering-qualifiers', `ibuffer-filtering-alist', and the 338`ibuffer-switch-to-saved-filter-groups' for saving and switching
270functions `ibuffer-switch-to-saved-filter-groups', 339between sets of filter groups, and the variable
271`ibuffer-save-filter-groups'." 340`ibuffer-save-with-custom' that affects how this information is
341saved."
272 :type '(repeat sexp) 342 :type '(repeat sexp)
273 :group 'ibuffer) 343 :group 'ibuffer)
274 344
275(defvar ibuffer-hidden-filter-groups nil 345(defvar ibuffer-hidden-filter-groups nil
276 "A list of filtering groups which are currently hidden.") 346 "The list of filter groups that are currently hidden.")
277 347
278(defvar ibuffer-filter-group-kill-ring nil) 348(defvar ibuffer-filter-group-kill-ring nil)
279 349
@@ -602,18 +672,38 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
602 672
603;;;###autoload 673;;;###autoload
604(defun ibuffer-included-in-filters-p (buf filters) 674(defun ibuffer-included-in-filters-p (buf filters)
675 "Return non-nil if BUF passes all FILTERS.
676
677BUF is a lisp buffer object, and FILTERS is a list of filter
678specifications with the same structure as
679`ibuffer-filtering-qualifiers'."
605 (not 680 (not
606 (memq nil ;; a filter will return nil if it failed 681 (memq nil ;; a filter will return nil if it failed
607 (mapcar 682 (mapcar #'(lambda (filter)
608 ;; filter should be like (TYPE . QUALIFIER), or 683 (ibuffer-included-in-filter-p buf filter))
609 ;; (or (TYPE . QUALIFIER) (TYPE . QUALIFIER) ...) 684 filters))))
610 #'(lambda (qual) 685
611 (ibuffer-included-in-filter-p buf qual)) 686(defun ibuffer-unary-operand (filter)
612 filters)))) 687 "Extracts operand from a unary compound FILTER specification.
688
689FILTER should be a cons cell of either form (f . d) or (f d),
690where operand d is itself a cons cell, or nil. Returns d."
691 (let* ((tail (cdr filter))
692 (maybe-q (car-safe tail)))
693 (if (consp maybe-q) maybe-q tail)))
613 694
614(defun ibuffer-included-in-filter-p (buf filter) 695(defun ibuffer-included-in-filter-p (buf filter)
696 "Return non-nil if BUF pass FILTER.
697
698BUF is a lisp buffer object, and FILTER is a filter
699specification, with the same structure as an element of the list
700`ibuffer-filtering-qualifiers'."
615 (if (eq (car filter) 'not) 701 (if (eq (car filter) 'not)
616 (not (ibuffer-included-in-filter-p-1 buf (cdr filter))) 702 (let ((inner (ibuffer-unary-operand filter)))
703 ;; Allows (not (not ...)) etc, which may be overkill
704 (if (eq (car inner) 'not)
705 (ibuffer-included-in-filter-p buf (ibuffer-unary-operand inner))
706 (not (ibuffer-included-in-filter-p-1 buf inner))))
617 (ibuffer-included-in-filter-p-1 buf filter))) 707 (ibuffer-included-in-filter-p-1 buf filter)))
618 708
619(defun ibuffer-included-in-filter-p-1 (buf filter) 709(defun ibuffer-included-in-filter-p-1 (buf filter)
@@ -621,9 +711,19 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
621 (not 711 (not
622 (pcase (car filter) 712 (pcase (car filter)
623 (`or 713 (`or
714 ;;; ATTN: Short-circuiting alternative with parallel structure w/`and
715 ;;(catch 'has-match
716 ;; (dolist (filter-spec (cdr filter) nil)
717 ;; (when (ibuffer-included-in-filter-p buf filter-spec)
718 ;; (throw 'has-match t))))
624 (memq t (mapcar #'(lambda (x) 719 (memq t (mapcar #'(lambda (x)
625 (ibuffer-included-in-filter-p buf x)) 720 (ibuffer-included-in-filter-p buf x))
626 (cdr filter)))) 721 (cdr filter))))
722 (`and
723 (catch 'no-match
724 (dolist (filter-spec (cdr filter) t)
725 (unless (ibuffer-included-in-filter-p buf filter-spec)
726 (throw 'no-match nil)))))
627 (`saved 727 (`saved
628 (let ((data (assoc (cdr filter) ibuffer-saved-filters))) 728 (let ((data (assoc (cdr filter) ibuffer-saved-filters)))
629 (unless data 729 (unless data
@@ -916,17 +1016,17 @@ group definitions by setting `ibuffer-filter-groups' to nil."
916 (when buf 1016 (when buf
917 (ibuffer-jump-to-buffer (buffer-name buf))))) 1017 (ibuffer-jump-to-buffer (buffer-name buf)))))
918 1018
919(defun ibuffer-push-filter (qualifier) 1019(defun ibuffer-push-filter (filter-specification)
920 "Add QUALIFIER to `ibuffer-filtering-qualifiers'." 1020 "Add FILTER-SPECIFICATION to `ibuffer-filtering-qualifiers'."
921 (push qualifier ibuffer-filtering-qualifiers)) 1021 (push filter-specification ibuffer-filtering-qualifiers))
922 1022
923;;;###autoload 1023;;;###autoload
924(defun ibuffer-decompose-filter () 1024(defun ibuffer-decompose-filter ()
925 "Separate the top compound filter (OR, NOT, or SAVED) in this buffer. 1025 "Separate this buffer's top compound filter (AND, OR, NOT, or SAVED).
926 1026
927This means that the topmost filter on the filtering stack, which must 1027This means that the topmost filter on the filtering stack, which must
928be a complex filter like (OR [name: foo] [mode: bar-mode]), will be 1028be a complex filter like (OR [name: foo] [mode: bar-mode]), will be
929turned into two separate filters [name: foo] and [mode: bar-mode]." 1029turned into separate filters, like [name: foo] and [mode: bar-mode]."
930 (interactive) 1030 (interactive)
931 (unless ibuffer-filtering-qualifiers 1031 (unless ibuffer-filtering-qualifiers
932 (error "No filters in effect")) 1032 (error "No filters in effect"))
@@ -935,14 +1035,14 @@ turned into two separate filters [name: foo] and [mode: bar-mode]."
935 (tail (cdr filters)) 1035 (tail (cdr filters))
936 (value 1036 (value
937 (pcase (caar filters) 1037 (pcase (caar filters)
938 (`or (nconc head tail)) 1038 ((or `or 'and) (nconc head tail))
939 (`saved 1039 (`saved
940 (let ((data (assoc head ibuffer-saved-filters))) 1040 (let ((data (assoc head ibuffer-saved-filters)))
941 (unless data 1041 (unless data
942 (ibuffer-filter-disable) 1042 (ibuffer-filter-disable)
943 (error "Unknown saved filter %s" head)) 1043 (error "Unknown saved filter %s" head))
944 (append (cdr data) tail))) 1044 (append (cdr data) tail)))
945 (`not (cons head tail)) 1045 (`not (cons (ibuffer-unary-operand (car filters)) tail))
946 (_ 1046 (_
947 (error "Filter type %s is not compound" (caar filters)))))) 1047 (error "Filter type %s is not compound" (caar filters))))))
948 (setq ibuffer-filtering-qualifiers value)) 1048 (setq ibuffer-filtering-qualifiers value))
@@ -971,31 +1071,36 @@ turned into two separate filters [name: foo] and [mode: bar-mode]."
971 ibuffer-filtering-qualifiers)) 1071 ibuffer-filtering-qualifiers))
972 (ibuffer-update nil t)) 1072 (ibuffer-update nil t))
973 1073
1074(defun ibuffer--or-and-filter (op decompose)
1075 (if decompose
1076 (if (eq op (caar ibuffer-filtering-qualifiers))
1077 (ibuffer-decompose-filter)
1078 (error "Top filter is not an %s" (upcase (symbol-name op))))
1079 (when (< (length ibuffer-filtering-qualifiers) 2)
1080 (error "Need two filters to %s" (upcase (symbol-name op))))
1081 ;; If either filter is an op, eliminate unnecessary nesting.
1082 (let ((first (pop ibuffer-filtering-qualifiers))
1083 (second (pop ibuffer-filtering-qualifiers)))
1084 (push (nconc (if (eq op (car first)) first (list op first))
1085 (if (eq op (car second)) (cdr second) (list second)))
1086 ibuffer-filtering-qualifiers)))
1087 (ibuffer-update nil t))
1088
974;;;###autoload 1089;;;###autoload
975(defun ibuffer-or-filter (&optional reverse) 1090(defun ibuffer-or-filter (&optional decompose)
976 "Replace the top two filters in this buffer with their logical OR. 1091 "Replace the top two filters in this buffer with their logical OR.
977If optional argument REVERSE is non-nil, instead break the top OR 1092If optional argument DECOMPOSE is non-nil, instead break the top OR
978filter into parts." 1093filter into parts."
979 (interactive "P") 1094 (interactive "P")
980 (if reverse 1095 (ibuffer--or-and-filter 'or decompose))
981 (progn 1096
982 (when (or (null ibuffer-filtering-qualifiers) 1097;;;###autoload
983 (not (eq 'or (caar ibuffer-filtering-qualifiers)))) 1098(defun ibuffer-and-filter (&optional decompose)
984 (error "Top filter is not an OR")) 1099 "Replace the top two filters in this buffer with their logical AND.
985 (let ((lim (pop ibuffer-filtering-qualifiers))) 1100If optional argument DECOMPOSE is non-nil, instead break the top AND
986 (setq ibuffer-filtering-qualifiers 1101filter into parts."
987 (nconc (cdr lim) ibuffer-filtering-qualifiers)))) 1102 (interactive "P")
988 (when (< (length ibuffer-filtering-qualifiers) 2) 1103 (ibuffer--or-and-filter 'and decompose))
989 (error "Need two filters to OR"))
990 ;; If the second filter is an OR, just add to it.
991 (let ((first (pop ibuffer-filtering-qualifiers))
992 (second (pop ibuffer-filtering-qualifiers)))
993 (if (eq 'or (car second))
994 (push (nconc (list 'or first) (cdr second))
995 ibuffer-filtering-qualifiers)
996 (push (list 'or first second)
997 ibuffer-filtering-qualifiers))))
998 (ibuffer-update nil t))
999 1104
1000(defun ibuffer-maybe-save-stuff () 1105(defun ibuffer-maybe-save-stuff ()
1001 (when ibuffer-save-with-custom 1106 (when ibuffer-save-with-custom
@@ -1069,7 +1174,9 @@ Interactively, prompt for NAME, and use the current filters."
1069 1174
1070(defun ibuffer-format-qualifier (qualifier) 1175(defun ibuffer-format-qualifier (qualifier)
1071 (if (eq (car-safe qualifier) 'not) 1176 (if (eq (car-safe qualifier) 'not)
1072 (concat " [NOT" (ibuffer-format-qualifier-1 (cdr qualifier)) "]") 1177 (concat " [NOT"
1178 (ibuffer-format-qualifier-1 (ibuffer-unary-operand qualifier))
1179 "]")
1073 (ibuffer-format-qualifier-1 qualifier))) 1180 (ibuffer-format-qualifier-1 qualifier)))
1074 1181
1075(defun ibuffer-format-qualifier-1 (qualifier) 1182(defun ibuffer-format-qualifier-1 (qualifier)
@@ -1078,14 +1185,16 @@ Interactively, prompt for NAME, and use the current filters."
1078 (concat " [filter: " (cdr qualifier) "]")) 1185 (concat " [filter: " (cdr qualifier) "]"))
1079 (`or 1186 (`or
1080 (concat " [OR" (mapconcat #'ibuffer-format-qualifier 1187 (concat " [OR" (mapconcat #'ibuffer-format-qualifier
1081 (cdr qualifier) "") "]")) 1188 (cdr qualifier) "") "]"))
1189 (`and
1190 (concat " [AND" (mapconcat #'ibuffer-format-qualifier
1191 (cdr qualifier) "") "]"))
1082 (_ 1192 (_
1083 (let ((type (assq (car qualifier) ibuffer-filtering-alist))) 1193 (let ((type (assq (car qualifier) ibuffer-filtering-alist)))
1084 (unless qualifier 1194 (unless qualifier
1085 (error "Ibuffer: bad qualifier %s" qualifier)) 1195 (error "Ibuffer: bad qualifier %s" qualifier))
1086 (concat " [" (cadr type) ": " (format "%s]" (cdr qualifier))))))) 1196 (concat " [" (cadr type) ": " (format "%s]" (cdr qualifier)))))))
1087 1197
1088
1089(defun ibuffer-list-buffer-modes (&optional include-parents) 1198(defun ibuffer-list-buffer-modes (&optional include-parents)
1090 "Create a completion table of buffer modes currently in use. 1199 "Create a completion table of buffer modes currently in use.
1091If INCLUDE-PARENTS is non-nil then include parent modes." 1200If INCLUDE-PARENTS is non-nil then include parent modes."
@@ -1103,7 +1212,7 @@ If INCLUDE-PARENTS is non-nil then include parent modes."
1103 1212
1104;;;###autoload (autoload 'ibuffer-filter-by-mode "ibuf-ext") 1213;;;###autoload (autoload 'ibuffer-filter-by-mode "ibuf-ext")
1105(define-ibuffer-filter mode 1214(define-ibuffer-filter mode
1106 "Toggle current view to buffers with major mode QUALIFIER." 1215 "Limit current view to buffers with major mode QUALIFIER."
1107 (:description "major mode" 1216 (:description "major mode"
1108 :reader 1217 :reader
1109 (let* ((buf (ibuffer-current-buffer)) 1218 (let* ((buf (ibuffer-current-buffer))
@@ -1123,7 +1232,7 @@ If INCLUDE-PARENTS is non-nil then include parent modes."
1123 1232
1124;;;###autoload (autoload 'ibuffer-filter-by-used-mode "ibuf-ext") 1233;;;###autoload (autoload 'ibuffer-filter-by-used-mode "ibuf-ext")
1125(define-ibuffer-filter used-mode 1234(define-ibuffer-filter used-mode
1126 "Toggle current view to buffers with major mode QUALIFIER. 1235 "Limit current view to buffers with major mode QUALIFIER.
1127Called interactively, this function allows selection of modes 1236Called interactively, this function allows selection of modes
1128currently used by buffers." 1237currently used by buffers."
1129 (:description "major mode in use" 1238 (:description "major mode in use"
@@ -1142,7 +1251,7 @@ currently used by buffers."
1142 1251
1143;;;###autoload (autoload 'ibuffer-filter-by-derived-mode "ibuf-ext") 1252;;;###autoload (autoload 'ibuffer-filter-by-derived-mode "ibuf-ext")
1144(define-ibuffer-filter derived-mode 1253(define-ibuffer-filter derived-mode
1145 "Toggle current view to buffers whose major mode inherits from QUALIFIER." 1254 "Limit current view to buffers whose major mode inherits from QUALIFIER."
1146 (:description "derived mode" 1255 (:description "derived mode"
1147 :reader 1256 :reader
1148 (intern 1257 (intern
@@ -1153,22 +1262,73 @@ currently used by buffers."
1153 1262
1154;;;###autoload (autoload 'ibuffer-filter-by-name "ibuf-ext") 1263;;;###autoload (autoload 'ibuffer-filter-by-name "ibuf-ext")
1155(define-ibuffer-filter name 1264(define-ibuffer-filter name
1156 "Toggle current view to buffers with name matching QUALIFIER." 1265 "Limit current view to buffers with name matching QUALIFIER."
1157 (:description "buffer name" 1266 (:description "buffer name"
1158 :reader (read-from-minibuffer "Filter by name (regexp): ")) 1267 :reader (read-from-minibuffer "Filter by name (regexp): "))
1159 (string-match qualifier (buffer-name buf))) 1268 (string-match qualifier (buffer-name buf)))
1160 1269
1270;;;###autoload (autoload 'ibuffer-filter-by-starred-name "ibuf-ext")
1271(define-ibuffer-filter starred-name
1272 "Limit current view to buffers with name beginning and ending
1273with *, along with an optional suffix of the form digits or
1274<digits>."
1275 (:description "starred buffer name"
1276 :reader nil)
1277 (string-match "\\`\\*[^*]+\\*\\(?:<[[:digit:]]+>\\)?\\'" (buffer-name buf)))
1278
1161;;;###autoload (autoload 'ibuffer-filter-by-filename "ibuf-ext") 1279;;;###autoload (autoload 'ibuffer-filter-by-filename "ibuf-ext")
1162(define-ibuffer-filter filename 1280(define-ibuffer-filter filename
1163 "Toggle current view to buffers with filename matching QUALIFIER." 1281 "Limit current view to buffers with full file name matching QUALIFIER.
1164 (:description "filename" 1282
1165 :reader (read-from-minibuffer "Filter by filename (regexp): ")) 1283For example, for a buffer associated with file '/a/b/c.d', this
1284matches against '/a/b/c.d'."
1285 (:description "full file name"
1286 :reader (read-from-minibuffer "Filter by full file name (regexp): "))
1166 (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name)) 1287 (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
1167 (string-match qualifier it))) 1288 (string-match qualifier it)))
1168 1289
1290;;;###autoload (autoload 'ibuffer-filter-by-basename "ibuf-ext")
1291(define-ibuffer-filter basename
1292 "Limit current view to buffers with file basename matching QUALIFIER.
1293
1294For example, for a buffer associated with file '/a/b/c.d', this
1295matches against 'c.d'."
1296 (:description "file basename"
1297 :reader (read-from-minibuffer
1298 "Filter by file name, without directory part (regex): "))
1299 (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
1300 (string-match qualifier (file-name-nondirectory it))))
1301
1302;;;###autoload (autoload 'ibuffer-filter-by-file-extension "ibuf-ext")
1303(define-ibuffer-filter file-extension
1304 "Limit current view to buffers with filename extension matching QUALIFIER.
1305
1306The separator character (typically `.') is not part of the
1307pattern. For example, for a buffer associated with file
1308'/a/b/c.d', this matches against 'd'."
1309 (:description "filename extension"
1310 :reader (read-from-minibuffer
1311 "Filter by filename extension without separator (regex): "))
1312 (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
1313 (string-match qualifier (or (file-name-extension it) ""))))
1314
1315;;;###autoload (autoload 'ibuffer-filter-by-directory "ibuf-ext")
1316(define-ibuffer-filter directory
1317 "Limit current view to buffers with directory matching QUALIFIER.
1318
1319For a buffer associated with file '/a/b/c.d', this matches
1320against '/a/b'. For a buffer not associated with a file, this
1321matches against the value of `default-directory' in that buffer."
1322 (:description "directory name"
1323 :reader (read-from-minibuffer "Filter by directory name (regex): "))
1324 (ibuffer-aif (with-current-buffer buf (ibuffer-buffer-file-name))
1325 (let ((dirname (file-name-directory it)))
1326 (when dirname (string-match qualifier dirname)))
1327 (when default-directory (string-match qualifier default-directory))))
1328
1169;;;###autoload (autoload 'ibuffer-filter-by-size-gt "ibuf-ext") 1329;;;###autoload (autoload 'ibuffer-filter-by-size-gt "ibuf-ext")
1170(define-ibuffer-filter size-gt 1330(define-ibuffer-filter size-gt
1171 "Toggle current view to buffers with size greater than QUALIFIER." 1331 "Limit current view to buffers with size greater than QUALIFIER."
1172 (:description "size greater than" 1332 (:description "size greater than"
1173 :reader 1333 :reader
1174 (string-to-number (read-from-minibuffer "Filter by size greater than: "))) 1334 (string-to-number (read-from-minibuffer "Filter by size greater than: ")))
@@ -1177,16 +1337,30 @@ currently used by buffers."
1177 1337
1178;;;###autoload (autoload 'ibuffer-filter-by-size-lt "ibuf-ext") 1338;;;###autoload (autoload 'ibuffer-filter-by-size-lt "ibuf-ext")
1179(define-ibuffer-filter size-lt 1339(define-ibuffer-filter size-lt
1180 "Toggle current view to buffers with size less than QUALIFIER." 1340 "Limit current view to buffers with size less than QUALIFIER."
1181 (:description "size less than" 1341 (:description "size less than"
1182 :reader 1342 :reader
1183 (string-to-number (read-from-minibuffer "Filter by size less than: "))) 1343 (string-to-number (read-from-minibuffer "Filter by size less than: ")))
1184 (< (with-current-buffer buf (buffer-size)) 1344 (< (with-current-buffer buf (buffer-size))
1185 qualifier)) 1345 qualifier))
1186 1346
1347;;;###autoload (autoload 'ibuffer-filter-by-modified "ibuf-ext")
1348(define-ibuffer-filter modified
1349 "Limit current view to buffers that are marked as modified."
1350 (:description "modified"
1351 :reader nil)
1352 (buffer-modified-p buf))
1353
1354;;;###autoload (autoload 'ibuffer-filter-by-visiting-file "ibuf-ext")
1355(define-ibuffer-filter visiting-file
1356 "Limit current view to buffers that are visiting a file."
1357 (:description "visiting a file"
1358 :reader nil)
1359 (with-current-buffer buf (buffer-file-name)))
1360
1187;;;###autoload (autoload 'ibuffer-filter-by-content "ibuf-ext") 1361;;;###autoload (autoload 'ibuffer-filter-by-content "ibuf-ext")
1188(define-ibuffer-filter content 1362(define-ibuffer-filter content
1189 "Toggle current view to buffers whose contents match QUALIFIER." 1363 "Limit current view to buffers whose contents match QUALIFIER."
1190 (:description "content" 1364 (:description "content"
1191 :reader (read-from-minibuffer "Filter by content (regexp): ")) 1365 :reader (read-from-minibuffer "Filter by content (regexp): "))
1192 (with-current-buffer buf 1366 (with-current-buffer buf
@@ -1196,12 +1370,33 @@ currently used by buffers."
1196 1370
1197;;;###autoload (autoload 'ibuffer-filter-by-predicate "ibuf-ext") 1371;;;###autoload (autoload 'ibuffer-filter-by-predicate "ibuf-ext")
1198(define-ibuffer-filter predicate 1372(define-ibuffer-filter predicate
1199 "Toggle current view to buffers for which QUALIFIER returns non-nil." 1373 "Limit current view to buffers for which QUALIFIER returns non-nil."
1200 (:description "predicate" 1374 (:description "predicate"
1201 :reader (read-minibuffer "Filter by predicate (form): ")) 1375 :reader (read-minibuffer "Filter by predicate (form): "))
1202 (with-current-buffer buf 1376 (with-current-buffer buf
1203 (eval qualifier))) 1377 (eval qualifier)))
1204 1378
1379;;;###autoload (autoload 'ibuffer-filter-chosen-by-completion "ibuf-ext")
1380(defun ibuffer-filter-chosen-by-completion ()
1381 "Select and apply filter chosen by completion against available filters.
1382Indicates corresponding key sequences in echo area after filtering.
1383
1384The completion matches against the filter description text of
1385each filter in `ibuffer-filtering-alist'."
1386 (interactive)
1387 (let* ((filters (mapcar (lambda (x) (cons (cadr x) (car x)))
1388 ibuffer-filtering-alist))
1389 (match (completing-read "Filter by: " filters nil t))
1390 (filter (cdr (assoc match filters)))
1391 (command (intern (concat "ibuffer-filter-by-" (symbol-name filter)))))
1392 (call-interactively command)
1393 (message "%s can be run with key sequences: %s"
1394 command
1395 (mapconcat #'key-description
1396 (where-is-internal command ibuffer-mode-map nil t)
1397 "or "))))
1398
1399
1205;;; Sorting 1400;;; Sorting
1206 1401
1207;;;###autoload 1402;;;###autoload
diff --git a/lisp/ibuffer.el b/lisp/ibuffer.el
index 94cee329d5a..5a740845bdf 100644
--- a/lisp/ibuffer.el
+++ b/lisp/ibuffer.el
@@ -518,26 +518,37 @@ directory, like `default-directory'."
518 (define-key map (kbd "s f") 'ibuffer-do-sort-by-filename/process) 518 (define-key map (kbd "s f") 'ibuffer-do-sort-by-filename/process)
519 (define-key map (kbd "s m") 'ibuffer-do-sort-by-major-mode) 519 (define-key map (kbd "s m") 'ibuffer-do-sort-by-major-mode)
520 520
521 (define-key map (kbd "/ RET") 'ibuffer-filter-by-mode)
521 (define-key map (kbd "/ m") 'ibuffer-filter-by-used-mode) 522 (define-key map (kbd "/ m") 'ibuffer-filter-by-used-mode)
522 (define-key map (kbd "/ M") 'ibuffer-filter-by-derived-mode) 523 (define-key map (kbd "/ M") 'ibuffer-filter-by-derived-mode)
523 (define-key map (kbd "/ n") 'ibuffer-filter-by-name) 524 (define-key map (kbd "/ n") 'ibuffer-filter-by-name)
524 (define-key map (kbd "/ c") 'ibuffer-filter-by-content) 525 (define-key map (kbd "/ *") 'ibuffer-filter-by-starred-name)
525 (define-key map (kbd "/ e") 'ibuffer-filter-by-predicate)
526 (define-key map (kbd "/ f") 'ibuffer-filter-by-filename) 526 (define-key map (kbd "/ f") 'ibuffer-filter-by-filename)
527 (define-key map (kbd "/ >") 'ibuffer-filter-by-size-gt) 527 (define-key map (kbd "/ b") 'ibuffer-filter-by-basename)
528 (define-key map (kbd "/ .") 'ibuffer-filter-by-file-extension)
528 (define-key map (kbd "/ <") 'ibuffer-filter-by-size-lt) 529 (define-key map (kbd "/ <") 'ibuffer-filter-by-size-lt)
530 (define-key map (kbd "/ >") 'ibuffer-filter-by-size-gt)
531 (define-key map (kbd "/ i") 'ibuffer-filter-by-modified)
532 (define-key map (kbd "/ v") 'ibuffer-filter-by-visiting-file)
533 (define-key map (kbd "/ c") 'ibuffer-filter-by-content)
534 (define-key map (kbd "/ e") 'ibuffer-filter-by-predicate)
535
529 (define-key map (kbd "/ r") 'ibuffer-switch-to-saved-filters) 536 (define-key map (kbd "/ r") 'ibuffer-switch-to-saved-filters)
530 (define-key map (kbd "/ a") 'ibuffer-add-saved-filters) 537 (define-key map (kbd "/ a") 'ibuffer-add-saved-filters)
531 (define-key map (kbd "/ x") 'ibuffer-delete-saved-filters) 538 (define-key map (kbd "/ x") 'ibuffer-delete-saved-filters)
532 (define-key map (kbd "/ d") 'ibuffer-decompose-filter) 539 (define-key map (kbd "/ d") 'ibuffer-decompose-filter)
533 (define-key map (kbd "/ s") 'ibuffer-save-filters) 540 (define-key map (kbd "/ s") 'ibuffer-save-filters)
534 (define-key map (kbd "/ p") 'ibuffer-pop-filter) 541 (define-key map (kbd "/ p") 'ibuffer-pop-filter)
542 (define-key map (kbd "/ <up>") 'ibuffer-pop-filter)
535 (define-key map (kbd "/ !") 'ibuffer-negate-filter) 543 (define-key map (kbd "/ !") 'ibuffer-negate-filter)
536 (define-key map (kbd "/ t") 'ibuffer-exchange-filters) 544 (define-key map (kbd "/ t") 'ibuffer-exchange-filters)
537 (define-key map (kbd "/ TAB") 'ibuffer-exchange-filters) 545 (define-key map (kbd "/ TAB") 'ibuffer-exchange-filters)
538 (define-key map (kbd "/ o") 'ibuffer-or-filter) 546 (define-key map (kbd "/ o") 'ibuffer-or-filter)
547 (define-key map (kbd "/ |") 'ibuffer-or-filter)
548 (define-key map (kbd "/ &") 'ibuffer-and-filter)
539 (define-key map (kbd "/ g") 'ibuffer-filters-to-filter-group) 549 (define-key map (kbd "/ g") 'ibuffer-filters-to-filter-group)
540 (define-key map (kbd "/ P") 'ibuffer-pop-filter-group) 550 (define-key map (kbd "/ P") 'ibuffer-pop-filter-group)
551 (define-key map (kbd "/ S-<up>") 'ibuffer-pop-filter-group)
541 (define-key map (kbd "/ D") 'ibuffer-decompose-filter-group) 552 (define-key map (kbd "/ D") 'ibuffer-decompose-filter-group)
542 (define-key map (kbd "/ /") 'ibuffer-filter-disable) 553 (define-key map (kbd "/ /") 'ibuffer-filter-disable)
543 554
@@ -657,13 +668,43 @@ directory, like `default-directory'."
657 ibuffer-filter-by-derived-mode)) 668 ibuffer-filter-by-derived-mode))
658 (define-key-after map [menu-bar view filter filter-by-name] 669 (define-key-after map [menu-bar view filter filter-by-name]
659 '(menu-item "Add filter by buffer name..." ibuffer-filter-by-name)) 670 '(menu-item "Add filter by buffer name..." ibuffer-filter-by-name))
671 (define-key-after map [menu-bar view filter filter-by-starred-name]
672 '(menu-item "Add filter by starred buffer name..."
673 ibuffer-filter-by-starred-name
674 :help "List buffers whose names begin with a star"))
660 (define-key-after map [menu-bar view filter filter-by-filename] 675 (define-key-after map [menu-bar view filter filter-by-filename]
661 '(menu-item "Add filter by filename..." ibuffer-filter-by-filename)) 676 '(menu-item "Add filter by full filename..." ibuffer-filter-by-filename
677 :help
678 (concat "For a buffer associated with file '/a/b/c.d', "
679 "list buffer if a given pattern matches '/a/b/c.d'")))
680 (define-key-after map [menu-bar view filter filter-by-basename]
681 '(menu-item "Add filter by file basename..."
682 ibuffer-filter-by-basename
683 :help (concat "For a buffer associated with file '/a/b/c.d', "
684 "list buffer if a given pattern matches 'c.d'")))
685 (define-key-after map [menu-bar view filter filter-by-file-extension]
686 '(menu-item "Add filter by file name extension..."
687 ibuffer-filter-by-file-extension
688 :help (concat "For a buffer associated with file '/a/b/c.d', "
689 "list buffer if a given pattern matches 'd'")))
690 (define-key-after map [menu-bar view filter filter-by-directory]
691 '(menu-item "Add filter by filename's directory..."
692 ibuffer-filter-by-directory
693 :help
694 (concat "For a buffer associated with file '/a/b/c.d', "
695 "list buffer if a given pattern matches '/a/b'")))
662 (define-key-after map [menu-bar view filter filter-by-size-lt] 696 (define-key-after map [menu-bar view filter filter-by-size-lt]
663 '(menu-item "Add filter by size less than..." ibuffer-filter-by-size-lt)) 697 '(menu-item "Add filter by size less than..." ibuffer-filter-by-size-lt))
664 (define-key-after map [menu-bar view filter filter-by-size-gt] 698 (define-key-after map [menu-bar view filter filter-by-size-gt]
665 '(menu-item "Add filter by size greater than..." 699 '(menu-item "Add filter by size greater than..."
666 ibuffer-filter-by-size-gt)) 700 ibuffer-filter-by-size-gt))
701 (define-key-after map [menu-bar view filter filter-by-modified]
702 '(menu-item "Add filter by modified buffer" ibuffer-filter-by-modified
703 :help "List buffers that are marked as modified"))
704 (define-key-after map [menu-bar view filter filter-by-visiting-file]
705 '(menu-item "Add filter by buffer visiting a file"
706 ibuffer-filter-by-visiting-file
707 :help "List buffers that are visiting files"))
667 (define-key-after map [menu-bar view filter filter-by-content] 708 (define-key-after map [menu-bar view filter filter-by-content]
668 '(menu-item "Add filter by content (regexp)..." 709 '(menu-item "Add filter by content (regexp)..."
669 ibuffer-filter-by-content)) 710 ibuffer-filter-by-content))
@@ -673,6 +714,12 @@ directory, like `default-directory'."
673 (define-key-after map [menu-bar view filter pop-filter] 714 (define-key-after map [menu-bar view filter pop-filter]
674 '(menu-item "Remove top filter" ibuffer-pop-filter 715 '(menu-item "Remove top filter" ibuffer-pop-filter
675 :enable (and (featurep 'ibuf-ext) ibuffer-filtering-qualifiers))) 716 :enable (and (featurep 'ibuf-ext) ibuffer-filtering-qualifiers)))
717 (define-key-after map [menu-bar view filter and-filter]
718 '(menu-item "AND top two filters" ibuffer-and-filter
719 :enable (and (featurep 'ibuf-ext) ibuffer-filtering-qualifiers
720 (cdr ibuffer-filtering-qualifiers))
721 :help
722 "Create a new filter which is the logical AND of the top two filters"))
676 (define-key-after map [menu-bar view filter or-filter] 723 (define-key-after map [menu-bar view filter or-filter]
677 '(menu-item "OR top two filters" ibuffer-or-filter 724 '(menu-item "OR top two filters" ibuffer-or-filter
678 :enable (and (featurep 'ibuf-ext) ibuffer-filtering-qualifiers 725 :enable (and (featurep 'ibuf-ext) ibuffer-filtering-qualifiers
diff --git a/test/lisp/ibuffer-tests.el b/test/lisp/ibuffer-tests.el
index 92ed101e6bf..40760abd96a 100644
--- a/test/lisp/ibuffer-tests.el
+++ b/test/lisp/ibuffer-tests.el
@@ -24,7 +24,8 @@
24 (require 'ibuf-macs)) 24 (require 'ibuf-macs))
25 25
26(ert-deftest ibuffer-autoload () 26(ert-deftest ibuffer-autoload ()
27 "Tests to see whether reftex-auc has been autoloaded" 27 "Tests to see whether ibuffer has been autoloaded"
28 (skip-unless (not (featurep 'ibuf-ext)))
28 (should 29 (should
29 (fboundp 'ibuffer-mark-unsaved-buffers)) 30 (fboundp 'ibuffer-mark-unsaved-buffers))
30 (should 31 (should
@@ -138,5 +139,669 @@
138 (should-not ibuffer-filtering-qualifiers)) 139 (should-not ibuffer-filtering-qualifiers))
139 (setq ibuffer-filtering-qualifiers filters)))) 140 (setq ibuffer-filtering-qualifiers filters))))
140 141
142;; Test Filter Inclusion
143(let* (test-buffer-list ; accumulated buffers to clean up
144 ;; Utility functions without polluting the environment
145 (set-buffer-mode
146 (lambda (buffer mode)
147 "Set BUFFER's major mode to MODE, a mode function, or fundamental."
148 (with-current-buffer buffer
149 (funcall (or mode #'fundamental-mode)))))
150 (set-buffer-contents
151 (lambda (buffer size include-content)
152 "Add exactly SIZE bytes to BUFFER, including INCLUDE-CONTENT."
153 (when (or size include-content)
154 (let* ((unit "\n")
155 (chunk "ccccccccccccccccccccccccccccccc\n")
156 (chunk-size (length chunk))
157 (size (if (and size include-content (stringp include-content))
158 (- size (length include-content))
159 size)))
160 (unless (or (null size) (> size 0))
161 (error "size argument must be nil or positive"))
162 (with-current-buffer buffer
163 (when include-content
164 (insert include-content))
165 (when size
166 (dotimes (_ (floor size chunk-size))
167 (insert chunk))
168 (dotimes (_ (mod size chunk-size))
169 (insert unit)))
170 ;; prevent query on cleanup
171 (set-buffer-modified-p nil))))))
172 (create-file-buffer
173 (lambda (prefix &rest args-plist)
174 "Create a file and buffer with designated properties.
175 PREFIX is a string giving the beginning of the name, and ARGS-PLIST
176 is a series of keyword-value pairs, with allowed keywords
177 :suffix STRING, :size NUMBER, :mode MODE-FUNC, :include-content STRING.
178 Returns the created buffer."
179 (let* ((suffix (plist-get args-plist :suffix))
180 (size (plist-get args-plist :size))
181 (include (plist-get args-plist :include-content))
182 (mode (plist-get args-plist :mode))
183 (file (make-temp-file prefix nil suffix))
184 (buf (find-file-noselect file t)))
185 (push buf test-buffer-list) ; record for cleanup
186 (funcall set-buffer-mode buf mode)
187 (funcall set-buffer-contents buf size include)
188 buf)))
189 (create-non-file-buffer
190 (lambda (prefix &rest args-plist)
191 "Create a non-file and buffer with designated properties.
192 PREFIX is a string giving the beginning of the name, and ARGS-PLIST
193 is a series of keyword-value pairs, with allowed keywords
194 :size NUMBER, :mode MODE-FUNC, :include-content STRING.
195 Returns the created buffer."
196 (let* ((size (plist-get args-plist :size))
197 (include (plist-get args-plist :include-content))
198 (mode (plist-get args-plist :mode))
199 (buf (generate-new-buffer prefix)))
200 (push buf test-buffer-list) ; record for cleanup
201 (funcall set-buffer-mode buf mode)
202 (funcall set-buffer-contents buf size include)
203 buf)))
204 (clean-up
205 (lambda ()
206 "Restore all emacs state modified during the tests"
207 (while test-buffer-list ; created temporary buffers
208 (let ((buf (pop test-buffer-list)))
209 (with-current-buffer buf (bury-buffer)) ; ensure not selected
210 (kill-buffer buf))))))
211 ;; Tests
212 (ert-deftest ibuffer-filter-inclusion-1 ()
213 "Tests inclusion using basic filter combinators with a single buffer."
214 (skip-unless (featurep 'ibuf-ext))
215 (unwind-protect
216 (let ((buf
217 (funcall create-file-buffer "ibuf-test-1" :size 100
218 :include-content "One ring to rule them all\n")))
219 (should (ibuffer-included-in-filters-p buf '((size-gt . 99))))
220 (should (ibuffer-included-in-filters-p buf '((size-lt . 101))))
221 (should (ibuffer-included-in-filters-p
222 buf '((mode . fundamental-mode))))
223 (should (ibuffer-included-in-filters-p
224 buf '((content . "ring to rule them all"))))
225 (should (ibuffer-included-in-filters-p
226 buf '((and (content . "ring to rule them all")))))
227 (should (ibuffer-included-in-filters-p
228 buf '((and (and (content . "ring to rule them all"))))))
229 (should (ibuffer-included-in-filters-p
230 buf '((and (and (and (content . "ring to rule them all")))))))
231 (should (ibuffer-included-in-filters-p
232 buf '((or (content . "ring to rule them all")))))
233 (should (ibuffer-included-in-filters-p
234 buf '((not (not (content . "ring to rule them all"))))))
235 (should (ibuffer-included-in-filters-p
236 buf '((and (size-gt . 99)
237 (content . "ring to rule them all")
238 (mode . fundamental-mode)
239 (basename . "\\`ibuf-test-1")))))
240 (should (ibuffer-included-in-filters-p
241 buf '((not (or (not (size-gt . 99))
242 (not (content . "ring to rule them all"))
243 (not (mode . fundamental-mode))
244 (not (basename . "\\`ibuf-test-1")))))))
245 (should (ibuffer-included-in-filters-p
246 buf '((and (or (size-gt . 99) (size-lt . 10))
247 (and (content . "ring.*all")
248 (content . "rule")
249 (content . "them all")
250 (content . "One"))
251 (not (mode . text-mode))
252 (basename . "\\`ibuf-test-1"))))))
253 (funcall clean-up)))
254
255 (ert-deftest ibuffer-filter-inclusion-2 ()
256 "Tests inclusion of basic filters in combination on a single buffer."
257 (skip-unless (featurep 'ibuf-ext))
258 (unwind-protect
259 (let ((buf
260 (funcall create-file-buffer "ibuf-test-2" :size 200
261 :mode #'text-mode
262 :include-content "and in the darkness find them\n")))
263 (should (ibuffer-included-in-filters-p buf '((size-gt . 199))))
264 (should (ibuffer-included-in-filters-p buf '((size-lt . 201))))
265 (should (ibuffer-included-in-filters-p buf '((not size-gt . 200))))
266 (should (ibuffer-included-in-filters-p buf '((not (size-gt . 200)))))
267 (should (ibuffer-included-in-filters-p
268 buf '((and (size-gt . 199) (size-lt . 201)))))
269 (should (ibuffer-included-in-filters-p
270 buf '((or (size-gt . 199) (size-gt . 201)))))
271 (should (ibuffer-included-in-filters-p
272 buf '((or (size-gt . 201) (size-gt . 199)))))
273 (should (ibuffer-included-in-filters-p
274 buf '((size-gt . 199) (mode . text-mode)
275 (content . "darkness find them"))))
276 (should (ibuffer-included-in-filters-p
277 buf '((and (size-gt . 199) (mode . text-mode)
278 (content . "darkness find them")))))
279 (should (ibuffer-included-in-filters-p
280 buf '((not (or (not (size-gt . 199)) (not (mode . text-mode))
281 (not (content . "darkness find them")))))))
282 (should (ibuffer-included-in-filters-p
283 buf '((or (size-gt . 200) (content . "darkness find them")
284 (derived-mode . emacs-lisp-mode)))))
285 (should-not (ibuffer-included-in-filters-p
286 buf '((or (size-gt . 200) (content . "rule them all")
287 (derived-mode . emacs-lisp-mode))))))
288 (funcall clean-up)))
289
290 (ert-deftest ibuffer-filter-inclusion-3 ()
291 "Tests inclusion with filename filters on specified buffers."
292 (skip-unless (featurep 'ibuf-ext))
293 (unwind-protect
294 (let* ((bufA
295 (funcall create-file-buffer "ibuf-test-3.a" :size 50
296 :mode #'text-mode
297 :include-content "...but a multitude of drops?\n"))
298 (bufB
299 (funcall create-non-file-buffer "ibuf-test-3.b" :size 50
300 :mode #'text-mode
301 :include-content "...but a multitude of drops?\n"))
302 (dirA (with-current-buffer bufA default-directory))
303 (dirB (with-current-buffer bufB default-directory)))
304 (should (ibuffer-included-in-filters-p
305 bufA '((basename . "ibuf-test-3"))))
306 (should (ibuffer-included-in-filters-p
307 bufA '((basename . "test-3\\.a"))))
308 (should (ibuffer-included-in-filters-p
309 bufA '((file-extension . "a"))))
310 (should (ibuffer-included-in-filters-p
311 bufA (list (cons 'directory dirA))))
312 (should-not (ibuffer-included-in-filters-p
313 bufB '((basename . "ibuf-test-3"))))
314 (should-not (ibuffer-included-in-filters-p
315 bufB '((file-extension . "b"))))
316 (should (ibuffer-included-in-filters-p
317 bufB (list (cons 'directory dirB))))
318 (should (ibuffer-included-in-filters-p
319 bufA '((name . "ibuf-test-3"))))
320 (should (ibuffer-included-in-filters-p
321 bufB '((name . "ibuf-test-3")))))
322 (funcall clean-up)))
323
324 (ert-deftest ibuffer-filter-inclusion-4 ()
325 "Tests inclusion with various filters on a single buffer."
326 (skip-unless (featurep 'ibuf-ext))
327 (unwind-protect
328 (let ((buf
329 (funcall create-file-buffer "ibuf-test-4"
330 :mode #'emacs-lisp-mode :suffix ".el"
331 :include-content "(message \"--%s--\" 'emacs-rocks)\n")))
332 (should (ibuffer-included-in-filters-p
333 buf '((file-extension . "el"))))
334 (should (ibuffer-included-in-filters-p
335 buf '((derived-mode . prog-mode))))
336 (should (ibuffer-included-in-filters-p
337 buf '((used-mode . emacs-lisp-mode))))
338 (should (ibuffer-included-in-filters-p
339 buf '((mode . emacs-lisp-mode))))
340 (with-current-buffer buf (set-buffer-modified-p t))
341 (should (ibuffer-included-in-filters-p buf '((modified))))
342 (with-current-buffer buf (set-buffer-modified-p nil))
343 (should (ibuffer-included-in-filters-p buf '((not modified))))
344 (should (ibuffer-included-in-filters-p
345 buf '((and (file-extension . "el")
346 (derived-mode . prog-mode)
347 (not modified)))))
348 (should (ibuffer-included-in-filters-p
349 buf '((or (file-extension . "tex")
350 (derived-mode . prog-mode)
351 (modified)))))
352 (should (ibuffer-included-in-filters-p
353 buf '((file-extension . "el")
354 (derived-mode . prog-mode)
355 (not modified)))))
356 (funcall clean-up)))
357
358 (ert-deftest ibuffer-filter-inclusion-5 ()
359 "Tests inclusion with various filters on a single buffer."
360 (skip-unless (featurep 'ibuf-ext))
361 (unwind-protect
362 (let ((buf
363 (funcall create-non-file-buffer "ibuf-test-5.el"
364 :mode #'emacs-lisp-mode
365 :include-content
366 "(message \"--%s--\" \"It really does!\")\n")))
367 (should-not (ibuffer-included-in-filters-p
368 buf '((file-extension . "el"))))
369 (should (ibuffer-included-in-filters-p
370 buf '((size-gt . 18))))
371 (should (ibuffer-included-in-filters-p
372 buf '((predicate . (lambda ()
373 (> (- (point-max) (point-min)) 18))))))
374 (should (ibuffer-included-in-filters-p
375 buf '((and (mode . emacs-lisp-mode)
376 (or (starred-name)
377 (size-gt . 18))
378 (and (not (size-gt . 100))
379 (content . "[Ii]t *really does!")
380 (or (name . "test-5")
381 (not (filename . "test-5")))))))))
382 (funcall clean-up)))
383
384 (ert-deftest ibuffer-filter-inclusion-6 ()
385 "Tests inclusion using saved filters and DeMorgan's laws."
386 (skip-unless (featurep 'ibuf-ext))
387 (unwind-protect
388 (let ((buf
389 (funcall create-non-file-buffer "*ibuf-test-6*" :size 65
390 :mode #'text-mode))
391 (buf2
392 (funcall create-file-buffer "ibuf-test-6a" :suffix ".html"
393 :mode #'html-mode
394 :include-content
395 "<HTML><BODY><H1>Hello, World!</H1></BODY></HTML>")))
396 (should (ibuffer-included-in-filters-p buf '((starred-name))))
397 (should-not (ibuffer-included-in-filters-p
398 buf '((saved . "text document"))))
399 (should (ibuffer-included-in-filters-p buf2 '((saved . "web"))))
400 (should (ibuffer-included-in-filters-p
401 buf2 '((not (and (not (derived-mode . sgml-mode))
402 (not (derived-mode . css-mode))
403 (not (mode . javascript-mode))
404 (not (mode . js2-mode))
405 (not (mode . scss-mode))
406 (not (derived-mode . haml-mode))
407 (not (mode . sass-mode)))))))
408 (should (ibuffer-included-in-filters-p
409 buf '((and (starred-name)
410 (or (size-gt . 50) (filename . "foo"))))))
411 (should (ibuffer-included-in-filters-p
412 buf '((not (or (not starred-name)
413 (and (size-lt . 51)
414 (not (filename . "foo")))))))))
415 (funcall clean-up)))
416
417 (ert-deftest ibuffer-filter-inclusion-7 ()
418 "Tests inclusion with various filters on a single buffer."
419 (skip-unless (featurep 'ibuf-ext))
420 (unwind-protect
421 (let ((buf
422 (funcall create-non-file-buffer "ibuf-test-7"
423 :mode #'artist-mode)))
424 (should (ibuffer-included-in-filters-p
425 buf '((not (starred-name)))))
426 (should (ibuffer-included-in-filters-p
427 buf '((not starred-name))))
428 (should (ibuffer-included-in-filters-p
429 buf '((not (not (not starred-name))))))
430 (should (ibuffer-included-in-filters-p
431 buf '((not (modified)))))
432 (should (ibuffer-included-in-filters-p
433 buf '((not modified))))
434 (should (ibuffer-included-in-filters-p
435 buf '((not (not (not modified)))))))
436 (funcall clean-up)))
437
438 (ert-deftest ibuffer-filter-inclusion-8 ()
439 "Tests inclusion with various filters."
440 (skip-unless (featurep 'ibuf-ext))
441 (unwind-protect
442 (let ((bufA
443 (funcall create-non-file-buffer "ibuf-test-8a"
444 :mode #'artist-mode))
445 (bufB (funcall create-non-file-buffer "*ibuf-test-8b*" :size 32))
446 (bufC (funcall create-file-buffer "ibuf-test8c" :suffix "*"
447 :size 64))
448 (bufD (funcall create-file-buffer "*ibuf-test8d" :size 128))
449 (bufE (funcall create-file-buffer "*ibuf-test8e" :suffix "*<2>"
450 :size 16))
451 (bufF (and (funcall create-non-file-buffer "*ibuf-test8f*")
452 (funcall create-non-file-buffer "*ibuf-test8f*"
453 :size 8))))
454 (with-current-buffer bufA (set-buffer-modified-p t))
455 (should (ibuffer-included-in-filters-p
456 bufA '((and (not starred-name)
457 (modified)
458 (name . "test-8")
459 (not (size-gt . 100))
460 (mode . picture-mode)))))
461 (with-current-buffer bufA (set-buffer-modified-p nil))
462 (should-not (ibuffer-included-in-filters-p
463 bufA '((or (starred-name) (visiting-file) (modified)))))
464 (should (ibuffer-included-in-filters-p
465 bufB '((and (starred-name)
466 (name . "test.*8b")
467 (size-gt . 31)
468 (not visiting-file)))))
469 (should (ibuffer-included-in-filters-p
470 bufC '((and (not (starred-name))
471 (visiting-file)
472 (name . "8c[^*]*\\*")
473 (size-lt . 65)))))
474 (should (ibuffer-included-in-filters-p
475 bufD '((and (not (starred-name))
476 (visiting-file)
477 (name . "\\`\\*.*test8d")
478 (size-lt . 129)
479 (size-gt . 127)))))
480 (should (ibuffer-included-in-filters-p
481 bufE '((and (starred-name)
482 (visiting-file)
483 (name . "8e.*?\\*<[[:digit:]]+>")
484 (size-gt . 10)))))
485 (should (ibuffer-included-in-filters-p
486 bufF '((and (starred-name)
487 (not (visiting-file))
488 (name . "8f\\*<[[:digit:]]>")
489 (size-lt . 10))))))
490 (funcall clean-up))))
491
492;; Test Filter Combination and Decomposition
493(let* (ibuffer-to-kill ; if non-nil, kill this buffer at cleanup
494 (ibuffer-already 'check) ; existing ibuffer buffer to use but not kill
495 ;; Utility functions without polluting the environment
496 (get-test-ibuffer
497 (lambda ()
498 "Returns a test ibuffer-mode buffer, creating one if necessary.
499 If a new buffer is created, it is named \"*Test-Ibuffer*\" and is
500 saved to `ibuffer-to-kill' for later cleanup."
501 (when (eq ibuffer-already 'check)
502 (setq ibuffer-already
503 (catch 'found-buf
504 (dolist (buf (buffer-list) nil)
505 (when (with-current-buffer buf
506 (derived-mode-p 'ibuffer-mode))
507 (throw 'found-buf buf))))))
508 (or ibuffer-already
509 ibuffer-to-kill
510 (let ((test-ibuf-name "*Test-Ibuffer*"))
511 (ibuffer nil test-ibuf-name nil t)
512 (setq ibuffer-to-kill (get-buffer test-ibuf-name))))))
513 (clean-up
514 (lambda ()
515 "Restore all emacs state modified during the tests"
516 (when ibuffer-to-kill ; created ibuffer
517 (with-current-buffer ibuffer-to-kill
518 (set-buffer-modified-p nil)
519 (bury-buffer))
520 (kill-buffer ibuffer-to-kill)
521 (setq ibuffer-to-kill nil))
522 (when (and ibuffer-already (not (eq ibuffer-already 'check)))
523 ;; restore existing ibuffer state
524 (ibuffer-update nil t)))))
525 ;; Tests
526 (ert-deftest ibuffer-decompose-filter ()
527 "Tests `ibuffer-decompose-filter' for and, or, not, and saved."
528 (skip-unless (featurep 'ibuf-ext))
529 (unwind-protect
530 (let ((ibuf (funcall get-test-ibuffer)))
531 (with-current-buffer ibuf
532 (let ((ibuffer-filtering-qualifiers nil)
533 (ibuffer-filter-groups nil)
534 (filters '((size-gt . 100) (not (starred-name))
535 (name . "foo"))))
536 (progn
537 (push (cons 'or filters) ibuffer-filtering-qualifiers)
538 (ibuffer-decompose-filter)
539 (should (equal filters ibuffer-filtering-qualifiers))
540 (setq ibuffer-filtering-qualifiers nil))
541 (progn
542 (push (cons 'and filters) ibuffer-filtering-qualifiers)
543 (ibuffer-decompose-filter)
544 (should (equal filters ibuffer-filtering-qualifiers))
545 (setq ibuffer-filtering-qualifiers nil))
546 (progn
547 (push (list 'not (car filters)) ibuffer-filtering-qualifiers)
548 (ibuffer-decompose-filter)
549 (should (equal (list (car filters))
550 ibuffer-filtering-qualifiers))
551 (setq ibuffer-filtering-qualifiers nil))
552 (progn
553 (push (cons 'not (car filters)) ibuffer-filtering-qualifiers)
554 (ibuffer-decompose-filter)
555 (should (equal (list (car filters))
556 ibuffer-filtering-qualifiers))
557 (setq ibuffer-filtering-qualifiers nil))
558 (let ((gnus (assoc "gnus" ibuffer-saved-filters)))
559 (push '(saved . "gnus") ibuffer-filtering-qualifiers)
560 (ibuffer-decompose-filter)
561 (should (equal (cdr gnus) ibuffer-filtering-qualifiers))
562 (ibuffer-decompose-filter)
563 (should (equal (cdr (cadr gnus)) ibuffer-filtering-qualifiers))
564 (setq ibuffer-filtering-qualifiers nil))
565 (when (not (assoc "__unknown__" ibuffer-saved-filters))
566 (push '(saved . "__uknown__") ibuffer-filtering-qualifiers)
567 (should-error (ibuffer-decompose-filter) :type 'error)
568 (setq ibuffer-filtering-qualifiers nil))
569 (progn
570 (push (car filters) ibuffer-filtering-qualifiers)
571 (should-error (ibuffer-decompose-filter) :type 'error)
572 (setq ibuffer-filtering-qualifiers nil)))))
573 (funcall clean-up)))
574
575 (ert-deftest ibuffer-and-filter ()
576 "Tests `ibuffer-and-filter' in an Ibuffer buffer."
577 (skip-unless (featurep 'ibuf-ext))
578 (unwind-protect
579 (let ((ibuf (funcall get-test-ibuffer)))
580 (with-current-buffer ibuf
581 (let ((ibuffer-filtering-qualifiers nil)
582 (ibuffer-filter-groups nil)
583 (filters [(size-gt . 100) (not (starred-name))
584 (filename . "A") (mode . text-mode)]))
585 (should-error (ibuffer-and-filter) :type 'error)
586 (progn
587 (push (aref filters 1) ibuffer-filtering-qualifiers)
588 (should-error (ibuffer-and-filter) :type 'error))
589 (should (progn
590 (push (aref filters 0) ibuffer-filtering-qualifiers)
591 (ibuffer-and-filter)
592 (and (equal (list 'and (aref filters 0) (aref filters 1))
593 (car ibuffer-filtering-qualifiers))
594 (null (cdr ibuffer-filtering-qualifiers)))))
595 (should (progn
596 (ibuffer-and-filter 'decompose)
597 (and (equal (aref filters 0)
598 (pop ibuffer-filtering-qualifiers))
599 (equal (aref filters 1)
600 (pop ibuffer-filtering-qualifiers))
601 (null ibuffer-filtering-qualifiers))))
602 (should (progn
603 (push (list 'and (aref filters 2) (aref filters 3))
604 ibuffer-filtering-qualifiers)
605 (push (list 'and (aref filters 0) (aref filters 1))
606 ibuffer-filtering-qualifiers)
607 (ibuffer-and-filter)
608 (and (equal (list 'and (aref filters 0) (aref filters 1)
609 (aref filters 2) (aref filters 3))
610 (car ibuffer-filtering-qualifiers))
611 (null (cdr ibuffer-filtering-qualifiers)))))
612 (pop ibuffer-filtering-qualifiers)
613 (should (progn
614 (push (list 'or (aref filters 2) (aref filters 3))
615 ibuffer-filtering-qualifiers)
616 (push (list 'and (aref filters 0) (aref filters 1))
617 ibuffer-filtering-qualifiers)
618 (ibuffer-and-filter)
619 (and (equal (list 'and (aref filters 0) (aref filters 1)
620 (list 'or (aref filters 2)
621 (aref filters 3)))
622 (car ibuffer-filtering-qualifiers))
623 (null (cdr ibuffer-filtering-qualifiers)))))
624 (pop ibuffer-filtering-qualifiers)
625 (should (progn
626 (push (list 'and (aref filters 2) (aref filters 3))
627 ibuffer-filtering-qualifiers)
628 (push (list 'or (aref filters 0) (aref filters 1))
629 ibuffer-filtering-qualifiers)
630 (ibuffer-and-filter)
631 (and (equal (list 'and (list 'or (aref filters 0)
632 (aref filters 1))
633 (aref filters 2) (aref filters 3))
634 (car ibuffer-filtering-qualifiers))
635 (null (cdr ibuffer-filtering-qualifiers)))))
636 (pop ibuffer-filtering-qualifiers)
637 (should (progn
638 (push (list 'or (aref filters 2) (aref filters 3))
639 ibuffer-filtering-qualifiers)
640 (push (list 'or (aref filters 0) (aref filters 1))
641 ibuffer-filtering-qualifiers)
642 (ibuffer-and-filter)
643 (and (equal (list 'and
644 (list 'or (aref filters 0)
645 (aref filters 1))
646 (list 'or (aref filters 2)
647 (aref filters 3)))
648 (car ibuffer-filtering-qualifiers))
649 (null (cdr ibuffer-filtering-qualifiers))))))))
650 (funcall clean-up)))
651
652 (ert-deftest ibuffer-or-filter ()
653 "Tests `ibuffer-or-filter' in an Ibuffer buffer."
654 (skip-unless (featurep 'ibuf-ext))
655 (unwind-protect
656 (let ((ibuf (funcall get-test-ibuffer)))
657 (with-current-buffer ibuf
658 (let ((ibuffer-filtering-qualifiers nil)
659 (ibuffer-filter-groups nil)
660 (filters [(size-gt . 100) (not (starred-name))
661 (filename . "A") (mode . text-mode)]))
662 (should-error (ibuffer-or-filter) :type 'error)
663 (progn
664 (push (aref filters 1) ibuffer-filtering-qualifiers)
665 (should-error (ibuffer-or-filter) :type 'error))
666 (should (progn
667 (push (aref filters 0) ibuffer-filtering-qualifiers)
668 (ibuffer-or-filter)
669 (and (equal (list 'or (aref filters 0) (aref filters 1))
670 (car ibuffer-filtering-qualifiers))
671 (null (cdr ibuffer-filtering-qualifiers)))))
672 (should (progn
673 (ibuffer-or-filter 'decompose)
674 (and (equal (aref filters 0)
675 (pop ibuffer-filtering-qualifiers))
676 (equal (aref filters 1)
677 (pop ibuffer-filtering-qualifiers))
678 (null ibuffer-filtering-qualifiers))))
679 (should (progn
680 (push (list 'or (aref filters 2) (aref filters 3))
681 ibuffer-filtering-qualifiers)
682 (push (list 'or (aref filters 0) (aref filters 1))
683 ibuffer-filtering-qualifiers)
684 (ibuffer-or-filter)
685 (and (equal (list 'or (aref filters 0) (aref filters 1)
686 (aref filters 2) (aref filters 3))
687 (car ibuffer-filtering-qualifiers))
688 (null (cdr ibuffer-filtering-qualifiers)))))
689 (pop ibuffer-filtering-qualifiers)
690 (should (progn
691 (push (list 'and (aref filters 2) (aref filters 3))
692 ibuffer-filtering-qualifiers)
693 (push (list 'or (aref filters 0) (aref filters 1))
694 ibuffer-filtering-qualifiers)
695 (ibuffer-or-filter)
696 (and (equal (list 'or (aref filters 0) (aref filters 1)
697 (list 'and (aref filters 2)
698 (aref filters 3)))
699 (car ibuffer-filtering-qualifiers))
700 (null (cdr ibuffer-filtering-qualifiers)))))
701 (pop ibuffer-filtering-qualifiers)
702 (should (progn
703 (push (list 'or (aref filters 2) (aref filters 3))
704 ibuffer-filtering-qualifiers)
705 (push (list 'and (aref filters 0) (aref filters 1))
706 ibuffer-filtering-qualifiers)
707 (ibuffer-or-filter)
708 (and (equal (list 'or (list 'and (aref filters 0)
709 (aref filters 1))
710 (aref filters 2) (aref filters 3))
711 (car ibuffer-filtering-qualifiers))
712 (null (cdr ibuffer-filtering-qualifiers)))))
713 (pop ibuffer-filtering-qualifiers)
714 (should (progn
715 (push (list 'and (aref filters 2) (aref filters 3))
716 ibuffer-filtering-qualifiers)
717 (push (list 'and (aref filters 0) (aref filters 1))
718 ibuffer-filtering-qualifiers)
719 (ibuffer-or-filter)
720 (and (equal (list 'or
721 (list 'and (aref filters 0)
722 (aref filters 1))
723 (list 'and (aref filters 2)
724 (aref filters 3)))
725 (car ibuffer-filtering-qualifiers))
726 (null (cdr ibuffer-filtering-qualifiers))))))))
727 (funcall clean-up))))
728
729(ert-deftest ibuffer-format-qualifier ()
730 "Tests string recommendation of filter from `ibuffer-format-qualifier'."
731 (skip-unless (featurep 'ibuf-ext))
732 (let ((test1 '(mode . org-mode))
733 (test2 '(size-lt . 100))
734 (test3 '(derived-mode . prog-mode))
735 (test4 '(or (size-gt . 10000)
736 (and (not (starred-name))
737 (directory . "\\<org\\>"))))
738 (test5 '(or (filename . "scratch")
739 (filename . "bonz")
740 (filename . "temp")))
741 (test6 '(or (mode . emacs-lisp-mode) (file-extension . "elc?")
742 (and (starred-name) (name . "elisp"))
743 (mode . lisp-interaction-mode)))
744 (description (lambda (q)
745 (cadr (assq q ibuffer-filtering-alist))))
746 (tag (lambda (&rest args )
747 (concat " [" (apply #'concat args) "]"))))
748 (should (equal (ibuffer-format-qualifier test1)
749 (funcall tag (funcall description 'mode)
750 ": " "org-mode")))
751 (should (equal (ibuffer-format-qualifier test2)
752 (funcall tag (funcall description 'size-lt)
753 ": " "100")))
754 (should (equal (ibuffer-format-qualifier test3)
755 (funcall tag (funcall description 'derived-mode)
756 ": " "prog-mode")))
757 (should (equal (ibuffer-format-qualifier test4)
758 (funcall tag "OR"
759 (funcall tag (funcall description 'size-gt)
760 ": " (format "%s" 10000))
761 (funcall tag "AND"
762 (funcall tag "NOT"
763 (funcall tag
764 (funcall description
765 'starred-name)
766 ": " "nil"))
767 (funcall tag
768 (funcall description 'directory)
769 ": " "\\<org\\>")))))
770 (should (equal (ibuffer-format-qualifier test5)
771 (funcall tag "OR"
772 (funcall tag (funcall description 'filename)
773 ": " "scratch")
774 (funcall tag (funcall description 'filename)
775 ": " "bonz")
776 (funcall tag (funcall description 'filename)
777 ": " "temp"))))
778 (should (equal (ibuffer-format-qualifier test6)
779 (funcall tag "OR"
780 (funcall tag (funcall description 'mode)
781 ": " "emacs-lisp-mode")
782 (funcall tag (funcall description 'file-extension)
783 ": " "elc?")
784 (funcall tag "AND"
785 (funcall tag
786 (funcall description 'starred-name)
787 ": " "nil")
788 (funcall tag
789 (funcall description 'name)
790 ": " "elisp"))
791 (funcall tag (funcall description 'mode)
792 ": " "lisp-interaction-mode"))))))
793
794(ert-deftest ibuffer-unary-operand ()
795 "Tests `ibuffer-unary-operand': (not cell) or (not . cell) -> cell."
796 (skip-unless (featurep 'ibuf-ext))
797 (should (equal (ibuffer-unary-operand '(not . (mode "foo")))
798 '(mode "foo")))
799 (should (equal (ibuffer-unary-operand '(not (mode "foo")))
800 '(mode "foo")))
801 (should (equal (ibuffer-unary-operand '(not "cdr"))
802 '("cdr")))
803 (should (equal (ibuffer-unary-operand '(not)) nil))
804 (should (equal (ibuffer-unary-operand '(not . a)) 'a)))
805
141(provide 'ibuffer-tests) 806(provide 'ibuffer-tests)
142;; ibuffer-tests.el ends here 807;; ibuffer-tests.el ends here