1 ;;; tinybuffer.el --- Change buffers in current window.
3 ;; This file is not part of Emacs.
7 ;; Copyright (C) 1996-2007 Jari Aalto
8 ;; Keywords: extensions
10 ;; Maintainer: Jari Aalto
12 ;; To get information on this program, call M-x tinybuffer-version.
13 ;; Look at the code with folding.el
15 ;; This program is free software; you can redistribute it and/or modify it
16 ;; under the terms of the GNU General Public License as published by the Free
17 ;; Software Foundation; either version 2 of the License, or (at your option)
20 ;; This program is distributed in the hope that it will be useful, but
21 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with program; see the file COPYING. If not, write to the
27 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
28 ;; Boston, MA 02110-1301, USA.
30 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
35 ;; ....................................................... &t-install ...
36 ;; Add following statement(s) to your ~/.emacs
38 ;; (require 'tinybuffer)
40 ;; or use autoload and your ~/.emacs starts lot faster. Preferred method.
42 ;; (autoload 'tinybuffer-iswitch-to-buffer "tinybuffer.el" "" t)
43 ;; (autoload 'tinybuffer-previous-buffer "tinybuffer.el" "" t)
44 ;; (autoload 'tinybuffer-next-buffer "tinybuffer.el" "" t)
45 ;; (autoload 'tinybuffer-sort-mode-toggle "tinybuffer.el" "" t)
47 ;; You don't need to copy these if you used the `require', but in order
48 ;; to trigger autoload you must insert these into your ~/.emacs. These
49 ;; are also the defaults bindings. If you use something other that these,
50 ;; reset the `tinybuffer-:load-hook' too.
52 ;; (defconst tinybuffer-:load-hook nil) ;; Don't load default bindings.
54 ;; ;; If you use Emacs with X window, these could be suitable keys.
56 ;; (global-set-key [(control <)] 'tinybuffer-previous-buffer)
57 ;; (global-set-key [(control >)] 'tinybuffer-next-buffer)
58 ;; (global-set-key [(control meta <)] 'tinybuffer-iswitch-to-buffer)
59 ;; (global-set-key [(alt <)] 'tinybuffer-sort-mode-toggle)
61 ;; ;; For non-windowed emacs; you want to program your own keys
62 ;; ;; Please check C-h l `view-lossage' for your keybindings, these
63 ;; ;; examples are from HP-UX numpad:
65 ;; (global-set-key "\eOq" 'tinybuffer-previous-buffer) ;; numpad 1
66 ;; (global-set-key "\eOr" 'tinybuffer-sort-mode-toggle) ;; numpad 2
67 ;; (global-set-key "\eOs" 'tinybuffer-next-buffer) ;; numpad 3
69 ;; ;; Here is code to switch between all visible windows
71 ;; (global-set-key [(f5)]
72 ;; (ti::definteractive ; in tinylibm.el
74 ;; (raise-frame (window-frame
76 ;; (current-buffer))))))
78 ;; If you have any questions, use this function to contact author
80 ;; M-x tinybuffer-submit-bug-report
85 ;; ..................................................... &t-commentary ...
92 ;; With this small package you can switch to next or previous buffer
93 ;; in a current window. If you only have small amount of buffers in
94 ;; `buffer-list', this may be the fastest way to select a working
95 ;; buffer. In the other hand, if you have more than 20 working
96 ;; buffers, I'd recommend that you use exellent substring buffer
97 ;; switching utility instead: *iswitchb.el* which is included in
98 ;; standard Emacs distribution
100 ;; If you have windowed environment and want to have hot list of your
101 ;; permanent buffers available, use *imenu.el* or *tinyhotlist.el* and
102 ;; you can select RMAIL; GNUS; VM; *scratch* buffers instantly.
106 ;; If you don't want default bindings, clear the installation with
107 ;; following command. This must be prior the 'require statement.
109 ;; (setq tinybuffer-:load-hook nil)
111 ;; To change buffers forward or backward, the default setup would install
112 ;; following key bindings:
114 ;; o Control-> Next buffer
115 ;; o Control-< Previous buffer
116 ;; o Alt-Control-< Iswitch mode, where you can scroll with < and >.
117 ;; Press RET to select or ESC/q to quit
118 ;; This may come handy if you have many buffers and just want to
119 ;; skip 2-5 buffers fast. E.g. if the buffers are font-lock
120 ;; controlled, switching to them with the C-, and C-, keys might
121 ;; be slow due to fontification which happens every time you
122 ;; switch over a buffer.
124 ;; In iswitch mode, the command prompt looks like following. The
125 ;; mode name is put last if buffer has and associated file name,
126 ;; so that filename gets as much display as possible.
128 ;; "TinyIswitch: my-lisp.el ~/elisp/my-lisp.el <Emacs lisp>"
129 ;; "TinyIswitch: test <dired> ~/tmp/test"
130 ;; "TinyIswitch: *Messages* <fundamental-mode>"
132 ;; Have a look at `tinybuffer-:ignore-regex' which you can configure
133 ;; to ignore some buffers permanently.
137 ;; Original idea for this package comes from *yic-buffer.el*
138 ;; by choo@cs.yale.edu (young-il choo) 1990-08-07.
146 ;;; ......................................................... &require ...
150 (eval-when-compile (ti::package-use-dynamic-compilation))
152 (ti::package-defgroup-tiny TinyBuffer tinybuffer-: extensions
153 "Changing buffers in current window.
154 With this small package you can switch to next or previous buffer
155 in a current window. If you only have small amount of buffers
156 it may be the fastest way.")
158 ;;{{{ setup: -- variables
160 ;;; ........................................................ &v-public ...
161 ;;; User configurable
163 (defcustom tinybuffer-:load-hook '(tinybuffer-install-default-bindings)
164 "*Hook run when file has been loaded.
165 Default value contains function `tinybuffer-install-default-bindings'."
169 (defcustom tinybuffer-:ignore-regexp
172 "\\|completion\\|summary"
173 "\\|buffer list\\|help\\|ispell\\|abbrev"
174 "\\|temp\\|tmp\\|vc\\|compile-log\\|occur")
175 "*Buffers to ignore when changing to another."
179 (defcustom tinybuffer-:sort-flag nil
180 "*Non-nil means that buffers are switched in sorted order."
184 (defcustom tinybuffer-:iswitch-to-buffer-keys '(?< ?>)
185 "*Keys to scroll buffers backward and forward in iswitch mode.
186 See \\[tinybuffer-iswitch-to-buffer]."
188 (character :tag "Backward")
189 (character :tag "Forward"))
192 (defcustom tinybuffer-:iswitch-show-directory-flag t
193 "*Non-nil means that directory name is shown in iswitch mode.
194 See \\[tinybuffer-iswitch-to-buffer]."
198 ;;; ....................................................... &v-private ...
199 ;;; Internal variables
201 (defvar tinybuffer-:buffer-list nil
202 "Global buffer list for `tinybuffer-iswitch-to-buffer'.")
204 ;;; ....................................................... &v-version ...
206 ;;;###autoload (autoload 'tinybuffer-version "tinybuffer" "Display commentary." t)
208 (ti::macrof-version-bug-report
211 tinybuffer-:version-id
212 "$Id: tinybuffer.el,v 2.41 2007/05/01 17:20:42 jaalto Exp $"
213 '(tinybuffer-:version-id
214 tinybuffer-:load-hook
215 tinybuffer-:ignore-regexp
216 tinybuffer-:sort-flag
217 tinybuffer-:iswitch-to-buffer-keys
218 tinybuffer-:iswitch-show-directory-flag
219 tinybuffer-:buffer-list)))
222 ;;{{{ code: functions
224 ;;; ########################################################### &Funcs ###
226 ;;; ----------------------------------------------------------------------
228 (defun tinybuffer-install-default-bindings ()
229 "Define default global keys."
231 (global-set-key [(control <)] 'tinybuffer-previous-buffer)
232 (global-set-key [(control >)] 'tinybuffer-next-buffer)
233 (global-set-key [(control meta <)] 'tinybuffer-iswitch-to-buffer)
234 (global-set-key [(control meta >)] 'tinybuffer-sort-mode-toggle))
236 ;;; ----------------------------------------------------------------------
238 (defun tinybuffer-start-list (buffer-pointer list)
239 "Let BUFFER-POINTER be first and arrange LIST."
240 (let* ((start (memq buffer-pointer list))
244 ;; Basic idea is this, say pointer is at B
248 ;; before B A --> take cdr --> A
250 ;; ret start + before = B C D A
253 (error "No such elt in list %s" buffer-pointer))
254 (setq before (cdr-safe (memq buffer-pointer rev)))
257 (union (reverse start) before))
260 ;;; ----------------------------------------------------------------------
262 (defun tinybuffer-buffer-filter (&optional blist)
263 "Filter BLIST, which defaults to `buffer-list'.
265 `tinybuffer-:ignore-regexp'"
267 (dolist (elt (or blist (buffer-list)) )
268 (if (not (string-match tinybuffer-:ignore-regexp
273 ;;; ----------------------------------------------------------------------
275 (defun tinybuffer-sort-buffer-list-1 (&optional blist reverse)
276 "Sort BLIST, which defaults to `buffer-list'. Optionally REVERSE."
277 (let* ((list (or blist (buffer-list))))
284 (string< (buffer-name b) (buffer-name a)))))
288 (string< (buffer-name a) (buffer-name b)))))))
291 ;;; ----------------------------------------------------------------------
293 (defun tinybuffer-sort-buffer-list (&optional reverse blist)
294 "Sort buffer list, optionally REVERSE and use BLIST."
298 (setq sorted (tinybuffer-sort-buffer-list-1 blist reverse))
300 ;; What is this? Okay, you see, when we sort the buffer list...
303 ;; ############# 'sorted' holds all
304 ;; %%%%%%% 'part' contains only these
306 ;; We're currently in C, and next one must be D. But if we're
307 ;; at the end, we're in G, and no buffers follow.
309 ;; So, to get past G, we have to make list in following way:
311 ;; @@@@@@ = %%%%% ############
312 ;; list = 'part' + 'sorted
314 ;; Sure, there is redundancy, since the 'sorted' holds all elements,
315 ;; but since we actually ignore buffers in the moving loop, we need
316 ;; all buffers past G.
318 (when (setq part (memq (current-buffer) sorted))
319 (setq list (cdr part))
320 (ti::nconc list sorted))
323 ;;; ----------------------------------------------------------------------
325 (defun tinybuffer-buffer-list-next (&optional reverse)
326 "Switch to next buffer in list, skipping unwanted ones. Optionally REVERSE.
327 See variable `tinybuffer-:ignore-regexp'."
328 (let* ((re tinybuffer-:ignore-regexp)
331 (tinybuffer-:sort-flag
332 (setq list (tinybuffer-sort-buffer-list reverse)))
334 (setq list (reverse (buffer-list))))
336 (setq list (buffer-list))))
338 (setq list (delq (current-buffer) list))
340 (dolist (buffer list)
341 (unless (string-match re (buffer-name buffer))
342 (setq go buffer) ;Stop and select it
347 "TinyBuffer: No buffers to circulate; see `tinybuffer-:ignore-regexp'"))
350 (switch-to-buffer go))))
352 ;;; ----------------------------------------------------------------------
354 (defun tinybuffer-init-buffer-list ()
355 "Initialize global variable `tinybuffer-:buffer-list'."
356 (let* ((list (tinybuffer-buffer-filter)))
357 (if tinybuffer-:sort-flag
358 (setq list (tinybuffer-start-list
360 (tinybuffer-sort-buffer-list-1 list))))
361 (setq tinybuffer-:buffer-list list)))
363 ;;; ----------------------------------------------------------------------
365 (defmacro tinybuffer-iswitch-next ()
366 "Return next buffer in list."
368 (let* ((first (car tinybuffer-:buffer-list))
369 (rest (cdr tinybuffer-:buffer-list))
372 (ti::nconc list first) ;add to the end
373 (setq tinybuffer-:buffer-list list) ;update list
376 ;;; ----------------------------------------------------------------------
378 (defmacro tinybuffer-iswitch-previous ()
379 "Return previous buffer in list."
381 (let* ((rev (reverse tinybuffer-:buffer-list))
383 (rest (reverse (cdr rev)))
386 (push last list) ;add to the end
387 (setq tinybuffer-:buffer-list list) ;update list
391 ;;{{{ code: interactive
393 ;;; ----------------------------------------------------------------------
396 (defun tinybuffer-iswitch-to-buffer ()
397 "Switch to buffer when RETURN/SPACE/TAB pressed.
398 Show buffer at echo area. ESC to cancel prompt.
402 The startup time of calling this function may be high, because it has
403 to build list of choices and possibly filter out unwanted buffers.
407 `tinybuffer-:iswitch-to-buffer-keys' keys to scroll buffer list"
409 (let* ((keys tinybuffer-:iswitch-to-buffer-keys)
410 (show-dir tinybuffer-:iswitch-show-directory-flag)
411 (go-list '(?\C-m ?\t ?\ ?\e ?\q ?\Q))
412 (quit-list '(?\e ?\q ?\Q))
413 (key-back (nth 0 keys))
414 (key-fw (nth 1 keys))
424 (tinybuffer-init-buffer-list)
429 (with-current-buffer (get-buffer str)
431 ((eq major-mode 'dired-mode)
433 (symbol-value 'dired-directory)))
435 (format "<%s>" (symbol-name major-mode))))))
438 (setq dir (or (buffer-file-name (get-buffer str))
441 ;; This formats the line so that it is visually more pleasant
442 ;; to read. If the file and dir are sticked together, it's
443 ;; hard to distinguish the words.
448 (if (and str (< (+ (length str) (length dir)) 55))
449 "TinyIswich: %-20s %s %s"
450 "TinyIswich: %s %s %s"))
456 (setq ch (ti::read-char-safe-until
457 (format fmt str (or dir "") (or mode "" ))))
459 ;;; (ti::d! str buffer (char= ch key-back) (char= ch key-fw) go-list)
462 ((and ch (char= ch key-back))
463 (setq buffer (tinybuffer-iswitch-previous)))
464 ((and ch (char= ch key-fw))
465 (setq buffer (tinybuffer-iswitch-next)))
466 ((and ch (ti::char-in-list-case ch go-list))
470 (setq str (buffer-name buffer))))
474 (not (ti::char-in-list-case ch quit-list)))
475 (switch-to-buffer buffer))))
477 ;;; ----------------------------------------------------------------------
480 (defun tinybuffer-previous-buffer ()
481 "Switch to previous buffer in current window."
483 (tinybuffer-buffer-list-next 'reverse))
485 ;;; ----------------------------------------------------------------------
488 (defun tinybuffer-next-buffer ()
489 "Switch to the other buffer (2nd in list-buffer) in current window."
491 (bury-buffer (current-buffer))
492 (tinybuffer-buffer-list-next))
494 ;;; ----------------------------------------------------------------------
496 (defun tinybuffer-sort-mode-toggle ()
499 (setq tinybuffer-:sort-flag (not tinybuffer-:sort-flag))
500 (message (concat "TinyBuffer: sort mode "
501 (if tinybuffer-:sort-flag
507 (provide 'tinybuffer)
508 (run-hooks 'tinybuffer-:load-hook)
510 ;;; tinybuffer.el ends here