comparison lisp/ibuf-ext.el @ 42702:fb0b4579a45d

Initial revision.
author Colin Walters <walters@gnu.org>
date Sun, 13 Jan 2002 05:55:24 +0000
parents
children ed597889bfc8
comparison
equal deleted inserted replaced
42701:86000f9347e6 42702:fb0b4579a45d
1 ;;; ibuf-ext.el --- extended features for ibuffer
2
3 ;; Copyright (C) 2000, 2001 Free Software Foundation, Inc.
4
5 ;; Author: Colin Walters <walters@verbum.org>
6 ;; Created: 2 Dec 2001
7 ;; X-RCS: $Id: ibuf-ext.el,v 1.30 2001/12/17 08:44:43 walters Exp $
8 ;; URL: http://cvs.verbum.org/ibuffer
9 ;; Keywords: buffer, convenience
10
11 ;; This file is not currently part of GNU Emacs.
12
13 ;; This program is free software; you can redistribute it and/or
14 ;; modify it under the terms of the GNU General Public License as
15 ;; published by the Free Software Foundation; either version 2, or (at
16 ;; your option) any later version.
17
18 ;; This program is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ;; General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with this program ; see the file COPYING. If not, write to
25 ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26 ;; Boston, MA 02111-1307, USA.
27
28 ;;; Commentary:
29
30 ;; These functions should be automatically loaded when called, but you
31 ;; can explicity (require 'ibuf-ext) in your ~/.emacs to have them
32 ;; preloaded.
33
34 ;;; Code:
35
36 (require 'ibuffer)
37
38 (eval-when-compile
39 (require 'derived)
40 (require 'ibuf-macs)
41 (require 'cl))
42
43 ;;; Utility functions
44 (defun ibuffer-delete-alist (key alist)
45 "Delete all entries in ALIST that have a key equal to KEY."
46 (let (entry)
47 (while (setq entry (assoc key alist))
48 (setq alist (delete entry alist)))
49 alist))
50
51 (defun ibuffer-depropertize-string (str &optional nocopy)
52 "Return a copy of STR with text properties removed.
53 If optional argument NOCOPY is non-nil, actually modify the string directly."
54 (let ((str (if nocopy
55 str
56 (copy-sequence str))))
57 (set-text-properties 0 (length str) nil str)
58 str))
59
60 (defcustom ibuffer-never-show-predicates nil
61 "A list of predicates (a regexp or function) for buffers not to display.
62 If a regexp, then it will be matched against the buffer's name.
63 If a function, it will be called with the buffer as an argument, and
64 should return non-nil if this buffer should not be shown."
65 :type '(repeat (choice regexp function))
66 :group 'ibuffer)
67
68 (defcustom ibuffer-always-show-predicates nil
69 "A list of predicates (a regexp or function) for buffers to always display.
70 If a regexp, then it will be matched against the buffer's name.
71 If a function, it will be called with the buffer as an argument, and
72 should return non-nil if this buffer should be shown.
73 Note that buffers matching one of these predicates will be shown
74 regardless of any active filters in this buffer."
75 :type '(repeat (choice regexp function))
76 :group 'ibuffer)
77
78 (defvar ibuffer-tmp-hide-regexps nil
79 "A list of regexps which should match buffer names to not show.")
80
81 (defvar ibuffer-tmp-show-regexps nil
82 "A list of regexps which should match buffer names to always show.")
83
84 (defvar ibuffer-auto-mode nil
85 "If non-nil, Ibuffer auto-mode should be enabled for this buffer.
86 Do not set this variable directly! Use the function
87 `ibuffer-auto-mode' instead.")
88
89 (defvar ibuffer-auto-buffers-changed nil)
90
91 (defcustom ibuffer-occur-match-face 'font-lock-warning-face
92 "Face used for displaying matched strings for `ibuffer-do-occur'."
93 :type 'face
94 :group 'ibuffer)
95
96 (defcustom ibuffer-saved-filters '(("gnus"
97 ((or (mode . message-mode)
98 (mode . mail-mode)
99 (mode . gnus-group-mode)
100 (mode . gnus-summary-mode)
101 (mode . gnus-article-mode))))
102 ("programming"
103 ((or (mode . emacs-lisp-mode)
104 (mode . cperl-mode)
105 (mode . c-mode)
106 (mode . java-mode)
107 (mode . idl-mode)
108 (mode . lisp-mode)))))
109
110 "An alist of filter qualifiers to switch between.
111
112 This variable should look like ((\"STRING\" QUALIFIERS)
113 (\"STRING\" QUALIFIERS) ...), where
114 QUALIFIERS is a list of the same form as
115 `ibuffer-filtering-qualifiers'.
116 See also the variables `ibuffer-filtering-qualifiers',
117 `ibuffer-filtering-alist', and the functions
118 `ibuffer-switch-to-saved-filters', `ibuffer-save-filters'."
119 :type '(repeat sexp)
120 :group 'ibuffer)
121
122 (defvar ibuffer-filtering-qualifiers nil
123 "A list like (SYMBOL . QUALIFIER) which filters the current buffer list.
124 See also `ibuffer-filtering-alist'.")
125
126 ;; This is now frobbed by `define-ibuffer-filter'.
127 (defvar ibuffer-filtering-alist nil
128 "An alist of (SYMBOL DESCRIPTION FUNCTION) which describes a filter.
129
130 You most likely do not want to modify this variable directly; see
131 `define-ibuffer-filter'.
132
133 SYMBOL is the symbolic name of the filter. DESCRIPTION is used when
134 displaying information to the user. FUNCTION is given a buffer and
135 the value of the qualifier, and returns non-nil if and only if the
136 buffer should be displayed.")
137
138 (defcustom ibuffer-old-time 3
139 "The number of days before a buffer is considered \"old\"."
140 :type 'integer
141 :group 'ibuffer)
142
143 (defcustom ibuffer-save-with-custom t
144 "If non-nil, then use Custom to save interactively changed variables.
145 Currently, this only applies to `ibuffer-saved-filters'."
146 :type 'boolean
147 :group 'ibuffer)
148
149 (defun ibuffer-ext-visible-p (buf all &optional ibuffer-buf)
150 (or
151 (ibuffer-buf-matches-predicates buf ibuffer-tmp-show-regexps)
152 (and (not
153 (or
154 (ibuffer-buf-matches-predicates buf ibuffer-tmp-hide-regexps)
155 (ibuffer-buf-matches-predicates buf ibuffer-never-show-predicates)))
156 (or all
157 (not
158 (ibuffer-buf-matches-predicates buf ibuffer-maybe-show-predicates)))
159 (or ibuffer-view-ibuffer
160 (and ibuffer-buf
161 (not (eq ibuffer-buf buf))))
162 (or
163 (ibuffer-included-in-filters-p buf ibuffer-filtering-qualifiers)
164 (ibuffer-buf-matches-predicates buf ibuffer-always-show-predicates)))))
165
166 (defun ibuffer-auto-update-changed ()
167 (when ibuffer-auto-buffers-changed
168 (setq ibuffer-auto-buffers-changed nil)
169 (mapcar #'(lambda (buf)
170 (ignore-errors
171 (with-current-buffer buf
172 (when (and ibuffer-auto-mode
173 (eq major-mode 'ibuffer-mode))
174 (ibuffer-update nil t)))))
175 (buffer-list))))
176
177 ;;;###autoload
178 (defun ibuffer-auto-mode (&optional arg)
179 "Toggle use of Ibuffer's auto-update facility.
180 With numeric ARG, enable auto-update if and only if ARG is positive."
181 (interactive)
182 (unless (eq major-mode 'ibuffer-mode)
183 (error "This buffer is not in Ibuffer mode"))
184 (set (make-local-variable 'ibuffer-auto-mode)
185 (if arg
186 (plusp arg)
187 (not ibuffer-auto-mode)))
188 (defadvice get-buffer-create (after ibuffer-notify-create activate)
189 (setq ibuffer-auto-buffers-changed t))
190 (defadvice kill-buffer (after ibuffer-notify-kill activate)
191 (setq ibuffer-auto-buffers-changed t))
192 (add-hook 'post-command-hook 'ibuffer-auto-update-changed)
193 (ibuffer-update-mode-name))
194
195 ;;;###autoload
196 (defun ibuffer-mouse-filter-by-mode (event)
197 "Enable or disable filtering by the major mode chosen via mouse."
198 (interactive "e")
199 (ibuffer-interactive-filter-by-mode event))
200
201 ;;;###autoload
202 (defun ibuffer-interactive-filter-by-mode (event-or-point)
203 "Enable or disable filtering by the major mode at point."
204 (interactive "d")
205 (if (eventp event-or-point)
206 (mouse-set-point event-or-point)
207 (goto-char event-or-point))
208 (let ((buf (ibuffer-current-buffer)))
209 (if (assq 'mode ibuffer-filtering-qualifiers)
210 (setq ibuffer-filtering-qualifiers
211 (ibuffer-delete-alist 'mode ibuffer-filtering-qualifiers))
212 (ibuffer-push-filter (cons 'mode
213 (with-current-buffer buf
214 major-mode)))))
215 (ibuffer-update nil t))
216
217 ;;;###autoload
218 (define-ibuffer-op shell-command-pipe (command)
219 "Pipe the contents of each marked buffer to shell command COMMAND."
220 (:interactive "sPipe to shell command: "
221 :opstring "Shell command executed on"
222 :modifier-p nil)
223 (shell-command-on-region
224 (point-min) (point-max) command
225 (get-buffer-create "* ibuffer-shell-output*")))
226
227 ;;;###autoload
228 (define-ibuffer-op shell-command-pipe-replace (command)
229 "Replace the contents of marked buffers with output of pipe to COMMAND."
230 (:interactive "sPipe to shell command (replace): "
231 :opstring "Buffer contents replaced in"
232 :active-opstring "replace buffer contents in"
233 :dangerous t
234 :modifier-p t)
235 (with-current-buffer buf
236 (shell-command-on-region (point-min) (point-max)
237 command nil t)))
238
239 ;;;###autoload
240 (define-ibuffer-op shell-command-file (command)
241 "Run shell command COMMAND separately on files of marked buffers."
242 (:interactive "sShell command on buffer's file: "
243 :opstring "Shell command executed on"
244 :modifier-p nil)
245 (shell-command (concat command " "
246 (shell-quote-argument
247 (if buffer-file-name
248 buffer-file-name
249 (make-temp-file
250 (substring (buffer-name) 0 (min 10 (length (buffer-name))))))))))
251
252 ;;;###autoload
253 (define-ibuffer-op eval (form)
254 "Evaluate FORM in each of the buffers.
255 Does not display the buffer during evaluation. See
256 `ibuffer-do-view-and-eval' for that."
257 (:interactive "xEval in buffers (form): "
258 :opstring "evaluated in"
259 :modifier-p :maybe)
260 (eval form))
261
262 ;;;###autoload
263 (define-ibuffer-op view-and-eval (form)
264 "Evaluate FORM while displaying each of the marked buffers.
265 To evaluate a form without viewing the buffer, see `ibuffer-do-eval'."
266 (:interactive "xEval viewing buffers (form): "
267 :opstring "evaluated in"
268 :complex t
269 :modifier-p :maybe)
270 (let ((ibuffer-buf (current-buffer)))
271 (unwind-protect
272 (progn
273 (switch-to-buffer buf)
274 (eval form))
275 (switch-to-buffer ibuffer-buf))))
276
277 ;;;###autoload
278 (define-ibuffer-op rename-uniquely ()
279 "Rename marked buffers as with `rename-uniquely'."
280 (:opstring "renamed"
281 :modifier-p t)
282 (rename-uniquely))
283
284 ;;;###autoload
285 (define-ibuffer-op revert ()
286 "Revert marked buffers as with `revert-buffer'."
287 (:dangerous t
288 :opstring "reverted"
289 :active-opstring "revert"
290 :modifier-p :maybe)
291 (revert-buffer t t))
292
293 ;;;###autoload
294 (define-ibuffer-op replace-regexp (from-str to-str)
295 "Perform a `replace-regexp' in marked buffers."
296 (:interactive
297 (let* ((from-str (read-from-minibuffer "Replace regexp: "))
298 (to-str (read-from-minibuffer (concat "Replace " from-str
299 " with: "))))
300 (list from-str to-str))
301 :opstring "replaced in"
302 :complex t
303 :modifier-p :maybe)
304 (save-window-excursion
305 (switch-to-buffer buf)
306 (save-excursion
307 (goto-char (point-min))
308 (let ((case-fold-search ibuffer-case-fold-search))
309 (while (re-search-forward from-str nil t)
310 (replace-match to-str))))
311 t))
312
313 ;;;###autoload
314 (define-ibuffer-op query-replace (&rest args)
315 "Perform a `query-replace' in marked buffers."
316 (:interactive
317 (query-replace-read-args "Query replace" t)
318 :opstring "replaced in"
319 :complex t
320 :modifier-p :maybe)
321 (save-window-excursion
322 (switch-to-buffer buf)
323 (save-excursion
324 (let ((case-fold-search ibuffer-case-fold-search))
325 (goto-char (point-min))
326 (apply #'query-replace args)))
327 t))
328
329 ;;;###autoload
330 (define-ibuffer-op query-replace-regexp (&rest args)
331 "Perform a `query-replace-regexp' in marked buffers."
332 (:interactive
333 (query-replace-read-args "Query replace regexp" t)
334 :opstring "replaced in"
335 :complex t
336 :modifier-p :maybe)
337 (save-window-excursion
338 (switch-to-buffer buf)
339 (save-excursion
340 (let ((case-fold-search ibuffer-case-fold-search))
341 (goto-char (point-min))
342 (apply #'query-replace-regexp args)))
343 t))
344
345 ;;;###autoload
346 (define-ibuffer-op print ()
347 "Print marked buffers as with `print-buffer'."
348 (:opstring "printed"
349 :modifier-p nil)
350 (print-buffer))
351
352 ;;;###autoload
353 (defun ibuffer-included-in-filters-p (buf filters)
354 (not
355 (memq nil ;; a filter will return nil if it failed
356 (mapcar
357 ;; filter should be like (TYPE . QUALIFIER), or
358 ;; (or (TYPE . QUALIFIER) (TYPE . QUALIFIER) ...)
359 #'(lambda (qual)
360 (ibuffer-included-in-filter-p buf qual))
361 filters))))
362
363 (defun ibuffer-included-in-filter-p (buf filter)
364 (if (eq (car filter) 'not)
365 (not (ibuffer-included-in-filter-p-1 buf (cdr filter)))
366 (ibuffer-included-in-filter-p-1 buf filter)))
367
368 (defun ibuffer-included-in-filter-p-1 (buf filter)
369 (not
370 (not
371 (case (car filter)
372 (or
373 (memq t (mapcar #'(lambda (x)
374 (ibuffer-included-in-filter-p buf x))
375 (cdr filter))))
376 (saved
377 (let ((data
378 (assoc (cdr filter)
379 ibuffer-saved-filters)))
380 (unless data
381 (ibuffer-filter-disable)
382 (error "Unknown saved filter %s" (cdr filter)))
383 (ibuffer-included-in-filters-p buf (cadr data))))
384 (t
385 (let ((filterdat (assq (car filter)
386 ibuffer-filtering-alist)))
387 ;; filterdat should be like (TYPE DESCRIPTION FUNC)
388 ;; just a sanity check
389 (unless filterdat
390 (ibuffer-filter-disable)
391 (error "Undefined filter %s" (car filter)))
392 (not
393 (not
394 (funcall (caddr filterdat)
395 buf
396 (cdr filter))))))))))
397
398 ;;;###autoload
399 (defun ibuffer-filter-disable ()
400 "Disable all filters currently in effect in this buffer."
401 (interactive)
402 (setq ibuffer-filtering-qualifiers nil)
403 (ibuffer-update nil t))
404
405 ;;;###autoload
406 (defun ibuffer-pop-filter ()
407 "Remove the top filter in this buffer."
408 (interactive)
409 (when (null ibuffer-filtering-qualifiers)
410 (error "No filters in effect"))
411 (pop ibuffer-filtering-qualifiers)
412 (ibuffer-update nil t))
413
414 (defun ibuffer-push-filter (qualifier)
415 "Add QUALIFIER to `ibuffer-filtering-qualifiers'."
416 (push qualifier ibuffer-filtering-qualifiers))
417
418 ;;;###autoload
419 (defun ibuffer-decompose-filter ()
420 "Separate the top compound filter (OR, NOT, or SAVED) in this buffer.
421
422 This means that the topmost filter on the filtering stack, which must
423 be a complex filter like (OR [name: foo] [mode: bar-mode]), will be
424 turned into two separate filters [name: foo] and [mode: bar-mode]."
425 (interactive)
426 (when (null ibuffer-filtering-qualifiers)
427 (error "No filters in effect"))
428 (let ((lim (pop ibuffer-filtering-qualifiers)))
429 (case (car lim)
430 (or
431 (setq ibuffer-filtering-qualifiers (append
432 (cdr lim)
433 ibuffer-filtering-qualifiers)))
434 (saved
435 (let ((data
436 (assoc (cdr lim)
437 ibuffer-saved-filters)))
438 (unless data
439 (ibuffer-filter-disable)
440 (error "Unknown saved filter %s" (cdr lim)))
441 (setq ibuffer-filtering-qualifiers (append
442 (cadr data)
443 ibuffer-filtering-qualifiers))))
444 (not
445 (push (cdr lim)
446 ibuffer-filtering-qualifiers))
447 (t
448 (error "Filter type %s is not compound" (car lim)))))
449 (ibuffer-update nil t))
450
451 ;;;###autoload
452 (defun ibuffer-exchange-filters ()
453 "Exchange the top two filters on the stack in this buffer."
454 (interactive)
455 (when (< (length ibuffer-filtering-qualifiers)
456 2)
457 (error "Need two filters to exchange"))
458 (let ((first (pop ibuffer-filtering-qualifiers))
459 (second (pop ibuffer-filtering-qualifiers)))
460 (push first ibuffer-filtering-qualifiers)
461 (push second ibuffer-filtering-qualifiers))
462 (ibuffer-update nil t))
463
464 ;;;###autoload
465 (defun ibuffer-negate-filter ()
466 "Negate the sense of the top filter in the current buffer."
467 (interactive)
468 (when (null ibuffer-filtering-qualifiers)
469 (error "No filters in effect"))
470 (let ((lim (pop ibuffer-filtering-qualifiers)))
471 (push (if (eq (car lim) 'not)
472 (cdr lim)
473 (cons 'not lim))
474 ibuffer-filtering-qualifiers))
475 (ibuffer-update nil t))
476
477 ;;;###autoload
478 (defun ibuffer-or-filter (&optional reverse)
479 "Replace the top two filters in this buffer with their logical OR.
480 If optional argument REVERSE is non-nil, instead break the top OR
481 filter into parts."
482 (interactive "P")
483 (if reverse
484 (progn
485 (when (or (null ibuffer-filtering-qualifiers)
486 (not (eq 'or (caar ibuffer-filtering-qualifiers))))
487 (error "Top filter is not an OR"))
488 (let ((lim (pop ibuffer-filtering-qualifiers)))
489 (setq ibuffer-filtering-qualifiers (nconc (cdr lim) ibuffer-filtering-qualifiers))))
490 (when (< (length ibuffer-filtering-qualifiers) 2)
491 (error "Need two filters to OR"))
492 ;; If the second filter is an OR, just add to it.
493 (let ((first (pop ibuffer-filtering-qualifiers))
494 (second (pop ibuffer-filtering-qualifiers)))
495 (if (eq 'or (car second))
496 (push (nconc (list 'or first) (cdr second)) ibuffer-filtering-qualifiers)
497 (push (list 'or first second)
498 ibuffer-filtering-qualifiers))))
499 (ibuffer-update nil t))
500
501 (defun ibuffer-maybe-save-saved-filters ()
502 (when ibuffer-save-with-custom
503 (if (fboundp 'customize-save-variable)
504 (progn
505 (customize-save-variable 'ibuffer-saved-filters
506 ibuffer-saved-filters))
507 (message "Not saved permanently: Customize not available"))))
508
509 ;;;###autoload
510 (defun ibuffer-save-filters (name filters)
511 "Save FILTERS in this buffer with name NAME in `ibuffer-saved-filters'.
512 Interactively, prompt for NAME, and use the current filters."
513 (interactive
514 (if (null ibuffer-filtering-qualifiers)
515 (error "No filters currently in effect")
516 (list
517 (read-from-minibuffer "Save current filters as: ")
518 ibuffer-filtering-qualifiers)))
519 (ibuffer-aif (assoc name ibuffer-saved-filters)
520 (setcdr it filters)
521 (push (list name filters) ibuffer-saved-filters))
522 (ibuffer-maybe-save-saved-filters)
523 (ibuffer-update-mode-name))
524
525 ;;;###autoload
526 (defun ibuffer-delete-saved-filters (name)
527 "Delete saved filters with NAME from `ibuffer-saved-filters'."
528 (interactive
529 (list
530 (if (null ibuffer-saved-filters)
531 (error "No saved filters")
532 (completing-read "Delete saved filters: "
533 ibuffer-saved-filters nil t))))
534 (setq ibuffer-saved-filters
535 (ibuffer-delete-alist name ibuffer-saved-filters))
536 (ibuffer-maybe-save-saved-filters)
537 (ibuffer-update nil t))
538
539 ;;;###autoload
540 (defun ibuffer-add-saved-filters (name)
541 "Add saved filters from `ibuffer-saved-filters' to this buffer's filters."
542 (interactive
543 (list
544 (if (null ibuffer-saved-filters)
545 (error "No saved filters")
546 (completing-read "Add saved filters: "
547 ibuffer-saved-filters nil t))))
548 (push (cons 'saved name) ibuffer-filtering-qualifiers)
549 (ibuffer-update nil t))
550
551 ;;;###autoload
552 (defun ibuffer-switch-to-saved-filters (name)
553 "Set this buffer's filters to filters with NAME from `ibuffer-saved-filters'.
554 If prefix argument ADD is non-nil, then add the saved filters instead
555 of replacing the current filters."
556 (interactive
557 (list
558 (if (null ibuffer-saved-filters)
559 (error "No saved filters")
560 (completing-read "Switch to saved filters: "
561 ibuffer-saved-filters nil t))))
562 (setq ibuffer-filtering-qualifiers (list (cons 'saved name)))
563 (ibuffer-update nil t))
564
565 (defun ibuffer-format-qualifier (qualifier)
566 (if (eq (car-safe qualifier) 'not)
567 (concat " [NOT" (ibuffer-format-qualifier-1 (cdr qualifier)) "]")
568 (ibuffer-format-qualifier-1 qualifier)))
569
570 (defun ibuffer-format-qualifier-1 (qualifier)
571 (case (car qualifier)
572 (saved
573 (concat " [filter: " (cdr qualifier) "]"))
574 (or
575 (concat " [OR" (mapconcat #'ibuffer-format-qualifier
576 (cdr qualifier) "") "]"))
577 (t
578 (let ((type (assq (car qualifier) ibuffer-filtering-alist)))
579 (unless qualifier
580 (error "Ibuffer: bad qualifier %s" qualifier))
581 (concat " [" (cadr type) ": " (format "%s]" (cdr qualifier)))))))
582
583 ;;; Extra operation definitions
584
585 ;;;###autoload
586 (define-ibuffer-filter mode
587 "Toggle current view to buffers with major mode QUALIFIER."
588 (:description "major mode"
589 :reader
590 (intern
591 (completing-read "Filter by major mode: " obarray
592 #'(lambda (e)
593 (string-match "-mode$"
594 (symbol-name e)))
595 t
596 (let ((buf (ibuffer-current-buffer)))
597 (if (and buf (buffer-live-p buf))
598 (with-current-buffer buf
599 (symbol-name major-mode))
600 "")))))
601 (eq qualifier (with-current-buffer buf major-mode)))
602
603 ;;;###autoload
604 (define-ibuffer-filter name
605 "Toggle current view to buffers with name matching QUALIFIER."
606 (:description "buffer name"
607 :reader
608 (read-from-minibuffer "Filter by name (regexp): "))
609 (string-match qualifier (buffer-name buf)))
610
611 ;;;###autoload
612 (define-ibuffer-filter filename
613 "Toggle current view to buffers with filename matching QUALIFIER."
614 (:description "filename"
615 :reader
616 (read-from-minibuffer "Filter by filename (regexp): "))
617 (ibuffer-awhen (buffer-file-name buf)
618 (string-match qualifier it)))
619
620 ;;;###autoload
621 (define-ibuffer-filter size-gt
622 "Toggle current view to buffers with size greater than QUALIFIER."
623 (:description "size greater than"
624 :reader
625 (string-to-number (read-from-minibuffer "Filter by size greater than: ")))
626 (> (with-current-buffer buf (buffer-size))
627 qualifier))
628
629 ;;;###autoload
630 (define-ibuffer-filter size-lt
631 "Toggle current view to buffers with size less than QUALIFIER."
632 (:description "size less than"
633 :reader
634 (string-to-number (read-from-minibuffer "Filter by size less than: ")))
635 (< (with-current-buffer buf (buffer-size))
636 qualifier))
637
638 ;;;###autoload
639 (define-ibuffer-filter content
640 "Toggle current view to buffers whose contents match QUALIFIER."
641 (:description "content"
642 :reader
643 (read-from-minibuffer "Filter by content (regexp): "))
644 (with-current-buffer buf
645 (save-excursion
646 (goto-char (point-min))
647 (re-search-forward qualifier nil t))))
648
649 ;;;###autoload
650 (define-ibuffer-filter predicate
651 "Toggle current view to buffers for which QUALIFIER returns non-nil."
652 (:description "predicate"
653 :reader
654 (read-minibuffer "Filter by predicate (form): "))
655 (with-current-buffer buf
656 (eval qualifier)))
657
658 ;;; Sorting
659
660 ;;;###autoload
661 (defun ibuffer-toggle-sorting-mode ()
662 "Toggle the current sorting mode.
663 Possible sorting modes are:
664 Recency - the last time the buffer was viewed
665 Name - the name of the buffer
666 Major Mode - the name of the major mode of the buffer
667 Size - the size of the buffer"
668 (interactive)
669 (let* ((keys (mapcar #'car ibuffer-sorting-functions-alist))
670 (entry (memq ibuffer-sorting-mode keys))
671 (next (or (cadr entry) (car keys)))
672 (nextentry (assq next ibuffer-sorting-functions-alist)))
673 (if (and entry nextentry)
674 (progn
675 (setq ibuffer-sorting-mode next)
676 (message "Sorting by %s" (cadr nextentry)))
677 (progn
678 (setq ibuffer-sorting-mode 'recency)
679 (message "Sorting by last view time"))))
680 (ibuffer-redisplay t))
681
682 ;;;###autoload
683 (defun ibuffer-invert-sorting ()
684 "Toggle whether or not sorting is in reverse order."
685 (interactive)
686 (setq ibuffer-sorting-reversep (not ibuffer-sorting-reversep))
687 (message "Sorting order %s"
688 (if ibuffer-sorting-reversep
689 "reversed"
690 "normal"))
691 (ibuffer-redisplay t))
692
693 ;;;###autoload
694 (define-ibuffer-sorter major-mode
695 "Sort the buffers by major modes.
696 Ordering is lexicographic."
697 (:description "major mode")
698 (string-lessp (downcase
699 (symbol-name (with-current-buffer
700 (car a)
701 major-mode)))
702 (downcase
703 (symbol-name (with-current-buffer
704 (car b)
705 major-mode)))))
706
707 ;;;###autoload
708 (define-ibuffer-sorter alphabetic
709 "Sort the buffers by their names.
710 Ordering is lexicographic."
711 (:description "buffer name")
712 (string-lessp
713 (buffer-name (car a))
714 (buffer-name (car b))))
715
716 ;;;###autoload
717 (define-ibuffer-sorter size
718 "Sort the buffers by their size."
719 (:description "size")
720 (< (with-current-buffer (car a)
721 (buffer-size))
722 (with-current-buffer (car b)
723 (buffer-size))))
724
725 ;;; Functions to emulate bs.el
726
727 ;;;###autoload
728 (defun ibuffer-bs-show ()
729 "Emulate `bs-show' from the bs.el package."
730 (interactive)
731 (ibuffer t "*Ibuffer-bs*" '((filename . ".*")) nil t)
732 (define-key (current-local-map) "a" 'ibuffer-bs-toggle-all))
733
734 (defun ibuffer-bs-toggle-all ()
735 "Emulate `bs-toggle-show-all' from the bs.el package."
736 (interactive)
737 (if ibuffer-filtering-qualifiers
738 (ibuffer-pop-filter)
739 (progn (ibuffer-push-filter '(filename . ".*"))
740 (ibuffer-update nil t))))
741
742 ;;; Handy functions
743
744 ;;;###autoload
745 (defun ibuffer-add-to-tmp-hide (regexp)
746 "Add REGEXP to `ibuffer-tmp-hide-regexps'.
747 This means that buffers whose name matches REGEXP will not be shown
748 for this ibuffer session."
749 (interactive
750 (list
751 (read-from-minibuffer "Never show buffers matching: "
752 (regexp-quote (buffer-name (ibuffer-current-buffer t))))))
753 (push regexp ibuffer-tmp-hide-regexps))
754
755 ;;;###autoload
756 (defun ibuffer-add-to-tmp-show (regexp)
757 "Add REGEXP to `ibuffer-tmp-show-regexps'.
758 This means that buffers whose name matches REGEXP will always be shown
759 for this ibuffer session."
760 (interactive
761 (list
762 (read-from-minibuffer "Always show buffers matching: "
763 (regexp-quote (buffer-name (ibuffer-current-buffer t))))))
764 (push regexp ibuffer-tmp-show-regexps))
765
766 ;;;###autoload
767 (defun ibuffer-forward-next-marked (&optional count mark direction)
768 "Move forward by COUNT marked buffers (default 1).
769
770 If MARK is non-nil, it should be a character denoting the type of mark
771 to move by. The default is `ibuffer-marked-char'.
772
773 If DIRECTION is non-nil, it should be an integer; negative integers
774 mean move backwards, non-negative integers mean move forwards."
775 (interactive "P")
776 (unless count
777 (setq count 1))
778 (unless mark
779 (setq mark ibuffer-marked-char))
780 (unless direction
781 (setq direction 1))
782 ;; Skip the title
783 (ibuffer-forward-line 0)
784 (let ((opos (point))
785 curmark)
786 (ibuffer-forward-line direction)
787 (while (not (or (= (point) opos)
788 (eq (setq curmark (ibuffer-current-mark))
789 mark)))
790 (ibuffer-forward-line direction))
791 (when (and (= (point) opos)
792 (not (eq (ibuffer-current-mark) mark)))
793 (error "No buffers with mark %c" mark))))
794
795 ;;;###autoload
796 (defun ibuffer-backwards-next-marked (&optional count mark)
797 "Move backwards by COUNT marked buffers (default 1).
798
799 If MARK is non-nil, it should be a character denoting the type of mark
800 to move by. The default is `ibuffer-marked-char'."
801 (interactive "P")
802 (ibuffer-forward-next-marked count mark -1))
803
804 ;;;###autoload
805 (defun ibuffer-do-kill-lines ()
806 "Hide all of the currently marked lines."
807 (interactive)
808 (if (= (ibuffer-count-marked-lines) 0)
809 (message "No buffers marked; use 'm' to mark a buffer")
810 (let ((count
811 (ibuffer-map-marked-lines
812 #'(lambda (buf mark beg end)
813 'kill))))
814 (message "Killed %s lines" count))))
815
816 ;;;###autoload
817 (defun ibuffer-jump-to-buffer (name)
818 "Move point to the buffer whose name is NAME."
819 (interactive (list nil))
820 (let ((table (mapcar #'(lambda (x)
821 (cons (buffer-name (car x))
822 (caddr x)))
823 (ibuffer-current-state-list t))))
824 (when (null table)
825 (error "No buffers!"))
826 (when (interactive-p)
827 (setq name (completing-read "Jump to buffer: " table nil t)))
828 (ibuffer-aif (assoc name table)
829 (goto-char (cdr it))
830 (error "No buffer with name %s" name))))
831
832 ;;;###autoload
833 (defun ibuffer-diff-with-file ()
834 "View the differences between this buffer and its associated file.
835 This requires the external program \"diff\" to be in your `exec-path'."
836 (interactive)
837 (let* ((buf (ibuffer-current-buffer))
838 (buf-filename (with-current-buffer buf
839 buffer-file-name)))
840 (unless (buffer-live-p buf)
841 (error "Buffer %s has been killed" buf))
842 (unless buf-filename
843 (error "Buffer %s has no associated file" buf))
844 (let ((diff-buf (get-buffer-create "*Ibuffer-diff*")))
845 (with-current-buffer diff-buf
846 (setq buffer-read-only nil)
847 (erase-buffer))
848 (let ((tempfile (make-temp-file "ibuffer-diff-")))
849 (unwind-protect
850 (progn
851 (with-current-buffer buf
852 (write-region (point-min) (point-max) tempfile nil 'nomessage))
853 (if (zerop
854 (apply #'call-process "diff" nil diff-buf nil
855 (append
856 (when (and (boundp 'ediff-custom-diff-options)
857 (stringp ediff-custom-diff-options))
858 (list ediff-custom-diff-options))
859 (list buf-filename tempfile))))
860 (message "No differences found")
861 (progn
862 (with-current-buffer diff-buf
863 (goto-char (point-min))
864 (if (fboundp 'diff-mode)
865 (diff-mode)
866 (fundamental-mode)))
867 (display-buffer diff-buf))))
868 (when (file-exists-p tempfile)
869 (delete-file tempfile)))))
870 nil))
871
872 ;;;###autoload
873 (defun ibuffer-copy-filename-as-kill (&optional arg)
874 "Copy filenames of marked buffers into the kill ring.
875 The names are separated by a space.
876 If a buffer has no filename, it is ignored.
877 With a zero prefix arg, use the complete pathname of each marked file.
878
879 You can then feed the file name(s) to other commands with C-y.
880
881 [ This docstring shamelessly stolen from the
882 `dired-copy-filename-as-kill' in \"dired-x\". ]"
883 ;; Add to docstring later:
884 ;; With C-u, use the relative pathname of each marked file.
885 (interactive "P")
886 (if (= (ibuffer-count-marked-lines) 0)
887 (message "No buffers marked; use 'm' to mark a buffer")
888 (let ((ibuffer-copy-filename-as-kill-result "")
889 (type (cond ((eql arg 0)
890 'full)
891 ;; ((eql arg 4)
892 ;; 'relative)
893 (t
894 'name))))
895 (ibuffer-map-marked-lines
896 #'(lambda (buf mark beg end)
897 (setq ibuffer-copy-filename-as-kill-result
898 (concat ibuffer-copy-filename-as-kill-result
899 (let ((name (buffer-file-name buf)))
900 (if name
901 (case type
902 (full
903 name)
904 (t
905 (file-name-nondirectory name)))
906 ""))
907 " "))))
908 (push ibuffer-copy-filename-as-kill-result kill-ring))))
909
910 (defun ibuffer-mark-on-buffer (func)
911 (let ((count
912 (ibuffer-map-lines
913 #'(lambda (buf mark beg end)
914 (when (funcall func buf)
915 (ibuffer-set-mark-1 ibuffer-marked-char)
916 t)))))
917 (ibuffer-redisplay t)
918 (message "Marked %s buffers" count)))
919
920 ;;;###autoload
921 (defun ibuffer-mark-by-name-regexp (regexp)
922 "Mark all buffers whose name matches REGEXP."
923 (interactive "sMark by name (regexp): ")
924 (ibuffer-mark-on-buffer
925 #'(lambda (buf)
926 (string-match regexp (buffer-name buf)))))
927
928 ;;;###autoload
929 (defun ibuffer-mark-by-mode-regexp (regexp)
930 "Mark all buffers whose major mode matches REGEXP."
931 (interactive "sMark by major mode (regexp): ")
932 (ibuffer-mark-on-buffer
933 #'(lambda (buf)
934 (with-current-buffer buf
935 (string-match regexp mode-name)))))
936
937 ;;;###autoload
938 (defun ibuffer-mark-by-file-name-regexp (regexp)
939 "Mark all buffers whose file name matches REGEXP."
940 (interactive "sMark by file name (regexp): ")
941 (ibuffer-mark-on-buffer
942 #'(lambda (buf)
943 (let ((name (or (buffer-file-name buf)
944 (with-current-buffer buf
945 (and
946 (boundp 'dired-directory)
947 (stringp dired-directory)
948 dired-directory)))))
949 (when name
950 (string-match regexp name))))))
951
952 ;;;###autoload
953 (defun ibuffer-mark-by-mode (mode)
954 "Mark all buffers whose major mode equals MODE."
955 (interactive
956 (list (intern (completing-read "Mark by major mode: " obarray
957 #'(lambda (e)
958 ;; kind of a hack...
959 (and (fboundp e)
960 (string-match "-mode$"
961 (symbol-name e))))
962 t
963 (let ((buf (ibuffer-current-buffer)))
964 (if (and buf (buffer-live-p buf))
965 (with-current-buffer buf
966 (cons (symbol-name major-mode)
967 0))
968 ""))))))
969 (ibuffer-mark-on-buffer
970 #'(lambda (buf)
971 (with-current-buffer buf
972 (eq major-mode mode)))))
973
974 ;;;###autoload
975 (defun ibuffer-mark-modified-buffers ()
976 "Mark all modified buffers."
977 (interactive)
978 (ibuffer-mark-on-buffer
979 #'(lambda (buf) (buffer-modified-p buf))))
980
981 ;;;###autoload
982 (defun ibuffer-mark-unsaved-buffers ()
983 "Mark all modified buffers that have an associated file."
984 (interactive)
985 (ibuffer-mark-on-buffer
986 #'(lambda (buf) (and (with-current-buffer buf buffer-file-name)
987 (buffer-modified-p buf)))))
988
989 ;;;###autoload
990 (defun ibuffer-mark-dissociated-buffers ()
991 "Mark all buffers whose associated file does not exist."
992 (interactive)
993 (ibuffer-mark-on-buffer
994 #'(lambda (buf)
995 (with-current-buffer buf
996 (or
997 (and buffer-file-name
998 (not (file-exists-p buffer-file-name)))
999 (and (eq major-mode 'dired-mode)
1000 (boundp 'dired-directory)
1001 (stringp dired-directory)
1002 (not (file-exists-p (file-name-directory dired-directory)))))))))
1003
1004 ;;;###autoload
1005 (defun ibuffer-mark-help-buffers ()
1006 "Mark buffers like *Help*, *Apropos*, *Info*."
1007 (interactive)
1008 (ibuffer-mark-on-buffer
1009 #'(lambda (buf)
1010 (with-current-buffer buf
1011 (or
1012 (eq major-mode 'apropos-mode)
1013 (eq major-mode 'help-mode)
1014 (eq major-mode 'info-mode))))))
1015
1016 ;;;###autoload
1017 (defun ibuffer-mark-old-buffers ()
1018 "Mark buffers which have not been viewed in `ibuffer-old-time' days."
1019 (interactive)
1020 (ibuffer-mark-on-buffer
1021 #'(lambda (buf)
1022 (with-current-buffer buf
1023 ;; hacked from midnight.el
1024 (when buffer-display-time
1025 (let* ((tm (current-time))
1026 (now (+ (* (float (ash 1 16)) (car tm))
1027 (float (cadr tm)) (* 0.0000001 (caddr tm))))
1028 (then (+ (* (float (ash 1 16))
1029 (car buffer-display-time))
1030 (float (cadr buffer-display-time))
1031 (* 0.0000001 (caddr buffer-display-time)))))
1032 (> (- now then) (* 24 60 60 ibuffer-old-time))))))))
1033
1034 ;;;###autoload
1035 (defun ibuffer-mark-special-buffers ()
1036 "Mark all buffers whose name begins and ends with '*'."
1037 (interactive)
1038 (ibuffer-mark-on-buffer
1039 #'(lambda (buf) (string-match "^\\*.+\\*$"
1040 (buffer-name buf)))))
1041
1042 ;;;###autoload
1043 (defun ibuffer-mark-read-only-buffers ()
1044 "Mark all read-only buffers."
1045 (interactive)
1046 (ibuffer-mark-on-buffer
1047 #'(lambda (buf)
1048 (with-current-buffer buf
1049 buffer-read-only))))
1050
1051 ;;;###autoload
1052 (defun ibuffer-mark-dired-buffers ()
1053 "Mark all `dired' buffers."
1054 (interactive)
1055 (ibuffer-mark-on-buffer
1056 #'(lambda (buf)
1057 (with-current-buffer buf
1058 (eq major-mode 'dired-mode)))))
1059
1060 ;;; An implementation of multi-buffer `occur'
1061
1062 (defvar ibuffer-occur-props nil)
1063
1064 (define-derived-mode ibuffer-occur-mode occur-mode "Ibuffer-Occur"
1065 "A special form of Occur mode for multiple buffers.
1066 Note this major mode is not meant for interactive use!
1067 See also `occur-mode'."
1068 (define-key ibuffer-occur-mode-map (kbd "n") 'forward-line)
1069 (define-key ibuffer-occur-mode-map (kbd "q") 'bury-buffer)
1070 (define-key ibuffer-occur-mode-map (kbd "p") 'previous-line)
1071 (define-key ibuffer-occur-mode-map (kbd "RET") 'ibuffer-occur-display-occurence)
1072 (define-key ibuffer-occur-mode-map (kbd "f") 'ibuffer-occur-goto-occurence)
1073 (define-key ibuffer-occur-mode-map [(mouse-2)] 'ibuffer-occur-mouse-display-occurence)
1074 (set (make-local-variable 'revert-buffer-function)
1075 #'ibuffer-occur-revert-buffer-function)
1076 (set (make-local-variable 'ibuffer-occur-props) nil)
1077 (setq buffer-read-only nil)
1078 (erase-buffer)
1079 (setq buffer-read-only t)
1080 (message (concat
1081 "Use RET "
1082 (if (or (and (< 21 emacs-major-version)
1083 window-system)
1084 (featurep 'mouse))
1085 "or mouse-2 ")
1086 "to display an occurence.")))
1087
1088 (defun ibuffer-occur-mouse-display-occurence (e)
1089 "Display occurence on this line in another window."
1090 (interactive "e")
1091 (let* ((occurbuf (save-window-excursion (mouse-select-window e)
1092 (selected-window)))
1093 (target (with-current-buffer occurbuf
1094 (get-text-property (save-excursion
1095 (mouse-set-point e)
1096 (point))
1097 'ibuffer-occur-target))))
1098 (unless target
1099 (error "No occurence on this line"))
1100 (let ((buf (car target))
1101 (line (cdr target)))
1102 (switch-to-buffer occurbuf)
1103 (delete-other-windows)
1104 (pop-to-buffer buf)
1105 (goto-line line))))
1106
1107 (defun ibuffer-occur-goto-occurence ()
1108 "Switch to the buffer which has the occurence on this line."
1109 (interactive)
1110 (ibuffer-occur-display-occurence t))
1111
1112 (defun ibuffer-occur-display-occurence (&optional goto)
1113 "Display occurence on this line in another window."
1114 (interactive "P")
1115 (let ((target (get-text-property (point) 'ibuffer-occur-target)))
1116 (unless target
1117 (error "No occurence on this line"))
1118 (let ((buf (car target))
1119 (line (cdr target)))
1120 (delete-other-windows)
1121 (if goto
1122 (switch-to-buffer buf)
1123 (pop-to-buffer buf))
1124 (goto-line line))))
1125
1126 ;;;###autoload
1127 (defun ibuffer-do-occur (regexp &optional nlines)
1128 "View lines which match REGEXP in all marked buffers.
1129 Optional argument NLINES says how many lines of context to display: it
1130 defaults to one."
1131 (interactive
1132 (list (let* ((default (car regexp-history))
1133 (input
1134 (read-from-minibuffer
1135 (if default
1136 (format "List lines matching regexp (default `%s'): "
1137 default)
1138 "List lines matching regexp: ")
1139 nil
1140 nil
1141 nil
1142 'regexp-history)))
1143 (if (equal input "")
1144 default
1145 input))
1146 current-prefix-arg))
1147 (if (or (not (integerp nlines))
1148 (< nlines 0))
1149 (setq nlines 1))
1150 (when (zerop (ibuffer-count-marked-lines))
1151 (ibuffer-set-mark 'ibuffer-marked-char))
1152 (let ((ibuffer-do-occur-bufs nil))
1153 ;; Accumulate a list of marked buffers
1154 (ibuffer-map-marked-lines
1155 #'(lambda (buf mark beg end)
1156 (push buf ibuffer-do-occur-bufs)))
1157 (ibuffer-do-occur-1 regexp ibuffer-do-occur-bufs
1158 (get-buffer-create "*Ibuffer-occur*")
1159 nlines)))
1160
1161 (defun ibuffer-do-occur-1 (regexp buffers out-buf nlines)
1162 (let ((count (ibuffer-occur-engine regexp buffers out-buf nlines)))
1163 (if (> count 0)
1164 (progn
1165 (switch-to-buffer out-buf)
1166 (setq buffer-read-only t)
1167 (delete-other-windows)
1168 (goto-char (point-min))
1169 (message "Found %s matches in %s buffers" count (length buffers)))
1170 (message "No matches found"))))
1171
1172
1173 (defun ibuffer-occur-revert-buffer-function (ignore-auto noconfirm)
1174 "Update the *Ibuffer occur* buffer."
1175 (assert (eq major-mode 'ibuffer-occur-mode))
1176 (ibuffer-do-occur-1 (car ibuffer-occur-props)
1177 (cadr ibuffer-occur-props)
1178 (current-buffer)
1179 (caddr ibuffer-occur-props)))
1180
1181 (defun ibuffer-occur-engine (regexp buffers out-buf nlines)
1182 (macrolet ((insert-get-point
1183 (&rest args)
1184 `(progn
1185 (insert ,@args)
1186 (point)))
1187 (maybe-put-overlay
1188 (over prop value)
1189 `(when (ibuffer-use-fontification)
1190 (overlay-put ,over ,prop ,value)))
1191 (maybe-ibuffer-propertize
1192 (obj &rest args)
1193 (let ((objsym (gensym "--maybe-ibuffer-propertize-")))
1194 `(let ((,objsym ,obj))
1195 (if (ibuffer-use-fontification)
1196 (propertize ,objsym ,@args)
1197 ,objsym)))))
1198 (with-current-buffer out-buf
1199 (ibuffer-occur-mode)
1200 (setq buffer-read-only nil)
1201 (let ((globalcount 0))
1202 ;; Map over all the buffers
1203 (dolist (buf buffers)
1204 (when (buffer-live-p buf)
1205 (let ((c 0) ;; count of matched lines
1206 (l 1) ;; line count
1207 (headerpt (with-current-buffer out-buf (point))))
1208 (save-excursion
1209 (set-buffer buf)
1210 (save-excursion
1211 (goto-char (point-min)) ;; begin searching in the buffer
1212 (while (not (eobp))
1213 ;; The line we're matching against
1214 (let ((curline (buffer-substring
1215 (line-beginning-position)
1216 (line-end-position))))
1217 (when (string-match regexp curline)
1218 (incf c) ;; increment match count
1219 (incf globalcount)
1220 ;; Depropertize the string, and maybe highlight the matches
1221 (setq curline
1222 (progn
1223 (ibuffer-depropertize-string curline t)
1224 (when (ibuffer-use-fontification)
1225 (let ((len (length curline))
1226 (start 0))
1227 (while (and (< start len)
1228 (string-match regexp curline start))
1229 (put-text-property (match-beginning 0)
1230 (match-end 0)
1231 'face ibuffer-occur-match-face
1232 curline)
1233 (setq start (match-end 0)))))
1234 curline))
1235 ;; Generate the string to insert for this match
1236 (let ((data
1237 (if (= nlines 1)
1238 ;; The simple display style
1239 (concat (maybe-ibuffer-propertize
1240 (format "%-6d:" l)
1241 'face 'bold)
1242 curline
1243 "\n")
1244 ;; The complex multi-line display style
1245 (let ((prevlines (nreverse
1246 (ibuffer-accumulate-lines (- nlines))))
1247 (nextlines (ibuffer-accumulate-lines nlines))
1248 ;; The lack of `flet' seriously sucks.
1249 (fun #'(lambda (lines)
1250 (mapcar
1251 #'(lambda (line)
1252 (concat " :" line "\n"))
1253 lines))))
1254 (setq prevlines (funcall fun prevlines))
1255 (setq nextlines (funcall fun nextlines))
1256 ;; Yes, I am trying to win the award for the
1257 ;; most consing.
1258 (apply #'concat
1259 (nconc
1260 prevlines
1261 (list
1262 (concat
1263 (maybe-ibuffer-propertize
1264 (format "%-6d" l)
1265 'face 'bold)
1266 ":"
1267 curline
1268 "\n"))
1269 nextlines))))))
1270 ;; Actually insert the match display data
1271 (with-current-buffer out-buf
1272 (let ((beg (point))
1273 (end (insert-get-point
1274 data)))
1275 (unless (= nlines 1)
1276 (insert "-------\n"))
1277 (put-text-property
1278 beg (1- end) 'ibuffer-occur-target (cons buf l))
1279 (put-text-property
1280 beg (1- end) 'mouse-face 'highlight))))))
1281 ;; On to the next line...
1282 (incf l)
1283 (forward-line 1))))
1284 (when (not (zerop c)) ;; is the count zero?
1285 (with-current-buffer out-buf
1286 (goto-char headerpt)
1287 (let ((beg (point))
1288 (end (insert-get-point
1289 (format "%d lines matching \"%s\" in buffer %s\n"
1290 c regexp (buffer-name buf)))))
1291 (let ((o (make-overlay beg end)))
1292 (maybe-put-overlay o 'face 'underline)))
1293 (goto-char (point-max)))))))
1294 (setq ibuffer-occur-props (list regexp buffers nlines))
1295 ;; Return the number of matches
1296 globalcount))))
1297
1298 (provide 'ibuf-ext)
1299
1300 ;;; ibuf-ext.el ends here