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