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