Merge from trunk
[bpt/emacs.git] / lisp / buff-menu.el
CommitLineData
5e684428 1;;; buff-menu.el --- buffer menu main function and support functions -*- coding:utf-8 -*-
c0274f38 2
73b0cd50 3;; Copyright (C) 1985-1987, 1993-1995, 2000-2011
e9bffc61 4;; Free Software Foundation, Inc.
2dd96f23 5
9750e079 6;; Maintainer: FSF
6d6c3f84 7;; Keywords: convenience
bd78fa1d 8;; Package: emacs
9750e079 9
2dd96f23
JB
10;; This file is part of GNU Emacs.
11
eb3fa2cf 12;; GNU Emacs is free software: you can redistribute it and/or modify
2dd96f23 13;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
2dd96f23
JB
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
eb3fa2cf 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
2dd96f23 24
e41b2db1
ER
25;;; Commentary:
26
27;; Edit, delete, or change attributes of all currently active Emacs
eb8c3be9 28;; buffers from a list summarizing their state. A good way to browse
e41b2db1 29;; any special or scratch buffers you have loaded, since you can't find
6d6c3f84 30;; them by filename. The single entry point is `list-buffers',
86dbbfc2
ER
31;; normally bound to C-x C-b.
32
33;;; Change Log:
34
42a19c2a
RS
35;; Buffer-menu-view: New function
36;; Buffer-menu-view-other-window: New function
37
86dbbfc2
ER
38;; Merged by esr with recent mods to Emacs 19 buff-menu, 23 Mar 1993
39;;
86dbbfc2
ER
40;; Modified by Bob Weiner, Motorola, Inc., 4/14/89
41;;
42;; Added optional backup argument to 'Buffer-menu-unmark' to make it undelete
43;; current entry and then move to previous one.
44;;
45;; Based on FSF code dating back to 1985.
e41b2db1 46
5de9bdab 47;;; Code:
10e1dad9 48
d5aacb46
SM
49;;Trying to preserve the old window configuration works well in
50;;simple scenarios, when you enter the buffer menu, use it, and exit it.
51;;But it does strange things when you switch back to the buffer list buffer
52;;with C-x b, later on, when the window configuration is different.
53;;The choice seems to be, either restore the window configuration
54;;in all cases, or in no cases.
55;;I decided it was better not to restore the window config at all. -- rms.
08c82d4d 56
d5aacb46
SM
57;;But since then, I changed buffer-menu to use the selected window,
58;;so q now once again goes back to the previous window configuration.
08c82d4d 59
d5aacb46
SM
60;;(defvar Buffer-menu-window-config nil
61;; "Window configuration saved from entry to `buffer-menu'.")
2dd96f23 62
d5aacb46
SM
63;; Put buffer *Buffer List* into proper mode right away
64;; so that from now on even list-buffers is enough to get a buffer menu.
2dd96f23 65
bc7bb432
JB
66(defgroup Buffer-menu nil
67 "Show a menu of all buffers in a buffer."
68 :group 'tools
69 :group 'convenience)
70
71(defcustom Buffer-menu-use-header-line t
9201cc28 72 "Non-nil means to use an immovable header-line."
bc7bb432
JB
73 :type 'boolean
74 :group 'Buffer-menu)
75
d247d0fe 76(defface buffer-menu-buffer
bc7bb432 77 '((t (:weight bold)))
d247d0fe 78 "Face used to highlight buffer names in the buffer menu."
2a8f2d4c 79 :group 'Buffer-menu)
d247d0fe 80(put 'Buffer-menu-buffer 'face-alias 'buffer-menu-buffer)
bc7bb432 81
4f0992b3 82(defcustom Buffer-menu-buffer+size-width 26
9201cc28 83 "How wide to jointly make the buffer name and size columns."
bc7bb432
JB
84 :type 'number
85 :group 'Buffer-menu)
86
4f0992b3 87(defcustom Buffer-menu-mode-width 16
9201cc28 88 "How wide to make the mode name column."
bc7bb432
JB
89 :type 'number
90 :group 'Buffer-menu)
91
3ce5f932
LT
92(defcustom Buffer-menu-use-frame-buffer-list t
93 "If non-nil, the Buffer Menu uses the selected frame's buffer list.
94Buffers that were never selected in that frame are listed at the end.
95If the value is nil, the Buffer Menu uses the global buffer list.
96This variable matters if the Buffer Menu is sorted by visited order,
97as it is by default."
98 :type 'boolean
99 :group 'Buffer-menu
100 :version "22.1")
101
d5aacb46 102;; This should get updated & resorted when you click on a column heading
bc7bb432 103(defvar Buffer-menu-sort-column nil
84bbd894
RS
104 "Which column to sort the menu on.
105Use 2 to sort by buffer names, or 5 to sort by file names.
392225ee 106A nil value means sort by visited order (the default).")
bc7bb432
JB
107
108(defconst Buffer-menu-buffer-column 4)
e5ea316b 109
7b0a86ab
LT
110(defvar Buffer-menu-files-only nil
111 "Non-nil if the current buffer-menu lists only file buffers.
112This variable determines whether reverting the buffer lists only
113file buffers. It affects both manual reverting and reverting by
114Auto Revert Mode.")
392225ee
JB
115(make-variable-buffer-local 'Buffer-menu-files-only)
116
362b9d48
GM
117(defvar Buffer-menu--buffers nil
118 "If non-nil, list of buffers shown in the current buffer-menu.
119This variable determines whether reverting the buffer lists only
120this buffers. It affects both manual reverting and reverting by
121Auto Revert Mode.")
122(make-variable-buffer-local 'Buffer-menu--buffers)
123
fb5614e8 124(defvar Info-current-file) ;; from info.el
b653cee4 125(defvar Info-current-node) ;; from info.el
fb5614e8 126
392225ee 127(defvar Buffer-menu-mode-map
6a6baf11
DN
128 (let ((map (make-keymap))
129 (menu-map (make-sparse-keymap)))
392225ee 130 (suppress-keymap map t)
392225ee
JB
131 (define-key map "v" 'Buffer-menu-select)
132 (define-key map "2" 'Buffer-menu-2-window)
133 (define-key map "1" 'Buffer-menu-1-window)
134 (define-key map "f" 'Buffer-menu-this-window)
135 (define-key map "e" 'Buffer-menu-this-window)
136 (define-key map "\C-m" 'Buffer-menu-this-window)
137 (define-key map "o" 'Buffer-menu-other-window)
138 (define-key map "\C-o" 'Buffer-menu-switch-other-window)
139 (define-key map "s" 'Buffer-menu-save)
140 (define-key map "d" 'Buffer-menu-delete)
141 (define-key map "k" 'Buffer-menu-delete)
142 (define-key map "\C-d" 'Buffer-menu-delete-backwards)
143 (define-key map "\C-k" 'Buffer-menu-delete)
144 (define-key map "x" 'Buffer-menu-execute)
145 (define-key map " " 'next-line)
146 (define-key map "n" 'next-line)
147 (define-key map "p" 'previous-line)
148 (define-key map "\177" 'Buffer-menu-backup-unmark)
149 (define-key map "~" 'Buffer-menu-not-modified)
392225ee
JB
150 (define-key map "u" 'Buffer-menu-unmark)
151 (define-key map "m" 'Buffer-menu-mark)
152 (define-key map "t" 'Buffer-menu-visit-tags-table)
153 (define-key map "%" 'Buffer-menu-toggle-read-only)
154 (define-key map "b" 'Buffer-menu-bury)
392225ee
JB
155 (define-key map "V" 'Buffer-menu-view)
156 (define-key map "T" 'Buffer-menu-toggle-files-only)
157 (define-key map [mouse-2] 'Buffer-menu-mouse-select)
158 (define-key map [follow-link] 'mouse-face)
9cda680d
JL
159 (define-key map (kbd "M-s a C-s") 'Buffer-menu-isearch-buffers)
160 (define-key map (kbd "M-s a M-C-s") 'Buffer-menu-isearch-buffers-regexp)
c9753fb4 161 (define-key map [menu-bar Buffer-menu-mode] (cons (purecopy "Buffer-Menu") menu-map))
6a6baf11 162 (define-key menu-map [quit]
905a9ed3 163 `(menu-item ,(purecopy "Quit") quit-window
c9753fb4 164 :help ,(purecopy "Remove the buffer menu from the display")))
6a6baf11 165 (define-key menu-map [rev]
905a9ed3
DN
166 `(menu-item ,(purecopy "Refresh") revert-buffer
167 :help ,(purecopy "Refresh the *Buffer List* buffer contents")))
a3c20c83 168 (define-key menu-map [s0] menu-bar-separator)
6a6baf11 169 (define-key menu-map [tf]
905a9ed3 170 `(menu-item ,(purecopy "Show only file buffers") Buffer-menu-toggle-files-only
6a6baf11 171 :button (:toggle . Buffer-menu-files-only)
905a9ed3 172 :help ,(purecopy "Toggle whether the current buffer-menu displays only file buffers")))
a3c20c83 173 (define-key menu-map [s1] menu-bar-separator)
6a6baf11
DN
174 ;; FIXME: The "Select" entries could use better names...
175 (define-key menu-map [sel]
905a9ed3
DN
176 `(menu-item ,(purecopy "Select marked") Buffer-menu-select
177 :help ,(purecopy "Select this line's buffer; also display buffers marked with `>'")))
6a6baf11 178 (define-key menu-map [bm2]
905a9ed3
DN
179 `(menu-item ,(purecopy "Select two") Buffer-menu-2-window
180 :help ,(purecopy "Select this line's buffer, with previous buffer in second window")))
6a6baf11 181 (define-key menu-map [bm1]
905a9ed3
DN
182 `(menu-item ,(purecopy "Select current") Buffer-menu-1-window
183 :help ,(purecopy "Select this line's buffer, alone, in full frame")))
6a6baf11 184 (define-key menu-map [ow]
905a9ed3
DN
185 `(menu-item ,(purecopy "Select in other window") Buffer-menu-other-window
186 :help ,(purecopy "Select this line's buffer in other window, leaving buffer menu visible")))
6a6baf11 187 (define-key menu-map [tw]
905a9ed3
DN
188 `(menu-item ,(purecopy "Select in current window") Buffer-menu-this-window
189 :help ,(purecopy "Select this line's buffer in this window")))
a3c20c83 190 (define-key menu-map [s2] menu-bar-separator)
6a6baf11 191 (define-key menu-map [is]
905a9ed3
DN
192 `(menu-item ,(purecopy "Regexp Isearch marked buffers") Buffer-menu-isearch-buffers-regexp
193 :help ,(purecopy "Search for a regexp through all marked buffers using Isearch")))
6a6baf11 194 (define-key menu-map [ir]
905a9ed3
DN
195 `(menu-item ,(purecopy "Isearch marked buffers") Buffer-menu-isearch-buffers
196 :help ,(purecopy "Search for a string through all marked buffers using Isearch")))
a3c20c83 197 (define-key menu-map [s3] menu-bar-separator)
6a6baf11 198 (define-key menu-map [by]
905a9ed3
DN
199 `(menu-item ,(purecopy "Bury") Buffer-menu-bury
200 :help ,(purecopy "Bury the buffer listed on this line")))
6a6baf11 201 (define-key menu-map [vt]
905a9ed3
DN
202 `(menu-item ,(purecopy "Set unmodified") Buffer-menu-not-modified
203 :help ,(purecopy "Mark buffer on this line as unmodified (no changes to save)")))
6a6baf11 204 (define-key menu-map [ex]
905a9ed3
DN
205 `(menu-item ,(purecopy "Execute") Buffer-menu-execute
206 :help ,(purecopy "Save and/or delete buffers marked with s or k commands")))
a3c20c83 207 (define-key menu-map [s4] menu-bar-separator)
6a6baf11 208 (define-key menu-map [delb]
905a9ed3
DN
209 `(menu-item ,(purecopy "Mark for delete and move backwards") Buffer-menu-delete-backwards
210 :help ,(purecopy "Mark buffer on this line to be deleted by x command and move up one line")))
6a6baf11 211 (define-key menu-map [del]
905a9ed3
DN
212 `(menu-item ,(purecopy "Mark for delete") Buffer-menu-delete
213 :help ,(purecopy "Mark buffer on this line to be deleted by x command")))
6a6baf11
DN
214
215 (define-key menu-map [sv]
905a9ed3
DN
216 `(menu-item ,(purecopy "Mark for save") Buffer-menu-save
217 :help ,(purecopy "Mark buffer on this line to be saved by x command")))
6a6baf11 218 (define-key menu-map [umk]
905a9ed3
DN
219 `(menu-item ,(purecopy "Unmark") Buffer-menu-unmark
220 :help ,(purecopy "Cancel all requested operations on buffer on this line and move down")))
6a6baf11 221 (define-key menu-map [mk]
905a9ed3
DN
222 `(menu-item ,(purecopy "Mark") Buffer-menu-mark
223 :help ,(purecopy "Mark buffer on this line for being displayed by v command")))
392225ee
JB
224 map)
225 "Local keymap for `Buffer-menu-mode' buffers.")
2dd96f23
JB
226
227;; Buffer Menu mode is suitable only for specially formatted data.
228(put 'Buffer-menu-mode 'mode-class 'special)
229
fa9d0f38 230(define-derived-mode Buffer-menu-mode special-mode "Buffer Menu"
2dd96f23
JB
231 "Major mode for editing a list of buffers.
232Each line describes one of the buffers in Emacs.
233Letters do not insert themselves; instead, they are commands.
234\\<Buffer-menu-mode-map>
2cf5fc7e
RS
235\\[Buffer-menu-mouse-select] -- select buffer you click on, in place of the buffer menu.
236\\[Buffer-menu-this-window] -- select current line's buffer in place of the buffer menu.
237\\[Buffer-menu-other-window] -- select that buffer in another window,
238 so the buffer menu buffer remains visible in its window.
42a19c2a
RS
239\\[Buffer-menu-view] -- select current line's buffer, but in view-mode.
240\\[Buffer-menu-view-other-window] -- select that buffer in
241 another window, in view-mode.
2cf5fc7e
RS
242\\[Buffer-menu-switch-other-window] -- make another window display that buffer.
243\\[Buffer-menu-mark] -- mark current line's buffer to be displayed.
244\\[Buffer-menu-select] -- select current line's buffer.
245 Also show buffers marked with m, in other windows.
dc6d9681 246\\[Buffer-menu-1-window] -- select that buffer in full-frame window.
2dd96f23
JB
247\\[Buffer-menu-2-window] -- select that buffer in one window,
248 together with buffer selected before this one in another window.
1fbb8c2e
JL
249\\[Buffer-menu-isearch-buffers] -- Do incremental search in the marked buffers.
250\\[Buffer-menu-isearch-buffers-regexp] -- Isearch for regexp in the marked buffers.
2dd96f23
JB
251\\[Buffer-menu-visit-tags-table] -- visit-tags-table this buffer.
252\\[Buffer-menu-not-modified] -- clear modified-flag on that buffer.
253\\[Buffer-menu-save] -- mark that buffer to be saved, and move down.
254\\[Buffer-menu-delete] -- mark that buffer to be deleted, and move down.
255\\[Buffer-menu-delete-backwards] -- mark that buffer to be deleted, and move up.
256\\[Buffer-menu-execute] -- delete or save marked buffers.
257\\[Buffer-menu-unmark] -- remove all kinds of marks from current line.
86dbbfc2 258 With prefix argument, also move up one line.
d91e2b1a 259\\[Buffer-menu-backup-unmark] -- back up a line and remove marks.
0f88624e 260\\[Buffer-menu-toggle-read-only] -- toggle read-only status of buffer on this line.
fa9d0f38 261\\[revert-buffer] -- update the list of buffers.
7b0a86ab 262\\[Buffer-menu-toggle-files-only] -- toggle whether the menu displays only file buffers.
a59faabe 263\\[Buffer-menu-bury] -- bury the buffer listed on this line."
7b0a86ab
LT
264 (set (make-local-variable 'revert-buffer-function)
265 'Buffer-menu-revert-function)
266 (set (make-local-variable 'buffer-stale-function)
012ad4a3 267 #'(lambda (&optional noconfirm) 'fast))
823316aa 268 (setq truncate-lines t)
392225ee 269 (setq buffer-read-only t))
710e5da8 270
bf48d4ef
GM
271(define-obsolete-variable-alias 'buffer-menu-mode-hook
272 'Buffer-menu-mode-hook "23.1")
273
710e5da8 274(defun Buffer-menu-revert-function (ignore1 ignore2)
fda73b45
LT
275 (or (eq buffer-undo-list t)
276 (setq buffer-undo-list nil))
7b0a86ab 277 ;; We can not use save-excursion here. The buffer gets erased.
3f9c9ff7
LT
278 (let ((opoint (point))
279 (eobp (eobp))
280 (ocol (current-column))
bfb2dda4
DP
281 (oline (progn (move-to-column 4)
282 (get-text-property (point) 'buffer)))
fda73b45
LT
283 (prop (point-min))
284 ;; do not make undo records for the reversion.
285 (buffer-undo-list t))
63de2160
LT
286 ;; We can be called by Auto Revert Mode with the "*Buffer Menu*"
287 ;; temporarily the current buffer. Make sure that the
288 ;; interactively current buffer is correctly identified with a `.'
289 ;; by `list-buffers-noselect'.
290 (with-current-buffer (window-buffer)
362b9d48 291 (list-buffers-noselect Buffer-menu-files-only Buffer-menu--buffers))
3f9c9ff7
LT
292 (if oline
293 (while (setq prop (next-single-property-change prop 'buffer))
294 (when (eq (get-text-property prop 'buffer) oline)
295 (goto-char prop)
296 (move-to-column ocol)))
297 (goto-char (if eobp (point-max) opoint)))))
7b0a86ab
LT
298
299(defun Buffer-menu-toggle-files-only (arg)
300 "Toggle whether the current buffer-menu displays only file buffers.
301With a positive ARG display only file buffers. With zero or
302negative ARG, display other buffers as well."
303 (interactive "P")
304 (setq Buffer-menu-files-only
305 (cond ((not arg) (not Buffer-menu-files-only))
306 ((> (prefix-numeric-value arg) 0) t)))
307 (revert-buffer))
308
2dd96f23 309\f
2dd96f23
JB
310(defun Buffer-menu-buffer (error-if-non-existent-p)
311 "Return buffer described by this line of buffer menu."
9b026d9f 312 (let* ((where (+ (line-beginning-position) Buffer-menu-buffer-column))
601ed8a0
GM
313 (name (and (not (eobp)) (get-text-property where 'buffer-name)))
314 (buf (and (not (eobp)) (get-text-property where 'buffer))))
1b40dbed
RS
315 (if name
316 (or (get-buffer name)
601ed8a0 317 (and buf (buffer-name buf) buf)
1b40dbed
RS
318 (if error-if-non-existent-p
319 (error "No buffer named `%s'" name)
320 nil))
601ed8a0 321 (or (and buf (buffer-name buf) buf)
463a7342
JPW
322 (if error-if-non-existent-p
323 (error "No buffer on this line")
601ed8a0 324 nil)))))
2dd96f23 325\f
06b1a5ef 326(defun buffer-menu (&optional arg)
2dd96f23
JB
327 "Make a menu of buffers so you can save, delete or select them.
328With argument, show only buffers that are visiting files.
329Type ? after invocation to get help on commands available.
6a0d92d3
RS
330Type q to remove the buffer menu from the display.
331
332The first column shows `>' for a buffer you have
333marked to be displayed, `D' for one you have marked for
334deletion, and `.' for the current buffer.
335
bc7bb432
JB
336The C column has a `.' for the buffer from which you came.
337The R column has a `%' if the buffer is read-only.
6a0d92d3
RS
338The M column has a `*' if it is modified,
339or `S' if you have marked it for saving.
6a0d92d3
RS
340After this come the buffer name, its size in characters,
341its major mode, and the visited file name (if any)."
08c82d4d
RS
342 (interactive "P")
343;;; (setq Buffer-menu-window-config (current-window-configuration))
344 (switch-to-buffer (list-buffers-noselect arg))
345 (message
346 "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
347
348(defun buffer-menu-other-window (&optional arg)
349 "Display a list of buffers in another window.
350With the buffer list buffer, you can save, delete or select the buffers.
351With argument, show only buffers that are visiting files.
352Type ? after invocation to get help on commands available.
6a0d92d3
RS
353Type q to remove the buffer menu from the display.
354For more information, see the function `buffer-menu'."
2dd96f23 355 (interactive "P")
3ed788ec 356;;; (setq Buffer-menu-window-config (current-window-configuration))
08c82d4d 357 (switch-to-buffer-other-window (list-buffers-noselect arg))
2dd96f23 358 (message
3ed788ec
RS
359 "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
360
bc7bb432
JB
361(defun Buffer-menu-no-header ()
362 (beginning-of-line)
363 (if (or Buffer-menu-use-header-line
364 (not (eq (char-after) ?C)))
365 t
366 (ding)
367 (forward-line 1)
368 nil))
369
2dd96f23
JB
370(defun Buffer-menu-mark ()
371 "Mark buffer on this line for being displayed by \\<Buffer-menu-mode-map>\\[Buffer-menu-select] command."
372 (interactive)
bc7bb432 373 (when (Buffer-menu-no-header)
70ef8857 374 (let ((inhibit-read-only t))
2dd96f23
JB
375 (delete-char 1)
376 (insert ?>)
377 (forward-line 1))))
378
86dbbfc2
ER
379(defun Buffer-menu-unmark (&optional backup)
380 "Cancel all requested operations on buffer on this line and move down.
bfa13d19 381Optional prefix arg means move up."
86dbbfc2 382 (interactive "P")
bc7bb432 383 (when (Buffer-menu-no-header)
2dd96f23
JB
384 (let* ((buf (Buffer-menu-buffer t))
385 (mod (buffer-modified-p buf))
70ef8857
SM
386 (readonly (with-current-buffer buf buffer-read-only))
387 (inhibit-read-only t))
2dd96f23 388 (delete-char 3)
bc7bb432 389 (insert (if readonly (if mod " %*" " % ") (if mod " *" " ")))))
86dbbfc2 390 (forward-line (if backup -1 1)))
2dd96f23
JB
391
392(defun Buffer-menu-backup-unmark ()
393 "Move up and cancel all requested operations on buffer on line above."
394 (interactive)
395 (forward-line -1)
396 (Buffer-menu-unmark)
397 (forward-line -1))
398
77a43e01
RS
399(defun Buffer-menu-delete (&optional arg)
400 "Mark buffer on this line to be deleted by \\<Buffer-menu-mode-map>\\[Buffer-menu-execute] command.
401Prefix arg is how many buffers to delete.
402Negative arg means delete backwards."
403 (interactive "p")
bc7bb432 404 (when (Buffer-menu-no-header)
70ef8857 405 (let ((inhibit-read-only t))
77a43e01
RS
406 (if (or (null arg) (= arg 0))
407 (setq arg 1))
408 (while (> arg 0)
409 (delete-char 1)
410 (insert ?D)
411 (forward-line 1)
412 (setq arg (1- arg)))
bc7bb432
JB
413 (while (and (< arg 0)
414 (Buffer-menu-no-header))
77a43e01
RS
415 (delete-char 1)
416 (insert ?D)
417 (forward-line -1)
418 (setq arg (1+ arg))))))
419
420(defun Buffer-menu-delete-backwards (&optional arg)
2dd96f23 421 "Mark buffer on this line to be deleted by \\<Buffer-menu-mode-map>\\[Buffer-menu-execute] command
77a43e01
RS
422and then move up one line. Prefix arg means move that many lines."
423 (interactive "p")
bc7bb432 424 (Buffer-menu-delete (- (or arg 1))))
2dd96f23
JB
425
426(defun Buffer-menu-save ()
427 "Mark buffer on this line to be saved by \\<Buffer-menu-mode-map>\\[Buffer-menu-execute] command."
428 (interactive)
bc7bb432 429 (when (Buffer-menu-no-header)
70ef8857 430 (let ((inhibit-read-only t))
bc7bb432 431 (forward-char 2)
2dd96f23
JB
432 (delete-char 1)
433 (insert ?S)
434 (forward-line 1))))
435
a8ce00dc 436(defun Buffer-menu-not-modified (&optional arg)
2dd96f23 437 "Mark buffer on this line as unmodified (no changes to save)."
a8ce00dc 438 (interactive "P")
70ef8857 439 (with-current-buffer (Buffer-menu-buffer t)
a8ce00dc 440 (set-buffer-modified-p arg))
2dd96f23
JB
441 (save-excursion
442 (beginning-of-line)
bc7bb432 443 (forward-char 2)
bfa13d19 444 (if (= (char-after) (if arg ?\s ?*))
70ef8857 445 (let ((inhibit-read-only t))
2dd96f23 446 (delete-char 1)
bfa13d19 447 (insert (if arg ?* ?\s))))))
2dd96f23 448
bfb2dda4
DP
449(defun Buffer-menu-beginning ()
450 (goto-char (point-min))
451 (unless Buffer-menu-use-header-line
452 (forward-line)))
453
2dd96f23
JB
454(defun Buffer-menu-execute ()
455 "Save and/or delete buffers marked with \\<Buffer-menu-mode-map>\\[Buffer-menu-save] or \\<Buffer-menu-mode-map>\\[Buffer-menu-delete] commands."
456 (interactive)
457 (save-excursion
bfb2dda4 458 (Buffer-menu-beginning)
bc7bb432 459 (while (re-search-forward "^..S" nil t)
2dd96f23 460 (let ((modp nil))
70ef8857 461 (with-current-buffer (Buffer-menu-buffer t)
2dd96f23
JB
462 (save-buffer)
463 (setq modp (buffer-modified-p)))
70ef8857 464 (let ((inhibit-read-only t))
2dd96f23 465 (delete-char -1)
bfa13d19 466 (insert (if modp ?* ?\s))))))
2dd96f23 467 (save-excursion
bfb2dda4 468 (Buffer-menu-beginning)
2dd96f23 469 (let ((buff-menu-buffer (current-buffer))
70ef8857 470 (inhibit-read-only t))
4f1ab860 471 (while (re-search-forward "^D" nil t)
2dd96f23
JB
472 (forward-char -1)
473 (let ((buf (Buffer-menu-buffer nil)))
474 (or (eq buf nil)
475 (eq buf buff-menu-buffer)
601ed8a0
GM
476 (save-excursion (kill-buffer buf)))
477 (if (and buf (buffer-name buf))
2dd96f23 478 (progn (delete-char 1)
bfa13d19 479 (insert ?\s))
2dd96f23 480 (delete-region (point) (progn (forward-line 1) (point)))
4f1ab860
DL
481 (unless (bobp)
482 (forward-char -1))))))))
2dd96f23
JB
483
484(defun Buffer-menu-select ()
485 "Select this line's buffer; also display buffers marked with `>'.
08c82d4d
RS
486You can mark buffers with the \\<Buffer-menu-mode-map>\\[Buffer-menu-mark] command.
487This command deletes and replaces all the previously existing windows
488in the selected frame."
2dd96f23
JB
489 (interactive)
490 (let ((buff (Buffer-menu-buffer t))
10e1dad9 491 (menu (current-buffer))
2dd96f23
JB
492 (others ())
493 tem)
bfb2dda4 494 (Buffer-menu-beginning)
a9fb4690 495 (while (re-search-forward "^>" nil t)
2dd96f23 496 (setq tem (Buffer-menu-buffer t))
70ef8857 497 (let ((inhibit-read-only t))
2dd96f23 498 (delete-char -1)
bfa13d19 499 (insert ?\s))
2dd96f23
JB
500 (or (eq tem buff) (memq tem others) (setq others (cons tem others))))
501 (setq others (nreverse others)
dc6d9681 502 tem (/ (1- (frame-height)) (1+ (length others))))
2dd96f23
JB
503 (delete-other-windows)
504 (switch-to-buffer buff)
505 (or (eq menu buff)
506 (bury-buffer menu))
86dbbfc2
ER
507 (if (equal (length others) 0)
508 (progn
3ed788ec
RS
509;;; ;; Restore previous window configuration before displaying
510;;; ;; selected buffers.
511;;; (if Buffer-menu-window-config
512;;; (progn
513;;; (set-window-configuration Buffer-menu-window-config)
514;;; (setq Buffer-menu-window-config nil)))
86dbbfc2
ER
515 (switch-to-buffer buff))
516 (while others
517 (split-window nil tem)
518 (other-window 1)
519 (switch-to-buffer (car others))
520 (setq others (cdr others)))
521 (other-window 1) ;back to the beginning!
522)))
523
9cda680d
JL
524(defun Buffer-menu-marked-buffers ()
525 "Return a list of buffers marked with the \\<Buffer-menu-mode-map>\\[Buffer-menu-mark] command."
526 (let (buffers)
527 (Buffer-menu-beginning)
528 (while (re-search-forward "^>" nil t)
529 (setq buffers (cons (Buffer-menu-buffer t) buffers)))
530 (nreverse buffers)))
531
532(defun Buffer-menu-isearch-buffers ()
533 "Search for a string through all marked buffers using Isearch."
534 (interactive)
535 (multi-isearch-buffers (Buffer-menu-marked-buffers)))
536
537(defun Buffer-menu-isearch-buffers-regexp ()
538 "Search for a regexp through all marked buffers using Isearch."
539 (interactive)
540 (multi-isearch-buffers-regexp (Buffer-menu-marked-buffers)))
08c82d4d 541
2dd96f23
JB
542\f
543(defun Buffer-menu-visit-tags-table ()
544 "Visit the tags table in the buffer on this line. See `visit-tags-table'."
545 (interactive)
546 (let ((file (buffer-file-name (Buffer-menu-buffer t))))
547 (if file
548 (visit-tags-table file)
549 (error "Specified buffer has no file"))))
550
551(defun Buffer-menu-1-window ()
dc6d9681 552 "Select this line's buffer, alone, in full frame."
2dd96f23
JB
553 (interactive)
554 (switch-to-buffer (Buffer-menu-buffer t))
555 (bury-buffer (other-buffer))
556 (delete-other-windows))
557
2cf5fc7e
RS
558(defun Buffer-menu-mouse-select (event)
559 "Select the buffer whose line you click on."
560 (interactive "e")
561 (let (buffer)
70ef8857 562 (with-current-buffer (window-buffer (posn-window (event-end event)))
2cf5fc7e
RS
563 (save-excursion
564 (goto-char (posn-point (event-end event)))
565 (setq buffer (Buffer-menu-buffer t))))
566 (select-window (posn-window (event-end event)))
d953b88c
RS
567 (if (and (window-dedicated-p (selected-window))
568 (eq (selected-window) (frame-root-window)))
569 (switch-to-buffer-other-frame buffer)
570 (switch-to-buffer buffer))))
2cf5fc7e 571
2dd96f23
JB
572(defun Buffer-menu-this-window ()
573 "Select this line's buffer in this window."
574 (interactive)
575 (switch-to-buffer (Buffer-menu-buffer t)))
576
577(defun Buffer-menu-other-window ()
578 "Select this line's buffer in other window, leaving buffer menu visible."
579 (interactive)
580 (switch-to-buffer-other-window (Buffer-menu-buffer t)))
581
35aaf00c
RM
582(defun Buffer-menu-switch-other-window ()
583 "Make the other window select this line's buffer.
584The current window remains selected."
585 (interactive)
a0e4e275
JL
586 (let ((pop-up-windows t)
587 same-window-buffer-names
588 same-window-regexps)
589 (display-buffer (Buffer-menu-buffer t))))
35aaf00c 590
2dd96f23
JB
591(defun Buffer-menu-2-window ()
592 "Select this line's buffer, with previous buffer in second window."
593 (interactive)
594 (let ((buff (Buffer-menu-buffer t))
595 (menu (current-buffer))
a0e4e275
JL
596 (pop-up-windows t)
597 same-window-buffer-names
598 same-window-regexps)
86dbbfc2 599 (delete-other-windows)
2dd96f23
JB
600 (switch-to-buffer (other-buffer))
601 (pop-to-buffer buff)
602 (bury-buffer menu)))
c0274f38 603
d91e2b1a 604(defun Buffer-menu-toggle-read-only ()
f450965c 605 "Toggle read-only status of buffer on this line, perhaps via version control."
d91e2b1a
ER
606 (interactive)
607 (let (char)
70ef8857
SM
608 (with-current-buffer (Buffer-menu-buffer t)
609 (toggle-read-only)
bfa13d19 610 (setq char (if buffer-read-only ?% ?\s)))
d91e2b1a
ER
611 (save-excursion
612 (beginning-of-line)
bc7bb432 613 (forward-char 1)
d91e2b1a 614 (if (/= (following-char) char)
70ef8857 615 (let ((inhibit-read-only t))
d91e2b1a
ER
616 (delete-char 1)
617 (insert char))))))
618
36570c92
RS
619(defun Buffer-menu-bury ()
620 "Bury the buffer listed on this line."
621 (interactive)
bc7bb432 622 (when (Buffer-menu-no-header)
a59faabe
RS
623 (save-excursion
624 (beginning-of-line)
625 (bury-buffer (Buffer-menu-buffer t))
626 (let ((line (buffer-substring (point) (progn (forward-line 1) (point))))
70ef8857 627 (inhibit-read-only t))
a59faabe
RS
628 (delete-region (point) (progn (forward-line -1) (point)))
629 (goto-char (point-max))
630 (insert line))
631 (message "Buried buffer moved to the end"))))
42a19c2a
RS
632
633
634(defun Buffer-menu-view ()
635 "View this line's buffer in View mode."
636 (interactive)
637 (view-buffer (Buffer-menu-buffer t)))
638
639
640(defun Buffer-menu-view-other-window ()
641 "View this line's buffer in View mode in another window."
642 (interactive)
643 (view-buffer-other-window (Buffer-menu-buffer t)))
0b03ce3a
RS
644\f
645
349b6bfe 646;;;###autoload
0b03ce3a
RS
647(define-key ctl-x-map "\C-b" 'list-buffers)
648
349b6bfe 649;;;###autoload
0b03ce3a
RS
650(defun list-buffers (&optional files-only)
651 "Display a list of names of existing buffers.
652The list is displayed in a buffer named `*Buffer List*'.
653Note that buffers with names starting with spaces are omitted.
654Non-null optional arg FILES-ONLY means mention only file buffers.
655
6a0d92d3 656For more information, see the function `buffer-menu'."
0b03ce3a 657 (interactive "P")
08c82d4d
RS
658 (display-buffer (list-buffers-noselect files-only)))
659
e1ff8dd0
SM
660(defconst Buffer-menu-short-ellipsis
661 ;; This file is preloaded, so we can't use char-displayable-p here
662 ;; because we don't know yet what display we're going to connect to.
663 ":" ;; (if (char-displayable-p ?…) "…" ":")
664 )
31e02fab 665
bc7bb432 666(defun Buffer-menu-buffer+size (name size &optional name-props size-props)
31e02fab
SM
667 (if (> (+ (string-width name) (string-width size) 2)
668 Buffer-menu-buffer+size-width)
bc7bb432 669 (setq name
31e02fab
SM
670 (let ((tail
671 (if (string-match "<[0-9]+>$" name)
672 (match-string 0 name)
673 "")))
674 (concat (truncate-string-to-width
675 name
676 (- Buffer-menu-buffer+size-width
677 (max (string-width size) 3)
678 (string-width tail)
679 2))
680 Buffer-menu-short-ellipsis
681 tail)))
ee5861c8
AS
682 ;; Don't put properties on (buffer-name).
683 (setq name (copy-sequence name)))
bc7bb432
JB
684 (add-text-properties 0 (length name) name-props name)
685 (add-text-properties 0 (length size) size-props size)
d19e23ae
CY
686 (let ((name+space-width (- Buffer-menu-buffer+size-width
687 (string-width size))))
688 (concat name
689 (propertize (make-string (- name+space-width (string-width name))
690 ?\s)
691 'display `(space :align-to ,(+ 4 name+space-width)))
692 size)))
bc7bb432 693
d5aacb46
SM
694(defun Buffer-menu-sort (column)
695 "Sort the buffer menu by COLUMN."
696 (interactive "P")
697 (when column
698 (setq column (prefix-numeric-value column))
699 (if (< column 2) (setq column 2))
700 (if (> column 5) (setq column 5)))
701 (setq Buffer-menu-sort-column column)
70ef8857 702 (let ((inhibit-read-only t) l buf m1 m2)
bfb2dda4
DP
703 (save-excursion
704 (Buffer-menu-beginning)
705 (while (not (eobp))
706 (when (buffer-live-p (setq buf (get-text-property (+ (point) 4) 'buffer)))
707 (setq m1 (char-after)
708 m1 (if (memq m1 '(?> ?D)) m1)
709 m2 (char-after (+ (point) 2))
710 m2 (if (eq m2 ?S) m2))
711 (if (or m1 m2)
712 (push (list buf m1 m2) l)))
713 (forward-line)))
52a0198d 714 (revert-buffer)
bfb2dda4
DP
715 (save-excursion
716 (Buffer-menu-beginning)
717 (while (not (eobp))
718 (when (setq buf (assq (get-text-property (+ (point) 4) 'buffer) l))
719 (setq m1 (cadr buf)
720 m2 (cadr (cdr buf)))
721 (when m1
722 (delete-char 1)
723 (insert m1)
724 (backward-char 1))
725 (when m2
726 (forward-char 2)
727 (delete-char 1)
728 (insert m2)))
729 (forward-line)))))
d5aacb46 730
104fc809
CY
731(defun Buffer-menu-sort-by-column (&optional e)
732 "Sort the buffer menu by the column clicked on."
733 (interactive (list last-input-event))
734 (if e (mouse-select-window e))
735 (let* ((pos (event-start e))
736 (obj (posn-object pos))
737 (col (if obj
738 (get-text-property (cdr obj) 'column (car obj))
739 (get-text-property (posn-point pos) 'column))))
740 (Buffer-menu-sort col)))
741
742(defvar Buffer-menu-sort-button-map
743 (let ((map (make-sparse-keymap)))
744 ;; This keymap handles both nil and non-nil values for
745 ;; Buffer-menu-use-header-line.
746 (define-key map [header-line mouse-1] 'Buffer-menu-sort-by-column)
747 (define-key map [header-line mouse-2] 'Buffer-menu-sort-by-column)
748 (define-key map [mouse-2] 'Buffer-menu-sort-by-column)
749 (define-key map [follow-link] 'mouse-face)
750 (define-key map "\C-m" 'Buffer-menu-sort-by-column)
751 map)
752 "Local keymap for Buffer menu sort buttons.")
753
d5aacb46
SM
754(defun Buffer-menu-make-sort-button (name column)
755 (if (equal column Buffer-menu-sort-column) (setq column nil))
104fc809
CY
756 (propertize name
757 'column column
758 'help-echo (concat
759 (if Buffer-menu-use-header-line
760 "mouse-1, mouse-2: sort by "
761 "mouse-2, RET: sort by ")
762 (if column (downcase name) "visited order"))
763 'mouse-face 'highlight
764 'keymap Buffer-menu-sort-button-map))
d5aacb46 765
61617913 766(defun list-buffers-noselect (&optional files-only buffer-list)
08c82d4d
RS
767 "Create and return a buffer with a list of names of existing buffers.
768The buffer is named `*Buffer List*'.
769Note that buffers with names starting with spaces are omitted.
770Non-null optional arg FILES-ONLY means mention only file buffers.
771
61617913
RS
772If BUFFER-LIST is non-nil, it should be a list of buffers;
773it means list those buffers and no others.
774
6a0d92d3 775For more information, see the function `buffer-menu'."
bc7bb432
JB
776 (let* ((old-buffer (current-buffer))
777 (standard-output standard-output)
bfa13d19 778 (mode-end (make-string (- Buffer-menu-mode-width 2) ?\s))
5e684428 779 (header (concat "CRM "
d5aacb46
SM
780 (Buffer-menu-buffer+size
781 (Buffer-menu-make-sort-button "Buffer" 2)
782 (Buffer-menu-make-sort-button "Size" 3))
783 " "
784 (Buffer-menu-make-sort-button "Mode" 4) mode-end
785 (Buffer-menu-make-sort-button "File" 5) "\n"))
f4872033 786 list desired-point)
748a336b 787 (when Buffer-menu-use-header-line
d5aacb46 788 (let ((pos 0))
fff861a2
JB
789 ;; Turn whitespace chars in the header into stretch specs so
790 ;; they work regardless of the header-line face.
791 (while (string-match "[ \t\n]+" header pos)
748a336b
SM
792 (setq pos (match-end 0))
793 (put-text-property (match-beginning 0) pos 'display
5e684428 794 ;; Assume fixed-size chars in the buffer.
6d3bce2b 795 (list 'space :align-to pos)
5e684428
SM
796 header)))
797 ;; Try to better align the one-char headers.
798 (put-text-property 0 3 'face 'fixed-pitch header)
799 ;; Add a "dummy" leading space to align the beginning of the header
800 ;; line with the beginning of the text (rather than with the left
c822571a 801 ;; scrollbar or the left fringe). --Stef
5e684428 802 (setq header (concat (propertize " " 'display '(space :align-to 0))
a6a2fd5e 803 header)))
d5aacb46 804 (with-current-buffer (get-buffer-create "*Buffer List*")
08c82d4d
RS
805 (setq buffer-read-only nil)
806 (erase-buffer)
807 (setq standard-output (current-buffer))
bc7bb432 808 (unless Buffer-menu-use-header-line
c822571a
SM
809 ;; Use U+2014 (EM DASH) to underline if possible, else use ASCII
810 ;; (i.e. U+002D, HYPHEN-MINUS).
811 (let ((underline (if (char-displayable-p ?\u2014) ?\u2014 ?-)))
5e684428
SM
812 (insert header
813 (apply 'string
814 (mapcar (lambda (c)
bfa13d19 815 (if (memq c '(?\n ?\s)) c underline))
5e684428 816 header)))))
a6a2fd5e 817 ;; Collect info for every buffer we're interested in.
3ce5f932
LT
818 (dolist (buffer (or buffer-list
819 (buffer-list
820 (when Buffer-menu-use-frame-buffer-list
821 (selected-frame)))))
a6a2fd5e
JL
822 (with-current-buffer buffer
823 (let ((name (buffer-name))
824 (file buffer-file-name))
825 (unless (and (not buffer-list)
826 (or
827 ;; Don't mention internal buffers.
828 (and (string= (substring name 0 1) " ") (null file))
829 ;; Maybe don't mention buffers without files.
830 (and files-only (not file))
831 (string= name "*Buffer List*")))
832 ;; Otherwise output info.
833 (let ((mode (concat (format-mode-line mode-name nil nil buffer)
834 (if mode-line-process
835 (format-mode-line mode-line-process
836 nil nil buffer))))
837 (bits (string
bfa13d19 838 (if (eq buffer old-buffer) ?. ?\s)
a6a2fd5e
JL
839 ;; Handle readonly status. The output buffer
840 ;; is special cased to appear readonly; it is
841 ;; actually made so at a later date.
842 (if (or (eq buffer standard-output)
843 buffer-read-only)
bfa13d19 844 ?% ?\s)
a6a2fd5e 845 ;; Identify modified buffers.
bfa13d19 846 (if (buffer-modified-p) ?* ?\s)
a6a2fd5e 847 ;; Space separator.
bfa13d19 848 ?\s)))
a6a2fd5e
JL
849 (unless file
850 ;; No visited file. Check local value of
fb5614e8
EZ
851 ;; list-buffers-directory and, for Info buffers,
852 ;; Info-current-file.
853 (cond ((and (boundp 'list-buffers-directory)
854 list-buffers-directory)
855 (setq file list-buffers-directory))
856 ((eq major-mode 'Info-mode)
857 (setq file Info-current-file)
858 (cond
26f544d3 859 ((equal file "dir")
fb5614e8
EZ
860 (setq file "*Info Directory*"))
861 ((eq file 'apropos)
862 (setq file "*Info Apropos*"))
863 ((eq file 'history)
864 (setq file "*Info History*"))
865 ((eq file 'toc)
866 (setq file "*Info TOC*"))
867 ((not (stringp file)) ;; avoid errors
b653cee4
EZ
868 (setq file nil))
869 (t
870 (setq file (concat "("
871 (file-name-nondirectory file)
9cda680d 872 ") "
b653cee4 873 Info-current-node)))))))
a6a2fd5e
JL
874 (push (list buffer bits name (buffer-size) mode file)
875 list))))))
876 ;; Preserve the original buffer-list ordering, just in case.
877 (setq list (nreverse list))
f4872033 878 ;; Place the buffers's info in the output buffer, sorted if necessary.
bc7bb432
JB
879 (dolist (buffer
880 (if Buffer-menu-sort-column
881 (sort list
882 (if (eq Buffer-menu-sort-column 3)
883 (lambda (a b)
884 (< (nth Buffer-menu-sort-column a)
885 (nth Buffer-menu-sort-column b)))
886 (lambda (a b)
887 (string< (nth Buffer-menu-sort-column a)
888 (nth Buffer-menu-sort-column b)))))
889 list))
890 (if (eq (car buffer) old-buffer)
891 (setq desired-point (point)))
892 (insert (cadr buffer)
893 ;; Put the buffer name into a text property
894 ;; so we don't have to extract it from the text.
895 ;; This way we avoid problems with unusual buffer names.
392640c4
NR
896 (let ((name (nth 2 buffer))
897 (size (int-to-string (nth 3 buffer))))
d19e23ae 898 (Buffer-menu-buffer+size name size
392640c4
NR
899 `(buffer-name ,name
900 buffer ,(car buffer)
901 font-lock-face buffer-menu-buffer
902 mouse-face highlight
9201cc28 903 help-echo
392640c4
NR
904 ,(if (>= (length name)
905 (- Buffer-menu-buffer+size-width
906 (max (length size) 3)
907 2))
908 name
909 "mouse-2: select this buffer"))))
a3e5a603
SM
910 " "
911 (if (> (string-width (nth 4 buffer)) Buffer-menu-mode-width)
912 (truncate-string-to-width (nth 4 buffer)
31e02fab 913 Buffer-menu-mode-width)
bc7bb432
JB
914 (nth 4 buffer)))
915 (when (nth 5 buffer)
916 (indent-to (+ Buffer-menu-buffer-column Buffer-menu-buffer+size-width
917 Buffer-menu-mode-width 4) 1)
918 (princ (abbreviate-file-name (nth 5 buffer))))
919 (princ "\n"))
0b03ce3a 920 (Buffer-menu-mode)
bc7bb432 921 (when Buffer-menu-use-header-line
748a336b 922 (setq header-line-format header))
08c82d4d
RS
923 ;; DESIRED-POINT doesn't have to be set; it is not when the
924 ;; current buffer is not displayed for some reason.
0b03ce3a 925 (and desired-point
08c82d4d 926 (goto-char desired-point))
7b0a86ab 927 (setq Buffer-menu-files-only files-only)
362b9d48 928 (setq Buffer-menu--buffers buffer-list)
7b0a86ab 929 (set-buffer-modified-p nil)
08c82d4d 930 (current-buffer))))
0b03ce3a 931
c0274f38 932;;; buff-menu.el ends here