Commits

Anonymous committed 7347930

2001-07-14 Steve Youngs <youngs@xemacs.org>

* ibuffer.el: Replaced with newer version that was in
xemacs-devel.

2001-07-14 Kevin Gallagher <kevingal@onramp.net>

* Import version 4.0.

2001-06-06 Mark Thomas <mthomas@edrc.cmu.edu>

* nnmail.el (nnmail-fix-eudora-headers): Change the In-Reply-To
fix so it works with older versions of XEmacs.

2001-07-14 Steve Youngs <youngs@xemacs.org>

* emacsbug.el (report-emacs-bug-address): Change address to
xemacs-beta@xemacs.org.

2001-06-06 Raymond Toy <toy@rtp.ericsson.se>

* cvs-edit.el (cvs-edit-done): Check for a null vc-comment-ring
and initialize if necessary.

2001-06-21 Jeff Mincy <jeff@delphioutpost.com>

* diff-mode.el (vc-backend-diff): Fix when default-major-mode is text-mode,
also enabled font-lock

2001-07-14 I. Sheldon <is@kaidea.freeserve.co.uk>

* vc.el (vc-populate-vc-log-hook): New.
(vc-log-template): New.
(vc-start-entry): Use vc-populate-vc-log-hook.
(vc-populate-vc-log-with-template): New.
(vc-populate-vc-log-with-goto-first-eol): New.

2001-07-14 Steve Youngs <youngs@xemacs.org>

* ibuffer.el: Move to edit-utils package.

* Makefile (ELCS): Remove ibuffer.elc

2001-06-16 Simon Josefsson <jas@extundo.com>

* zenirc.el (zenirc-font-lock-keywords): Some font-lock defaults.
(toplevel): Put font-lock-defaults on mode.
(zenirc-mode): Allow font-lock initialization.

Comments (0)

Files changed (2)

+2001-07-14  Steve Youngs  <youngs@xemacs.org>
+
+	* ibuffer.el: Replaced with newer version that was in 
+	xemacs-devel.
+
 2001-07-10  Vin Shelton  <acs@xemacs.org>
 
 	* search-buffers.el: Remove trailing ^Ms.
 ;; Emacs Lisp Archive Entry
 ;; Author: Colin Walters <walters@cis.ohio-state.edu>
 ;; Created: 8 Sep 2000
-;; Version: 1.9
+;; Version: 2.1 (CVS)
 ;; X-RCS: $Id$
 ;; URL: http://www.cis.ohio-state.edu/~walters
 ;; Keywords: buffer, convenience
 ;;       ibuffer-elide-long-columns t)
 
 ;; Remember, you can switch between formats using
-;; `ibuffer-switch-format', bound to ` by default. 
+;; `ibuffer-switch-format', bound to ` by default.
 
 ;;; Change Log:
 
+;; Changes from 2.0 to 2.1
+
+;; Changes from 1.9 to 2.0
+
+;;  * Make "dangerous" operations actually operate when
+;;    `ibuffer-expert' is non-nil.  Thanks John Wiegley
+;;    <johnw@gnu.org> for catching this one!
+;;  * Attempt to fix `ibuffer-popup-menu' for XEmacs.  Thanks to
+;;    "Dr. Volker Zell" <Dr.Volker.Zell@oracle.com> for catching this.
+;;  * Limits are now a stack, instead of only being applicable once.
+;;    You can also OR them together now.  This means that you could,
+;;    for example, create an ibuffer buffer limited to two major
+;;    modes, as well as much fancier things.  See the updated
+;;    `ibuffer-mode' documentation for more details.  This creates the
+;;    following new user-visible functions: `ibuffer-pop-limit' and
+;;    `ibuffer-or-limits'.  Many internal ones were changed.
+;;  * Limits can now be saved and restored using mnemonic names, using
+;;    the functions `ibuffer-save-limits' and
+;;    `ibuffer-switch-to-saved-limits'.  The limits are stored in the
+;;    new variable `ibuffer-saved-limits', and can optionally be saved
+;;    permanently using Customize, if the new variable
+;;    `ibuffer-save-with-custom' is non-nil.
+;;  * The macro `ibuffer-define-limiter' no longer takes a useless
+;;    parameter.
+;;  * The function `ibuffer-unmark-all' now queries for which marks to
+;;    delete (like Dired).
+;;  * Fix testing for face availability on XEmacs.
+;;  * Don't move point after `ibuffer-popup-menu'.
+;;  * Allow setting `ibuffer-always-show-last-buffer' to :nomini.
+;;    (This one is a hack, hopefully to be implemented better in later
+;;    releases.)
+
 ;; Changes from 1.8 to 1.9
 
 ;;  * Use :value for `const' customization types.  Patch from Alastair
 (require 'easymenu)
 (require 'derived)
 (require 'font-lock)
+;; Needed for Emacs 20
+(unless (fboundp 'popup-menu)
+  (require 'lmenu))
 
 (eval-when-compile
   ;; From Emacs 21
                            str)
       str)))
 
+(if (fboundp 'delete-if)
+    (defalias 'ibuffer-delete-if 'delete-if)
+  (defun ibuffer-delete-if (test list)
+    "Remove by side effect all elements of LIST for which TEST returns non-nil."
+    (let ((beg list)
+	  (last list))
+      (while list
+	(when (funcall test (car list))
+	  (setcdr last (cdr list)))
+	(setq last list
+	      list (cdr list)))
+      beg)))
+
 ;; Emacs 20/XEmacs compatibility
 (cond ((fboundp 'make-temp-file)
        (defalias 'ibuffer-make-temp-file 'make-temp-file))
       (t
        (error "Couldn't make a suitable definition of `ibuffer-event-window'")))
 
+(if (fboundp 'find-face)
+    ;; XEmacs
+    (progn
+      (defun ibuffer-valid-face-name-p (name)
+	(find-face name))
+      (defalias 'ibuffer-find-face 'find-face))
+  ;; Emacs
+  (progn
+    (defun ibuffer-valid-face-name-p (name)
+      (facep name))
+    (defalias 'ibuffer-find-face 'identity)))
+
 (defgroup ibuffer nil
   "An advanced replacement for `buffer-menu'.
 
   :type '(repeat sexp)
   :group 'ibuffer)
 
+(defcustom ibuffer-saved-limits nil
+  "An alist of limiting qualifiers to switch between.
+
+This variable should look like ((\"STRING\" QUALIFIERS)
+                                (\"STRING\" QUALIFIERS) ...), where
+QUALIFIERS is a list of the same form as
+`ibuffer-limiting-qualifiers'.
+See also the variables `ibuffer-limiting-qualifiers',
+`ibuffer-limiting-alist', and the functions
+`ibuffer-switch-to-saved-limits', `ibuffer-save-limits'."
+  :type '(repeat sexp)
+  :group 'ibuffer)
+
+(defcustom ibuffer-save-with-custom t
+  "If non-nil, then use Custom to save interactively changed variables.
+Currently, this only applies to `ibuffer-saved-limits'."
+  :type 'boolean
+  :group 'ibuffer)
+
 (defcustom ibuffer-elide-long-columns t
   "If non-nil, then elide column entries which exceed their max length."
   :type 'boolean
   "If non-nil, always display the previous buffer.  This variable
 takes precedence over limiting, and even
 `ibuffer-never-show-regexps'."
-  :type 'boolean
+  :type '(choice (const :tag "Always" :value t)
+		 (const :tag "Never" :value nil)
+		 (const :tag "Always except minibuffer" :value :nomini))
   :group 'ibuffer)
 
 (defcustom ibuffer-dired-filenames t
   (append
    '(cond)
    (mapcar #'(lambda (x)
-	       `((facep ',x)
-		 ,x)) faces)
+	       `((ibuffer-valid-face-name-p ',x)
+		 (ibuffer-find-face ',x))) faces)
    `((t 'default))))
 
 (defface ibuffer-marked-face '((t (:foreground "green")))
     (define-key map (kbd "/ f") 'ibuffer-limit-by-filename)
     (define-key map (kbd "/ >") 'ibuffer-limit-by-size-gt)
     (define-key map (kbd "/ <") 'ibuffer-limit-by-size-lt)
+    (define-key map (kbd "/ r") 'ibuffer-switch-to-saved-limits)
+    (define-key map (kbd "/ s") 'ibuffer-save-limits)
+    (define-key map (kbd "/ p") 'ibuffer-pop-limit)
+    (define-key map (kbd "/ t") 'ibuffer-exchange-limits)
+    (define-key map (kbd "/ TAB") 'ibuffer-exchange-limits)
+    (define-key map (kbd "/ o") 'ibuffer-or-limit)
     (define-key map (kbd "/ /") 'ibuffer-limit-disable)
   
     (define-key map (kbd "q") 'ibuffer-quit)
     (define-key map (kbd "T") 'ibuffer-do-toggle-read-only)
     (define-key map (kbd "U") 'ibuffer-do-replace-regexp)
     (define-key map (kbd "V") 'ibuffer-do-revert)
-    (define-key map (kbd "B") 'ibuffer-do-bury)
     (define-key map (kbd "W") 'ibuffer-do-view-and-eval)
     (define-key map (kbd "X") 'ibuffer-do-shell-command)
   
       ["Limit by filename..." ibuffer-limit-by-filename t]
       ["Limit by size less than..." ibuffer-limit-by-size-lt t]
       ["Limit by size greater than..." ibuffer-limit-by-size-gt t]
-      ["Limit by content..." ibuffer-limit-by-content t])))
+      ["Limit by content..." ibuffer-limit-by-content t]
+      ["Save current limits..." ibuffer-save-limits t]
+      ["Restore saved limits..." ibuffer-switch-to-saved-limits t])))
 
 (unless ibuffer-mode-sort-menu
   (easy-menu-define
       ["Mark old buffers" ibuffer-mark-old-buffers t]
       ["Unmark All" ibuffer-unmark-all t])))
 
+(defvar ibuffer-operate-menu-data 
+  '("Operate"
+    ["View" ibuffer-do-view t]
+    ["View (separate frame)" ibuffer-do-view-other-frame t]
+    ["Save" ibuffer-do-save t]
+    ["Replace (regexp)..." ibuffer-do-replace-regexp t]
+    ["Query Replace..." ibuffer-do-query-replace t]
+    ["Query Replace (regexp)..." ibuffer-do-query-replace-regexp t]
+    ["Print" ibuffer-do-print t]
+    ["Revert" ibuffer-do-revert t]
+    ["Rename Uniquely" ibuffer-do-rename-uniquely t]
+    ["Kill" ibuffer-do-delete t]
+    ["List lines matching..." ibuffer-do-occur t]
+    ["Shell Command..." ibuffer-do-shell-command t]
+    ["Shell Command (replace)..." ibuffer-do-shell-command-replace t]
+    ["Eval..." ibuffer-do-eval t]
+    ["Eval (viewing buffer)..." ibuffer-do-view-and-eval t]))
+
 (unless ibuffer-mode-operate-menu
   (easy-menu-define
     ibuffer-mode-operate-menu ibuffer-mode-map ""
-    '("Operate"
-      ["View" ibuffer-do-view t]
-      ["View (separate frame)" ibuffer-do-view-other-frame t]
-      ["Save" ibuffer-do-save t]
-      ["Replace (regexp)..." ibuffer-do-replace-regexp t]
-      ["Query Replace..." ibuffer-do-query-replace t]
-      ["Query Replace (regexp)..." ibuffer-do-query-replace-regexp t]
-      ["Print" ibuffer-do-print t]
-      ["Revert" ibuffer-do-revert t]
-      ["Bury" ibuffer-do-bury t]
-      ["Rename Uniquely" ibuffer-do-rename-uniquely t]
-      ["Kill" ibuffer-do-delete t]
-      ["List lines matching..." ibuffer-do-occur t]
-      ["Shell Command..." ibuffer-do-shell-command t]
-      ["Shell Command (replace)..." ibuffer-do-shell-command-replace t]
-      ["Eval..." ibuffer-do-eval t]
-      ["Eval (viewing buffer)..." ibuffer-do-view-and-eval t])))
-
-(defvar ibuffer-popup-menu
-  '("Ibuffer"
-    ("Operate"
-     ("Save" . ibuffer-do-save)
-     ("Replace (regexp)..." . ibuffer-do-replace-regexp)
-     ("Query Replace..." . ibuffer-do-query-replace)
-     ("Query Replace Regexp..." . ibuffer-do-query-replace-regexp)
-     ("Print" . ibuffer-do-print)
-     ("Revert" . ibuffer-do-revert)
-     ("Rename Uniquely" . ibuffer-do-rename-uniquely)
-     ("Kill" . ibuffer-do-delete)
-     ("List lines matching..." . ibuffer-do-occur)
-     ("Shell Command..." . ibuffer-do-shell-command)
-     ("Shell Command (replace)..." . ibuffer-do-shell-command-replace)
-     ("Eval..." . ibuffer-do-eval)
-     ("Eval (viewing buffer)..." . ibuffer-do-view-and-eval))))
+    ibuffer-operate-menu-data))
+
+(defvar ibuffer-popup-menu ibuffer-operate-menu-data)
 
 ;;; Utility functions
 (defun ibuffer-columnize-and-insert-list (list &optional pad-width)
 (defun ibuffer-mouse-popup-menu (event)
   "Display a menu of operations."
   (interactive "e")
-  (let ((pt (ibuffer-event-position event)))
-    (unwind-protect
-	(save-excursion
-	  (setq buffer-read-only nil)
-	  (let ((choice (x-popup-menu t ibuffer-popup-menu)))
-	    (message "choice: %s" choice)
-	    (when choice
-	      (ibuffer-save-marks
-		;; hm.  we could probably do this in a better fashion
-		(ibuffer-unmark-all)
-		(setq buffer-read-only nil)
-		(goto-char pt)
-		(ibuffer-set-mark ibuffer-marked-char)
-		(setq buffer-read-only nil)
-		(call-interactively choice)
-		(setq buffer-read-only nil)))))
-      (setq buffer-read-only t))))
+  (let ((origline (count-lines (point-min) (point))))
+    (let ((pt (ibuffer-event-position event)))
+      (unwind-protect
+	  (progn
+	    (setq buffer-read-only nil)
+	    (ibuffer-save-marks
+	     ;; hm.  we could probably do this in a better fashion
+	     (ibuffer-unmark-all ?\r)
+	     (setq buffer-read-only nil)
+	     (goto-char pt)
+	     (ibuffer-set-mark ibuffer-marked-char)
+	     (setq buffer-read-only nil)
+	     (save-excursion
+	       (if (featurep 'xemacs)
+		   (popup-menu-and-execute-in-window
+		    ibuffer-popup-menu
+		    (selected-window))
+		 (popup-menu ibuffer-popup-menu)))))
+	(progn
+	  (setq buffer-read-only t)
+	  (goto-line (1+ origline)))))))
 
 (defun ibuffer-mouse-limit-by-mode (event)
   "Enable or disable limiting by the major mode chosen via mouse."
   (interactive "d")
   (goto-char point)
   (let ((buf (ibuffer-current-buffer)))
-    (ibuffer-limit-by-mode (if (ibuffer-get-qualifier 'mode)
-			       nil
-			     (with-current-buffer buf
-			       major-mode)))))
+    (if (assq 'mode ibuffer-limiting-qualifiers)
+	(setq ibuffer-limiting-qualifiers
+	      (ibuffer-delete-alist 'mode ibuffer-limiting-qualifiers))
+      (ibuffer-push-limit (cons 'mode 
+				(with-current-buffer buf
+				  major-mode)))))
+  (ibuffer-update nil t))
+
 (defun ibuffer-goto-beg ()
   (goto-char (point-min))
   (while (and (get-text-property (point) 'ibuffer-title)
   (interactive "P")
   (unless arg
     (setq arg 1))
+  (when (get-text-property (point) 'ibuffer-title)
+    (while (and (get-text-property (point) 'ibuffer-title)
+		(not (eobp)))
+      (forward-line 1))
+    (forward-line -1))
   (while (> arg 0)
     (forward-line 1)
     (when (eobp)
 
 (defmacro ibuffer-save-marks (&rest body)
   "Save the marked status of the buffers and execute BODY; restore marks."
-  `(let ((ibuffer-save-marks-tmp-mark-list (ibuffer-current-state-list)))
-     (unwind-protect
-	 (progn
-	   ,@body)
-       (ibuffer-insert-buffers-and-marks ibuffer-save-marks-tmp-mark-list))
-     (ibuffer-redisplay t)))
+  (let ((bufsym (gensym)))
+    `(let ((,bufsym (current-buffer))
+	   (ibuffer-save-marks-tmp-mark-list (ibuffer-current-state-list)))
+       (unwind-protect
+	   (progn
+	     (save-excursion
+	       ,@body))
+	 (with-current-buffer ,bufsym
+	   (ibuffer-insert-buffers-and-marks
+	    ;; Get rid of dead buffers
+	    (ibuffer-delete-if #'(lambda (e)
+				   (not (buffer-live-p (car e))))
+			       ibuffer-save-marks-tmp-mark-list))
+	   (ibuffer-redisplay t))))))
+
 ;; (put 'ibuffer-save-marks 'lisp-indent-function 0)
 
 (defun ibuffer-visit-buffer ()
 
 (defun ibuffer-confirm-operation-on (operation names)
   "Display a buffer asking whether to perform OPERATION on NAMES."
-  (unless ibuffer-expert
-    (if (= (length names) 1)
-	(yes-or-no-p (format "Really %s buffer %s? " operation (car names)))
-      (let ((buf (get-buffer-create "*Ibuffer confirmation*")))
-	(with-current-buffer buf
-	  (setq buffer-read-only nil)
-	  (erase-buffer)
-	  (ibuffer-columnize-and-insert-list names)
-	  (goto-char (point-min))
-	  (setq buffer-read-only t))
-	(let ((lastwin (car (last (ibuffer-window-list)))))
-	  ;; Now attempt to display the buffer...
-	  (save-window-excursion
-	    (select-window lastwin)
-	    ;; The window might be too small to split; in that case,
-	    ;; try a few times to increase its size before giving up.
-	    (let ((attempts 0)
-		  (trying t))
-	      (while trying
-		(condition-case err
-		    (progn
-		      (split-window)
-		      (setq trying nil))
-		  (error
-		   ;; Handle a failure
-		   (if (or (> (incf attempts) 4)
-			   (and (stringp (cadr err))
-				;; This definitely falls in the ghetto hack category...
-				(not (string-match "too small" (cadr err)))))
-		       (apply #'signal err)
-		     (enlarge-window 3))))))
-	    ;; This part doesn't work correctly sometimes under XEmacs.
-	    (select-window (next-window))
-	    (switch-to-buffer buf)
-	    (if (> (- (ibuffer-window-buffer-height (selected-window)) 2)
-		   (window-height))
+  (or ibuffer-expert
+      (if (= (length names) 1)
+	  (yes-or-no-p (format "Really %s buffer %s? " operation (car names)))
+	(let ((buf (get-buffer-create "*Ibuffer confirmation*")))
+	  (with-current-buffer buf
+	    (setq buffer-read-only nil)
+	    (erase-buffer)
+	    (ibuffer-columnize-and-insert-list names)
+	    (goto-char (point-min))
+	    (setq buffer-read-only t))
+	  (let ((lastwin (car (last (ibuffer-window-list)))))
+	    ;; Now attempt to display the buffer...
+	    (save-window-excursion
+	      (select-window lastwin)
+	      ;; The window might be too small to split; in that case,
+	     ;; try a few times to increase its size before giving up.
+	      (let ((attempts 0)
+		    (trying t))
+		(while trying
+		  (condition-case err
+		      (progn
+			(split-window)
+			(setq trying nil))
+		    (error
+		     ;; Handle a failure
+		     (if (or (> (incf attempts) 4)
+			     (and (stringp (cadr err))
+	       ;; This definitely falls in the ghetto hack category...
+				  (not (string-match "too small" (cadr err)))))
+			 (apply #'signal err)
+		       (enlarge-window 3))))))
+	   ;; This part doesn't work correctly sometimes under XEmacs.
+	      (select-window (next-window))
+	      (switch-to-buffer buf)
+	      (if (> (- (ibuffer-window-buffer-height (selected-window)) 2)
+		     (window-height))
+		  (unwind-protect
+		      (progn
+			(delete-other-windows)
+			(yes-or-no-p (format "Really %s %d buffers? "
+					     operation (length names))))
+		    (kill-buffer buf))
 		(unwind-protect
 		    (progn
-		      (delete-other-windows)
+		      (shrink-window-if-larger-than-buffer)
 		      (yes-or-no-p (format "Really %s %d buffers? "
 					   operation (length names))))
-		  (kill-buffer buf))
-	      (unwind-protect
-		  (progn
-		    (shrink-window-if-larger-than-buffer)
-		    (yes-or-no-p (format "Really %s %d buffers? "
-					 operation (length names))))
-		(delete-window)
-		(kill-buffer buf)))))))))
+		  (delete-window)
+		  (kill-buffer buf)))))))))
 
 (defmacro* ibuffer-define-op (op args
 				 (&key documentation
 		'kill))))
       (message "Killed %s lines" count))))
 
-(defun ibuffer-unmark-all ()
-  "Unmark all currently marked buffers."
-  (interactive)
-  (if (= (ibuffer-count-marked-lines) 0)
+(defun ibuffer-unmark-all (mark)
+  "Unmark all buffers with mark MARK."
+  (interactive "cRemove marks (RET means all):")
+  (if (= (ibuffer-count-marked-lines t) 0)
       (message "No buffers marked; use 'm' to mark a buffer")
-    (ibuffer-map-marked-lines
-     #'(lambda (buf mark beg end)
-	 (ibuffer-set-mark ? )
-	 t))))
+    (cond
+     ((char-equal mark ibuffer-marked-char)
+      (ibuffer-map-marked-lines
+       #'(lambda (buf mark beg end)
+	   (ibuffer-set-mark ? )
+	   t)))
+      ((char-equal mark ibuffer-deletion-char)
+       (ibuffer-map-deletion-lines
+	#'(lambda (buf mark beg end)
+	    (ibuffer-set-mark ? )
+	    t)))
+      (t
+       (ibuffer-map-lines
+	#'(lambda (buf mark beg end)
+	    (when (not (char-equal mark ? ))
+	      (ibuffer-set-mark ? ))
+	    t))))))
 
 (defun ibuffer-toggle-marks ()
   "Toggle which buffers are marked.
 		 ibuffer-buffer-names-with-mark-result))))
     ibuffer-buffer-names-with-mark-result))
 
-(defun ibuffer-count-marked-lines ()
-  (ibuffer-map-lines-nomodify
-   #'(lambda (buf mark beg end)
-       (eq mark ibuffer-marked-char))))
+(defun ibuffer-count-marked-lines (&optional all)
+  (if all
+      (ibuffer-map-lines-nomodify
+       #'(lambda (buf mark beg end)
+	   (not (eq mark ? ))))
+    (ibuffer-map-lines-nomodify
+     #'(lambda (buf mark beg end)
+	 (eq mark ibuffer-marked-char)))))
 
 (defun ibuffer-count-deletion-lines ()
   (ibuffer-map-lines-nomodify
   (assert (eq major-mode 'ibuffer-mode))
   (let ((curline (count-lines (point-min)
 			      (ibuffer-line-beginning-position)))
+	(deleted-lines-count 0)
 	(ibuffer-map-lines-total 0)
         (ibuffer-map-lines-count 0))
     (unwind-protect
                      ((eq result 'kill)
                       (delete-region (ibuffer-line-beginning-position)
                                      (1+ (ibuffer-line-end-position)))
+		      (incf deleted-lines-count)
                       (incf ibuffer-map-lines-count))
                      (t
                       (incf ibuffer-map-lines-count)
                (ibuffer-maybe-shrink-to-fit))
 	     (unless nomodify
 	       (set-buffer-modified-p nil))
-	     (goto-line (1+ curline))))))
+	     (goto-line (- (1+ curline) deleted-lines-count))))))
 
 (defun ibuffer-map-lines-nomodify (function)
   "As `ibuffer-map-lines', but don't set the modification flag."
 		(ibuffer-current-state-list))))
 
 (defun ibuffer-current-state-list ()
-  "Return a list like (BUF MARK) of all buffers in an ibuffer."
-  (let ((ibuffer-tmp-result-tmp '()))
+  "Return a list like (BUF . MARK) of all buffers in an ibuffer."
+  (let ((ibuffer-current-state-list-tmp '()))
     ;; ah, if only we had closures.  I bet this will mysteriously
     ;; break later.  Don't blame me.
     (ibuffer-map-lines-nomodify
      #'(lambda (buf mark beg end)
 	 (when (buffer-live-p buf)
-	   (push (cons buf mark) ibuffer-tmp-result-tmp))))
-    (nreverse ibuffer-tmp-result-tmp)))
+	   (push (cons buf mark) ibuffer-current-state-list-tmp))))
+    (nreverse ibuffer-current-state-list-tmp)))
 
 (defun ibuffer-canonicalize-state-list (bmarklist)
   "Order BMARKLIST in the same way as the current buffer list."
 			 (or ibuffer-view-ibuffer
 			     (not (eq ibuffer-buf buf)))
 			 (or
-			  (ibuffer-included-in-limits-p buf)
+			  (ibuffer-included-in-limits-p buf ibuffer-limiting-qualifiers)
 			  (ibuffer-name-matches-regexps
 			   name
 			   ibuffer-always-show-regexps))))
 		 e)))
 	 bmarklist)))
 
-(defun ibuffer-included-in-limits-p (buf)
+(defun ibuffer-included-in-limits-p (buf limits)
   (not
    (memq nil ;; a filter will return nil if it failed
 	 (mapcar
-	  ;; filter should be like (TYPE . QUALIFIER)
-	  #'(lambda (filter)
-	      ;; filterdat should be like (TYPE DESCRIPTION FUNC)
-	      (let ((filterdat (assq (car filter)
-				     ibuffer-limiting-alist)))
-		;; just a sanity check
-		(unless filterdat
-		  (ibuffer-limit-disable)
-		  (error "Undefined filter %s" (car filter)))
-		(funcall (caddr filterdat)
-			 buf
-			 (cdr filter))))
-	  ibuffer-limiting-qualifiers))))
+	  ;; filter should be like (TYPE . QUALIFIER), or
+	  ;; (or (TYPE . QUALIFIER) (TYPE . QUALIFIER) ...)
+	  #'(lambda (qual)
+	      (ibuffer-included-in-limit-p buf qual))
+	  limits))))
+
+(defun ibuffer-included-in-limit-p (buf filter)
+  (not
+   (not
+    (if (eq (car filter) 'or)
+	(memq t (mapcar #'(lambda (x)
+			  (ibuffer-included-in-limit-p buf x))
+			(cdr filter)))
+      (let ((filterdat (assq (car filter)
+			     ibuffer-limiting-alist)))
+	;; filterdat should be like (TYPE DESCRIPTION FUNC)
+	;; just a sanity check
+	(unless filterdat
+	  (ibuffer-limit-disable)
+	  (error "Undefined filter %s" (car filter)))
+	(not
+	 (not
+	  (funcall (caddr filterdat)
+		   buf
+		   (cdr filter)))))))))
 
 (defun ibuffer-limit-disable ()
   "Disable all limits currently in effect."
   (interactive)
   (setq ibuffer-limiting-qualifiers nil)
-  (ibuffer-update-mode-name)
+  (ibuffer-update nil t))
+
+(defun ibuffer-pop-limit ()
+  "Remove the top limit."
+  (interactive)
+  (when (null ibuffer-limiting-qualifiers)
+    (error "No limits in effect"))
+  (pop ibuffer-limiting-qualifiers)
+  (ibuffer-update nil t))
+
+(defun ibuffer-push-limit (qualifier)
+  "Add QUALIFIER to `ibuffer-limiting-qualifiers'."
+  (push qualifier ibuffer-limiting-qualifiers)
+  (ibuffer-update nil t))
+
+(defun ibuffer-exchange-limits ()
+  "Exchange the top two limits on the stack."
+  (interactive)
+  (when (< (length ibuffer-limiting-qualifiers)
+	   2)
+    (error "Need two limits to exchange"))
+  (let ((first (pop ibuffer-limiting-qualifiers))
+	(second (pop ibuffer-limiting-qualifiers)))
+    (push first ibuffer-limiting-qualifiers)
+    (push second ibuffer-limiting-qualifiers))
+  (ibuffer-update nil t))
+
+(defun ibuffer-or-limit (&optional reverse)
+  "Replace the top two limits with their logical OR.
+If optional argument REVERSE is non-nil, instead break the top OR
+limit into parts."
+  (interactive "P")
+  (if reverse
+      (progn
+	(when (or (null ibuffer-limiting-qualifiers)
+		  (not (eq 'or (caar ibuffer-limiting-qualifiers))))
+	  (error "Top limit is not an OR"))
+	(let ((lim (pop ibuffer-limiting-qualifiers)))
+	  (setq ibuffer-limiting-qualifiers (nconc (cdr lim) ibuffer-limiting-qualifiers))))
+    (when (< (length ibuffer-limiting-qualifiers) 2)
+      (error "Need two limits to OR"))
+    ;; If the second limit is an OR, just add to it.
+    (let ((first (pop ibuffer-limiting-qualifiers))
+	  (second (pop ibuffer-limiting-qualifiers)))
+      (if (eq 'or (car second))
+	  (push (nconc (list 'or first) (cdr second)) ibuffer-limiting-qualifiers)
+	(push (list 'or first second)
+	      ibuffer-limiting-qualifiers))))
+  (ibuffer-update nil t))
+
+(defun ibuffer-save-limits (name limits)
+  "Save LIMITS with name NAME in `ibuffer-saved-limits'.
+Interactively, prompt for NAME, and use the current limits."
+  (interactive
+   (if (null ibuffer-limiting-qualifiers)
+       (error "No limits currently in effect")
+     (list
+      (read-from-minibuffer "Save current limits as: ")
+      ibuffer-limiting-qualifiers)))
+  (ibuffer-aif (assoc name ibuffer-saved-limits)
+      (setcdr it limits)
+    (push (list name limits) ibuffer-saved-limits))
+  (when ibuffer-save-with-custom
+    (if (fboundp 'customize-save-variable)
+	(condition-case e
+	    (customize-save-variable 'ibuffer-saved-limits ibuffer-saved-limits)
+	  (error nil
+		 (message "Warning: unable to save limits: %s" (cdr e))))
+      (message "Warning: customize not available"))))
+
+(defun ibuffer-switch-to-saved-limits (name)
+  "Set the current limits to limits with NAME from `ibuffer-saved-limits'."
+  (interactive
+   (list
+    (if (null ibuffer-saved-limits)
+	(error "No saved limits")
+      (completing-read "Switch to saved limits: "
+		       ibuffer-saved-limits nil t))))
+  (setq ibuffer-limiting-qualifiers (cadr (assoc name ibuffer-saved-limits)))
   (ibuffer-update nil t))
 
 (defun ibuffer-update-mode-name ()
   (setq mode-name "Ibuffer")
-  (dolist (qual ibuffer-limiting-qualifiers)
-    (let ((type (assq (car qual) ibuffer-limiting-alist)))
-      (unless qual
-	(error "Ibuffer: bad qualifier %s" qual))
-      (setq mode-name (concat mode-name " ["
-			      (cadr type) ": " (format "%s]" (cdr qual)))))))
-
-(defun ibuffer-add-qualifier (type qual)
-  "Add a qualifier to the local variable `ibuffer-limiting-qualifiers'."
-  (setq ibuffer-limiting-qualifiers
-	(cons (cons type qual)
-	      ibuffer-limiting-qualifiers)))
-
-(defun ibuffer-remove-qualifier (type)
-  "Remove a qualifier from the local variable `ibuffer-limiting-qualifiers'."
-  (setq ibuffer-limiting-qualifiers
-	(ibuffer-delete-alist type
-			      ibuffer-limiting-qualifiers)))
-
-(defun ibuffer-get-qualifier (type)
-  "Retrieve the data associated with qualifier TYPE.
-See also `ibuffer-limiting-qualifiers'."
-  (cdr (assq type ibuffer-limiting-qualifiers)))
-
-(defmacro* ibuffer-define-limiter (name param (&key documentation
-						    reader
-						    description)
+  (dolist (qualifier ibuffer-limiting-qualifiers)
+    (setq mode-name
+	  (concat mode-name (ibuffer-format-qualifier qualifier)))))
+
+(defun ibuffer-format-qualifier (qualifier)
+  (if (eq 'or (car qualifier))
+      (concat " [OR" (mapconcat #'ibuffer-format-qualifier
+				(cdr qualifier) "") "]")
+    (let ((type (assq (car qualifier) ibuffer-limiting-alist)))
+      (unless qualifier
+	(error "Ibuffer: bad qualifier %s" qualifier))
+      (concat " [" (cadr type) ": " (format "%s]" (cdr qualifier))))))
+
+(defmacro* ibuffer-define-limiter (name (&key documentation
+					      reader
+					      description)
 					&rest body)
-  "Define a limitation named NAME, with parameter PARAM.
+  "Define a limitation named NAME.
 DOCUMENTATION is the documentation of the function.
 READER is a form which should read a qualifier from the user.
 DESCRIPTION is a short string describing the limitation.
 not a particular buffer should be displayed or not.  The forms in BODY
 will be evaluated with BUF bound to the buffer object, and QUALIFIER
 bound to the current value of the limitation."
-  `(progn
-     (defun ,(intern (concat "ibuffer-limit-by-" (symbol-name name))) (,param)
-       ,(concat (or documentation "")
-	      "\nTo disable the limit, call this function again.")
-       (interactive
-	(list
-	 (if (ibuffer-get-qualifier ',name)
-	     nil
-	   (progn
-	     ,reader))))
-       (cond (,param
-	      (ibuffer-add-qualifier ',name ,param)
-	      (message
-	       (format ,(concat (format "View limited by %s:"
-					description)
-				" %s")
-			       ,param)))
-	     (t
-	      (ibuffer-remove-qualifier ',name)
-	      (message ,(format "Limiting by %s disabled"
-				description))))
-       (ibuffer-update nil t))
-     (push (list ',name ,description
-		 #'(lambda (buf qualifier)
-		     ,@body))
-	   ibuffer-limiting-alist)))
+  (let ((fn-name (intern (concat "ibuffer-limit-by-" (symbol-name name)))))
+    `(progn 
+       (defun ,fn-name (qualifier)
+	 ,(concat (or documentation "This limit is not documented."))
+	 (interactive (list ,reader))
+	 (ibuffer-push-limit (cons ',name qualifier))
+	 (message
+	  (format ,(concat (format "Limit by %s added: " description)
+			   " %s")
+		  qualifier))
+	 (ibuffer-update nil t))
+       (push (list ',name ,description
+		   #'(lambda (buf qualifier)
+		       ,@body))
+	     ibuffer-limiting-alist))))
 
 ;; (put 'ibuffer-define-limiter 'lisp-indent-function 2)
 
-(ibuffer-define-limiter mode mode
+(ibuffer-define-limiter mode 
   (:documentation
-   "Toggle current view to buffers with major mode MODE."
+   "Toggle current view to buffers with major mode QUALIFIER."
    :description "major mode"
    :reader
    (intern
 			 "")))))
   (eq qualifier (with-current-buffer buf major-mode)))
 
-(ibuffer-define-limiter name regexp
+(ibuffer-define-limiter name 
   (:documentation
-   "Toggle current view to buffers with name matching REGEXP."
+   "Toggle current view to buffers with name matching QUALIFIER."
    :description "buffer name"
    :reader
    (read-from-minibuffer "Limit by name (regexp): "))
   (string-match qualifier (buffer-name buf)))
 
-(ibuffer-define-limiter filename regexp
+(ibuffer-define-limiter filename
   (:documentation
-   "Toggle current view to buffers with filename matching REGEXP."
+   "Toggle current view to buffers with filename matching QUALIFIER."
    :description "filename"
    :reader
    (read-from-minibuffer "Limit by filename (regexp): "))
   (ibuffer-awhen (buffer-file-name buf)
     (string-match qualifier it)))
 
-(ibuffer-define-limiter size-gt size
+(ibuffer-define-limiter size-gt 
   (:documentation
-   "Toggle current view to buffers with size greater than SIZE."
+   "Toggle current view to buffers with size greater than QUALIFIER."
    :description "size greater than"
    :reader
    (string-to-number (read-from-minibuffer "Limit by size greater than: ")))
   (> (with-current-buffer buf (buffer-size))
      qualifier))
 
-(ibuffer-define-limiter size-lt size
+(ibuffer-define-limiter size-lt 
   (:documentation
-   "Toggle current view to buffers with size less than SIZE."
+   "Toggle current view to buffers with size less than QUALIFIER."
    :description "size less than"
    :reader
    (string-to-number (read-from-minibuffer "Limit by size less than: ")))
   (< (with-current-buffer buf (buffer-size))
      qualifier))
   
-(ibuffer-define-limiter content regexp
+(ibuffer-define-limiter content
   (:documentation
-   "Toggle current view to buffers whose contents match REGEXP."
+   "Toggle current view to buffers whose contents match QUALIFIER."
    :description "content"
    :reader
    (read-from-minibuffer "Limit by content (regexp): "))
 iff arg ARG is non-nil.
 Do not display messages if SILENT is non-nil."
   (interactive "P")
-  (let ((blist (ibuffer-filter-buffers (current-buffer)
-				       (cadr (buffer-list))
-				       (ibuffer-current-buffers-with-marks)
-				       arg)))
+  (let* ((bufs (buffer-list))
+	 (blist (ibuffer-filter-buffers
+		(current-buffer)
+		(if (and
+		     (cadr bufs)
+		     (eq ibuffer-always-show-last-buffer
+			 :nomini)
+		     ;; This is a hack.
+		     (string-match " \\*Minibuf"
+				   (buffer-name (cadr bufs))))
+		    (caddr bufs)
+		  (cadr bufs))
+		(ibuffer-current-buffers-with-marks)
+		arg)))
     (when (null blist)
       (if ibuffer-limiting-qualifiers
 	  (message "No buffers! (note: limiting in effect)")
 
 Limiting commands:
 
-  '\\[ibuffer-limit-by-mode]' - Limit the view by major mode.
-  '\\[ibuffer-limit-by-name]' - Limit the view by buffer name.
-  '\\[ibuffer-limit-by-content]' - Limit the view by buffer content.
-  '\\[ibuffer-limit-by-filename]' - Limit the view by filename.
-  '\\[ibuffer-limit-by-size-gt]' - Limit the view by buffer size.
-  '\\[ibuffer-limit-by-size-lt]' - Limit the view by buffer size.
+  '\\[ibuffer-limit-by-mode]' - Add a limit by major mode.
+  '\\[ibuffer-limit-by-name]' - Add a limit by buffer name.
+  '\\[ibuffer-limit-by-content]' - Add a limit by buffer content.
+  '\\[ibuffer-limit-by-filename]' - Add a limit by filename.
+  '\\[ibuffer-limit-by-size-gt]' - Add a limit by buffer size.
+  '\\[ibuffer-limit-by-size-lt]' - Add a limit by buffer size.
+  '\\[ibuffer-save-limits]' - Save the current limits with a name.
+  '\\[ibuffer-switch-to-saved-limits]' - Switch to previously saved limits.
+  '\\[ibuffer-or-limits]' - Replace the top two limits with their logical OR.
+  '\\[ibuffer-pop-limit]' - Remove the top limit.
   '\\[ibuffer-limit-disable]' - Remove all limiting currently in effect.
     
 Sorting commands:
  You can limit your ibuffer view to a selection of the buffers, via
 different critera.  For example, suppose you are working on an Emacs
 Lisp project.  You can create an Ibuffer buffer which is limited to
-just `emacs-lisp' modes via '\\[ibuffer-limit-by-mode]
-emacs-lisp-mode RET'.
-
-To undo a limit, type the limit keybinding again.  For instance, you
-can undo the limit in the example above by typing '\\[ibuffer-limit-by-mode]' again.
-
-You can even combine limits: Suppose you only want to see buffers in
-`emacs-lisp' mode, whose names begin with \"gnus\".  You can
-accomplish this via: '\\[ibuffer-limit-by-mode] emacs-lisp-mode RET
-                      \\[ibuffer-limit-by-name] ^gnus RET'.
-
-If you wish to disable all limiting currently in effect, use
-  '\\[ibuffer-limit-disable]'."
+just `emacs-lisp' modes via '\\[ibuffer-limit-by-mode] emacs-lisp-mode RET'.
+
+You can combine limits, in a stack-like manner.  For example, suppose
+you only want to see buffers in `emacs-lisp' mode, whose names begin
+with \"gnus\".  You can accomplish this via:
+'\\[ibuffer-limit-by-mode] emacs-lisp-mode RET \\[ibuffer-limit-by-name] ^gnus RET'.
+
+Additionally, you can OR the top two limits together with
+'\\[ibuffer-or-limits]'.  To see all buffers in either
+`emacs-lisp-mode' or `lisp-interaction-mode', type:
+
+'\\[ibuffer-limit-by-mode] emacs-lisp-mode RET \\[ibuffer-limit-by-mode] lisp-interaction-mode RET \\[ibuffer-or-limits]'.
+
+Limits can also be saved and restored using mnemonic names: see the
+functions `ibuffer-save-limits' and `ibuffer-switch-to-saved-limits'.
+
+To remove the top limit on the stack, use '\\[ibuffer-pop-limit]', and
+to disable all limiting currently in effect, use
+'\\[ibuffer-limit-disable]'."
   (kill-all-local-variables)
   (use-local-map ibuffer-mode-map)
   (setq major-mode 'ibuffer-mode)