1 ;;; ibuf-ext.el --- extensions for ibuffer -*-byte-compile-dynamic: t;-*-
3 ;; Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
5 ;; Author: Colin Walters <walters@verbum.org>
7 ;; Keywords: buffer, convenience
9 ;; This file is part of GNU Emacs.
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or (at
14 ;; your option) any later version.
16 ;; This program is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 ;; General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with this program ; see the file COPYING. If not, write to
23 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
28 ;; These functions should be automatically loaded when called, but you
29 ;; can explicity (require 'ibuf-ext) in your ~/.emacs to have them
42 (defun ibuffer-delete-alist (key alist)
43 "Delete all entries in ALIST that have a key equal to KEY."
45 (while (setq entry (assoc key alist))
46 (setq alist (delete entry alist)))
49 (defun ibuffer-depropertize-string (str &optional nocopy)
50 "Return a copy of STR with text properties removed.
51 If optional argument NOCOPY is non-nil, actually modify the string directly."
54 (copy-sequence str))))
55 (set-text-properties 0 (length str) nil str)
58 (defun ibuffer-split-list (ibuffer-split-list-fn ibuffer-split-list-elts)
61 (dolist (ibuffer-split-list-elt ibuffer-split-list-elts)
62 (if (funcall ibuffer-split-list-fn ibuffer-split-list-elt)
63 (push ibuffer-split-list-elt hip-crowd)
64 (push ibuffer-split-list-elt lamers)))
65 ;; Too bad Emacs Lisp doesn't have multiple values.
66 (list (nreverse hip-crowd) (nreverse lamers))))
68 (defvar ibuffer-tmp-hide-regexps nil
69 "A list of regexps which should match buffer names to not show.")
71 (defvar ibuffer-tmp-show-regexps nil
72 "A list of regexps which should match buffer names to always show.")
74 (defvar ibuffer-auto-mode nil
75 "If non-nil, Ibuffer auto-mode should be enabled for this buffer.
76 Do not set this variable directly! Use the function
77 `ibuffer-auto-mode' instead.")
79 (defvar ibuffer-auto-buffers-changed nil)
81 (defvar ibuffer-filtering-qualifiers nil
82 "A list like (SYMBOL . QUALIFIER) which filters the current buffer list.
83 See also `ibuffer-filtering-alist'.")
85 ;; This is now frobbed by `define-ibuffer-filter'.
86 (defvar ibuffer-filtering-alist nil
87 "An alist of (SYMBOL DESCRIPTION FUNCTION) which describes a filter.
89 You most likely do not want to modify this variable directly; see
90 `define-ibuffer-filter'.
92 SYMBOL is the symbolic name of the filter. DESCRIPTION is used when
93 displaying information to the user. FUNCTION is given a buffer and
94 the value of the qualifier, and returns non-nil if and only if the
95 buffer should be displayed.")
97 (defvar ibuffer-cached-filter-formats nil)
98 (defvar ibuffer-compiled-filter-formats nil)
100 (defvar ibuffer-filter-groups nil
101 "A list like ((\"NAME\" ((SYMBOL . QUALIFIER) ...) ...) which groups buffers.
102 The SYMBOL should be one from `ibuffer-filtering-alist'.
103 The QUALIFIER should be the same as QUALIFIER in
104 `ibuffer-filtering-qualifiers'.")
106 (defcustom ibuffer-show-empty-filter-groups t
107 "If non-nil, then show the names of filter groups which are empty."
111 (defcustom ibuffer-saved-filter-groups
113 ((or (mode . message-mode)
115 (mode . gnus-group-mode)
116 (mode . gnus-summary-mode)
117 (mode . gnus-article-mode))))
119 ((or (mode . emacs-lisp-mode)
124 (mode . lisp-mode)))))
126 "An alist of filtering groups to switch between.
128 This variable should look like ((\"STRING\" QUALIFIERS)
129 (\"STRING\" QUALIFIERS) ...), where
130 QUALIFIERS is a list of the same form as
131 `ibuffer-filtering-qualifiers'.
133 See also the variables `ibuffer-filter-groups',
134 `ibuffer-filtering-qualifiers', `ibuffer-filtering-alist', and the
135 functions `ibuffer-switch-to-saved-filter-group',
136 `ibuffer-save-filter-group'."
140 (defvar ibuffer-hidden-filter-groups nil
141 "A list of filtering groups which are currently hidden.")
143 (defvar ibuffer-filter-group-kill-ring nil)
145 (defun ibuffer-ext-visible-p (buf all &optional ibuffer-buf)
147 (ibuffer-buf-matches-predicates buf ibuffer-tmp-show-regexps)
150 (ibuffer-buf-matches-predicates buf ibuffer-tmp-hide-regexps)
151 (ibuffer-buf-matches-predicates buf ibuffer-never-show-predicates)))
154 (ibuffer-buf-matches-predicates buf ibuffer-maybe-show-predicates)))
155 (or ibuffer-view-ibuffer
157 (not (eq ibuffer-buf buf))))
159 (ibuffer-included-in-filters-p buf ibuffer-filtering-qualifiers)
160 (ibuffer-buf-matches-predicates buf ibuffer-always-show-predicates)))))
162 (defun ibuffer-auto-update-changed ()
163 (when ibuffer-auto-buffers-changed
164 (setq ibuffer-auto-buffers-changed nil)
165 (mapcar #'(lambda (buf)
167 (with-current-buffer buf
168 (when (and ibuffer-auto-mode
169 (eq major-mode 'ibuffer-mode))
170 (ibuffer-update nil t)))))
174 (defun ibuffer-auto-mode (&optional arg)
175 "Toggle use of Ibuffer's auto-update facility.
176 With numeric ARG, enable auto-update if and only if ARG is positive."
178 (unless (eq major-mode 'ibuffer-mode)
179 (error "This buffer is not in Ibuffer mode"))
180 (set (make-local-variable 'ibuffer-auto-mode)
183 (not ibuffer-auto-mode)))
184 (defadvice get-buffer-create (after ibuffer-notify-create activate)
185 (setq ibuffer-auto-buffers-changed t))
186 (defadvice kill-buffer (after ibuffer-notify-kill activate)
187 (setq ibuffer-auto-buffers-changed t))
188 (add-hook 'post-command-hook 'ibuffer-auto-update-changed)
189 (ibuffer-update-mode-name))
191 (defun ibuffer-mouse-filter-by-mode (event)
192 "Enable or disable filtering by the major mode chosen via mouse."
194 (ibuffer-interactive-filter-by-mode event))
196 (defun ibuffer-interactive-filter-by-mode (event-or-point)
197 "Enable or disable filtering by the major mode at point."
199 (if (eventp event-or-point)
200 (mouse-set-point event-or-point)
201 (goto-char event-or-point))
202 (let ((buf (ibuffer-current-buffer)))
203 (if (assq 'mode ibuffer-filtering-qualifiers)
204 (setq ibuffer-filtering-qualifiers
205 (ibuffer-delete-alist 'mode ibuffer-filtering-qualifiers))
206 (ibuffer-push-filter (cons 'mode
207 (with-current-buffer buf
209 (ibuffer-update nil t))
211 (defun ibuffer-mouse-toggle-filter-group (event)
212 "Toggle the display status of the filter group chosen with the mouse."
214 (ibuffer-toggle-filter-group-1 (save-excursion
215 (mouse-set-point event)
218 (defun ibuffer-toggle-filter-group ()
219 "Toggle the display status of the filter group on this line."
221 (ibuffer-toggle-filter-group-1 (point)))
223 (defun ibuffer-toggle-filter-group-1 (posn)
224 (let ((name (get-text-property posn 'ibuffer-filter-group-name)))
225 (unless (stringp name)
226 (error "No filtering group name present"))
227 (if (member name ibuffer-hidden-filter-groups)
228 (setq ibuffer-hidden-filter-groups
229 (delete name ibuffer-hidden-filter-groups))
230 (push name ibuffer-hidden-filter-groups))
231 (ibuffer-update nil t)))
233 (defun ibuffer-forward-filter-group (&optional count)
234 "Move point forwards by COUNT filtering groups."
239 (when (get-text-property (point) 'ibuffer-filter-group-name)
240 (goto-char (next-single-property-change
241 (point) 'ibuffer-filter-group-name
243 (goto-char (next-single-property-change
244 (point) 'ibuffer-filter-group-name
246 (ibuffer-forward-filter-group (1- count)))
247 (ibuffer-forward-line 0))
250 (defun ibuffer-backward-filter-group (&optional count)
251 "Move point backwards by COUNT filtering groups."
256 (when (get-text-property (point) 'ibuffer-filter-group-name)
257 (goto-char (previous-single-property-change
258 (point) 'ibuffer-filter-group-name
260 (goto-char (previous-single-property-change
261 (point) 'ibuffer-filter-group-name
263 (ibuffer-backward-filter-group (1- count)))
264 (when (= (point) (point-min))
265 (goto-char (point-max))
266 (ibuffer-backward-filter-group 1))
267 (ibuffer-forward-line 0))
269 (define-ibuffer-op shell-command-pipe (command)
270 "Pipe the contents of each marked buffer to shell command COMMAND."
271 (:interactive "sPipe to shell command: "
272 :opstring "Shell command executed on"
274 (shell-command-on-region
275 (point-min) (point-max) command
276 (get-buffer-create "* ibuffer-shell-output*")))
278 (define-ibuffer-op shell-command-pipe-replace (command)
279 "Replace the contents of marked buffers with output of pipe to COMMAND."
280 (:interactive "sPipe to shell command (replace): "
281 :opstring "Buffer contents replaced in"
282 :active-opstring "replace buffer contents in"
285 (with-current-buffer buf
286 (shell-command-on-region (point-min) (point-max)
289 (define-ibuffer-op shell-command-file (command)
290 "Run shell command COMMAND separately on files of marked buffers."
291 (:interactive "sShell command on buffer's file: "
292 :opstring "Shell command executed on"
294 (shell-command (concat command " "
295 (shell-quote-argument
298 (ibuffer-make-temp-file
299 (substring (buffer-name) 0 (min 10 (length (buffer-name))))))))))
301 (define-ibuffer-op eval (form)
302 "Evaluate FORM in each of the buffers.
303 Does not display the buffer during evaluation. See
304 `ibuffer-do-view-and-eval' for that."
305 (:interactive "xEval in buffers (form): "
306 :opstring "evaluated in"
310 (define-ibuffer-op view-and-eval (form)
311 "Evaluate FORM while displaying each of the marked buffers.
312 To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
313 (:interactive "xEval viewing buffers (form): "
314 :opstring "evaluated in"
317 (let ((ibuffer-buf (current-buffer)))
320 (switch-to-buffer buf)
322 (switch-to-buffer ibuffer-buf))))
324 (define-ibuffer-op rename-uniquely ()
325 "Rename marked buffers as with `rename-uniquely'."
330 (define-ibuffer-op revert ()
331 "Revert marked buffers as with `revert-buffer'."
334 :active-opstring "revert"
338 (define-ibuffer-op replace-regexp (from-str to-str)
339 "Perform a `replace-regexp' in marked buffers."
341 (let* ((from-str (read-from-minibuffer "Replace regexp: "))
342 (to-str (read-from-minibuffer (concat "Replace " from-str
344 (list from-str to-str))
345 :opstring "replaced in"
348 (save-window-excursion
349 (switch-to-buffer buf)
351 (goto-char (point-min))
352 (let ((case-fold-search ibuffer-case-fold-search))
353 (while (re-search-forward from-str nil t)
354 (replace-match to-str))))
357 (define-ibuffer-op query-replace (&rest args)
358 "Perform a `query-replace' in marked buffers."
360 (query-replace-read-args "Query replace" t)
361 :opstring "replaced in"
364 (save-window-excursion
365 (switch-to-buffer buf)
367 (let ((case-fold-search ibuffer-case-fold-search))
368 (goto-char (point-min))
369 (apply #'query-replace args)))
372 (define-ibuffer-op query-replace-regexp (&rest args)
373 "Perform a `query-replace-regexp' in marked buffers."
375 (query-replace-read-args "Query replace regexp" t)
376 :opstring "replaced in"
379 (save-window-excursion
380 (switch-to-buffer buf)
382 (let ((case-fold-search ibuffer-case-fold-search))
383 (goto-char (point-min))
384 (apply #'query-replace-regexp args)))
387 (define-ibuffer-op print ()
388 "Print marked buffers as with `print-buffer'."
394 (defun ibuffer-included-in-filters-p (buf filters)
396 (memq nil ;; a filter will return nil if it failed
398 ;; filter should be like (TYPE . QUALIFIER), or
399 ;; (or (TYPE . QUALIFIER) (TYPE . QUALIFIER) ...)
401 (ibuffer-included-in-filter-p buf qual))
404 (defun ibuffer-included-in-filter-p (buf filter)
405 (if (eq (car filter) 'not)
406 (not (ibuffer-included-in-filter-p-1 buf (cdr filter)))
407 (ibuffer-included-in-filter-p-1 buf filter)))
409 (defun ibuffer-included-in-filter-p-1 (buf filter)
414 (memq t (mapcar #'(lambda (x)
415 (ibuffer-included-in-filter-p buf x))
420 ibuffer-saved-filters)))
422 (ibuffer-filter-disable)
423 (error "Unknown saved filter %s" (cdr filter)))
424 (ibuffer-included-in-filters-p buf (cadr data))))
426 (let ((filterdat (assq (car filter)
427 ibuffer-filtering-alist)))
428 ;; filterdat should be like (TYPE DESCRIPTION FUNC)
429 ;; just a sanity check
431 (ibuffer-filter-disable)
432 (error "Undefined filter %s" (car filter)))
435 (funcall (caddr filterdat)
437 (cdr filter))))))))))
439 (defun ibuffer-generate-filter-groups (bmarklist)
440 (let ((filter-group-alist (append ibuffer-filter-groups
441 (list (cons "Default" nil)))))
442 ;; (dolist (hidden ibuffer-hidden-filter-groups)
443 ;; (setq filter-group-alist (ibuffer-delete-alist
444 ;; hidden filter-group-alist)))
445 (let ((vec (make-vector (length filter-group-alist) nil))
447 (dolist (filtergroup filter-group-alist)
448 (let ((filterset (cdr filtergroup)))
449 (destructuring-bind (hip-crowd lamers)
450 (ibuffer-split-list (lambda (bufmark)
451 (ibuffer-included-in-filters-p (car bufmark)
454 (aset vec i hip-crowd)
456 (setq bmarklist lamers))))
459 (push (cons (car (nth j filter-group-alist))
464 (defun ibuffer-filters-to-filter-group (name)
465 "Make the current filters into a filtering group."
466 (interactive "sName for filtering group: ")
467 (when (null ibuffer-filtering-qualifiers)
468 (error "No filters in effect"))
469 (push (cons name ibuffer-filtering-qualifiers) ibuffer-filter-groups)
470 (ibuffer-filter-disable))
473 (defun ibuffer-set-filter-groups-by-mode ()
474 "Set the current filter groups to filter by mode."
476 (setq ibuffer-filter-groups
477 (mapcar (lambda (mode)
478 (cons (format "%s" mode) `((mode . ,mode))))
481 (mapcar (lambda (buf) (with-current-buffer buf major-mode))
483 (unless ibuffer-view-ibuffer
484 (setq modes (delq 'ibuffer-mode modes)))
486 (ibuffer-update nil t))
489 (defun ibuffer-pop-filter-group ()
490 "Remove the first filtering group."
492 (when (null ibuffer-filter-groups)
493 (error "No filtering groups active"))
494 (pop ibuffer-filter-groups)
495 (ibuffer-update nil t))
498 (defun ibuffer-clear-filter-groups ()
499 "Remove all filtering groups."
501 (setq ibuffer-filter-groups nil)
502 (ibuffer-update nil t))
504 (defun ibuffer-current-filter-groups-with-position ()
506 (goto-char (point-min))
509 (while (and (not (eobp))
510 (setq pos (next-single-property-change
511 (point) 'ibuffer-filter-group-name)))
513 (push (cons (get-text-property (point) 'ibuffer-filter-group-name)
516 (goto-char (next-single-property-change
517 pos 'ibuffer-filter-group-name)))
521 (defun ibuffer-jump-to-filter-group (name)
522 "Move point to the filter group whose name is NAME."
523 (interactive (list nil))
524 (let ((table (ibuffer-current-filter-groups-with-position)))
525 (when (interactive-p)
526 (setq name (completing-read "Jump to filter group: " table nil t)))
527 (ibuffer-aif (assoc name table)
529 (error "No filter group with name %s" name))))
532 (defun ibuffer-kill-filter-group (name)
533 "Delete the filtering group named NAME."
534 (interactive (list nil))
535 (when (interactive-p)
536 (setq name (completing-read "Kill filter group: "
537 ibuffer-filter-groups nil t)))
538 (ibuffer-aif (assoc name ibuffer-filter-groups)
539 (setq ibuffer-filter-groups (ibuffer-delete-alist
540 name ibuffer-filter-groups))
541 (error "No filter group with name \"%s\"" name))
542 (ibuffer-update nil t))
545 (defun ibuffer-kill-line (&optional arg)
547 (ibuffer-aif (save-excursion
548 (ibuffer-forward-line 0)
549 (get-text-property (point) 'ibuffer-filter-group-name))
551 (when (equal it "Default")
552 (error "Can't kill default filtering group"))
553 (push (copy-tree (assoc it ibuffer-filter-groups))
554 ibuffer-filter-group-kill-ring)
555 (ibuffer-kill-filter-group it))
556 (funcall (if (interactive-p) #'call-interactively #'funcall)
560 (defun ibuffer-yank (&optional arg)
562 (unless ibuffer-filter-group-kill-ring
563 (error "ibuffer-filter-group-kill-ring is empty"))
565 (ibuffer-forward-line 0)
566 (let* ((last-killed (pop ibuffer-filter-group-kill-ring))
567 (all-groups ibuffer-filter-groups)
568 (cur (or (get-text-property (point) 'ibuffer-filter-group-name)
569 (get-text-property (point) 'ibuffer-filter-group)
571 (pos (or (position cur (mapcar #'car all-groups) :test #'equal)
572 (length all-groups))))
574 (push last-killed ibuffer-filter-groups))
575 ((= pos (length all-groups))
576 (setq ibuffer-filter-groups
577 (nconc ibuffer-filter-groups (list last-killed))))
579 (let ((cell (nthcdr pos ibuffer-filter-groups)))
580 (setf (cdr cell) (cons (car cell) (cdr cell)))
581 (setf (car cell) last-killed))))))
582 (ibuffer-update nil t))
585 (defun ibuffer-save-filter-groups (name groups)
586 "Save all active filter groups GROUPS as NAME.
587 They are added to `ibuffer-saved-filter-groups'. Interactively,
588 prompt for NAME, and use the current filters."
590 (if (null ibuffer-filter-groups)
591 (error "No filter groups active")
593 (read-from-minibuffer "Save current filter groups as: ")
594 ibuffer-filter-groups)))
595 (ibuffer-aif (assoc name ibuffer-saved-filter-groups)
597 (push (cons name groups) ibuffer-saved-filter-groups))
598 (ibuffer-maybe-save-stuff)
599 (ibuffer-update-mode-name))
602 (defun ibuffer-delete-saved-filter-groups (name)
603 "Delete saved filter groups with NAME.
604 They are removed from `ibuffer-saved-filter-groups'."
607 (if (null ibuffer-saved-filter-groups)
608 (error "No saved filters")
609 (completing-read "Delete saved filter group: "
610 ibuffer-saved-filter-groups nil t))))
611 (setq ibuffer-saved-filter-groups
612 (ibuffer-delete-alist name ibuffer-saved-filter-groups))
613 (ibuffer-maybe-save-stuff)
614 (ibuffer-update nil t))
617 (defun ibuffer-switch-to-saved-filter-groups (name)
618 "Set this buffer's filter groups to saved version with NAME.
619 The value from `ibuffer-saved-filters' is used.
620 If prefix argument ADD is non-nil, then add the saved filters instead
621 of replacing the current filters."
624 (if (null ibuffer-saved-filter-groups)
625 (error "No saved filters")
626 (completing-read "Switch to saved filter group: "
627 ibuffer-saved-filter-groups nil t))))
628 (setq ibuffer-filter-groups (cdr (assoc name ibuffer-saved-filter-groups)))
629 (ibuffer-update nil t))
632 (defun ibuffer-filter-disable ()
633 "Disable all filters currently in effect in this buffer."
635 (setq ibuffer-filtering-qualifiers nil)
636 (ibuffer-update nil t))
639 (defun ibuffer-pop-filter ()
640 "Remove the top filter in this buffer."
642 (when (null ibuffer-filtering-qualifiers)
643 (error "No filters in effect"))
644 (pop ibuffer-filtering-qualifiers)
645 (ibuffer-update nil t))
647 (defun ibuffer-push-filter (qualifier)
648 "Add QUALIFIER to `ibuffer-filtering-qualifiers'."
649 (push qualifier ibuffer-filtering-qualifiers))
652 (defun ibuffer-decompose-filter ()
653 "Separate the top compound filter (OR, NOT, or SAVED) in this buffer.
655 This means that the topmost filter on the filtering stack, which must
656 be a complex filter like (OR [name: foo] [mode: bar-mode]), will be
657 turned into two separate filters [name: foo] and [mode: bar-mode]."
659 (when (null ibuffer-filtering-qualifiers)
660 (error "No filters in effect"))
661 (let ((lim (pop ibuffer-filtering-qualifiers)))
664 (setq ibuffer-filtering-qualifiers (append
666 ibuffer-filtering-qualifiers)))
670 ibuffer-saved-filters)))
672 (ibuffer-filter-disable)
673 (error "Unknown saved filter %s" (cdr lim)))
674 (setq ibuffer-filtering-qualifiers (append
676 ibuffer-filtering-qualifiers))))
679 ibuffer-filtering-qualifiers))
681 (error "Filter type %s is not compound" (car lim)))))
682 (ibuffer-update nil t))
685 (defun ibuffer-exchange-filters ()
686 "Exchange the top two filters on the stack in this buffer."
688 (when (< (length ibuffer-filtering-qualifiers)
690 (error "Need two filters to exchange"))
691 (let ((first (pop ibuffer-filtering-qualifiers))
692 (second (pop ibuffer-filtering-qualifiers)))
693 (push first ibuffer-filtering-qualifiers)
694 (push second ibuffer-filtering-qualifiers))
695 (ibuffer-update nil t))
698 (defun ibuffer-negate-filter ()
699 "Negate the sense of the top filter in the current buffer."
701 (when (null ibuffer-filtering-qualifiers)
702 (error "No filters in effect"))
703 (let ((lim (pop ibuffer-filtering-qualifiers)))
704 (push (if (eq (car lim) 'not)
707 ibuffer-filtering-qualifiers))
708 (ibuffer-update nil t))
711 (defun ibuffer-or-filter (&optional reverse)
712 "Replace the top two filters in this buffer with their logical OR.
713 If optional argument REVERSE is non-nil, instead break the top OR
718 (when (or (null ibuffer-filtering-qualifiers)
719 (not (eq 'or (caar ibuffer-filtering-qualifiers))))
720 (error "Top filter is not an OR"))
721 (let ((lim (pop ibuffer-filtering-qualifiers)))
722 (setq ibuffer-filtering-qualifiers (nconc (cdr lim) ibuffer-filtering-qualifiers))))
723 (when (< (length ibuffer-filtering-qualifiers) 2)
724 (error "Need two filters to OR"))
725 ;; If the second filter is an OR, just add to it.
726 (let ((first (pop ibuffer-filtering-qualifiers))
727 (second (pop ibuffer-filtering-qualifiers)))
728 (if (eq 'or (car second))
729 (push (nconc (list 'or first) (cdr second)) ibuffer-filtering-qualifiers)
730 (push (list 'or first second)
731 ibuffer-filtering-qualifiers))))
732 (ibuffer-update nil t))
734 (defun ibuffer-maybe-save-stuff ()
735 (when ibuffer-save-with-custom
736 (if (fboundp 'customize-save-variable)
738 (customize-save-variable 'ibuffer-saved-filters
739 ibuffer-saved-filters)
740 (customize-save-variable 'ibuffer-saved-filter-groups
741 ibuffer-saved-filter-groups))
742 (message "Not saved permanently: Customize not available"))))
745 (defun ibuffer-save-filters (name filters)
746 "Save FILTERS in this buffer with name NAME in `ibuffer-saved-filters'.
747 Interactively, prompt for NAME, and use the current filters."
749 (if (null ibuffer-filtering-qualifiers)
750 (error "No filters currently in effect")
752 (read-from-minibuffer "Save current filters as: ")
753 ibuffer-filtering-qualifiers)))
754 (ibuffer-aif (assoc name ibuffer-saved-filters)
756 (push (list name filters) ibuffer-saved-filters))
757 (ibuffer-maybe-save-saved-stuff)
758 (ibuffer-update-mode-name))
761 (defun ibuffer-delete-saved-filters (name)
762 "Delete saved filters with NAME from `ibuffer-saved-filters'."
765 (if (null ibuffer-saved-filters)
766 (error "No saved filters")
767 (completing-read "Delete saved filters: "
768 ibuffer-saved-filters nil t))))
769 (setq ibuffer-saved-filters
770 (ibuffer-delete-alist name ibuffer-saved-filters))
771 (ibuffer-maybe-save-stuff)
772 (ibuffer-update nil t))
775 (defun ibuffer-add-saved-filters (name)
776 "Add saved filters from `ibuffer-saved-filters' to this buffer's filters."
779 (if (null ibuffer-saved-filters)
780 (error "No saved filters")
781 (completing-read "Add saved filters: "
782 ibuffer-saved-filters nil t))))
783 (push (cons 'saved name) ibuffer-filtering-qualifiers)
784 (ibuffer-update nil t))
787 (defun ibuffer-switch-to-saved-filters (name)
788 "Set this buffer's filters to filters with NAME from `ibuffer-saved-filters'.
789 If prefix argument ADD is non-nil, then add the saved filters instead
790 of replacing the current filters."
793 (if (null ibuffer-saved-filters)
794 (error "No saved filters")
795 (completing-read "Switch to saved filters: "
796 ibuffer-saved-filters nil t))))
797 (setq ibuffer-filtering-qualifiers (list (cons 'saved name)))
798 (ibuffer-update nil t))
800 (defun ibuffer-format-qualifier (qualifier)
801 (if (eq (car-safe qualifier) 'not)
802 (concat " [NOT" (ibuffer-format-qualifier-1 (cdr qualifier)) "]")
803 (ibuffer-format-qualifier-1 qualifier)))
805 (defun ibuffer-format-qualifier-1 (qualifier)
806 (case (car qualifier)
808 (concat " [filter: " (cdr qualifier) "]"))
810 (concat " [OR" (mapconcat #'ibuffer-format-qualifier
811 (cdr qualifier) "") "]"))
813 (let ((type (assq (car qualifier) ibuffer-filtering-alist)))
815 (error "Ibuffer: bad qualifier %s" qualifier))
816 (concat " [" (cadr type) ": " (format "%s]" (cdr qualifier)))))))
818 ;;; Extra operation definitions
820 (define-ibuffer-filter mode
821 "Toggle current view to buffers with major mode QUALIFIER."
822 (:description "major mode"
825 (completing-read "Filter by major mode: " obarray
827 (string-match "-mode$"
830 (let ((buf (ibuffer-current-buffer)))
831 (if (and buf (buffer-live-p buf))
832 (with-current-buffer buf
833 (symbol-name major-mode))
835 (eq qualifier (with-current-buffer buf major-mode)))
837 (define-ibuffer-filter name
838 "Toggle current view to buffers with name matching QUALIFIER."
839 (:description "buffer name"
841 (read-from-minibuffer "Filter by name (regexp): "))
842 (string-match qualifier (buffer-name buf)))
844 (define-ibuffer-filter filename
845 "Toggle current view to buffers with filename matching QUALIFIER."
846 (:description "filename"
848 (read-from-minibuffer "Filter by filename (regexp): "))
849 (ibuffer-awhen (buffer-file-name buf)
850 (string-match qualifier it)))
852 (define-ibuffer-filter size-gt
853 "Toggle current view to buffers with size greater than QUALIFIER."
854 (:description "size greater than"
856 (string-to-number (read-from-minibuffer "Filter by size greater than: ")))
857 (> (with-current-buffer buf (buffer-size))
860 (define-ibuffer-filter size-lt
861 "Toggle current view to buffers with size less than QUALIFIER."
862 (:description "size less than"
864 (string-to-number (read-from-minibuffer "Filter by size less than: ")))
865 (< (with-current-buffer buf (buffer-size))
868 (define-ibuffer-filter content
869 "Toggle current view to buffers whose contents match QUALIFIER."
870 (:description "content"
872 (read-from-minibuffer "Filter by content (regexp): "))
873 (with-current-buffer buf
875 (goto-char (point-min))
876 (re-search-forward qualifier nil t))))
878 (define-ibuffer-filter predicate
879 "Toggle current view to buffers for which QUALIFIER returns non-nil."
880 (:description "predicate"
882 (read-minibuffer "Filter by predicate (form): "))
883 (with-current-buffer buf
889 (defun ibuffer-toggle-sorting-mode ()
890 "Toggle the current sorting mode.
891 Default sorting modes are:
892 Recency - the last time the buffer was viewed
893 Alphabetic - the `buffer-name' of the buffer
894 Major Mode - the `major-mode' of the buffer
895 Mode Name - the `mode-name' of the buffer
896 Size - the `buffer-size' of the buffer"
898 (let ((modes (if (eq ibuffer-toggle-sorting-modes 'all)
899 (cons 'recency (mapcar 'car ibuffer-sorting-functions-alist))
900 (if (listp ibuffer-toggle-sorting-modes)
901 ibuffer-toggle-sorting-modes
902 '(alphabetic major-mode mode-name buffer-size)))))
903 (setq modes (sort modes 'string-lessp))
904 (let ((next (or (car-safe (cdr-safe (memq ibuffer-sorting-mode modes)))
906 (setq ibuffer-sorting-mode next)
907 (message "Sorting by %s (%s)"
908 (if (eq next 'recency)
910 (cadr (assq next ibuffer-sorting-functions-alist)))
912 (ibuffer-redisplay t))
915 (defun ibuffer-invert-sorting ()
916 "Toggle whether or not sorting is in reverse order."
918 (setq ibuffer-sorting-reversep (not ibuffer-sorting-reversep))
919 (message "Sorting order %s"
920 (if ibuffer-sorting-reversep
923 (ibuffer-redisplay t))
925 (define-ibuffer-sorter major-mode
926 "Sort the buffers by major modes.
927 Ordering is lexicographic."
928 (:description "major mode")
929 (string-lessp (downcase
930 (symbol-name (with-current-buffer
934 (symbol-name (with-current-buffer
938 (define-ibuffer-sorter mode-name
939 "Sort the buffer by mode names.
940 Ordering is lexicographic."
941 (:description "major mode name")
942 (string-lessp (downcase
951 (define-ibuffer-sorter alphabetic
952 "Sort the buffers by their names.
953 Ordering is lexicographic."
954 (:description "buffer name")
956 (buffer-name (car a))
957 (buffer-name (car b))))
959 (define-ibuffer-sorter size
960 "Sort the buffers by their size."
961 (:description "buffer size")
962 (< (with-current-buffer (car a)
964 (with-current-buffer (car b)
967 ;;; Functions to emulate bs.el
970 (defun ibuffer-bs-show ()
971 "Emulate `bs-show' from the bs.el package."
973 (ibuffer t "*Ibuffer-bs*" '((filename . ".*")) nil t)
974 (define-key (current-local-map) "a" 'ibuffer-bs-toggle-all))
976 (defun ibuffer-bs-toggle-all ()
977 "Emulate `bs-toggle-show-all' from the bs.el package."
979 (if ibuffer-filtering-qualifiers
981 (progn (ibuffer-push-filter '(filename . ".*"))
982 (ibuffer-update nil t))))
987 (defun ibuffer-add-to-tmp-hide (regexp)
988 "Add REGEXP to `ibuffer-tmp-hide-regexps'.
989 This means that buffers whose name matches REGEXP will not be shown
990 for this ibuffer session."
993 (read-from-minibuffer "Never show buffers matching: "
994 (regexp-quote (buffer-name (ibuffer-current-buffer t))))))
995 (push regexp ibuffer-tmp-hide-regexps))
998 (defun ibuffer-add-to-tmp-show (regexp)
999 "Add REGEXP to `ibuffer-tmp-show-regexps'.
1000 This means that buffers whose name matches REGEXP will always be shown
1001 for this ibuffer session."
1004 (read-from-minibuffer "Always show buffers matching: "
1005 (regexp-quote (buffer-name (ibuffer-current-buffer t))))))
1006 (push regexp ibuffer-tmp-show-regexps))
1009 (defun ibuffer-forward-next-marked (&optional count mark direction)
1010 "Move forward by COUNT marked buffers (default 1).
1012 If MARK is non-nil, it should be a character denoting the type of mark
1013 to move by. The default is `ibuffer-marked-char'.
1015 If DIRECTION is non-nil, it should be an integer; negative integers
1016 mean move backwards, non-negative integers mean move forwards."
1021 (setq mark ibuffer-marked-char))
1025 (ibuffer-forward-line 0)
1026 (let ((opos (point))
1028 (ibuffer-forward-line direction)
1029 (while (not (or (= (point) opos)
1030 (eq (setq curmark (ibuffer-current-mark))
1032 (ibuffer-forward-line direction))
1033 (when (and (= (point) opos)
1034 (not (eq (ibuffer-current-mark) mark)))
1035 (error "No buffers with mark %c" mark))))
1038 (defun ibuffer-backwards-next-marked (&optional count mark)
1039 "Move backwards by COUNT marked buffers (default 1).
1041 If MARK is non-nil, it should be a character denoting the type of mark
1042 to move by. The default is `ibuffer-marked-char'."
1044 (ibuffer-forward-next-marked count mark -1))
1047 (defun ibuffer-do-kill-lines ()
1048 "Hide all of the currently marked lines."
1050 (if (= (ibuffer-count-marked-lines) 0)
1051 (message "No buffers marked; use 'm' to mark a buffer")
1053 (ibuffer-map-marked-lines
1054 #'(lambda (buf mark)
1056 (message "Killed %s lines" count))))
1059 (defun ibuffer-jump-to-buffer (name)
1060 "Move point to the buffer whose name is NAME."
1061 (interactive (list nil))
1062 (let ((table (mapcar #'(lambda (x)
1063 (cons (buffer-name (car x))
1065 (ibuffer-current-state-list t))))
1067 (error "No buffers!"))
1068 (when (interactive-p)
1069 (setq name (completing-read "Jump to buffer: " table nil t)))
1070 (ibuffer-aif (assoc name table)
1071 (goto-char (cdr it))
1072 (error "No buffer with name %s" name))))
1075 (defun ibuffer-diff-with-file ()
1076 "View the differences between this buffer and its associated file.
1077 This requires the external program \"diff\" to be in your `exec-path'."
1079 (let* ((buf (ibuffer-current-buffer))
1080 (buf-filename (with-current-buffer buf
1082 (unless (buffer-live-p buf)
1083 (error "Buffer %s has been killed" buf))
1084 (unless buf-filename
1085 (error "Buffer %s has no associated file" buf))
1086 (let ((diff-buf (get-buffer-create "*Ibuffer-diff*")))
1087 (with-current-buffer diff-buf
1088 (setq buffer-read-only nil)
1090 (let ((tempfile (ibuffer-make-temp-file "ibuffer-diff-")))
1093 (with-current-buffer buf
1094 (write-region (point-min) (point-max) tempfile nil 'nomessage))
1096 (apply #'call-process "diff" nil diff-buf nil
1098 (when (and (boundp 'ediff-custom-diff-options)
1099 (stringp ediff-custom-diff-options))
1100 (list ediff-custom-diff-options))
1101 (list buf-filename tempfile))))
1102 (message "No differences found")
1104 (with-current-buffer diff-buf
1105 (goto-char (point-min))
1106 (if (fboundp 'diff-mode)
1108 (fundamental-mode)))
1109 (display-buffer diff-buf))))
1110 (when (file-exists-p tempfile)
1111 (delete-file tempfile)))))
1115 (defun ibuffer-copy-filename-as-kill (&optional arg)
1116 "Copy filenames of marked buffers into the kill ring.
1117 The names are separated by a space.
1118 If a buffer has no filename, it is ignored.
1119 With a zero prefix arg, use the complete pathname of each marked file.
1121 You can then feed the file name(s) to other commands with C-y.
1123 [ This docstring shamelessly stolen from the
1124 `dired-copy-filename-as-kill' in \"dired-x\". ]"
1125 ;; Add to docstring later:
1126 ;; With C-u, use the relative pathname of each marked file.
1128 (if (= (ibuffer-count-marked-lines) 0)
1129 (message "No buffers marked; use 'm' to mark a buffer")
1130 (let ((ibuffer-copy-filename-as-kill-result "")
1131 (type (cond ((eql arg 0)
1137 (ibuffer-map-marked-lines
1138 #'(lambda (buf mark)
1139 (setq ibuffer-copy-filename-as-kill-result
1140 (concat ibuffer-copy-filename-as-kill-result
1141 (let ((name (buffer-file-name buf)))
1147 (file-name-nondirectory name)))
1150 (push ibuffer-copy-filename-as-kill-result kill-ring))))
1152 (defun ibuffer-mark-on-buffer (func &optional ibuffer-mark-on-buffer-mark group)
1155 #'(lambda (buf mark)
1156 (when (funcall func buf)
1157 (ibuffer-set-mark-1 (or ibuffer-mark-on-buffer-mark
1158 ibuffer-marked-char))
1162 (ibuffer-redisplay t)
1163 (message "Marked %s buffers" count)))
1166 (defun ibuffer-mark-by-name-regexp (regexp)
1167 "Mark all buffers whose name matches REGEXP."
1168 (interactive "sMark by name (regexp): ")
1169 (ibuffer-mark-on-buffer
1171 (string-match regexp (buffer-name buf)))))
1174 (defun ibuffer-mark-by-mode-regexp (regexp)
1175 "Mark all buffers whose major mode matches REGEXP."
1176 (interactive "sMark by major mode (regexp): ")
1177 (ibuffer-mark-on-buffer
1179 (with-current-buffer buf
1180 (string-match regexp mode-name)))))
1183 (defun ibuffer-mark-by-file-name-regexp (regexp)
1184 "Mark all buffers whose file name matches REGEXP."
1185 (interactive "sMark by file name (regexp): ")
1186 (ibuffer-mark-on-buffer
1188 (let ((name (or (buffer-file-name buf)
1189 (with-current-buffer buf
1191 (boundp 'dired-directory)
1192 (stringp dired-directory)
1193 dired-directory)))))
1195 (string-match regexp name))))))
1198 (defun ibuffer-mark-by-mode (mode)
1199 "Mark all buffers whose major mode equals MODE."
1201 (list (intern (completing-read "Mark by major mode: " obarray
1203 ;; kind of a hack...
1205 (string-match "-mode$"
1208 (let ((buf (ibuffer-current-buffer)))
1209 (if (and buf (buffer-live-p buf))
1210 (with-current-buffer buf
1211 (cons (symbol-name major-mode)
1214 (ibuffer-mark-on-buffer
1216 (with-current-buffer buf
1217 (eq major-mode mode)))))
1220 (defun ibuffer-mark-modified-buffers ()
1221 "Mark all modified buffers."
1223 (ibuffer-mark-on-buffer
1224 #'(lambda (buf) (buffer-modified-p buf))))
1227 (defun ibuffer-mark-unsaved-buffers ()
1228 "Mark all modified buffers that have an associated file."
1230 (ibuffer-mark-on-buffer
1231 #'(lambda (buf) (and (with-current-buffer buf buffer-file-name)
1232 (buffer-modified-p buf)))))
1235 (defun ibuffer-mark-dissociated-buffers ()
1236 "Mark all buffers whose associated file does not exist."
1238 (ibuffer-mark-on-buffer
1240 (with-current-buffer buf
1242 (and buffer-file-name
1243 (not (file-exists-p buffer-file-name)))
1244 (and (eq major-mode 'dired-mode)
1245 (boundp 'dired-directory)
1246 (stringp dired-directory)
1247 (not (file-exists-p (file-name-directory dired-directory)))))))))
1250 (defun ibuffer-mark-help-buffers ()
1251 "Mark buffers like *Help*, *Apropos*, *Hyper Apropos*, *Info*."
1253 (ibuffer-mark-on-buffer
1255 (with-current-buffer buf
1256 (memq major-mode ibuffer-help-buffer-modes)))))
1259 (defun ibuffer-mark-old-buffers ()
1260 "Mark buffers which have not been viewed in `ibuffer-old-time' hours."
1262 (ibuffer-mark-on-buffer
1264 (with-current-buffer buf
1265 ;; hacked from midnight.el
1266 (let ((bdt (if (boundp 'ibuffer-buffer-display-time)
1267 ibuffer-buffer-display-time
1268 buffer-display-time)))
1270 (let* ((tm (current-time))
1271 (now (+ (* (float (ash 1 16)) (car tm))
1272 (float (cadr tm)) (* 0.0000001 (caddr tm))))
1273 (then (+ (* (float (ash 1 16))
1276 (* 0.0000001 (caddr bdt)))))
1277 (> (- now then) (* 60 60 ibuffer-old-time)))))))))
1280 (defun ibuffer-mark-special-buffers ()
1281 "Mark all buffers whose name begins and ends with '*'."
1283 (ibuffer-mark-on-buffer
1284 #'(lambda (buf) (string-match "^\\*.+\\*$"
1285 (buffer-name buf)))))
1288 (defun ibuffer-mark-read-only-buffers ()
1289 "Mark all read-only buffers."
1291 (ibuffer-mark-on-buffer
1293 (with-current-buffer buf
1294 buffer-read-only))))
1297 (defun ibuffer-mark-dired-buffers ()
1298 "Mark all `dired' buffers."
1300 (ibuffer-mark-on-buffer
1302 (with-current-buffer buf
1303 (eq major-mode 'dired-mode)))))
1305 ;;; An implementation of multi-buffer `occur'
1307 (defvar ibuffer-occur-props nil)
1308 (make-variable-buffer-local 'ibuffer-occur-props)
1310 (define-derived-mode ibuffer-occur-mode occur-mode "Ibuffer-Occur"
1311 "A special form of Occur mode for multiple buffers.
1312 Note this major mode is not meant for interactive use!
1313 See also `occur-mode'."
1314 (define-key ibuffer-occur-mode-map (kbd "n") 'forward-line)
1315 (define-key ibuffer-occur-mode-map (kbd "q") 'bury-buffer)
1316 (define-key ibuffer-occur-mode-map (kbd "p") 'previous-line)
1317 (define-key ibuffer-occur-mode-map (kbd "RET") 'ibuffer-occur-display-occurence)
1318 (define-key ibuffer-occur-mode-map (kbd "f") 'ibuffer-occur-goto-occurence)
1319 (define-key ibuffer-occur-mode-map [button2] 'ibuffer-occur-mouse-display-occurence)
1320 (set (make-local-variable 'revert-buffer-function)
1321 #'ibuffer-occur-revert-buffer-function)
1322 (set (make-local-variable 'ibuffer-occur-props) nil)
1323 (setq buffer-read-only nil)
1325 (setq buffer-read-only t)
1328 (if (or (and (< 21 emacs-major-version)
1332 "to display an occurence.")))
1334 (defun ibuffer-occur-mouse-display-occurence (e)
1335 "Display occurence on this line in another window."
1337 (let* ((occurbuf (window-buffer (ibuffer-event-window e)))
1338 (target (with-current-buffer occurbuf
1339 (get-text-property (ibuffer-event-position e)
1340 'ibuffer-occur-target))))
1342 (error "No occurence on this line"))
1343 (let ((buf (car target))
1344 (line (cdr target)))
1345 (switch-to-buffer occurbuf)
1346 (delete-other-windows)
1350 (defun ibuffer-occur-goto-occurence ()
1351 "Switch to the buffer which has the occurence on this line."
1353 (ibuffer-occur-display-occurence t))
1355 (defun ibuffer-occur-display-occurence (&optional goto)
1356 "Display occurence on this line in another window."
1358 (let ((target (get-text-property (point) 'ibuffer-occur-target)))
1360 (error "No occurence on this line"))
1361 (let ((buf (car target))
1362 (line (cdr target)))
1363 (delete-other-windows)
1365 (switch-to-buffer buf)
1366 (pop-to-buffer buf))
1370 (defun ibuffer-do-occur (regexp &optional nlines)
1371 "View lines which match REGEXP in all marked buffers.
1372 Optional argument NLINES says how many lines of context to display: it
1375 (list (let* ((default (car regexp-history))
1377 (read-from-minibuffer
1379 (format "List lines matching regexp (default `%s'): "
1381 "List lines matching regexp: ")
1386 (if (equal input "")
1389 current-prefix-arg))
1390 (if (or (not (integerp nlines))
1393 (when (zerop (ibuffer-count-marked-lines))
1394 (ibuffer-set-mark ibuffer-marked-char))
1395 (let ((ibuffer-do-occur-bufs nil))
1396 ;; Accumulate a list of marked buffers
1397 (ibuffer-map-marked-lines
1398 #'(lambda (buf mark)
1399 (push buf ibuffer-do-occur-bufs)))
1400 (ibuffer-do-occur-1 regexp ibuffer-do-occur-bufs
1401 (get-buffer-create "*Ibuffer-occur*")
1404 (defun ibuffer-do-occur-1 (regexp buffers out-buf nlines)
1405 (let ((count (ibuffer-occur-engine regexp buffers out-buf nlines)))
1408 (switch-to-buffer out-buf)
1409 (setq buffer-read-only t)
1410 (delete-other-windows)
1411 (goto-char (point-min))
1412 (message "Found %s matches in %s buffers" count (length buffers)))
1413 (message "No matches found"))))
1416 (defun ibuffer-occur-revert-buffer-function (ignore-auto noconfirm)
1417 "Update the *Ibuffer occur* buffer."
1418 (assert (eq major-mode 'ibuffer-occur-mode))
1419 (ibuffer-do-occur-1 (car ibuffer-occur-props)
1420 (cadr ibuffer-occur-props)
1422 (caddr ibuffer-occur-props)))
1424 (defun ibuffer-occur-engine (regexp buffers out-buf nlines)
1425 (macrolet ((insert-get-point
1430 (maybe-put-text-property
1431 (beg end &rest args)
1432 `(when ibuffer-use-fontification
1433 (put-text-property ,beg ,end ,@args)))
1434 (maybe-ibuffer-propertize
1436 (let ((objsym (gensym "--maybe-ibuffer-propertize-")))
1437 `(let ((,objsym ,obj))
1438 (if ibuffer-use-fontification
1439 (ibuffer-propertize ,objsym ,@args)
1441 (with-current-buffer out-buf
1442 (ibuffer-occur-mode)
1443 (setq buffer-read-only nil)
1444 (let ((globalcount 0))
1445 ;; Map over all the buffers
1446 (dolist (buf buffers)
1447 (when (buffer-live-p buf)
1448 (let ((c 0) ;; count of matched lines
1450 (headerpt (with-current-buffer out-buf (point))))
1454 (goto-char (point-min)) ;; begin searching in the buffer
1456 ;; The line we're matching against
1457 (let ((curline (buffer-substring
1458 (ibuffer-line-beginning-position)
1459 (ibuffer-line-end-position))))
1460 (when (string-match regexp curline)
1461 (incf c) ;; increment match count
1463 ;; Depropertize the string, and maybe highlight the matches
1466 (ibuffer-depropertize-string curline t)
1467 (when ibuffer-use-fontification
1468 (let ((len (length curline))
1470 (while (and (< start len)
1471 (string-match regexp curline start))
1472 (put-text-property (match-beginning 0)
1474 'face ibuffer-occur-match-face
1476 (setq start (match-end 0)))))
1478 ;; Generate the string to insert for this match
1481 ;; The simple display style
1482 (concat (maybe-ibuffer-propertize
1487 ;; The complex multi-line display style
1488 (let ((prevlines (nreverse
1489 (ibuffer-accumulate-lines (- nlines))))
1490 (nextlines (ibuffer-accumulate-lines nlines))
1491 ;; The lack of `flet' seriously sucks.
1492 (fun #'(lambda (lines)
1495 (concat " :" line "\n"))
1497 (setq prevlines (funcall fun prevlines))
1498 (setq nextlines (funcall fun nextlines))
1499 ;; Yes, I am trying to win the award for the
1506 (maybe-ibuffer-propertize
1513 ;; Actually insert the match display data
1514 (with-current-buffer out-buf
1516 (end (insert-get-point
1518 (unless (= nlines 1)
1519 (insert "-------\n"))
1521 beg (1- end) 'ibuffer-occur-target (cons buf l))
1523 beg (1- end) 'mouse-face 'highlight))))))
1524 ;; On to the next line...
1527 (when (not (zerop c)) ;; is the count zero?
1528 (with-current-buffer out-buf
1529 (goto-char headerpt)
1531 (end (insert-get-point
1532 (format "%d lines matching \"%s\" in buffer %s\n"
1533 c regexp (buffer-name buf)))))
1534 (maybe-put-text-property beg (1- end) 'face 'underline))
1535 (goto-char (point-max)))))))
1536 (setq ibuffer-occur-props (list regexp buffers nlines))
1537 ;; Return the number of matches
1542 ;;; ibuf-ext.el ends here