* admin/grammars/Makefile.in (bootstrap-clean): Don't delete Makefile,
[bpt/emacs.git] / lisp / ibuf-ext.el
index 6628a07..a3c5b06 100644 (file)
@@ -1,34 +1,32 @@
 ;;; ibuf-ext.el --- extensions for ibuffer
 
-;; Copyright (C) 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;; Copyright (C) 2000-2014 Free Software Foundation, Inc.
 
 ;; Author: Colin Walters <walters@verbum.org>
 ;; Maintainer: John Paul Wallington <jpw@gnu.org>
 ;; Created: 2 Dec 2001
 ;; Keywords: buffer, convenience
+;; Package: ibuffer
 
 ;; This file is part of GNU Emacs.
 
-;; This program is free software; you can redistribute it and/or
-;; modify it under the terms of the GNU General Public License as
-;; published by the Free Software Foundation; either version 2, or (at
-;; your option) any later version.
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
-;; This program is distributed in the hope that it will be useful, but
-;; WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-;; General Public License for more details.
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with this program ; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;; These functions should be automatically loaded when called, but you
-;; can explicity (require 'ibuf-ext) in your ~/.emacs to have them
+;; can explicitly (require 'ibuf-ext) in your ~/.emacs to have them
 ;; preloaded.
 
 ;;; Code:
@@ -37,7 +35,7 @@
 
 (eval-when-compile
   (require 'ibuf-macs)
-  (require 'cl))
+  (require 'cl-lib))
 
 ;;; Utility functions
 (defun ibuffer-delete-alist (key alist)
@@ -93,11 +91,6 @@ regardless of any active filters in this buffer."
 (defvar ibuffer-tmp-show-regexps nil
   "A list of regexps which should match buffer names to always show.")
 
-(defvar ibuffer-auto-mode nil
-  "If non-nil, Ibuffer auto-mode should be enabled for this buffer.
-Do not set this variable directly!  Use the function
-`ibuffer-auto-mode' instead.")
-
 (defvar ibuffer-auto-buffers-changed nil)
 
 (defcustom ibuffer-saved-filters '(("gnus"
@@ -180,8 +173,8 @@ QUALIFIERS is a list of the same form as
 
 See also the variables `ibuffer-filter-groups',
 `ibuffer-filtering-qualifiers', `ibuffer-filtering-alist', and the
-functions `ibuffer-switch-to-saved-filter-group',
-`ibuffer-save-filter-group'."
+functions `ibuffer-switch-to-saved-filter-groups',
+`ibuffer-save-filter-groups'."
   :type '(repeat sexp)
   :group 'ibuffer)
 
@@ -222,30 +215,26 @@ Currently, this only applies to `ibuffer-saved-filters' and
         (ibuffer-included-in-filters-p buf ibuffer-filtering-qualifiers)
         (ibuffer-buf-matches-predicates buf ibuffer-always-show-predicates)))))
 
-(defun ibuffer-auto-update-changed ()
-  (when (frame-or-buffer-changed-p 'ibuffer-auto-buffers-changed)
-    (mapcar #'(lambda (buf)
-               (ignore-errors
-                 (with-current-buffer buf
-                   (when (and ibuffer-auto-mode
-                              (eq major-mode 'ibuffer-mode))
-                     (ibuffer-update nil t)))))
-           (buffer-list))))
-
 ;;;###autoload
-(defun ibuffer-auto-mode (&optional arg)
-  "Toggle use of Ibuffer's auto-update facility.
-With numeric ARG, enable auto-update if and only if ARG is positive."
-  (interactive)
-  (unless (eq major-mode 'ibuffer-mode)
+(define-minor-mode ibuffer-auto-mode
+  "Toggle use of Ibuffer's auto-update facility (Ibuffer Auto mode).
+With a prefix argument ARG, enable Ibuffer Auto mode if ARG is
+positive, and disable it otherwise.  If called from Lisp, enable
+the mode if ARG is omitted or nil."
+  nil nil nil
+  (unless (derived-mode-p 'ibuffer-mode)
     (error "This buffer is not in Ibuffer mode"))
-  (set (make-local-variable 'ibuffer-auto-mode)
-       (if arg
-          (plusp arg)
-        (not ibuffer-auto-mode)))
-  (frame-or-buffer-changed-p 'ibuffer-auto-buffers-changed)
-  (add-hook 'post-command-hook 'ibuffer-auto-update-changed)
-  (ibuffer-update-mode-name))
+  (frame-or-buffer-changed-p 'ibuffer-auto-buffers-changed) ; Initialize state vector
+  (add-hook 'post-command-hook 'ibuffer-auto-update-changed))
+
+(defun ibuffer-auto-update-changed ()
+  (when (frame-or-buffer-changed-p 'ibuffer-auto-buffers-changed)
+    (dolist (buf (buffer-list))
+      (ignore-errors
+       (with-current-buffer buf
+         (when (and ibuffer-auto-mode
+                    (derived-mode-p 'ibuffer-mode))
+           (ibuffer-update nil t)))))))
 
 ;;;###autoload
 (defun ibuffer-mouse-filter-by-mode (event)
@@ -264,9 +253,7 @@ With numeric ARG, enable auto-update if and only if ARG is positive."
     (if (assq 'mode ibuffer-filtering-qualifiers)
        (setq ibuffer-filtering-qualifiers
              (ibuffer-delete-alist 'mode ibuffer-filtering-qualifiers))
-      (ibuffer-push-filter (cons 'mode
-                               (with-current-buffer buf
-                                 major-mode)))))
+      (ibuffer-push-filter (cons 'mode (buffer-local-value 'major-mode buf)))))
   (ibuffer-update nil t))
 
 ;;;###autoload
@@ -414,6 +401,24 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
    :modifier-p :maybe)
   (revert-buffer t t))
 
+;;;###autoload (autoload 'ibuffer-do-isearch "ibuf-ext")
+(define-ibuffer-op ibuffer-do-isearch ()
+  "Perform a `isearch-forward' in marked buffers."
+  (:interactive ()
+   :opstring "searched in"
+   :complex t
+   :modifier-p :maybe)
+  (multi-isearch-buffers (ibuffer-get-marked-buffers)))
+
+;;;###autoload (autoload 'ibuffer-do-isearch-regexp "ibuf-ext")
+(define-ibuffer-op ibuffer-do-isearch-regexp ()
+  "Perform a `isearch-forward-regexp' in marked buffers."
+  (:interactive ()
+   :opstring "searched regexp in"
+   :complex t
+   :modifier-p :maybe)
+  (multi-isearch-buffers-regexp (ibuffer-get-marked-buffers)))
+
 ;;;###autoload (autoload 'ibuffer-do-replace-regexp "ibuf-ext")
 (define-ibuffer-op replace-regexp (from-str to-str)
   "Perform a `replace-regexp' in marked buffers."
@@ -492,32 +497,26 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
 (defun ibuffer-included-in-filter-p-1 (buf filter)
   (not
    (not
-    (case (car filter)
-      (or
+    (pcase (car filter)
+      (`or
        (memq t (mapcar #'(lambda (x)
                           (ibuffer-included-in-filter-p buf x))
                       (cdr filter))))
-      (saved
+      (`saved
        (let ((data
              (assoc (cdr filter)
                     ibuffer-saved-filters)))
         (unless data
-          (ibuffer-filter-disable)
+          (ibuffer-filter-disable t)
           (error "Unknown saved filter %s" (cdr filter)))
         (ibuffer-included-in-filters-p buf (cadr data))))
-      (t
-       (let ((filterdat (assq (car filter)
-                             ibuffer-filtering-alist)))
-        ;; filterdat should be like (TYPE DESCRIPTION FUNC)
-        ;; just a sanity check
-       (unless filterdat
-         (ibuffer-filter-disable)
-         (error "Undefined filter %s" (car filter)))
-       (not
-        (not
-         (funcall (caddr filterdat)
-                  buf
-                  (cdr filter))))))))))
+      (_
+       (pcase-let ((`(,_type ,_desc ,func)
+                    (assq (car filter) ibuffer-filtering-alist)))
+         (unless func
+           (ibuffer-filter-disable t)
+           (error "Undefined filter %s" (car filter)))
+         (funcall func buf (cdr filter))))))))
 
 (defun ibuffer-generate-filter-groups (bmarklist &optional noempty nodefault)
   (let ((filter-group-alist (if nodefault
@@ -531,13 +530,14 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
          (i 0))
       (dolist (filtergroup filter-group-alist)
        (let ((filterset (cdr filtergroup)))
-         (multiple-value-bind (hip-crowd lamers)
-             (ibuffer-split-list (lambda (bufmark)
-                                   (ibuffer-included-in-filters-p (car bufmark)
-                                                                  filterset))
-                                 bmarklist)
+         (cl-multiple-value-bind (hip-crowd lamers)
+             (cl-values-list
+              (ibuffer-split-list (lambda (bufmark)
+                                    (ibuffer-included-in-filters-p (car bufmark)
+                                                                   filterset))
+                                  bmarklist))
            (aset vec i hip-crowd)
-           (incf i)
+           (cl-incf i)
            (setq bmarklist lamers))))
       (let (ret)
        (dotimes (j i ret)
@@ -565,8 +565,8 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
                   (cons (format "%s" mode) `((mode . ,mode))))
                 (let ((modes
                        (ibuffer-remove-duplicates
-                        (mapcar (lambda (buf) 
-                                 (with-current-buffer buf major-mode))
+                        (mapcar (lambda (buf)
+                                 (buffer-local-value 'major-mode buf))
                                 (buffer-list)))))
                   (if ibuffer-view-ibuffer
                      modes
@@ -604,7 +604,7 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
 ;;;###autoload
 (defun ibuffer-decompose-filter-group (group)
   "Decompose the filter group GROUP into active filters."
-  (interactive 
+  (interactive
    (list (ibuffer-read-filter-group-name "Decompose filter group: " t)))
   (let ((data (cdr (assoc group ibuffer-filter-groups))))
     (setq ibuffer-filter-groups (ibuffer-delete-alist
@@ -639,7 +639,7 @@ To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
 ;;;###autoload
 (defun ibuffer-jump-to-filter-group (name)
   "Move point to the filter group whose name is NAME."
-  (interactive 
+  (interactive
    (list (ibuffer-read-filter-group-name "Jump to filter group: ")))
   (ibuffer-aif (assoc name (ibuffer-current-filter-groups-with-position))
       (goto-char (cdr it))
@@ -683,7 +683,7 @@ See also `ibuffer-kill-filter-group'."
                  (if (equal (car groups) group)
                      (setq found t
                            groups nil)
-                   (incf res)
+                   (cl-incf res)
                    (setq groups (cdr groups))))
                res)))
     (cond ((not found)
@@ -732,8 +732,7 @@ prompt for NAME, and use the current filters."
   (ibuffer-aif (assoc name ibuffer-saved-filter-groups)
       (setcdr it groups)
     (push (cons name groups) ibuffer-saved-filter-groups))
-  (ibuffer-maybe-save-stuff)
-  (ibuffer-update-mode-name))
+  (ibuffer-maybe-save-stuff))
 
 ;;;###autoload
 (defun ibuffer-delete-saved-filter-groups (name)
@@ -753,24 +752,32 @@ They are removed from `ibuffer-saved-filter-groups'."
 ;;;###autoload
 (defun ibuffer-switch-to-saved-filter-groups (name)
   "Set this buffer's filter groups to saved version with NAME.
-The value from `ibuffer-saved-filters' is used.
-If prefix argument ADD is non-nil, then add the saved filters instead
-of replacing the current filters."
+The value from `ibuffer-saved-filter-groups' is used."
   (interactive
    (list
-    (if (null ibuffer-saved-filter-groups)
-       (error "No saved filters")
-      (completing-read "Switch to saved filter group: "
-                      ibuffer-saved-filter-groups nil t))))
+    (cond ((null ibuffer-saved-filter-groups)
+           (error "No saved filters"))
+          ;; `ibuffer-saved-filter-groups' is a user variable that defaults
+          ;; to nil.  We assume that with one element in this list the user
+          ;; knows what she wants.  See bug#12331.
+          ((null (cdr ibuffer-saved-filter-groups))
+           (caar ibuffer-saved-filter-groups))
+          (t
+           (completing-read "Switch to saved filter group: "
+                            ibuffer-saved-filter-groups nil t)))))
   (setq ibuffer-filter-groups (cdr (assoc name ibuffer-saved-filter-groups))
        ibuffer-hidden-filter-groups nil)
   (ibuffer-update nil t))
 
 ;;;###autoload
-(defun ibuffer-filter-disable ()
-  "Disable all filters currently in effect in this buffer."
+(defun ibuffer-filter-disable (&optional delete-filter-groups)
+  "Disable all filters currently in effect in this buffer.
+With optional arg DELETE-FILTER-GROUPS non-nil, delete all filter
+group definitions by setting `ibuffer-filter-groups' to nil."
   (interactive)
   (setq ibuffer-filtering-qualifiers nil)
+  (if delete-filter-groups
+      (setq ibuffer-filter-groups nil))
   (let ((buf (ibuffer-current-buffer)))
     (ibuffer-update nil t)
     (when buf
@@ -803,12 +810,12 @@ turned into two separate filters [name: foo] and [mode: bar-mode]."
   (when (null ibuffer-filtering-qualifiers)
     (error "No filters in effect"))
   (let ((lim (pop ibuffer-filtering-qualifiers)))
-    (case (car lim)
-      (or
+    (pcase (car lim)
+      (`or
        (setq ibuffer-filtering-qualifiers (append
                                          (cdr lim)
                                          ibuffer-filtering-qualifiers)))
-      (saved
+      (`saved
        (let ((data
              (assoc (cdr lim)
                     ibuffer-saved-filters)))
@@ -818,10 +825,10 @@ turned into two separate filters [name: foo] and [mode: bar-mode]."
         (setq ibuffer-filtering-qualifiers (append
                                            (cadr data)
                                            ibuffer-filtering-qualifiers))))
-      (not
+      (`not
        (push (cdr lim)
             ibuffer-filtering-qualifiers))
-      (t
+      (_
        (error "Filter type %s is not compound" (car lim)))))
   (ibuffer-update nil t))
 
@@ -863,7 +870,7 @@ filter into parts."
                  (not (eq 'or (caar ibuffer-filtering-qualifiers))))
          (error "Top filter is not an OR"))
        (let ((lim (pop ibuffer-filtering-qualifiers)))
-         (setq ibuffer-filtering-qualifiers 
+         (setq ibuffer-filtering-qualifiers
                (nconc (cdr lim) ibuffer-filtering-qualifiers))))
     (when (< (length ibuffer-filtering-qualifiers) 2)
       (error "Need two filters to OR"))
@@ -900,8 +907,7 @@ Interactively, prompt for NAME, and use the current filters."
   (ibuffer-aif (assoc name ibuffer-saved-filters)
       (setcdr it filters)
     (push (list name filters) ibuffer-saved-filters))
-  (ibuffer-maybe-save-stuff)
-  (ibuffer-update-mode-name))
+  (ibuffer-maybe-save-stuff))
 
 ;;;###autoload
 (defun ibuffer-delete-saved-filters (name)
@@ -931,9 +937,7 @@ Interactively, prompt for NAME, and use the current filters."
 
 ;;;###autoload
 (defun ibuffer-switch-to-saved-filters (name)
-  "Set this buffer's filters to filters with NAME from `ibuffer-saved-filters'.
-If prefix argument ADD is non-nil, then add the saved filters instead
-of replacing the current filters."
+  "Set this buffer's filters to filters with NAME from `ibuffer-saved-filters'."
   (interactive
    (list
     (if (null ibuffer-saved-filters)
@@ -956,36 +960,30 @@ of replacing the current filters."
     (ibuffer-format-qualifier-1 qualifier)))
 
 (defun ibuffer-format-qualifier-1 (qualifier)
-  (case (car qualifier)
-    (saved
+  (pcase (car qualifier)
+    (`saved
      (concat " [filter: " (cdr qualifier) "]"))
-    (or
+    (`or
      (concat " [OR" (mapconcat #'ibuffer-format-qualifier
                               (cdr qualifier) "") "]"))
-    (t
+    (_
      (let ((type (assq (car qualifier) ibuffer-filtering-alist)))
        (unless qualifier
         (error "Ibuffer: bad qualifier %s" qualifier))
        (concat " [" (cadr type) ": " (format "%s]" (cdr qualifier)))))))
 
 
-(defun ibuffer-list-buffer-modes ()
-  "Create an alist of buffer modes currently in use.
-The list returned will be of the form (\"MODE-NAME\" . MODE-SYMBOL)."
-  (let ((bufs (buffer-list))
-       (modes)
-       (this-mode))
-    (while bufs
-      (setq this-mode
-           (with-current-buffer
-               (car bufs)
-             major-mode)
-           bufs (cdr bufs))
-      (add-to-list
-       'modes
-       `(,(symbol-name this-mode) .
-        ,this-mode)))
-    modes))
+(defun ibuffer-list-buffer-modes (&optional include-parents)
+  "Create a completion table of buffer modes currently in use.
+If INCLUDE-PARENTS is non-nil then include parent modes."
+  (let ((modes))
+    (dolist (buf (buffer-list))
+      (let ((this-mode (buffer-local-value 'major-mode buf)))
+        (while (and this-mode (not (memq this-mode modes)))
+          (push this-mode modes)
+          (setq this-mode (and include-parents
+                               (get this-mode 'derived-mode-parent))))))
+    (mapcar #'symbol-name modes)))
 
 
 ;;; Extra operation definitions
@@ -995,18 +993,20 @@ The list returned will be of the form (\"MODE-NAME\" . MODE-SYMBOL)."
   "Toggle current view to buffers with major mode QUALIFIER."
   (:description "major mode"
    :reader
-   (intern
-    (completing-read "Filter by major mode: " obarray
-                    #'(lambda (e)
-                        (string-match "-mode$"
-                                      (symbol-name e)))
-                    t
-                    (let ((buf (ibuffer-current-buffer)))
-                      (if (and buf (buffer-live-p buf))
-                          (with-current-buffer buf
-                            (symbol-name major-mode))
-                        "")))))
-  (eq qualifier (with-current-buffer buf major-mode)))
+   (let* ((buf (ibuffer-current-buffer))
+          (default (if (and buf (buffer-live-p buf))
+                       (symbol-name (buffer-local-value
+                                     'major-mode buf)))))
+     (intern
+      (completing-read
+       (if default
+           (format "Filter by major mode (default %s): " default)
+         "Filter by major mode: ")
+       obarray
+       #'(lambda (e)
+           (string-match "-mode\\'" (symbol-name e)))
+       t nil nil default))))
+  (eq qualifier (buffer-local-value 'major-mode buf)))
 
 ;;;###autoload (autoload 'ibuffer-filter-by-used-mode "ibuf-ext")
 (define-ibuffer-filter used-mode
@@ -1014,18 +1014,29 @@ The list returned will be of the form (\"MODE-NAME\" . MODE-SYMBOL)."
 Called interactively, this function allows selection of modes
 currently used by buffers."
   (:description "major mode in use"
+   :reader
+   (let* ((buf (ibuffer-current-buffer))
+          (default (if (and buf (buffer-live-p buf))
+                       (symbol-name (buffer-local-value
+                                     'major-mode buf)))))
+     (intern
+      (completing-read
+       (if default
+           (format "Filter by major mode (default %s): " default)
+         "Filter by major mode: ")
+       (ibuffer-list-buffer-modes) nil t nil nil default))))
+  (eq qualifier (buffer-local-value 'major-mode buf)))
+
+;;;###autoload (autoload 'ibuffer-filter-by-derived-mode "ibuf-ext")
+(define-ibuffer-filter derived-mode
+    "Toggle current view to buffers whose major mode inherits from QUALIFIER."
+  (:description "derived mode"
                :reader
                (intern
-                (completing-read "Filter by major mode: "
-                                 (ibuffer-list-buffer-modes)
-                                 nil
-                                 t
-                                 (let ((buf (ibuffer-current-buffer)))
-                                   (if (and buf (buffer-live-p buf))
-                                       (with-current-buffer buf
-                                         (symbol-name major-mode))
-                                     "")))))
-  (eq qualifier (with-current-buffer buf major-mode)))
+                (completing-read "Filter by derived mode: "
+                                 (ibuffer-list-buffer-modes t)
+                                  nil t)))
+  (with-current-buffer buf (derived-mode-p qualifier)))
 
 ;;;###autoload (autoload 'ibuffer-filter-by-name "ibuf-ext")
 (define-ibuffer-filter name
@@ -1039,15 +1050,7 @@ currently used by buffers."
   "Toggle current view to buffers with filename matching QUALIFIER."
   (:description "filename"
    :reader (read-from-minibuffer "Filter by filename (regexp): "))
-  (ibuffer-awhen (with-current-buffer buf
-                  (or buffer-file-name
-                      (and (boundp 'dired-directory)
-                           (let ((dired-dir
-                                  (if (stringp dired-directory)
-                                      dired-directory
-                                    (car dired-directory))))
-                             (and dired-dir
-                                  (expand-file-name dired-dir))))))
+  (ibuffer-awhen (with-current-buffer buf (ibuffer-buffer-file-name))
     (string-match qualifier it)))
 
 ;;;###autoload (autoload 'ibuffer-filter-by-size-gt  "ibuf-ext")
@@ -1123,13 +1126,9 @@ Default sorting modes are:
 Ordering is lexicographic."
   (:description "major mode")
   (string-lessp (downcase
-                (symbol-name (with-current-buffer
-                                 (car a)
-                               major-mode)))
+                (symbol-name (buffer-local-value 'major-mode (car a))))
                (downcase
-                (symbol-name (with-current-buffer
-                                 (car b)
-                               major-mode)))))
+                (symbol-name (buffer-local-value 'major-mode (car b))))))
 
 ;;;###autoload (autoload 'ibuffer-do-sort-by-mode-name "ibuf-ext")
 (define-ibuffer-sorter mode-name
@@ -1137,13 +1136,13 @@ Ordering is lexicographic."
 Ordering is lexicographic."
   (:description "major mode name")
   (string-lessp (downcase
-                 (with-current-buffer
-                     (car a)
-                   mode-name))
+                (with-current-buffer
+                    (car a)
+                  (format-mode-line mode-name)))
                (downcase
                 (with-current-buffer
                     (car b)
-                  mode-name))))
+                  (format-mode-line mode-name)))))
 
 ;;;###autoload (autoload 'ibuffer-do-sort-by-alphabetic "ibuf-ext")
 (define-ibuffer-sorter alphabetic
@@ -1163,6 +1162,20 @@ Ordering is lexicographic."
      (with-current-buffer (car b)
        (buffer-size))))
 
+;;;###autoload (autoload 'ibuffer-do-sort-by-filename/process "ibuf-ext")
+(define-ibuffer-sorter filename/process
+ "Sort the buffers by their file name/process name."
+  (:description "file name")
+  (string-lessp
+   ;; FIXME: For now just compare the file name and the process name
+   ;; (if it exists).  Is there a better way to do this?
+   (or (buffer-file-name (car a))
+       (let ((pr-a (get-buffer-process (car a))))
+        (and (processp pr-a) (process-name pr-a))))
+   (or (buffer-file-name (car b))
+       (let ((pr-b (get-buffer-process (car b))))
+        (and (processp pr-b) (process-name pr-b))))))
+
 ;;; Functions to emulate bs.el
 
 ;;;###autoload
@@ -1222,12 +1235,10 @@ mean move backwards, non-negative integers mean move forwards."
     (setq direction 1))
   ;; Skip the title
   (ibuffer-forward-line 0)
-  (let ((opos (point))
-       curmark)
+  (let ((opos (point)))
     (ibuffer-forward-line direction)
     (while (not (or (= (point) opos)
-                   (eq (setq curmark (ibuffer-current-mark))
-                       mark)))
+                   (eq (ibuffer-current-mark) mark)))
       (ibuffer-forward-line direction))
     (when (and (= (point) opos)
               (not (eq (ibuffer-current-mark) mark)))
@@ -1250,7 +1261,7 @@ to move by.  The default is `ibuffer-marked-char'."
       (message "No buffers marked; use 'm' to mark a buffer")
     (let ((count
           (ibuffer-map-marked-lines
-           #'(lambda (buf mark)
+           #'(lambda (_buf _mark)
                'kill))))
       (message "Killed %s lines" count))))
 
@@ -1282,7 +1293,7 @@ a prefix argument reverses the meaning of that variable."
     (let (buf-point)
       ;; Blindly search for our buffer: it is very likely that it is
       ;; not in a hidden filter group.
-      (ibuffer-map-lines #'(lambda (buf marks)
+      (ibuffer-map-lines #'(lambda (buf _marks)
                             (when (string= (buffer-name buf) name)
                               (setq buf-point (point))
                               nil))
@@ -1296,7 +1307,7 @@ a prefix argument reverses the meaning of that variable."
          (dolist (group ibuffer-hidden-filter-groups)
            (ibuffer-jump-to-filter-group group)
            (ibuffer-toggle-filter-group)
-           (ibuffer-map-lines #'(lambda (buf marks)
+           (ibuffer-map-lines #'(lambda (buf _marks)
                                   (when (string= (buffer-name buf) name)
                                     (setq buf-point (point))
                                     nil))
@@ -1311,15 +1322,68 @@ a prefix argument reverses the meaning of that variable."
          (error "No buffer with name %s" name)
        (goto-char buf-point)))))
 
+(declare-function diff-sentinel "diff"
+                 (code &optional old-temp-file new-temp-file))
+
+(defun ibuffer-diff-buffer-with-file-1 (buffer)
+  (let ((bufferfile (buffer-local-value 'buffer-file-name buffer))
+       (tempfile (make-temp-file "buffer-content-")))
+    (when bufferfile
+      (unwind-protect
+         (progn
+           (with-current-buffer buffer
+             (write-region nil nil tempfile nil 'nomessage))
+           (let* ((old (expand-file-name bufferfile))
+                  (new (expand-file-name tempfile))
+                  (oldtmp (file-local-copy old))
+                  (newtmp (file-local-copy new))
+                  (switches diff-switches)
+                  (command
+                   (mapconcat
+                    'identity
+                    `(,diff-command
+                      ;; Use explicitly specified switches
+                      ,@(if (listp switches) switches (list switches))
+                      ,@(if (or old new)
+                            (list "-L" (shell-quote-argument old)
+                                  "-L" (shell-quote-argument
+                                        (format "Buffer %s" (buffer-name buffer)))))
+                      ,(shell-quote-argument (or oldtmp old))
+                      ,(shell-quote-argument (or newtmp new)))
+                    " ")))
+             (let ((inhibit-read-only t))
+               (insert command "\n")
+               (diff-sentinel
+                (call-process shell-file-name nil
+                              (current-buffer) nil
+                              shell-command-switch command))
+               (insert "\n")))))
+      (sit-for 0)
+      (when (file-exists-p tempfile)
+       (delete-file tempfile)))))
+
 ;;;###autoload
 (defun ibuffer-diff-with-file ()
-  "View the differences between this buffer and its associated file.
+  "View the differences between marked buffers and their associated files.
+If no buffers are marked, use buffer at point.
 This requires the external program \"diff\" to be in your `exec-path'."
   (interactive)
-  (let ((buf (ibuffer-current-buffer)))
-    (unless (buffer-live-p buf)
-      (error "Buffer %s has been killed" buf))
-    (diff-buffer-with-file buf)))
+  (require 'diff)
+  (let ((marked-bufs (ibuffer-get-marked-buffers)))
+    (when (null marked-bufs)
+      (setq marked-bufs (list (ibuffer-current-buffer t))))
+    (with-current-buffer (get-buffer-create "*Ibuffer Diff*")
+      (setq buffer-read-only nil)
+      (buffer-disable-undo (current-buffer))
+      (erase-buffer)
+      (buffer-enable-undo (current-buffer))
+      (diff-mode)
+      (dolist (buf marked-bufs)
+       (unless (buffer-live-p buf)
+         (error "Buffer %s has been killed" buf))
+       (ibuffer-diff-buffer-with-file-1 buf))
+      (setq buffer-read-only t)))
+  (switch-to-buffer "*Ibuffer Diff*"))
 
 ;;;###autoload
 (defun ibuffer-copy-filename-as-kill (&optional arg)
@@ -1331,7 +1395,7 @@ If a buffer has no filename, it is ignored.
 With no prefix arg, use the filename sans its directory of each marked file.
 With a zero prefix arg, use the complete filename of each marked file.
 With \\[universal-argument], use the filename of each marked file relative
-to `ibuffer-default-directory' iff non-nil, otherwise `default-directory'.
+to `ibuffer-default-directory' if non-nil, otherwise `default-directory'.
 
 You can then feed the file name(s) to other commands with \\[yank]."
   (interactive "p")
@@ -1345,19 +1409,19 @@ You can then feed the file name(s) to other commands with \\[yank]."
                      (t
                       'name))))
       (ibuffer-map-marked-lines
-       #'(lambda (buf mark)
+       #'(lambda (buf _mark)
           (setq ibuffer-copy-filename-as-kill-result
                 (concat ibuffer-copy-filename-as-kill-result
                         (let ((name (buffer-file-name buf)))
                           (if name
-                              (case type
-                                (full
+                              (pcase type
+                                (`full
                                  name)
-                                (relative
+                                (`relative
                                  (file-relative-name
                                   name (or ibuffer-default-directory
                                            default-directory)))
-                                (t
+                                (_
                                  (file-name-nondirectory name)))
                             ""))
                         " "))))
@@ -1366,7 +1430,7 @@ You can then feed the file name(s) to other commands with \\[yank]."
 (defun ibuffer-mark-on-buffer (func &optional ibuffer-mark-on-buffer-mark group)
   (let ((count
         (ibuffer-map-lines
-         #'(lambda (buf mark)
+         #'(lambda (buf _mark)
              (when (funcall func buf)
                (ibuffer-set-mark-1 (or ibuffer-mark-on-buffer-mark
                                        ibuffer-marked-char))
@@ -1374,7 +1438,8 @@ You can then feed the file name(s) to other commands with \\[yank]."
          nil
          group)))
     (ibuffer-redisplay t)
-    (message "Marked %s buffers" count)))
+    (unless (eq ibuffer-mark-on-buffer-mark ?\s)
+      (message "Marked %s buffers" count))))
 
 ;;;###autoload
 (defun ibuffer-mark-by-name-regexp (regexp)
@@ -1391,7 +1456,7 @@ You can then feed the file name(s) to other commands with \\[yank]."
   (ibuffer-mark-on-buffer
    #'(lambda (buf)
        (with-current-buffer buf
-        (string-match regexp mode-name)))))
+        (string-match regexp (format-mode-line mode-name nil nil buf))))))
 
 ;;;###autoload
 (defun ibuffer-mark-by-file-name-regexp (regexp)
@@ -1412,23 +1477,19 @@ You can then feed the file name(s) to other commands with \\[yank]."
 (defun ibuffer-mark-by-mode (mode)
   "Mark all buffers whose major mode equals MODE."
   (interactive
-   (list (intern (completing-read "Mark by major mode: " obarray
-                                 #'(lambda (e)
-                                     ;; kind of a hack...
-                                      (and (fboundp e)
-                                           (string-match "-mode$"
-                                                         (symbol-name e))))
-                                 t
-                                 (let ((buf (ibuffer-current-buffer)))
-                                   (if (and buf (buffer-live-p buf))
-                                       (with-current-buffer buf
-                                         (cons (symbol-name major-mode)
-                                               0))
-                                     ""))))))
+   (let* ((buf (ibuffer-current-buffer))
+          (default (if (and buf (buffer-live-p buf))
+                       (symbol-name (buffer-local-value
+                                     'major-mode buf)))))
+     (list (intern
+            (completing-read
+             (if default
+                 (format "Mark by major mode (default %s): " default)
+               "Mark by major mode: ")
+             (ibuffer-list-buffer-modes) nil t nil nil default)))))
   (ibuffer-mark-on-buffer
    #'(lambda (buf)
-       (with-current-buffer buf
-        (eq major-mode mode)))))
+       (eq (buffer-local-value 'major-mode buf) mode))))
 
 ;;;###autoload
 (defun ibuffer-mark-modified-buffers ()
@@ -1442,7 +1503,7 @@ You can then feed the file name(s) to other commands with \\[yank]."
   "Mark all modified buffers that have an associated file."
   (interactive)
   (ibuffer-mark-on-buffer
-   #'(lambda (buf) (and (with-current-buffer buf buffer-file-name)
+   #'(lambda (buf) (and (buffer-local-value 'buffer-file-name buf)
                        (buffer-modified-p buf)))))
 
 ;;;###autoload
@@ -1462,7 +1523,7 @@ You can then feed the file name(s) to other commands with \\[yank]."
 
 ;;;###autoload
 (defun ibuffer-mark-help-buffers ()
-  "Mark buffers like *Help*, *Apropos*, *Info*."
+  "Mark buffers whose major mode is in variable `ibuffer-help-buffer-modes'."
   (interactive)
   (ibuffer-mark-on-buffer
    #'(lambda (buf)
@@ -1482,20 +1543,15 @@ You can then feed the file name(s) to other commands with \\[yank]."
 
 ;;;###autoload
 (defun ibuffer-mark-old-buffers ()
-  "Mark buffers which have not been viewed in `ibuffer-old-time' days."
+  "Mark buffers which have not been viewed in `ibuffer-old-time' hours."
   (interactive)
   (ibuffer-mark-on-buffer
    #'(lambda (buf)
        (with-current-buffer buf
         ;; hacked from midnight.el
         (when buffer-display-time
-          (let* ((tm (current-time))
-                 (now (+ (* (float (ash 1 16)) (car tm))
-                         (float (cadr tm)) (* 0.0000001 (caddr tm))))
-                 (then (+ (* (float (ash 1 16))
-                             (car buffer-display-time))
-                          (float (cadr buffer-display-time))
-                          (* 0.0000001 (caddr buffer-display-time)))))
+          (let* ((now (float-time))
+                 (then (float-time buffer-display-time)))
             (> (- now then) (* 60 60 ibuffer-old-time))))))))
 
 ;;;###autoload
@@ -1511,18 +1567,14 @@ You can then feed the file name(s) to other commands with \\[yank]."
   "Mark all read-only buffers."
   (interactive)
   (ibuffer-mark-on-buffer
-   #'(lambda (buf)
-       (with-current-buffer buf
-        buffer-read-only))))
+   #'(lambda (buf) (buffer-local-value 'buffer-read-only buf))))
 
 ;;;###autoload
 (defun ibuffer-mark-dired-buffers ()
   "Mark all `dired' buffers."
   (interactive)
   (ibuffer-mark-on-buffer
-   #'(lambda (buf)
-       (with-current-buffer buf
-        (eq major-mode 'dired-mode)))))
+   #'(lambda (buf) (eq (buffer-local-value 'major-mode buf) 'dired-mode))))
 
 ;;;###autoload
 (defun ibuffer-do-occur (regexp &optional nlines)
@@ -1538,11 +1590,14 @@ defaults to one."
   (let ((ibuffer-do-occur-bufs nil))
     ;; Accumulate a list of marked buffers
     (ibuffer-map-marked-lines
-     #'(lambda (buf mark)
+     #'(lambda (buf _mark)
         (push buf ibuffer-do-occur-bufs)))
     (occur-1 regexp nlines ibuffer-do-occur-bufs)))
 
 (provide 'ibuf-ext)
 
-;;; arch-tag: 9af21953-deda-4c30-b76d-f81d9128e76d
+;; Local Variables:
+;; generated-autoload-file: "ibuffer.el"
+;; End:
+
 ;;; ibuf-ext.el ends here