ibuf-ext.el 53 KB

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