Update years in copyright notice; nfc.
[bpt/emacs.git] / lisp / bs.el
CommitLineData
6448a6b3
GM
1;;; bs.el --- menu for selecting and displaying buffers
2
0d30b337 3;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
aaef169d 4;; 2005, 2006 Free Software Foundation, Inc.
6448a6b3
GM
5;; Author: Olaf Sylvester <Olaf.Sylvester@netsurf.de>
6;; Maintainer: Olaf Sylvester <Olaf.Sylvester@netsurf.de>
7;; Keywords: convenience
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
6448a6b3
GM
25
26;;; Commentary:
27
28;; Version: 1.17
91b69101 29;; X-URL: http://www.geekware.de/software/emacs
6448a6b3
GM
30;;
31;; The bs-package contains a main function bs-show for poping up a
32;; buffer in a way similar to `list-buffers' and `electric-buffer-list':
33;; The new buffer offers a Buffer Selection Menu for manipulating
34;; the buffer list and buffers.
35;;
36;; -----------------------------------------------------------------------
37;; | MR Buffer Size Mode File |
38;; | -- ------ ---- ---- ---- |
39;; |. bs.el 14690 Emacs-Lisp /home/sun/sylvester/el/bs.e$|
40;; | % executable.el 9429 Emacs-Lisp /usr/share/emacs/19.34/lisp$|
41;; | % vc.el 104893 Emacs-Lisp /usr/share/emacs/19.34/lisp$|
42;; | % test_vc.el 486 Emacs-Lisp /home/sun/sylvester/el/test$|
43;; | % vc-hooks.el 43605 Emacs-Lisp /usr/share/emacs/19.34/lisp$|
44;; -----------------------------------------------------------------------
45
46;;; Quick Installation und Customization:
47
48;; Use
49;; M-x bs-show
50;; for buffer selection or optional bind a key to main function `bs-show'
51;; (global-set-key "\C-x\C-b" 'bs-show) ;; or another key
52;;
53;; For customization use
54;; M-x bs-customize
55
56
57;;; More Commentary:
58
59;; bs-show will generate a new buffer named *buffer-selection*, which shows
60;; all buffers or a subset of them, and has possibilities for deleting,
61;; saving and selecting buffers. For more details see docstring of
62;; function `bs-mode'. A current configuration describes which buffers appear
63;; in *buffer-selection*. See docstring of variable `bs-configurations' for
64;; more details.
65;;
66;; The package bs combines the advantages of the Emacs functions
67;; `list-buffers' and `electric-buffer-list'.
68;;
880ed9f1 69;; Additional features for Buffer Selection Menu:
6448a6b3
GM
70;; - configurable list of buffers (show only files etc.).
71;; - comfortable way to change displayed subset of all buffers.
72;; - show sorted list of buffers.
73;; - cyclic navigation:
74;; - goes to top of buffer list if you are on last line and press down.
75;; - goes to end of buffer list if you are on first line and press up.
76;; - Offer an alternative buffer list by prefix key C-u.
77
78;;; Cycling through buffers
79
80;; This package offers two functions for buffer cycling. If you want to cycle
81;; through buffer list you can use `bs-cycle-next' or `bs-cycle-previous'.
82;; Bind these function to a key like
83;; (global-set-key [(f9)] 'bs-cycle-previous)
84;; (global-set-key [(f10)] 'bs-cycle-next)
85;;
86;; Both functions use a special subset of all buffers for cycling to avoid
87;; to go through internal buffers like *Messages*.
88;;
89;; Cycling through buffers ignores sorting because sorting destroys
90;; the logical buffer list. If buffer list is sorted by size you
91;; won't be able to cycle to the smallest buffer.
92
93;;; Customization:
94
95;; There is a customization group called `bs' in group `convenience'.
96;; Start customization by M-x bs-customize
97;;
98;; Buffer list
99;; -----------
100;; You can define your own configurations by extending variable
101;; `bs-configurations' (see docstring for details).
102;;
103;; `bs-default-configuration' contains the name of default configuration.
104;; The default value is "files" which means to show only files.
105;;
106;; If you always want to see all buffers, customize variable
107;; `bs-default-configuration' in customization group `bs'.
108;;
109;; Configure sorting
110;; -----------------
111;; You can define functions for sorting the buffer list.
112;; When selecting buffers, you can step through available sorting
113;; methods with key 'S'.
114;; To define a new way of sorting, customize variable `bs-sort-functions'.
115;;
116;; There are four basic functions for sorting:
117;; by buffer name, by mode, by size, or by filename
118;;
119;; Configure buffer cycling
120;; ------------------------
121;; When cycling through buffer list the functions for cycling will use
122;; the current configuration of bs to calculate the buffer list.
123;; If you want to use a different configuration for cycling you have to set
124;; the variable `bs-cycle-configuration-name'. You can customize this variable.
125;;
126;; For example: If you use the configuration called "files-and-scratch" you
127;; can cycle through all file buffers and *scratch* although your current
128;; configuration perhaps is "files" which ignores buffer *scratch*.
129
130;;; History:
131
132;;; Code:
133
e47a90e5
JB
134(defvar font-lock-verbose)
135
6448a6b3
GM
136;; ----------------------------------------------------------------------
137;; Globals for customization
138;; ----------------------------------------------------------------------
139
140(defgroup bs nil
141 "Buffer Selection: Maintaining buffers by buffer menu."
00a9f6a5 142 :version "21.1"
dc7904f5 143 :link '(emacs-commentary-link "bs")
91b69101 144 :link '(url-link "http://www.geekware.de/software/emacs")
6448a6b3
GM
145 :group 'convenience)
146
880ed9f1
GM
147(defgroup bs-appearance nil
148 "Buffer Selection appearance: Appearance of bs buffer menu."
6448a6b3
GM
149 :group 'bs)
150
151(defcustom bs-attributes-list
152 '(("" 1 1 left bs--get-marked-string)
153 ("M" 1 1 left bs--get-modified-string)
154 ("R" 2 2 left bs--get-readonly-string)
155 ("Buffer" bs--get-name-length 10 left bs--get-name)
156 ("" 1 1 left " ")
157 ("Size" 8 8 right bs--get-size-string)
158 ("" 1 1 left " ")
159 ("Mode" 12 12 right bs--get-mode-name)
160 ("" 2 2 left " ")
161 ("File" 12 12 left bs--get-file-name)
162 ("" 2 2 left " "))
163 "*List specifying the layout of a Buffer Selection Menu buffer.
164Each entry specifies a column and is a list of the form of:
c0cb1027 165\(HEADER MINIMUM-LENGTH MAXIMUM-LENGTH ALIGNMENT FUN-OR-STRING)
91b69101
JB
166
167HEADER : String for header for first line or a function
168 which calculates column title.
169MINIMUM-LENGTH : Minimum width of column (number or name of function).
170 The function must return a positive integer.
171MAXIMUM-LENGTH : Maximum width of column (number or name of function)
172 (currently ignored).
173ALIGNMENT : Alignment of column (`left', `right', `middle').
174FUN-OR-STRING : Name of a function for calculating the value or a
175 string for a constant value.
176
880ed9f1 177The function gets as parameter the buffer where we have started
6448a6b3 178buffer selection and the list of all buffers to show. The function must
880ed9f1
GM
179return a string representing the column's value."
180 :group 'bs-appearance
6448a6b3
GM
181 :type '(repeat sexp))
182
6448a6b3
GM
183(defun bs--make-header-match-string ()
184 "Return a regexp matching the first line of a Buffer Selection Menu buffer."
185 (let ((res "^\\(")
880ed9f1 186 (ele bs-attributes-list))
6448a6b3
GM
187 (while ele
188 (setq res (concat res (car (car ele)) " *"))
189 (setq ele (cdr ele)))
190 (concat res "$\\)")))
191
d8754ce5 192;; Font-Lock-Settings
6448a6b3 193(defvar bs-mode-font-lock-keywords
880ed9f1 194 (list ;; header in font-lock-type-face
dc7904f5
DL
195 (list (bs--make-header-match-string)
196 '(1 font-lock-type-face append) '(1 'bold append))
197 ;; Buffername embedded by *
198 (list "^\\(.*\\*.*\\*.*\\)$"
a5b09d5a
DL
199 1
200 ;; problem in XEmacs with font-lock-constant-face
201 (if (facep 'font-lock-constant-face)
202 'font-lock-constant-face
203 'font-lock-comment-face))
dc7904f5
DL
204 ;; Dired-Buffers
205 '("^..\\(.*Dired by .*\\)$" 1 font-lock-function-name-face)
206 ;; the star for modified buffers
207 '("^.\\(\\*\\) +[^\\*]" 1 font-lock-comment-face))
6448a6b3
GM
208 "Default font lock expressions for Buffer Selection Menu.")
209
210(defcustom bs-max-window-height 20
211 "*Maximal window height of Buffer Selection Menu."
880ed9f1 212 :group 'bs-appearance
6448a6b3
GM
213 :type 'integer)
214
215(defvar bs-dont-show-regexp nil
216 "Regular expression specifying which buffers not to show.
217A buffer whose name matches this regular expression will not be
218included in the buffer list.")
219
220(defvar bs-must-show-regexp nil
221 "Regular expression for specifying buffers which must be shown.
222A buffer whose name matches this regular expression will be
223included in the buffer list.
224Note that this variable is temporary: if the configuration is changed
225it is reset to nil. Use `bs-must-always-show-regexp' to specify buffers
226that must always be shown regardless of the configuration.")
227
228(defcustom bs-must-always-show-regexp nil
229 "*Regular expression for specifying buffers to show always.
230A buffer whose name matches this regular expression will
231be shown regardless of current configuration of Buffer Selection Menu."
232 :group 'bs
233 :type '(choice (const :tag "Nothing at all" nil) regexp))
234
235(defvar bs-dont-show-function nil
236 "Function for specifying buffers not to show.
237The function gets one argument - the buffer to test. The function must
238return a value different from nil to ignore the buffer in
239Buffer Selection Menu.")
240
241(defvar bs-must-show-function nil
242 "Function for specifying buffers which must be shown.
243The function gets one argument - the buffer to test.")
244
245(defvar bs-buffer-sort-function nil
246 "Sort function to sort the buffers that appear in Buffer Selection Menu.
880ed9f1 247The function gets two arguments - the buffers to compare.")
6448a6b3
GM
248
249(defcustom bs-maximal-buffer-name-column 45
250 "*Maximum column width for buffer names.
251The column for buffer names has dynamic width. The width depends on
252maximal and minimal length of names of buffers to show. The maximal
253width is bounded by `bs-maximal-buffer-name-column'.
254See also `bs-minimal-buffer-name-column'."
880ed9f1 255 :group 'bs-appearance
6448a6b3
GM
256 :type 'integer)
257
258(defcustom bs-minimal-buffer-name-column 15
259 "*Minimum column width for buffer names.
260The column for buffer names has dynamic width. The width depends on
261maximal and minimal length of names of buffers to show. The minimal
262width is bounded by `bs-minimal-buffer-name-column'.
263See also `bs-maximal-buffer-name-column'."
880ed9f1 264 :group 'bs-appearance
6448a6b3
GM
265 :type 'integer)
266
267(defconst bs-header-lines-length 2
268 "Number of lines for headers in Buffer Selection Menu.")
269
270(defcustom bs-configurations
271 '(("all" nil nil nil nil nil)
272 ("files" nil nil nil bs-visits-non-file bs-sort-buffer-interns-are-last)
880ed9f1 273 ("files-and-scratch" "^\\*scratch\\*$" nil nil bs-visits-non-file
6448a6b3
GM
274 bs-sort-buffer-interns-are-last)
275 ("all-intern-last" nil nil nil nil bs-sort-buffer-interns-are-last))
276 "*List of all configurations you can use in the Buffer Selection Menu.
277A configuration describes which buffers appear in Buffer Selection Menu
880ed9f1 278and also the order of buffers. A configuration is a list with
6448a6b3
GM
279six elements. The first element is a string and describes the configuration.
280The following five elements represent the values for Buffer Selection Menu
880ed9f1
GM
281configuration variables `bs-must-show-regexp', `bs-must-show-function',
282`bs-dont-show-regexp', `bs-dont-show-function' and `bs-buffer-sort-function'.
6448a6b3 283By setting these variables you define a configuration."
880ed9f1 284 :group 'bs-appearance
6448a6b3
GM
285 :type '(repeat sexp))
286
287(defcustom bs-default-configuration "files"
880ed9f1 288 "*Name of default configuration used by the Buffer Selection Menu.
6448a6b3
GM
289\\<bs-mode-map>
290Will be changed using key \\[bs-select-next-configuration].
291Must be a string used in `bs-configurations' for naming a configuration."
292 :group 'bs
293 :type 'string)
294
295(defcustom bs-alternative-configuration "all"
296 "*Name of configuration used when calling `bs-show' with \
297\\[universal-argument] as prefix key.
298Must be a string used in `bs-configurations' for naming a configuration."
299 :group 'bs
300 :type 'string)
301
302(defvar bs-current-configuration bs-default-configuration
303 "Name of current configuration.
880ed9f1 304Must be a string used in `bs-configurations' for naming a configuration.")
6448a6b3
GM
305
306(defcustom bs-cycle-configuration-name nil
307 "*Name of configuration used when cycling through the buffer list.
308A value of nil means to use current configuration `bs-default-configuration'.
309Must be a string used in `bs-configurations' for naming a configuration."
310 :group 'bs
311 :type '(choice (const :tag "like current configuration" nil)
312 string))
313
314(defcustom bs-string-show-always "+"
315 "*String added in column 1 indicating a buffer will always be shown."
880ed9f1 316 :group 'bs-appearance
6448a6b3
GM
317 :type 'string)
318
319(defcustom bs-string-show-never "-"
320 "*String added in column 1 indicating a buffer will never be shown."
880ed9f1 321 :group 'bs-appearance
6448a6b3
GM
322 :type 'string)
323
324(defcustom bs-string-current "."
325 "*String added in column 1 indicating the current buffer."
880ed9f1 326 :group 'bs-appearance
6448a6b3
GM
327 :type 'string)
328
329(defcustom bs-string-current-marked "#"
330 "*String added in column 1 indicating the current buffer when it is marked."
880ed9f1 331 :group 'bs-appearance
6448a6b3
GM
332 :type 'string)
333
334(defcustom bs-string-marked ">"
335 "*String added in column 1 indicating a marked buffer."
880ed9f1 336 :group 'bs-appearance
6448a6b3
GM
337 :type 'string)
338
339(defcustom bs-string-show-normally " "
340 "*String added in column 1 indicating a unmarked buffer."
880ed9f1 341 :group 'bs-appearance
6448a6b3
GM
342 :type 'string)
343
344(defvar bs--name-entry-length 20
345 "Maximum length of all displayed buffer names.
346Used internally, only.")
347
348;; ----------------------------------------------------------------------
880ed9f1 349;; Internal globals
6448a6b3
GM
350;; ----------------------------------------------------------------------
351
352(defvar bs-buffer-show-mark nil
353 "Flag for the current mode for showing this buffer.
880ed9f1
GM
354A value of nil means buffer will be shown depending on the current
355configuration.
6448a6b3
GM
356A value of `never' means to never show the buffer.
357A value of `always' means to show buffer regardless of the configuration.")
358
359(make-variable-buffer-local 'bs-buffer-show-mark)
360
361;; Make face named region (for XEmacs)
362(unless (facep 'region)
363 (make-face 'region)
364 (set-face-background 'region "gray75"))
365
6448a6b3
GM
366(defun bs--sort-by-name (b1 b2)
367 "Compare buffers B1 and B2 by buffer name."
368 (string< (buffer-name b1)
dc7904f5 369 (buffer-name b2)))
6448a6b3
GM
370
371(defun bs--sort-by-filename (b1 b2)
372 "Compare buffers B1 and B2 by file name."
373 (string< (or (buffer-file-name b1) "")
dc7904f5 374 (or (buffer-file-name b2) "")))
6448a6b3
GM
375
376(defun bs--sort-by-mode (b1 b2)
377 "Compare buffers B1 and B2 by mode name."
378 (save-excursion
379 (string< (progn (set-buffer b1) (format "%s" mode-name))
dc7904f5 380 (progn (set-buffer b2) (format "%s" mode-name)))))
6448a6b3
GM
381
382(defun bs--sort-by-size (b1 b2)
383 "Compare buffers B1 and B2 by buffer size."
384 (save-excursion
385 (< (progn (set-buffer b1) (buffer-size))
386 (progn (set-buffer b2) (buffer-size)))))
387
388(defcustom bs-sort-functions
389 '(("by name" bs--sort-by-name "Buffer" region)
390 ("by size" bs--sort-by-size "Size" region)
391 ("by mode" bs--sort-by-mode "Mode" region)
392 ("by filename" bs--sort-by-filename "File" region)
393 ("by nothing" nil nil nil))
394 "*List of all possible sorting aspects for Buffer Selection Menu.
395You can add a new entry with a call to `bs-define-sort-function'.
396Each element is a list of four elements (NAME FUNCTION REGEXP-FOR-SORTING FACE)
397NAME specifies the sort order defined by function FUNCTION.
398FUNCTION nil means don't sort the buffer list. Otherwise the functions
399must have two parameters - the buffers to compare.
400REGEXP-FOR-SORTING is a regular expression which describes the
401column title to highlight.
402FACE is a face used to fontify the sorted column title. A value of nil means
403don't highlight."
404 :group 'bs
405 :type '(repeat sexp))
406
407(defun bs-define-sort-function (name fun &optional regexp-for-sorting face)
408 "Define a new function for buffer sorting in Buffer Selection Menu.
409NAME specifies the sort order defined by function FUN.
410A value of nil for FUN means don't sort the buffer list. Otherwise the
411functions must have two parameters - the buffers to compare.
412REGEXP-FOR-SORTING is a regular expression which describes the
413column title to highlight.
414FACE is a face used to fontify the sorted column title. A value of nil means
415don't highlight.
416The new sort aspect will be inserted into list `bs-sort-functions'."
417 (let ((tupel (assoc name bs-sort-functions)))
418 (if tupel
dc7904f5 419 (setcdr tupel (list fun regexp-for-sorting face))
6448a6b3 420 (setq bs-sort-functions
dc7904f5
DL
421 (cons (list name fun regexp-for-sorting face)
422 bs-sort-functions)))))
6448a6b3
GM
423
424(defvar bs--current-sort-function nil
425 "Description of the current function for sorting the buffer list.
426This is an element of `bs-sort-functions'.")
427
428(defcustom bs-default-sort-name "by nothing"
429 "*Name of default sort behavior.
430Must be \"by nothing\" or a string used in `bs-sort-functions' for
431naming a sort behavior. Default is \"by nothing\" which means no sorting."
432 :group 'bs
433 :type 'string
434 :set (lambda (var-name value)
dc7904f5
DL
435 (set var-name value)
436 (setq bs--current-sort-function
437 (assoc value bs-sort-functions))))
6448a6b3
GM
438
439(defvar bs--buffer-coming-from nil
440 "The buffer in which the user started the current Buffer Selection Menu.")
441
442(defvar bs--show-all nil
443 "Flag whether showing all buffers regardless of current configuration.
444Non nil means to show all buffers. Otherwise show buffers
445defined by current configuration `bs-current-configuration'.")
446
447(defvar bs--window-config-coming-from nil
448 "Window configuration before starting Buffer Selection Menu.")
449
450(defvar bs--intern-show-never "^ \\|\\*buffer-selection\\*"
451 "Regular expression specifying which buffers never to show.
452A buffer whose name matches this regular expression will never be
453included in the buffer list.")
454
455(defvar bs-current-list nil
456 "List of buffers shown in Buffer Selection Menu.
457Used internally, only.")
458
459(defvar bs--marked-buffers nil
460 "Currently marked buffers in Buffer Selection Menu.")
461
e6e76838
AS
462(defvar bs-mode-map
463 (let ((map (make-sparse-keymap)))
464 (define-key map " " 'bs-select)
465 (define-key map "f" 'bs-select)
466 (define-key map "v" 'bs-view)
467 (define-key map "!" 'bs-select-in-one-window)
468 (define-key map [mouse-2] 'bs-mouse-select) ;; for GNU EMACS
469 (define-key map [button2] 'bs-mouse-select) ;; for XEmacs
470 (define-key map "F" 'bs-select-other-frame)
471 (let ((key ?1))
472 (while (<= key ?9)
473 (define-key map (char-to-string key) 'digit-argument)
474 (setq key (1+ key))))
475 (define-key map "-" 'negative-argument)
476 (define-key map "\e-" 'negative-argument)
477 (define-key map "o" 'bs-select-other-window)
478 (define-key map "\C-o" 'bs-tmp-select-other-window)
479 ;; for GNU EMACS
480 (define-key map [mouse-3] 'bs-mouse-select-other-frame)
481 ;; for XEmacs
482 (define-key map [button3] 'bs-mouse-select-other-frame)
483 (define-key map [up] 'bs-up)
484 (define-key map "n" 'bs-down)
485 (define-key map "p" 'bs-up)
486 (define-key map [down] 'bs-down)
487 (define-key map "\C-m" 'bs-select)
488 (define-key map "b" 'bs-bury-buffer)
489 (define-key map "s" 'bs-save)
490 (define-key map "S" 'bs-show-sorted)
491 (define-key map "a" 'bs-toggle-show-all)
492 (define-key map "d" 'bs-delete)
493 (define-key map "\C-d" 'bs-delete-backward)
494 (define-key map "k" 'bs-delete)
495 (define-key map "g" 'bs-refresh)
496 (define-key map "C" 'bs-set-configuration-and-refresh)
497 (define-key map "c" 'bs-select-next-configuration)
498 (define-key map "q" 'bs-kill)
499 ;; (define-key map "z" 'bs-kill)
500 (define-key map "\C-c\C-c" 'bs-kill)
501 (define-key map "\C-g" 'bs-abort)
502 (define-key map "\C-]" 'bs-abort)
503 (define-key map "%" 'bs-toggle-readonly)
504 (define-key map "~" 'bs-clear-modified)
505 (define-key map "M" 'bs-toggle-current-to-show)
506 (define-key map "+" 'bs-set-current-buffer-to-show-always)
507 ;;(define-key map "-" 'bs-set-current-buffer-to-show-never)
508 (define-key map "t" 'bs-visit-tags-table)
509 (define-key map "m" 'bs-mark-current)
510 (define-key map "u" 'bs-unmark-current)
511 (define-key map ">" 'scroll-right)
512 (define-key map "<" 'scroll-left)
513 (define-key map "?" 'bs-help)
514 map)
6448a6b3
GM
515 "Keymap of `bs-mode'.")
516
6448a6b3
GM
517;; ----------------------------------------------------------------------
518;; Functions
519;; ----------------------------------------------------------------------
520
521(defun bs-buffer-list (&optional list sort-description)
522 "Return a list of buffers to be shown.
880ed9f1 523LIST is a list of buffers to test for appearance in Buffer Selection Menu.
6448a6b3
GM
524The result list depends on the global variables `bs-dont-show-regexp',
525`bs-must-show-regexp', `bs-dont-show-function', `bs-must-show-function'
526and `bs-buffer-sort-function'.
527If SORT-DESCRIPTION isn't nil the list will be sorted by
528a special function. SORT-DESCRIPTION is an element of `bs-sort-functions'."
529 (setq sort-description (or sort-description bs--current-sort-function)
dc7904f5 530 list (or list (buffer-list)))
6448a6b3
GM
531 (let ((result nil))
532 (while list
533 (let* ((buffername (buffer-name (car list)))
dc7904f5
DL
534 (int-show-never (string-match bs--intern-show-never buffername))
535 (ext-show-never (and bs-dont-show-regexp
536 (string-match bs-dont-show-regexp
537 buffername)))
538 (extern-must-show (or (and bs-must-always-show-regexp
539 (string-match
540 bs-must-always-show-regexp
541 buffername))
542 (and bs-must-show-regexp
543 (string-match bs-must-show-regexp
544 buffername))))
545 (extern-show-never-from-fun (and bs-dont-show-function
546 (funcall bs-dont-show-function
547 (car list))))
548 (extern-must-show-from-fun (and bs-must-show-function
549 (funcall bs-must-show-function
550 (car list))))
e64dbd8b 551 (show-flag (buffer-local-value 'bs-buffer-show-mark (car list))))
dc7904f5
DL
552 (if (or (eq show-flag 'always)
553 (and (or bs--show-all (not (eq show-flag 'never)))
554 (not int-show-never)
555 (or bs--show-all
556 extern-must-show
557 extern-must-show-from-fun
558 (and (not ext-show-never)
559 (not extern-show-never-from-fun)))))
560 (setq result (cons (car list)
561 result)))
562 (setq list (cdr list))))
6448a6b3
GM
563 (setq result (reverse result))
564 ;; The current buffer which was the start point of bs should be an element
565 ;; of result list, so that we can leave with space and be back in the
566 ;; buffer we started bs-show.
567 (if (and bs--buffer-coming-from
dc7904f5
DL
568 (buffer-live-p bs--buffer-coming-from)
569 (not (memq bs--buffer-coming-from result)))
570 (setq result (cons bs--buffer-coming-from result)))
6448a6b3
GM
571 ;; sorting
572 (if (and sort-description
dc7904f5
DL
573 (nth 1 sort-description))
574 (setq result (sort result (nth 1 sort-description)))
6448a6b3
GM
575 ;; else standard sorting
576 (bs-buffer-sort result))))
577
578(defun bs-buffer-sort (buffer-list)
579 "Sort buffers in BUFFER-LIST according to `bs-buffer-sort-function'."
580 (if bs-buffer-sort-function
581 (sort buffer-list bs-buffer-sort-function)
582 buffer-list))
583
584(defun bs--redisplay (&optional keep-line-p sort-description)
585 "Redisplay whole Buffer Selection Menu.
586If KEEP-LINE-P is non nil the point will stay on current line.
587SORT-DESCRIPTION is an element of `bs-sort-functions'"
588 (let ((line (1+ (count-lines 1 (point)))))
589 (bs-show-in-buffer (bs-buffer-list nil sort-description))
590 (if keep-line-p
dc7904f5 591 (goto-line line))
6448a6b3
GM
592 (beginning-of-line)))
593
594(defun bs--goto-current-buffer ()
595 "Goto line which represents the current buffer;
596actually the line which begins with character in `bs-string-current' or
597`bs-string-current-marked'."
dc7904f5
DL
598 (let ((regexp (concat "^"
599 (regexp-quote bs-string-current)
600 "\\|^"
601 (regexp-quote bs-string-current-marked)))
602 point)
6448a6b3
GM
603 (save-excursion
604 (goto-char (point-min))
605 (if (search-forward-regexp regexp nil t)
dc7904f5 606 (setq point (- (point) 1))))
6448a6b3 607 (if point
dc7904f5 608 (goto-char point))))
6448a6b3
GM
609
610(defun bs--current-config-message ()
611 "Return a string describing the current `bs-mode' configuration."
612 (if bs--show-all
613 "Show all buffers."
614 (format "Show buffer by configuration %S"
dc7904f5 615 bs-current-configuration)))
6448a6b3
GM
616
617(defun bs-mode ()
618 "Major mode for editing a subset of Emacs' buffers.
619\\<bs-mode-map>
620Aside from two header lines each line describes one buffer.
621Move to a line representing the buffer you want to edit and select
622buffer by \\[bs-select] or SPC. Abort buffer list with \\[bs-kill].
623There are many key commands similar to `Buffer-menu-mode' for
624manipulating the buffer list and buffers.
625For faster navigation each digit key is a digit argument.
626
627\\[bs-select] or SPACE -- select current line's buffer and other marked buffers.
628\\[bs-toggle-show-all] -- toggle between all buffers and a special subset.
629\\[bs-select-other-window] -- select current line's buffer in other window.
630\\[bs-tmp-select-other-window] -- make another window display that buffer and
631 remain in Buffer Selection Menu.
632\\[bs-mouse-select] -- select current line's buffer and other marked buffers.
e148f726
TTN
633\\[bs-save] -- save current line's buffer immediately.
634\\[bs-delete] -- kill current line's buffer immediately.
6448a6b3
GM
635\\[bs-toggle-readonly] -- toggle read-only status of current line's buffer.
636\\[bs-clear-modified] -- clear modified-flag on that buffer.
637\\[bs-mark-current] -- mark current line's buffer to be displayed.
638\\[bs-unmark-current] -- unmark current line's buffer to be displayed.
639\\[bs-show-sorted] -- display buffer list sorted by next sort aspect.
640\\[bs-set-configuration-and-refresh] -- ask user for a configuration and \
641apply selected configuration.
642\\[bs-select-next-configuration] -- select and apply next \
643available Buffer Selection Menu configuration.
644\\[bs-kill] -- leave Buffer Selection Menu without a selection.
880ed9f1 645\\[bs-toggle-current-to-show] -- toggle status of appearance.
6448a6b3
GM
646\\[bs-set-current-buffer-to-show-always] -- mark current line's buffer \
647to show always.
880ed9f1 648\\[bs-visit-tags-table] -- call `visit-tags-table' on current line's buffer.
6448a6b3
GM
649\\[bs-help] -- display this help text."
650 (interactive)
651 (kill-all-local-variables)
652 (use-local-map bs-mode-map)
653 (make-local-variable 'font-lock-defaults)
654 (make-local-variable 'font-lock-verbose)
655 (setq major-mode 'bs-mode
dc7904f5
DL
656 mode-name "Buffer-Selection-Menu"
657 buffer-read-only t
658 truncate-lines t
659 font-lock-defaults '(bs-mode-font-lock-keywords t)
660 font-lock-verbose nil)
2a16b26c 661 (run-mode-hooks 'bs-mode-hook))
6448a6b3
GM
662
663(defun bs-kill ()
664 "Let buffer disappear and reset window-configuration."
665 (interactive)
666 (bury-buffer (current-buffer))
667 (set-window-configuration bs--window-config-coming-from))
668
669(defun bs-abort ()
670 "Ding and leave Buffer Selection Menu without a selection."
dc7904f5 671 (interactive)
6448a6b3
GM
672 (ding)
673 (bs-kill))
674
675(defun bs-set-configuration-and-refresh ()
676 "Ask user for a configuration and apply selected configuration.
677Refresh whole Buffer Selection Menu."
678 (interactive)
679 (call-interactively 'bs-set-configuration)
680 (bs--redisplay t))
681
682(defun bs-refresh ()
683 "Refresh whole Buffer Selection Menu."
684 (interactive)
685 (bs--redisplay t))
686
687(defun bs--window-for-buffer (buffer-name)
688 "Return a window showing a buffer with name BUFFER-NAME.
689Take only windows of current frame into account.
690Return nil if there is no such buffer."
691 (let ((window nil))
692 (walk-windows (lambda (wind)
dc7904f5
DL
693 (if (string= (buffer-name (window-buffer wind))
694 buffer-name)
695 (setq window wind))))
6448a6b3
GM
696 window))
697
698(defun bs--set-window-height ()
699 "Change the height of the selected window to suit the current buffer list."
700 (unless (one-window-p t)
ca6b8248 701 (fit-window-to-buffer (selected-window) bs-max-window-height)))
6448a6b3
GM
702
703(defun bs--current-buffer ()
704 "Return buffer on current line.
880ed9f1 705Raise an error if not on a buffer line."
6448a6b3
GM
706 (beginning-of-line)
707 (let ((line (+ (- bs-header-lines-length)
dc7904f5 708 (count-lines 1 (point)))))
6448a6b3 709 (if (< line 0)
dc7904f5 710 (error "You are on a header row"))
6448a6b3
GM
711 (nth line bs-current-list)))
712
713(defun bs--update-current-line ()
714 "Update the entry on current line for Buffer Selection Menu."
715 (let ((buffer (bs--current-buffer))
dc7904f5 716 (inhibit-read-only t))
6448a6b3
GM
717 (beginning-of-line)
718 (delete-region (point) (line-end-position))
719 (bs--insert-one-entry buffer)
720 (beginning-of-line)))
721
722(defun bs-view ()
723 "View current line's buffer in View mode.
724Leave Buffer Selection Menu."
725 (interactive)
726 (view-buffer (bs--current-buffer)))
727
728(defun bs-select ()
729 "Select current line's buffer and other marked buffers.
730If there are no marked buffers the window configuration before starting
731Buffer Selectin Menu will be restored.
732If there are marked buffers each marked buffer and the current line's buffer
733will be selected in a window.
734Leave Buffer Selection Menu."
735 (interactive)
736 (let ((buffer (bs--current-buffer)))
737 (bury-buffer (current-buffer))
738 (set-window-configuration bs--window-config-coming-from)
739 (switch-to-buffer buffer)
740 (if bs--marked-buffers
dc7904f5
DL
741 ;; Some marked buffers for selection
742 (let* ((all (delq buffer bs--marked-buffers))
743 (height (/ (1- (frame-height)) (1+ (length all)))))
744 (delete-other-windows)
745 (switch-to-buffer buffer)
746 (while all
747 (split-window nil height)
748 (other-window 1)
749 (switch-to-buffer (car all))
750 (setq all (cdr all)))
751 ;; goto window we have started bs.
752 (other-window 1)))))
6448a6b3
GM
753
754(defun bs-select-other-window ()
755 "Select current line's buffer by `switch-to-buffer-other-window'.
756The window configuration before starting Buffer Selectin Menu will be restored
757unless there is no other window. In this case a new window will be created.
758Leave Buffer Selection Menu."
759 (interactive)
760 (let ((buffer (bs--current-buffer)))
761 (bury-buffer (current-buffer))
762 (set-window-configuration bs--window-config-coming-from)
763 (switch-to-buffer-other-window buffer)))
764
765(defun bs-tmp-select-other-window ()
766 "Make the other window select this line's buffer.
767The current window remains selected."
768 (interactive)
769 (let ((buffer (bs--current-buffer)))
770 (display-buffer buffer t)))
771
772(defun bs-select-other-frame ()
773 "Select current line's buffer in new created frame.
774Leave Buffer Selection Menu."
775 (interactive)
776 (let ((buffer (bs--current-buffer)))
777 (bury-buffer (current-buffer))
778 (set-window-configuration bs--window-config-coming-from)
779 (switch-to-buffer-other-frame buffer)))
780
781(defun bs-mouse-select-other-frame (event)
782 "Select selected line's buffer in new created frame.
783Leave Buffer Selection Menu.
784EVENT: a mouse click EVENT."
785 (interactive "e")
786 (mouse-set-point event)
787 (bs-select-other-frame))
788
789(defun bs-mouse-select (event)
790 "Select buffer on mouse click EVENT.
791Select buffer by `bs-select'."
792 (interactive "e")
793 (mouse-set-point event)
794 (bs-select))
795
796(defun bs-select-in-one-window ()
797 "Select current line's buffer in one window and delete other windows.
798Leave Buffer Selection Menu."
799 (interactive)
800 (bs-select)
801 (delete-other-windows))
802
803(defun bs-bury-buffer ()
804 "Bury buffer on current line."
805 (interactive)
806 (bury-buffer (bs--current-buffer))
807 (bs--redisplay t))
808
809(defun bs-save ()
810 "Save buffer on current line."
811 (interactive)
812 (let ((buffer (bs--current-buffer)))
813 (save-excursion
814 (set-buffer buffer)
815 (save-buffer))
816 (bs--update-current-line)))
817
818(defun bs-visit-tags-table ()
819 "Visit the tags table in the buffer on this line.
820See `visit-tags-table'."
821 (interactive)
822 (let ((file (buffer-file-name (bs--current-buffer))))
823 (if file
dc7904f5 824 (visit-tags-table file)
6448a6b3
GM
825 (error "Specified buffer has no file"))))
826
827(defun bs-toggle-current-to-show ()
828 "Toggle status of showing flag for buffer in current line."
829 (interactive)
830 (let ((buffer (bs--current-buffer))
dc7904f5 831 res)
6448a6b3
GM
832 (save-excursion
833 (set-buffer buffer)
834 (setq res (cond ((null bs-buffer-show-mark)
dc7904f5
DL
835 'never)
836 ((eq bs-buffer-show-mark 'never)
837 'always)
838 (t nil)))
6448a6b3
GM
839 (setq bs-buffer-show-mark res))
840 (bs--update-current-line)
841 (bs--set-window-height)
842 (bs--show-config-message res)))
843
844(defun bs-set-current-buffer-to-show-always (&optional not-to-show-p)
845 "Toggle status of buffer on line to `always shown'.
846NOT-TO-SHOW-P: prefix argument.
847With no prefix argument the buffer on current line is marked to show
848always. Otherwise it is marked to show never."
849 (interactive "P")
850 (if not-to-show-p
851 (bs-set-current-buffer-to-show-never)
852 (bs--set-toggle-to-show (bs--current-buffer) 'always)))
853
854(defun bs-set-current-buffer-to-show-never ()
855 "Toggle status of buffer on line to `never shown'."
856 (interactive)
857 (bs--set-toggle-to-show (bs--current-buffer) 'never))
858
859(defun bs--set-toggle-to-show (buffer what)
860 "Set value `bs-buffer-show-mark' of buffer BUFFER to WHAT.
861Redisplay current line and display a message describing
862the status of buffer on current line."
e64dbd8b 863 (with-current-buffer buffer (setq bs-buffer-show-mark what))
6448a6b3
GM
864 (bs--update-current-line)
865 (bs--set-window-height)
866 (bs--show-config-message what))
867
868(defun bs-mark-current (count)
869 "Mark buffers.
870COUNT is the number of buffers to mark.
871Move cursor vertically down COUNT lines."
872 (interactive "p")
873 (let ((dir (if (> count 0) 1 -1))
dc7904f5 874 (count (abs count)))
6448a6b3
GM
875 (while (> count 0)
876 (let ((buffer (bs--current-buffer)))
dc7904f5
DL
877 (if buffer
878 (setq bs--marked-buffers (cons buffer bs--marked-buffers)))
879 (bs--update-current-line)
880 (bs-down dir))
6448a6b3
GM
881 (setq count (1- count)))))
882
883(defun bs-unmark-current (count)
884 "Unmark buffers.
885COUNT is the number of buffers to unmark.
886Move cursor vertically down COUNT lines."
887 (interactive "p")
888 (let ((dir (if (> count 0) 1 -1))
dc7904f5 889 (count (abs count)))
6448a6b3
GM
890 (while (> count 0)
891 (let ((buffer (bs--current-buffer)))
dc7904f5
DL
892 (if buffer
893 (setq bs--marked-buffers (delq buffer bs--marked-buffers)))
894 (bs--update-current-line)
895 (bs-down dir))
6448a6b3
GM
896 (setq count (1- count)))))
897
898(defun bs--show-config-message (what)
899 "Show message indicating the new showing status WHAT.
900WHAT is a value of nil, `never', or `always'."
901 (bs-message-without-log (cond ((null what)
dc7904f5
DL
902 "Buffer will be shown normally.")
903 ((eq what 'never)
904 "Mark buffer to never be shown.")
905 (t "Mark buffer to show always."))))
6448a6b3
GM
906
907(defun bs-delete ()
908 "Kill buffer on current line."
909 (interactive)
910 (let ((current (bs--current-buffer))
dc7904f5 911 (inhibit-read-only t))
706e6a52
EZ
912 (unless (kill-buffer current)
913 (error "Buffer was not deleted"))
6448a6b3 914 (setq bs-current-list (delq current bs-current-list))
6448a6b3
GM
915 (beginning-of-line)
916 (delete-region (point) (save-excursion
dc7904f5
DL
917 (end-of-line)
918 (if (eobp) (point) (1+ (point)))))
6448a6b3 919 (if (eobp)
dc7904f5
DL
920 (progn
921 (backward-delete-char 1)
922 (beginning-of-line)
923 (recenter -1)))
6448a6b3
GM
924 (bs--set-window-height)))
925
926(defun bs-delete-backward ()
927 "Like `bs-delete' but go to buffer in front of current."
928 (interactive)
929 (let ((on-last-line-p (save-excursion (end-of-line) (eobp))))
930 (bs-delete)
931 (unless on-last-line-p
dc7904f5 932 (bs-up 1))))
6448a6b3
GM
933
934(defun bs-show-sorted ()
935 "Show buffer list sorted by buffer name."
936 (interactive)
937 (setq bs--current-sort-function
dc7904f5
DL
938 (bs-next-config-aux (car bs--current-sort-function)
939 bs-sort-functions))
6448a6b3
GM
940 (bs--redisplay)
941 (bs--goto-current-buffer)
942 (bs-message-without-log "Sorted %s" (car bs--current-sort-function)))
943
944(defun bs-apply-sort-faces (&optional sort-description)
945 "Set text properties for the sort described by SORT-DESCRIPTION.
946SORT-DESCRIPTION is an element of `bs-sort-functions'.
947Default is `bs--current-sort-function'."
948 (let ((sort-description (or sort-description
dc7904f5 949 bs--current-sort-function)))
6448a6b3
GM
950 (save-excursion
951 (goto-char (point-min))
6968a481
EZ
952 (if (and (nth 2 sort-description)
953 (search-forward-regexp (nth 2 sort-description) nil t))
954 (let ((inhibit-read-only t))
955 (put-text-property (match-beginning 0)
956 (match-end 0)
957 'face
958 (or (nth 3 sort-description)
959 'region)))))))
6448a6b3
GM
960
961(defun bs-toggle-show-all ()
962 "Toggle show all buffers / show buffers with current configuration."
963 (interactive)
964 (setq bs--show-all (not bs--show-all))
965 (bs--redisplay)
966 (bs--goto-current-buffer)
967 (bs-message-without-log "%s" (bs--current-config-message)))
968
969(defun bs-toggle-readonly ()
970 "Toggle read-only status for buffer on current line.
880ed9f1 971Uses function `vc-toggle-read-only'."
6448a6b3
GM
972 (interactive)
973 (let ((buffer (bs--current-buffer)))
974 (save-excursion
975 (set-buffer buffer)
976 (vc-toggle-read-only))
977 (bs--update-current-line)))
978
979(defun bs-clear-modified ()
980 "Set modified flag for buffer on current line to nil."
981 (interactive)
982 (let ((buffer (bs--current-buffer)))
983 (save-excursion
984 (set-buffer buffer)
985 (set-buffer-modified-p nil)))
986 (bs--update-current-line))
987
988(defun bs--nth-wrapper (count fun &rest args)
989 "Call COUNT times function FUN with arguments ARGS."
990 (setq count (or count 1))
991 (while (> count 0)
992 (apply fun args)
993 (setq count (1- count))))
994
995(defun bs-up (arg)
996 "Move cursor vertically up ARG lines in Buffer Selection Menu."
997 (interactive "p")
998 (if (and arg (numberp arg) (< arg 0))
999 (bs--nth-wrapper (- arg) 'bs--down)
1000 (bs--nth-wrapper arg 'bs--up)))
1001
1002(defun bs--up ()
1003 "Move cursor vertically up one line.
1004If on top of buffer list go to last line."
1005 (interactive "p")
ca6b8248
JB
1006 (if (> (count-lines 1 (point)) bs-header-lines-length)
1007 (forward-line -1)
1008 (goto-char (point-max))
1009 (beginning-of-line)
1010 (recenter -1)))
6448a6b3
GM
1011
1012(defun bs-down (arg)
1013 "Move cursor vertically down ARG lines in Buffer Selection Menu."
1014 (interactive "p")
1015 (if (and arg (numberp arg) (< arg 0))
1016 (bs--nth-wrapper (- arg) 'bs--up)
1017 (bs--nth-wrapper arg 'bs--down)))
1018
1019(defun bs--down ()
1020 "Move cursor vertically down one line.
1021If at end of buffer list go to first line."
ca6b8248
JB
1022 (if (eq (line-end-position) (point-max))
1023 (goto-line (1+ bs-header-lines-length))
1024 (forward-line 1)))
6448a6b3
GM
1025
1026(defun bs-visits-non-file (buffer)
1027 "Return t or nil whether BUFFER visits no file.
1028A value of t means BUFFER belongs to no file.
1029A value of nil means BUFFER belongs to a file."
1030 (not (buffer-file-name buffer)))
1031
1032(defun bs-sort-buffer-interns-are-last (b1 b2)
880ed9f1 1033 "Function for sorting internal buffers B1 and B2 at the end of all buffers."
6448a6b3
GM
1034 (string-match "^\\*" (buffer-name b2)))
1035
1036;; ----------------------------------------------------------------------
1037;; Configurations:
1038;; ----------------------------------------------------------------------
1039
1040(defun bs-config-clear ()
1041 "*Reset all variables which specify a configuration.
1042These variables are `bs-dont-show-regexp', `bs-must-show-regexp',
1043`bs-dont-show-function', `bs-must-show-function' and
1044`bs-buffer-sort-function'."
1045 (setq bs-dont-show-regexp nil
dc7904f5
DL
1046 bs-must-show-regexp nil
1047 bs-dont-show-function nil
1048 bs-must-show-function nil
1049 bs-buffer-sort-function nil))
6448a6b3
GM
1050
1051(defun bs-config--only-files ()
1052 "Define a configuration for showing only buffers visiting a file."
1053 (bs-config-clear)
880ed9f1 1054 (setq ;; I want to see *-buffers at the end
dc7904f5
DL
1055 bs-buffer-sort-function 'bs-sort-buffer-interns-are-last
1056 ;; Don't show files who don't belong to a file
1057 bs-dont-show-function 'bs-visits-non-file))
6448a6b3
GM
1058
1059(defun bs-config--files-and-scratch ()
1060 "Define a configuration for showing buffer *scratch* and file buffers."
1061 (bs-config-clear)
880ed9f1 1062 (setq ;; I want to see *-buffers at the end
dc7904f5
DL
1063 bs-buffer-sort-function 'bs-sort-buffer-interns-are-last
1064 ;; Don't show files who don't belong to a file
1065 bs-dont-show-function 'bs-visits-non-file
1066 ;; Show *scratch* buffer.
880ed9f1 1067 bs-must-show-regexp "^\\*scratch\\*$"))
6448a6b3
GM
1068
1069(defun bs-config--all ()
1070 "Define a configuration for showing all buffers.
1071Reset all according variables by `bs-config-clear'."
1072 (bs-config-clear))
1073
1074(defun bs-config--all-intern-last ()
1075 "Define a configuration for showing all buffers.
880ed9f1 1076Internal buffers appear at end of all buffers."
6448a6b3
GM
1077 (bs-config-clear)
1078 ;; I want to see *-buffers at the end
1079 (setq bs-buffer-sort-function 'bs-sort-buffer-interns-are-last))
1080
1081(defun bs-set-configuration (name)
1082 "Set configuration to the one saved under string NAME in `bs-configurations'.
1083When called interactively ask user for a configuration and apply selected
1084configuration."
1085 (interactive (list (completing-read "Use configuration: "
dc7904f5
DL
1086 bs-configurations
1087 nil
1088 t)))
6448a6b3
GM
1089 (let ((list (assoc name bs-configurations)))
1090 (if list
dc7904f5
DL
1091 (if (listp list)
1092 (setq bs-current-configuration name
1093 bs-must-show-regexp (nth 1 list)
1094 bs-must-show-function (nth 2 list)
1095 bs-dont-show-regexp (nth 3 list)
1096 bs-dont-show-function (nth 4 list)
1097 bs-buffer-sort-function (nth 5 list))
1098 ;; for backward compability
1099 (funcall (cdr list)))
6448a6b3
GM
1100 ;; else
1101 (ding)
1102 (bs-message-without-log "No bs-configuration named %S." name))))
1103
1104(defun bs-help ()
1105 "Help for `bs-show'."
1106 (interactive)
1107 (describe-function 'bs-mode))
1108
1109(defun bs-next-config-aux (start-name list)
1110 "Get the next assoc after START-NAME in list LIST.
1111Will return the first if START-NAME is at end."
1112 (let ((assocs list)
dc7904f5
DL
1113 (length (length list))
1114 pos)
6448a6b3
GM
1115 (while (and assocs (not pos))
1116 (if (string= (car (car assocs)) start-name)
dc7904f5 1117 (setq pos (- length (length assocs))))
6448a6b3
GM
1118 (setq assocs (cdr assocs)))
1119 (setq pos (1+ pos))
1120 (if (eq pos length)
dc7904f5 1121 (car list)
6448a6b3
GM
1122 (nth pos list))))
1123
1124(defun bs-next-config (name)
1125 "Return next configuration with respect to configuration with name NAME."
1126 (bs-next-config-aux name bs-configurations))
1127
1128(defun bs-select-next-configuration (&optional start-name)
1129 "Apply next configuration START-NAME and refresh buffer list.
1130If START-NAME is nil the current configuration `bs-current-configuration'
1131will be used."
1132 (interactive)
1133 (let ((config (bs-next-config (or start-name bs-current-configuration))))
1134 (bs-set-configuration (car config))
1135 (setq bs-default-configuration bs-current-configuration)
1136 (bs--redisplay t)
1137 (bs--set-window-height)
880ed9f1 1138 (bs-message-without-log "Selected configuration: %s" (car config))))
6448a6b3
GM
1139
1140(defun bs-show-in-buffer (list)
1141 "Display buffer list LIST in buffer *buffer-selection*.
1142Select buffer *buffer-selection* and display buffers according to current
1143configuration `bs-current-configuration'. Set window height, fontify buffer
1144and move point to current buffer."
1145 (setq bs-current-list list)
1146 (switch-to-buffer (get-buffer-create "*buffer-selection*"))
1147 (bs-mode)
1148 (let* ((inhibit-read-only t)
dc7904f5
DL
1149 (map-fun (lambda (entry)
1150 (length (buffer-name entry))))
1151 (max-length-of-names (apply 'max
1152 (cons 0 (mapcar map-fun list))))
1153 (name-entry-length (min bs-maximal-buffer-name-column
1154 (max bs-minimal-buffer-name-column
1155 max-length-of-names))))
6448a6b3
GM
1156 (erase-buffer)
1157 (setq bs--name-entry-length name-entry-length)
1158 (bs--show-header)
1159 (while list
1160 (bs--insert-one-entry (car list))
1161 (insert "\n")
1162 (setq list (cdr list)))
1163 (delete-backward-char 1)
1164 (bs--set-window-height)
1165 (bs--goto-current-buffer)
1166 (font-lock-fontify-buffer)
1167 (bs-apply-sort-faces)))
1168
1169(defun bs-next-buffer (&optional buffer-list sorting-p)
1170 "Return next buffer and buffer list for buffer cycling in BUFFER-LIST.
1171Ignore sorting when SORTING-P is nil.
1172If BUFFER-LIST is nil the result of `bs-buffer-list' will be used as
1173buffer list. The result is a cons of normally the second element of
1174BUFFER-LIST and the buffer list used for buffer cycling."
1175 (let* ((bs--current-sort-function (if sorting-p
dc7904f5
DL
1176 bs--current-sort-function))
1177 (bs-buffer-list (or buffer-list (bs-buffer-list))))
6448a6b3 1178 (cons (or (car (cdr bs-buffer-list))
dc7904f5
DL
1179 (car bs-buffer-list)
1180 (current-buffer))
1181 bs-buffer-list)))
6448a6b3
GM
1182
1183(defun bs-previous-buffer (&optional buffer-list sorting-p)
1184 "Return previous buffer and buffer list for buffer cycling in BUFFER-LIST.
1185Ignore sorting when SORTING-P is nil.
1186If BUFFER-LIST is nil the result of `bs-buffer-list' will be used as
1187buffer list. The result is a cons of last element of BUFFER-LIST and the
1188buffer list used for buffer cycling."
1189 (let* ((bs--current-sort-function (if sorting-p
dc7904f5
DL
1190 bs--current-sort-function))
1191 (bs-buffer-list (or buffer-list (bs-buffer-list))))
6448a6b3 1192 (cons (or (car (last bs-buffer-list))
dc7904f5
DL
1193 (current-buffer))
1194 bs-buffer-list)))
6448a6b3
GM
1195
1196(defun bs-message-without-log (&rest args)
1197 "Like `message' but don't log it on the message log.
a1d5a11b 1198All arguments ARGS are transferred to function `message'."
6448a6b3
GM
1199 (let ((message-log-max nil))
1200 (apply 'message args)))
1201
1202(defvar bs--cycle-list nil
880ed9f1 1203 "Current buffer list used for cycling.")
6448a6b3
GM
1204
1205;;;###autoload
1206(defun bs-cycle-next ()
1207 "Select next buffer defined by buffer cycling.
1208The buffers taking part in buffer cycling are defined
1209by buffer configuration `bs-cycle-configuration-name'."
1210 (interactive)
1211 (let ((bs--buffer-coming-from (current-buffer))
dc7904f5
DL
1212 (bs-dont-show-regexp bs-dont-show-regexp)
1213 (bs-must-show-regexp bs-must-show-regexp)
1214 (bs-dont-show-function bs-dont-show-function)
1215 (bs-must-show-function bs-must-show-function)
038e04cb
GM
1216 (bs--show-all nil))
1217 (bs-set-configuration (or bs-cycle-configuration-name bs-default-configuration))
6448a6b3 1218 (let ((bs-buffer-sort-function nil)
dc7904f5 1219 (bs--current-sort-function nil))
6448a6b3 1220 (let* ((tupel (bs-next-buffer (if (or (eq last-command
dc7904f5
DL
1221 'bs-cycle-next)
1222 (eq last-command
1223 'bs-cycle-previous))
1224 bs--cycle-list)))
1225 (next (car tupel))
1226 (cycle-list (cdr tupel)))
1227 (setq bs--cycle-list (append (cdr cycle-list)
1228 (list (car cycle-list))))
1229 (bury-buffer)
1230 (switch-to-buffer next)
1231 (bs-message-without-log "Next buffers: %s"
1232 (or (cdr bs--cycle-list)
1233 "this buffer"))))))
6448a6b3
GM
1234
1235
1236;;;###autoload
1237(defun bs-cycle-previous ()
1238 "Select previous buffer defined by buffer cycling.
1239The buffers taking part in buffer cycling are defined
1240by buffer configuration `bs-cycle-configuration-name'."
1241 (interactive)
1242 (let ((bs--buffer-coming-from (current-buffer))
dc7904f5
DL
1243 (bs-dont-show-regexp bs-dont-show-regexp)
1244 (bs-must-show-regexp bs-must-show-regexp)
1245 (bs-dont-show-function bs-dont-show-function)
1246 (bs-must-show-function bs-must-show-function)
038e04cb
GM
1247 (bs--show-all nil))
1248 (bs-set-configuration (or bs-cycle-configuration-name bs-default-configuration))
6448a6b3 1249 (let ((bs-buffer-sort-function nil)
dc7904f5 1250 (bs--current-sort-function nil))
6448a6b3 1251 (let* ((tupel (bs-previous-buffer (if (or (eq last-command
dc7904f5
DL
1252 'bs-cycle-next)
1253 (eq last-command
1254 'bs-cycle-previous))
1255 bs--cycle-list)))
1256 (prev-buffer (car tupel))
1257 (cycle-list (cdr tupel)))
1258 (setq bs--cycle-list (append (last cycle-list)
1259 (reverse (cdr (reverse cycle-list)))))
1260 (switch-to-buffer prev-buffer)
1261 (bs-message-without-log "Previous buffers: %s"
1262 (or (reverse (cdr bs--cycle-list))
1263 "this buffer"))))))
6448a6b3
GM
1264
1265(defun bs--get-value (fun &optional args)
1266 "Apply function FUN with arguments ARGS.
1267Return result of evaluation. Will return FUN if FUN is a number
1268or a string."
1269 (cond ((numberp fun)
dc7904f5
DL
1270 fun)
1271 ((stringp fun)
1272 fun)
1273 (t (apply fun args))))
6448a6b3
GM
1274
1275(defun bs--get-marked-string (start-buffer all-buffers)
1276 "Return a string which describes whether current buffer is marked.
1277START-BUFFER is the buffer where we started buffer selection.
1278ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu.
1279The result string is one of `bs-string-current', `bs-string-current-marked',
1280`bs-string-marked', `bs-string-show-normally', `bs-string-show-never', or
1281`bs-string-show-always'."
880ed9f1 1282 (cond ;; current buffer is the buffer we started buffer selection.
dc7904f5
DL
1283 ((eq (current-buffer) start-buffer)
1284 (if (memq (current-buffer) bs--marked-buffers)
1285 bs-string-current-marked ; buffer is marked
1286 bs-string-current))
1287 ;; current buffer is marked
1288 ((memq (current-buffer) bs--marked-buffers)
1289 bs-string-marked)
1290 ;; current buffer hasn't a special mark.
1291 ((null bs-buffer-show-mark)
1292 bs-string-show-normally)
1293 ;; current buffer has a mark not to show itself.
1294 ((eq bs-buffer-show-mark 'never)
1295 bs-string-show-never)
1296 ;; otherwise current buffer is marked to show always.
1297 (t
1298 bs-string-show-always)))
6448a6b3
GM
1299
1300(defun bs--get-modified-string (start-buffer all-buffers)
1301 "Return a string which describes whether current buffer is modified.
1302START-BUFFER is the buffer where we started buffer selection.
1303ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu."
1304 (if (buffer-modified-p) "*" " "))
1305
1306(defun bs--get-readonly-string (start-buffer all-buffers)
1307 "Return a string which describes whether current buffer is read only.
1308START-BUFFER is the buffer where we started buffer selection.
1309ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu."
1310 (if buffer-read-only "%" " "))
1311
1312(defun bs--get-size-string (start-buffer all-buffers)
1313 "Return a string which describes the size of current buffer.
1314START-BUFFER is the buffer where we started buffer selection.
1315ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu."
1316 (int-to-string (buffer-size)))
1317
1318(defun bs--get-name (start-buffer all-buffers)
1319 "Return name of current buffer for Buffer Selection Menu.
1320The name of current buffer gets additional text properties
1321for mouse highlighting.
1322START-BUFFER is the buffer where we started buffer selection.
1323ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu."
635ed242
JB
1324 (propertize (buffer-name)
1325 'help-echo "mouse-2: select this buffer, mouse-3: select in other frame"
1326 'mouse-face 'highlight))
6448a6b3 1327
6448a6b3
GM
1328(defun bs--get-mode-name (start-buffer all-buffers)
1329 "Return the name of mode of current buffer for Buffer Selection Menu.
1330START-BUFFER is the buffer where we started buffer selection.
1331ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu."
1332 mode-name)
1333
1334(defun bs--get-file-name (start-buffer all-buffers)
1335 "Return string for column 'File' in Buffer Selection Menu.
1336This is the variable `buffer-file-name' of current buffer.
00a9f6a5 1337If current mode is `dired-mode' or `shell-mode' it returns the
6448a6b3
GM
1338default directory.
1339START-BUFFER is the buffer where we started buffer selection.
1340ALL-BUFFERS is the list of buffer appearing in Buffer Selection Menu."
1341 (let ((string (copy-sequence (if (member major-mode
dc7904f5
DL
1342 '(shell-mode dired-mode))
1343 default-directory
1344 (or buffer-file-name "")))))
855eca56
EZ
1345 (add-text-properties
1346 0 (length string)
1347 '(mouse-face highlight
1348 help-echo "mouse-2: select this buffer, mouse-3: select in other frame")
1349 string)
6448a6b3
GM
1350 string))
1351
6448a6b3
GM
1352(defun bs--insert-one-entry (buffer)
1353 "Generate one entry for buffer BUFFER in Buffer Selection Menu.
1354It goes over all columns described in `bs-attributes-list'
1355and evaluates corresponding string. Inserts string in current buffer;
1356normally *buffer-selection*."
1357 (let ((string "")
dc7904f5
DL
1358 (columns bs-attributes-list)
1359 (to-much 0)
6448a6b3
GM
1360 (apply-args (append (list bs--buffer-coming-from bs-current-list))))
1361 (save-excursion
1362 (while columns
dc7904f5
DL
1363 (set-buffer buffer)
1364 (let ((min (bs--get-value (nth 1 (car columns))))
1365 ;;(max (bs--get-value (nth 2 (car columns)))) refered no more
1366 (align (nth 3 (car columns)))
1367 (fun (nth 4 (car columns)))
1368 (val nil)
1369 new-string)
1370 (setq val (bs--get-value fun apply-args))
1371 (setq new-string (bs--format-aux val align (- min to-much)))
1372 (setq string (concat string new-string))
1373 (if (> (length new-string) min)
1374 (setq to-much (- (length new-string) min)))
1375 ) ; let
1376 (setq columns (cdr columns))))
6448a6b3
GM
1377 (insert string)
1378 string))
1379
1380(defun bs--format-aux (string align len)
1381 "Generate a string with STRING with alignment ALIGN and length LEN.
1382ALIGN is one of the symbols `left', `middle', or `right'."
635ed242
JB
1383 (let* ((width (length string))
1384 (len (max len width)))
1385 (format (format "%%%s%ds" (if (eq align 'right) "" "-") len)
1386 (if (eq align 'middle)
1387 (concat (make-string (/ (- len width) 2) ?\s) string)
1388 string))))
6448a6b3
GM
1389
1390(defun bs--show-header ()
1391 "Insert header for Buffer Selection Menu in current buffer."
1392 (mapcar '(lambda (string)
dc7904f5
DL
1393 (insert string "\n"))
1394 (bs--create-header)))
6448a6b3
GM
1395
1396(defun bs--get-name-length ()
1397 "Return value of `bs--name-entry-length'."
1398 bs--name-entry-length)
1399
1400(defun bs--create-header ()
1401 "Return all header lines used in Buffer Selection Menu as a list of strings."
1402 (list (mapconcat (lambda (column)
dc7904f5
DL
1403 (bs--format-aux (bs--get-value (car column))
1404 (nth 3 column) ; align
1405 (bs--get-value (nth 1 column))))
1406 bs-attributes-list
1407 "")
1408 (mapconcat (lambda (column)
1409 (let ((length (length (bs--get-value (car column)))))
1410 (bs--format-aux (make-string length ?-)
1411 (nth 3 column) ; align
1412 (bs--get-value (nth 1 column)))))
1413 bs-attributes-list
1414 "")))
6448a6b3
GM
1415
1416(defun bs--show-with-configuration (name &optional arg)
880ed9f1 1417 "Display buffer list of configuration with name NAME.
6448a6b3
GM
1418Set configuration NAME and determine window for Buffer Selection Menu.
1419Unless current buffer is buffer *buffer-selection* we have to save
1420the buffer we started Buffer Selection Menu and the current window
1421configuration to restore buffer and window configuration after a
1422selection. If there is already a window displaying *buffer-selection*
1423select this window for Buffer Selection Menu. Otherwise open a new
1424window.
1425The optional argument ARG is the prefix argument when calling a function
1426for buffer selection."
1427 (bs-set-configuration name)
1428 (let ((bs--show-all (or bs--show-all arg)))
dc7904f5 1429 (unless (string= "*buffer-selection*" (buffer-name))
6448a6b3
GM
1430 ;; Only when not in buffer *buffer-selection*
1431 ;; we have to set the buffer we started the command
1432 (progn
dc7904f5
DL
1433 (setq bs--buffer-coming-from (current-buffer))
1434 (setq bs--window-config-coming-from (current-window-configuration))))
1435 (let ((liste (bs-buffer-list))
1436 (active-window (bs--window-for-buffer "*buffer-selection*")))
1437 (if active-window
1438 (select-window active-window)
1439 (if (> (window-height (selected-window)) 7)
1440 (progn
1441 (split-window-vertically)
1442 (other-window 1))))
1443 (bs-show-in-buffer liste)
1444 (bs-message-without-log "%s" (bs--current-config-message)))))
6448a6b3
GM
1445
1446(defun bs--configuration-name-for-prefix-arg (prefix-arg)
1447 "Convert prefix argument PREFIX-ARG to a name of a buffer configuration.
1448If PREFIX-ARG is nil return `bs-default-configuration'.
1449If PREFIX-ARG is an integer return PREFIX-ARG element of `bs-configurations'.
1450Otherwise return `bs-alternative-configuration'."
880ed9f1 1451 (cond ;; usually activation
dc7904f5
DL
1452 ((null prefix-arg)
1453 bs-default-configuration)
1454 ;; call with integer as prefix argument
1455 ((integerp prefix-arg)
1456 (if (and (< 0 prefix-arg) (<= prefix-arg (length bs-configurations)))
1457 (car (nth (1- prefix-arg) bs-configurations))
1458 bs-default-configuration))
1459 ;; call by prefix argument C-u
1460 (t bs-alternative-configuration)))
6448a6b3
GM
1461
1462;; ----------------------------------------------------------------------
1463;; Main function bs-customize and bs-show
1464;; ----------------------------------------------------------------------
1465
1466;;;###autoload
1467(defun bs-customize ()
1468 "Customization of group bs for Buffer Selection Menu."
1469 (interactive)
1470 (customize-group "bs"))
1471
1472;;;###autoload
1473(defun bs-show (arg)
dc7904f5 1474 "Make a menu of buffers so you can manipulate buffers or the buffer list.
6448a6b3
GM
1475\\<bs-mode-map>
1476There are many key commands similar to `Buffer-menu-mode' for
1477manipulating buffer list and buffers itself.
1478User can move with [up] or [down], select a buffer
1479by \\[bs-select] or [SPC]\n
1480Type \\[bs-kill] to leave Buffer Selection Menu without a selection.
1481Type \\[bs-help] after invocation to get help on commands available.
1482With prefix argument ARG show a different buffer list. Function
1483`bs--configuration-name-for-prefix-arg' determine accordingly
1484name of buffer configuration."
1485 (interactive "P")
1486 (setq bs--marked-buffers nil)
1487 (bs--show-with-configuration (bs--configuration-name-for-prefix-arg arg)))
1488
d8754ce5 1489;; Now provide feature bs
6448a6b3
GM
1490(provide 'bs)
1491
ab5796a9 1492;;; arch-tag: c0d9ab34-bf06-4368-ae9d-af88878e6802
6448a6b3 1493;;; bs.el ends here