Add L and R categories to standard category table, and use them.
[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
be71f810 120these buffers. It affects both manual reverting and reverting by
362b9d48
GM
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)
06b60517 267 (lambda (&optional _noconfirm) 'fast))
823316aa 268 (setq truncate-lines t)
acb28818
EZ
269 (setq buffer-read-only t)
270 ;; Force L2R direction, to avoid messing the display if the first
7e2ad32c 271 ;; buffer in the list happens to begin with a strong R2L character.
acb28818 272 (setq bidi-paragraph-direction 'left-to-right))
710e5da8 273
bf48d4ef
GM
274(define-obsolete-variable-alias 'buffer-menu-mode-hook
275 'Buffer-menu-mode-hook "23.1")
276
06b60517 277(defun Buffer-menu-revert-function (_ignore1 _ignore2)
fda73b45
LT
278 (or (eq buffer-undo-list t)
279 (setq buffer-undo-list nil))
7b0a86ab 280 ;; We can not use save-excursion here. The buffer gets erased.
3f9c9ff7
LT
281 (let ((opoint (point))
282 (eobp (eobp))
283 (ocol (current-column))
60ed8c72 284 (oline (progn (move-to-column Buffer-menu-buffer-column)
bfb2dda4 285 (get-text-property (point) 'buffer)))
fda73b45
LT
286 (prop (point-min))
287 ;; do not make undo records for the reversion.
288 (buffer-undo-list t))
63de2160
LT
289 ;; We can be called by Auto Revert Mode with the "*Buffer Menu*"
290 ;; temporarily the current buffer. Make sure that the
291 ;; interactively current buffer is correctly identified with a `.'
292 ;; by `list-buffers-noselect'.
293 (with-current-buffer (window-buffer)
362b9d48 294 (list-buffers-noselect Buffer-menu-files-only Buffer-menu--buffers))
3f9c9ff7
LT
295 (if oline
296 (while (setq prop (next-single-property-change prop 'buffer))
297 (when (eq (get-text-property prop 'buffer) oline)
298 (goto-char prop)
299 (move-to-column ocol)))
300 (goto-char (if eobp (point-max) opoint)))))
7b0a86ab
LT
301
302(defun Buffer-menu-toggle-files-only (arg)
303 "Toggle whether the current buffer-menu displays only file buffers.
304With a positive ARG display only file buffers. With zero or
305negative ARG, display other buffers as well."
306 (interactive "P")
307 (setq Buffer-menu-files-only
308 (cond ((not arg) (not Buffer-menu-files-only))
309 ((> (prefix-numeric-value arg) 0) t)))
310 (revert-buffer))
311
2dd96f23 312\f
2dd96f23
JB
313(defun Buffer-menu-buffer (error-if-non-existent-p)
314 "Return buffer described by this line of buffer menu."
9b026d9f 315 (let* ((where (+ (line-beginning-position) Buffer-menu-buffer-column))
601ed8a0
GM
316 (name (and (not (eobp)) (get-text-property where 'buffer-name)))
317 (buf (and (not (eobp)) (get-text-property where 'buffer))))
1b40dbed
RS
318 (if name
319 (or (get-buffer name)
601ed8a0 320 (and buf (buffer-name buf) buf)
1b40dbed
RS
321 (if error-if-non-existent-p
322 (error "No buffer named `%s'" name)
323 nil))
601ed8a0 324 (or (and buf (buffer-name buf) buf)
463a7342
JPW
325 (if error-if-non-existent-p
326 (error "No buffer on this line")
601ed8a0 327 nil)))))
2dd96f23 328\f
06b1a5ef 329(defun buffer-menu (&optional arg)
2dd96f23
JB
330 "Make a menu of buffers so you can save, delete or select them.
331With argument, show only buffers that are visiting files.
332Type ? after invocation to get help on commands available.
6a0d92d3
RS
333Type q to remove the buffer menu from the display.
334
335The first column shows `>' for a buffer you have
336marked to be displayed, `D' for one you have marked for
337deletion, and `.' for the current buffer.
338
bc7bb432
JB
339The C column has a `.' for the buffer from which you came.
340The R column has a `%' if the buffer is read-only.
6a0d92d3
RS
341The M column has a `*' if it is modified,
342or `S' if you have marked it for saving.
6a0d92d3
RS
343After this come the buffer name, its size in characters,
344its major mode, and the visited file name (if any)."
08c82d4d
RS
345 (interactive "P")
346;;; (setq Buffer-menu-window-config (current-window-configuration))
347 (switch-to-buffer (list-buffers-noselect arg))
348 (message
349 "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
350
351(defun buffer-menu-other-window (&optional arg)
352 "Display a list of buffers in another window.
353With the buffer list buffer, you can save, delete or select the buffers.
354With argument, show only buffers that are visiting files.
355Type ? after invocation to get help on commands available.
6a0d92d3
RS
356Type q to remove the buffer menu from the display.
357For more information, see the function `buffer-menu'."
2dd96f23 358 (interactive "P")
3ed788ec 359;;; (setq Buffer-menu-window-config (current-window-configuration))
08c82d4d 360 (switch-to-buffer-other-window (list-buffers-noselect arg))
2dd96f23 361 (message
3ed788ec
RS
362 "Commands: d, s, x, u; f, o, 1, 2, m, v; ~, %%; q to quit; ? for help."))
363
bc7bb432
JB
364(defun Buffer-menu-no-header ()
365 (beginning-of-line)
366 (if (or Buffer-menu-use-header-line
367 (not (eq (char-after) ?C)))
368 t
369 (ding)
370 (forward-line 1)
371 nil))
372
2dd96f23
JB
373(defun Buffer-menu-mark ()
374 "Mark buffer on this line for being displayed by \\<Buffer-menu-mode-map>\\[Buffer-menu-select] command."
375 (interactive)
bc7bb432 376 (when (Buffer-menu-no-header)
70ef8857 377 (let ((inhibit-read-only t))
2dd96f23
JB
378 (delete-char 1)
379 (insert ?>)
380 (forward-line 1))))
381
86dbbfc2
ER
382(defun Buffer-menu-unmark (&optional backup)
383 "Cancel all requested operations on buffer on this line and move down.
bfa13d19 384Optional prefix arg means move up."
86dbbfc2 385 (interactive "P")
bc7bb432 386 (when (Buffer-menu-no-header)
2dd96f23
JB
387 (let* ((buf (Buffer-menu-buffer t))
388 (mod (buffer-modified-p buf))
70ef8857
SM
389 (readonly (with-current-buffer buf buffer-read-only))
390 (inhibit-read-only t))
2dd96f23 391 (delete-char 3)
bc7bb432 392 (insert (if readonly (if mod " %*" " % ") (if mod " *" " ")))))
86dbbfc2 393 (forward-line (if backup -1 1)))
2dd96f23
JB
394
395(defun Buffer-menu-backup-unmark ()
396 "Move up and cancel all requested operations on buffer on line above."
397 (interactive)
398 (forward-line -1)
399 (Buffer-menu-unmark)
400 (forward-line -1))
401
77a43e01
RS
402(defun Buffer-menu-delete (&optional arg)
403 "Mark buffer on this line to be deleted by \\<Buffer-menu-mode-map>\\[Buffer-menu-execute] command.
404Prefix arg is how many buffers to delete.
405Negative arg means delete backwards."
406 (interactive "p")
bc7bb432 407 (when (Buffer-menu-no-header)
70ef8857 408 (let ((inhibit-read-only t))
77a43e01
RS
409 (if (or (null arg) (= arg 0))
410 (setq arg 1))
411 (while (> arg 0)
412 (delete-char 1)
413 (insert ?D)
414 (forward-line 1)
415 (setq arg (1- arg)))
bc7bb432
JB
416 (while (and (< arg 0)
417 (Buffer-menu-no-header))
77a43e01
RS
418 (delete-char 1)
419 (insert ?D)
420 (forward-line -1)
421 (setq arg (1+ arg))))))
422
423(defun Buffer-menu-delete-backwards (&optional arg)
2dd96f23 424 "Mark buffer on this line to be deleted by \\<Buffer-menu-mode-map>\\[Buffer-menu-execute] command
77a43e01
RS
425and then move up one line. Prefix arg means move that many lines."
426 (interactive "p")
bc7bb432 427 (Buffer-menu-delete (- (or arg 1))))
2dd96f23
JB
428
429(defun Buffer-menu-save ()
430 "Mark buffer on this line to be saved by \\<Buffer-menu-mode-map>\\[Buffer-menu-execute] command."
431 (interactive)
bc7bb432 432 (when (Buffer-menu-no-header)
70ef8857 433 (let ((inhibit-read-only t))
bc7bb432 434 (forward-char 2)
2dd96f23
JB
435 (delete-char 1)
436 (insert ?S)
437 (forward-line 1))))
438
a8ce00dc 439(defun Buffer-menu-not-modified (&optional arg)
2dd96f23 440 "Mark buffer on this line as unmodified (no changes to save)."
a8ce00dc 441 (interactive "P")
70ef8857 442 (with-current-buffer (Buffer-menu-buffer t)
a8ce00dc 443 (set-buffer-modified-p arg))
2dd96f23
JB
444 (save-excursion
445 (beginning-of-line)
bc7bb432 446 (forward-char 2)
bfa13d19 447 (if (= (char-after) (if arg ?\s ?*))
70ef8857 448 (let ((inhibit-read-only t))
2dd96f23 449 (delete-char 1)
bfa13d19 450 (insert (if arg ?* ?\s))))))
2dd96f23 451
bfb2dda4
DP
452(defun Buffer-menu-beginning ()
453 (goto-char (point-min))
454 (unless Buffer-menu-use-header-line
455 (forward-line)))
456
2dd96f23
JB
457(defun Buffer-menu-execute ()
458 "Save and/or delete buffers marked with \\<Buffer-menu-mode-map>\\[Buffer-menu-save] or \\<Buffer-menu-mode-map>\\[Buffer-menu-delete] commands."
459 (interactive)
460 (save-excursion
bfb2dda4 461 (Buffer-menu-beginning)
bc7bb432 462 (while (re-search-forward "^..S" nil t)
2dd96f23 463 (let ((modp nil))
70ef8857 464 (with-current-buffer (Buffer-menu-buffer t)
2dd96f23
JB
465 (save-buffer)
466 (setq modp (buffer-modified-p)))
70ef8857 467 (let ((inhibit-read-only t))
2dd96f23 468 (delete-char -1)
bfa13d19 469 (insert (if modp ?* ?\s))))))
2dd96f23 470 (save-excursion
bfb2dda4 471 (Buffer-menu-beginning)
2dd96f23 472 (let ((buff-menu-buffer (current-buffer))
70ef8857 473 (inhibit-read-only t))
4f1ab860 474 (while (re-search-forward "^D" nil t)
2dd96f23
JB
475 (forward-char -1)
476 (let ((buf (Buffer-menu-buffer nil)))
477 (or (eq buf nil)
478 (eq buf buff-menu-buffer)
601ed8a0
GM
479 (save-excursion (kill-buffer buf)))
480 (if (and buf (buffer-name buf))
2dd96f23 481 (progn (delete-char 1)
bfa13d19 482 (insert ?\s))
2dd96f23 483 (delete-region (point) (progn (forward-line 1) (point)))
4f1ab860
DL
484 (unless (bobp)
485 (forward-char -1))))))))
2dd96f23
JB
486
487(defun Buffer-menu-select ()
488 "Select this line's buffer; also display buffers marked with `>'.
08c82d4d
RS
489You can mark buffers with the \\<Buffer-menu-mode-map>\\[Buffer-menu-mark] command.
490This command deletes and replaces all the previously existing windows
491in the selected frame."
2dd96f23
JB
492 (interactive)
493 (let ((buff (Buffer-menu-buffer t))
10e1dad9 494 (menu (current-buffer))
2dd96f23
JB
495 (others ())
496 tem)
bfb2dda4 497 (Buffer-menu-beginning)
a9fb4690 498 (while (re-search-forward "^>" nil t)
2dd96f23 499 (setq tem (Buffer-menu-buffer t))
70ef8857 500 (let ((inhibit-read-only t))
2dd96f23 501 (delete-char -1)
bfa13d19 502 (insert ?\s))
2dd96f23
JB
503 (or (eq tem buff) (memq tem others) (setq others (cons tem others))))
504 (setq others (nreverse others)
dc6d9681 505 tem (/ (1- (frame-height)) (1+ (length others))))
2dd96f23
JB
506 (delete-other-windows)
507 (switch-to-buffer buff)
508 (or (eq menu buff)
509 (bury-buffer menu))
86dbbfc2
ER
510 (if (equal (length others) 0)
511 (progn
3ed788ec
RS
512;;; ;; Restore previous window configuration before displaying
513;;; ;; selected buffers.
514;;; (if Buffer-menu-window-config
515;;; (progn
516;;; (set-window-configuration Buffer-menu-window-config)
517;;; (setq Buffer-menu-window-config nil)))
86dbbfc2
ER
518 (switch-to-buffer buff))
519 (while others
520 (split-window nil tem)
521 (other-window 1)
522 (switch-to-buffer (car others))
523 (setq others (cdr others)))
524 (other-window 1) ;back to the beginning!
525)))
526
9cda680d
JL
527(defun Buffer-menu-marked-buffers ()
528 "Return a list of buffers marked with the \\<Buffer-menu-mode-map>\\[Buffer-menu-mark] command."
529 (let (buffers)
530 (Buffer-menu-beginning)
531 (while (re-search-forward "^>" nil t)
532 (setq buffers (cons (Buffer-menu-buffer t) buffers)))
533 (nreverse buffers)))
534
535(defun Buffer-menu-isearch-buffers ()
536 "Search for a string through all marked buffers using Isearch."
537 (interactive)
538 (multi-isearch-buffers (Buffer-menu-marked-buffers)))
539
540(defun Buffer-menu-isearch-buffers-regexp ()
541 "Search for a regexp through all marked buffers using Isearch."
542 (interactive)
543 (multi-isearch-buffers-regexp (Buffer-menu-marked-buffers)))
08c82d4d 544
2dd96f23
JB
545\f
546(defun Buffer-menu-visit-tags-table ()
547 "Visit the tags table in the buffer on this line. See `visit-tags-table'."
548 (interactive)
549 (let ((file (buffer-file-name (Buffer-menu-buffer t))))
550 (if file
551 (visit-tags-table file)
552 (error "Specified buffer has no file"))))
553
554(defun Buffer-menu-1-window ()
dc6d9681 555 "Select this line's buffer, alone, in full frame."
2dd96f23
JB
556 (interactive)
557 (switch-to-buffer (Buffer-menu-buffer t))
558 (bury-buffer (other-buffer))
559 (delete-other-windows))
560
2cf5fc7e
RS
561(defun Buffer-menu-mouse-select (event)
562 "Select the buffer whose line you click on."
563 (interactive "e")
564 (let (buffer)
70ef8857 565 (with-current-buffer (window-buffer (posn-window (event-end event)))
2cf5fc7e
RS
566 (save-excursion
567 (goto-char (posn-point (event-end event)))
568 (setq buffer (Buffer-menu-buffer t))))
569 (select-window (posn-window (event-end event)))
d953b88c
RS
570 (if (and (window-dedicated-p (selected-window))
571 (eq (selected-window) (frame-root-window)))
572 (switch-to-buffer-other-frame buffer)
573 (switch-to-buffer buffer))))
2cf5fc7e 574
2dd96f23
JB
575(defun Buffer-menu-this-window ()
576 "Select this line's buffer in this window."
577 (interactive)
578 (switch-to-buffer (Buffer-menu-buffer t)))
579
580(defun Buffer-menu-other-window ()
581 "Select this line's buffer in other window, leaving buffer menu visible."
582 (interactive)
583 (switch-to-buffer-other-window (Buffer-menu-buffer t)))
584
35aaf00c
RM
585(defun Buffer-menu-switch-other-window ()
586 "Make the other window select this line's buffer.
587The current window remains selected."
588 (interactive)
a0e4e275
JL
589 (let ((pop-up-windows t)
590 same-window-buffer-names
591 same-window-regexps)
592 (display-buffer (Buffer-menu-buffer t))))
35aaf00c 593
2dd96f23
JB
594(defun Buffer-menu-2-window ()
595 "Select this line's buffer, with previous buffer in second window."
596 (interactive)
597 (let ((buff (Buffer-menu-buffer t))
598 (menu (current-buffer))
a0e4e275
JL
599 (pop-up-windows t)
600 same-window-buffer-names
601 same-window-regexps)
86dbbfc2 602 (delete-other-windows)
2dd96f23
JB
603 (switch-to-buffer (other-buffer))
604 (pop-to-buffer buff)
605 (bury-buffer menu)))
c0274f38 606
d91e2b1a 607(defun Buffer-menu-toggle-read-only ()
f450965c 608 "Toggle read-only status of buffer on this line, perhaps via version control."
d91e2b1a
ER
609 (interactive)
610 (let (char)
70ef8857
SM
611 (with-current-buffer (Buffer-menu-buffer t)
612 (toggle-read-only)
bfa13d19 613 (setq char (if buffer-read-only ?% ?\s)))
d91e2b1a
ER
614 (save-excursion
615 (beginning-of-line)
bc7bb432 616 (forward-char 1)
d91e2b1a 617 (if (/= (following-char) char)
70ef8857 618 (let ((inhibit-read-only t))
d91e2b1a
ER
619 (delete-char 1)
620 (insert char))))))
621
36570c92
RS
622(defun Buffer-menu-bury ()
623 "Bury the buffer listed on this line."
624 (interactive)
bc7bb432 625 (when (Buffer-menu-no-header)
a59faabe
RS
626 (save-excursion
627 (beginning-of-line)
628 (bury-buffer (Buffer-menu-buffer t))
629 (let ((line (buffer-substring (point) (progn (forward-line 1) (point))))
70ef8857 630 (inhibit-read-only t))
a59faabe
RS
631 (delete-region (point) (progn (forward-line -1) (point)))
632 (goto-char (point-max))
633 (insert line))
634 (message "Buried buffer moved to the end"))))
42a19c2a
RS
635
636
637(defun Buffer-menu-view ()
638 "View this line's buffer in View mode."
639 (interactive)
640 (view-buffer (Buffer-menu-buffer t)))
641
642
643(defun Buffer-menu-view-other-window ()
644 "View this line's buffer in View mode in another window."
645 (interactive)
646 (view-buffer-other-window (Buffer-menu-buffer t)))
0b03ce3a
RS
647\f
648
349b6bfe 649;;;###autoload
0b03ce3a
RS
650(define-key ctl-x-map "\C-b" 'list-buffers)
651
349b6bfe 652;;;###autoload
0b03ce3a
RS
653(defun list-buffers (&optional files-only)
654 "Display a list of names of existing buffers.
655The list is displayed in a buffer named `*Buffer List*'.
656Note that buffers with names starting with spaces are omitted.
657Non-null optional arg FILES-ONLY means mention only file buffers.
658
6a0d92d3 659For more information, see the function `buffer-menu'."
0b03ce3a 660 (interactive "P")
08c82d4d
RS
661 (display-buffer (list-buffers-noselect files-only)))
662
e1ff8dd0
SM
663(defconst Buffer-menu-short-ellipsis
664 ;; This file is preloaded, so we can't use char-displayable-p here
665 ;; because we don't know yet what display we're going to connect to.
666 ":" ;; (if (char-displayable-p ?…) "…" ":")
667 )
31e02fab 668
3e26a4a2 669(defun Buffer-menu-buffer+size (name size &optional name-props size-props)
31e02fab
SM
670 (if (> (+ (string-width name) (string-width size) 2)
671 Buffer-menu-buffer+size-width)
bc7bb432 672 (setq name
31e02fab
SM
673 (let ((tail
674 (if (string-match "<[0-9]+>$" name)
675 (match-string 0 name)
676 "")))
677 (concat (truncate-string-to-width
678 name
679 (- Buffer-menu-buffer+size-width
680 (max (string-width size) 3)
681 (string-width tail)
682 2))
683 Buffer-menu-short-ellipsis
f635daa1 684 (bidi-string-mark-left-to-right tail))))
ee5861c8 685 ;; Don't put properties on (buffer-name).
f635daa1 686 (setq name (bidi-string-mark-left-to-right name)))
bc7bb432
JB
687 (add-text-properties 0 (length name) name-props name)
688 (add-text-properties 0 (length size) size-props size)
d19e23ae
CY
689 (let ((name+space-width (- Buffer-menu-buffer+size-width
690 (string-width size))))
691 (concat name
692 (propertize (make-string (- name+space-width (string-width name))
693 ?\s)
4ac619f0
AA
694 'display `(space :align-to
695 ,(+ Buffer-menu-buffer-column
696 name+space-width)))
d19e23ae 697 size)))
bc7bb432 698
d5aacb46
SM
699(defun Buffer-menu-sort (column)
700 "Sort the buffer menu by COLUMN."
701 (interactive "P")
702 (when column
703 (setq column (prefix-numeric-value column))
704 (if (< column 2) (setq column 2))
705 (if (> column 5) (setq column 5)))
706 (setq Buffer-menu-sort-column column)
70ef8857 707 (let ((inhibit-read-only t) l buf m1 m2)
bfb2dda4
DP
708 (save-excursion
709 (Buffer-menu-beginning)
710 (while (not (eobp))
60ed8c72
AA
711 (when (buffer-live-p
712 (setq buf (get-text-property
713 (+ (point)
714 Buffer-menu-buffer-column)
715 'buffer)))
bfb2dda4
DP
716 (setq m1 (char-after)
717 m1 (if (memq m1 '(?> ?D)) m1)
718 m2 (char-after (+ (point) 2))
719 m2 (if (eq m2 ?S) m2))
720 (if (or m1 m2)
721 (push (list buf m1 m2) l)))
722 (forward-line)))
52a0198d 723 (revert-buffer)
bfb2dda4
DP
724 (save-excursion
725 (Buffer-menu-beginning)
726 (while (not (eobp))
60ed8c72
AA
727 (when (setq buf (assq (get-text-property (+ (point)
728 Buffer-menu-buffer-column)
729 'buffer) l))
bfb2dda4
DP
730 (setq m1 (cadr buf)
731 m2 (cadr (cdr buf)))
732 (when m1
733 (delete-char 1)
734 (insert m1)
735 (backward-char 1))
736 (when m2
737 (forward-char 2)
738 (delete-char 1)
739 (insert m2)))
740 (forward-line)))))
d5aacb46 741
104fc809
CY
742(defun Buffer-menu-sort-by-column (&optional e)
743 "Sort the buffer menu by the column clicked on."
744 (interactive (list last-input-event))
745 (if e (mouse-select-window e))
746 (let* ((pos (event-start e))
747 (obj (posn-object pos))
748 (col (if obj
749 (get-text-property (cdr obj) 'column (car obj))
750 (get-text-property (posn-point pos) 'column))))
751 (Buffer-menu-sort col)))
752
753(defvar Buffer-menu-sort-button-map
754 (let ((map (make-sparse-keymap)))
755 ;; This keymap handles both nil and non-nil values for
756 ;; Buffer-menu-use-header-line.
757 (define-key map [header-line mouse-1] 'Buffer-menu-sort-by-column)
758 (define-key map [header-line mouse-2] 'Buffer-menu-sort-by-column)
759 (define-key map [mouse-2] 'Buffer-menu-sort-by-column)
760 (define-key map [follow-link] 'mouse-face)
761 (define-key map "\C-m" 'Buffer-menu-sort-by-column)
762 map)
763 "Local keymap for Buffer menu sort buttons.")
764
d5aacb46
SM
765(defun Buffer-menu-make-sort-button (name column)
766 (if (equal column Buffer-menu-sort-column) (setq column nil))
104fc809
CY
767 (propertize name
768 'column column
769 'help-echo (concat
770 (if Buffer-menu-use-header-line
771 "mouse-1, mouse-2: sort by "
772 "mouse-2, RET: sort by ")
773 (if column (downcase name) "visited order"))
774 'mouse-face 'highlight
775 'keymap Buffer-menu-sort-button-map))
d5aacb46 776
61617913 777(defun list-buffers-noselect (&optional files-only buffer-list)
08c82d4d
RS
778 "Create and return a buffer with a list of names of existing buffers.
779The buffer is named `*Buffer List*'.
780Note that buffers with names starting with spaces are omitted.
781Non-null optional arg FILES-ONLY means mention only file buffers.
782
61617913
RS
783If BUFFER-LIST is non-nil, it should be a list of buffers;
784it means list those buffers and no others.
785
6a0d92d3 786For more information, see the function `buffer-menu'."
bc7bb432
JB
787 (let* ((old-buffer (current-buffer))
788 (standard-output standard-output)
bfa13d19 789 (mode-end (make-string (- Buffer-menu-mode-width 2) ?\s))
5e684428 790 (header (concat "CRM "
d5aacb46
SM
791 (Buffer-menu-buffer+size
792 (Buffer-menu-make-sort-button "Buffer" 2)
793 (Buffer-menu-make-sort-button "Size" 3))
794 " "
795 (Buffer-menu-make-sort-button "Mode" 4) mode-end
796 (Buffer-menu-make-sort-button "File" 5) "\n"))
f4872033 797 list desired-point)
748a336b 798 (when Buffer-menu-use-header-line
d5aacb46 799 (let ((pos 0))
fff861a2
JB
800 ;; Turn whitespace chars in the header into stretch specs so
801 ;; they work regardless of the header-line face.
802 (while (string-match "[ \t\n]+" header pos)
748a336b
SM
803 (setq pos (match-end 0))
804 (put-text-property (match-beginning 0) pos 'display
5e684428 805 ;; Assume fixed-size chars in the buffer.
6d3bce2b 806 (list 'space :align-to pos)
5e684428
SM
807 header)))
808 ;; Try to better align the one-char headers.
809 (put-text-property 0 3 'face 'fixed-pitch header)
810 ;; Add a "dummy" leading space to align the beginning of the header
811 ;; line with the beginning of the text (rather than with the left
c822571a 812 ;; scrollbar or the left fringe). --Stef
5e684428 813 (setq header (concat (propertize " " 'display '(space :align-to 0))
a6a2fd5e 814 header)))
d5aacb46 815 (with-current-buffer (get-buffer-create "*Buffer List*")
08c82d4d
RS
816 (setq buffer-read-only nil)
817 (erase-buffer)
818 (setq standard-output (current-buffer))
acb28818 819 ;; Force L2R direction, to avoid messing the display if the
7e2ad32c 820 ;; first buffer in the list happens to begin with a strong R2L
acb28818
EZ
821 ;; character.
822 (setq bidi-paragraph-direction 'left-to-right)
bc7bb432 823 (unless Buffer-menu-use-header-line
c822571a
SM
824 ;; Use U+2014 (EM DASH) to underline if possible, else use ASCII
825 ;; (i.e. U+002D, HYPHEN-MINUS).
826 (let ((underline (if (char-displayable-p ?\u2014) ?\u2014 ?-)))
5e684428
SM
827 (insert header
828 (apply 'string
829 (mapcar (lambda (c)
bfa13d19 830 (if (memq c '(?\n ?\s)) c underline))
5e684428 831 header)))))
a6a2fd5e 832 ;; Collect info for every buffer we're interested in.
3ce5f932
LT
833 (dolist (buffer (or buffer-list
834 (buffer-list
835 (when Buffer-menu-use-frame-buffer-list
836 (selected-frame)))))
a6a2fd5e
JL
837 (with-current-buffer buffer
838 (let ((name (buffer-name))
839 (file buffer-file-name))
840 (unless (and (not buffer-list)
841 (or
842 ;; Don't mention internal buffers.
843 (and (string= (substring name 0 1) " ") (null file))
844 ;; Maybe don't mention buffers without files.
845 (and files-only (not file))
846 (string= name "*Buffer List*")))
847 ;; Otherwise output info.
848 (let ((mode (concat (format-mode-line mode-name nil nil buffer)
849 (if mode-line-process
850 (format-mode-line mode-line-process
851 nil nil buffer))))
852 (bits (string
bfa13d19 853 (if (eq buffer old-buffer) ?. ?\s)
a6a2fd5e
JL
854 ;; Handle readonly status. The output buffer
855 ;; is special cased to appear readonly; it is
856 ;; actually made so at a later date.
857 (if (or (eq buffer standard-output)
858 buffer-read-only)
bfa13d19 859 ?% ?\s)
a6a2fd5e 860 ;; Identify modified buffers.
bfa13d19 861 (if (buffer-modified-p) ?* ?\s)
a6a2fd5e 862 ;; Space separator.
bfa13d19 863 ?\s)))
a6a2fd5e
JL
864 (unless file
865 ;; No visited file. Check local value of
fb5614e8
EZ
866 ;; list-buffers-directory and, for Info buffers,
867 ;; Info-current-file.
868 (cond ((and (boundp 'list-buffers-directory)
869 list-buffers-directory)
870 (setq file list-buffers-directory))
871 ((eq major-mode 'Info-mode)
872 (setq file Info-current-file)
873 (cond
26f544d3 874 ((equal file "dir")
fb5614e8
EZ
875 (setq file "*Info Directory*"))
876 ((eq file 'apropos)
877 (setq file "*Info Apropos*"))
878 ((eq file 'history)
879 (setq file "*Info History*"))
880 ((eq file 'toc)
881 (setq file "*Info TOC*"))
882 ((not (stringp file)) ;; avoid errors
b653cee4
EZ
883 (setq file nil))
884 (t
885 (setq file (concat "("
886 (file-name-nondirectory file)
9cda680d 887 ") "
b653cee4 888 Info-current-node)))))))
a6a2fd5e
JL
889 (push (list buffer bits name (buffer-size) mode file)
890 list))))))
891 ;; Preserve the original buffer-list ordering, just in case.
892 (setq list (nreverse list))
f4872033 893 ;; Place the buffers's info in the output buffer, sorted if necessary.
bc7bb432
JB
894 (dolist (buffer
895 (if Buffer-menu-sort-column
896 (sort list
897 (if (eq Buffer-menu-sort-column 3)
898 (lambda (a b)
899 (< (nth Buffer-menu-sort-column a)
900 (nth Buffer-menu-sort-column b)))
901 (lambda (a b)
902 (string< (nth Buffer-menu-sort-column a)
903 (nth Buffer-menu-sort-column b)))))
904 list))
905 (if (eq (car buffer) old-buffer)
906 (setq desired-point (point)))
907 (insert (cadr buffer)
908 ;; Put the buffer name into a text property
909 ;; so we don't have to extract it from the text.
910 ;; This way we avoid problems with unusual buffer names.
392640c4
NR
911 (let ((name (nth 2 buffer))
912 (size (int-to-string (nth 3 buffer))))
d19e23ae 913 (Buffer-menu-buffer+size name size
392640c4
NR
914 `(buffer-name ,name
915 buffer ,(car buffer)
916 font-lock-face buffer-menu-buffer
917 mouse-face highlight
9201cc28 918 help-echo
392640c4
NR
919 ,(if (>= (length name)
920 (- Buffer-menu-buffer+size-width
921 (max (length size) 3)
922 2))
923 name
3e26a4a2 924 "mouse-2: select this buffer"))))
a3e5a603
SM
925 " "
926 (if (> (string-width (nth 4 buffer)) Buffer-menu-mode-width)
927 (truncate-string-to-width (nth 4 buffer)
31e02fab 928 Buffer-menu-mode-width)
bc7bb432
JB
929 (nth 4 buffer)))
930 (when (nth 5 buffer)
931 (indent-to (+ Buffer-menu-buffer-column Buffer-menu-buffer+size-width
932 Buffer-menu-mode-width 4) 1)
933 (princ (abbreviate-file-name (nth 5 buffer))))
934 (princ "\n"))
0b03ce3a 935 (Buffer-menu-mode)
bc7bb432 936 (when Buffer-menu-use-header-line
748a336b 937 (setq header-line-format header))
08c82d4d
RS
938 ;; DESIRED-POINT doesn't have to be set; it is not when the
939 ;; current buffer is not displayed for some reason.
0b03ce3a 940 (and desired-point
08c82d4d 941 (goto-char desired-point))
7b0a86ab 942 (setq Buffer-menu-files-only files-only)
362b9d48 943 (setq Buffer-menu--buffers buffer-list)
7b0a86ab 944 (set-buffer-modified-p nil)
08c82d4d 945 (current-buffer))))
0b03ce3a 946
c0274f38 947;;; buff-menu.el ends here