]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinybuffer.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinybuffer.el
1 ;;; tinybuffer.el --- Change buffers in current window.
2
3 ;; This file is not part of Emacs.
4
5 ;;{{{ Id
6
7 ;; Copyright (C) 1996-2007 Jari Aalto
8 ;; Keywords:     extensions
9 ;; Author:       Jari Aalto
10 ;; Maintainer:   Jari Aalto
11 ;;
12 ;; To get information on this program, call M-x tinybuffer-version.
13 ;; Look at the code with folding.el
14
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)
18 ;; any later version.
19 ;;
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
23 ;; for more details.
24 ;;
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.
29 ;;
30 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
31
32 ;;}}}
33 ;;{{{ Install
34
35 ;; ....................................................... &t-install ...
36 ;;  Add following statement(s) to your ~/.emacs
37 ;;
38 ;;      (require 'tinybuffer)
39 ;;
40 ;;  or use autoload and your ~/.emacs starts lot faster. Preferred method.
41 ;;
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)
46 ;;
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.
51 ;;
52 ;;    (defconst tinybuffer-:load-hook nil)  ;; Don't load default bindings.
53 ;;
54 ;;    ;;    If you use Emacs with X window, these could be suitable keys.
55 ;;
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)
60 ;;
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:
64 ;;
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
68 ;;
69 ;;    ;;    Here is code to switch between all visible windows
70 ;;
71 ;;    (global-set-key [(f5)]
72 ;;                   (ti::definteractive        ; in tinylibm.el
73 ;;                     (other-window 1 t)
74 ;;                     (raise-frame (window-frame
75 ;;                                   (get-buffer-window
76 ;;                                    (current-buffer))))))
77 ;;
78 ;;   If you have any questions, use this function to contact author
79 ;;
80 ;;      M-x tinybuffer-submit-bug-report
81
82 ;;}}}
83 ;;{{{ Documentation
84
85 ;; ..................................................... &t-commentary ...
86
87 ;;; Commentary:
88
89 ;;
90 ;;  Preface, May 1996
91 ;;
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
99 ;;
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.
103 ;;
104 ;;  Description
105 ;;
106 ;;      If you don't want default bindings, clear the installation with
107 ;;      following command. This must be prior the 'require statement.
108 ;;
109 ;;          (setq tinybuffer-:load-hook nil)
110 ;;
111 ;;      To change buffers forward or backward, the default setup would install
112 ;;      following key bindings:
113 ;;
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.
123 ;;
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.
127 ;;
128 ;;              "TinyIswitch: my-lisp.el     ~/elisp/my-lisp.el <Emacs lisp>"
129 ;;              "TinyIswitch: test           <dired> ~/tmp/test"
130 ;;              "TinyIswitch: *Messages*     <fundamental-mode>"
131 ;;
132 ;;      Have a look at `tinybuffer-:ignore-regex' which you can configure
133 ;;      to ignore some buffers permanently.
134 ;;
135 ;;  Thanks
136 ;;
137 ;;      Original idea for this package comes from *yic-buffer.el*
138 ;;      by choo@cs.yale.edu (young-il choo) 1990-08-07.
139
140 ;;}}}
141
142 ;;; Change Log:
143
144 ;;; Code:
145
146 ;;; ......................................................... &require ...
147
148 (require 'tinylibm)
149
150 (eval-when-compile (ti::package-use-dynamic-compilation))
151
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.")
157
158 ;;{{{ setup: -- variables
159
160 ;;; ........................................................ &v-public ...
161 ;;; User configurable
162
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'."
166   :type  'hook
167   :group 'TinyBuffer)
168
169 (defcustom tinybuffer-:ignore-regexp
170   (concat
171    "^ "                                 ;hidden buffers
172    "\\|completion\\|summary"
173    "\\|buffer list\\|help\\|ispell\\|abbrev"
174    "\\|temp\\|tmp\\|vc\\|compile-log\\|occur")
175   "*Buffers to ignore when changing to another."
176   :type  'regexp
177   :group 'TinyBuffer)
178
179 (defcustom tinybuffer-:sort-flag nil
180   "*Non-nil means that buffers are switched in sorted order."
181   :type  'boolean
182   :group 'TinyBuffer)
183
184 (defcustom tinybuffer-:iswitch-to-buffer-keys  '(?< ?>)
185   "*Keys to scroll buffers backward and forward in iswitch mode.
186 See \\[tinybuffer-iswitch-to-buffer]."
187   :type '(list
188           (character :tag "Backward")
189           (character :tag "Forward"))
190   :group 'TinyBuffer)
191
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]."
195   :type  'boolean
196   :group 'TinyBuffer)
197
198 ;;; ....................................................... &v-private ...
199 ;;; Internal variables
200
201 (defvar tinybuffer-:buffer-list  nil
202   "Global buffer list for `tinybuffer-iswitch-to-buffer'.")
203
204 ;;; ....................................................... &v-version ...
205
206 ;;;###autoload (autoload 'tinybuffer-version "tinybuffer" "Display commentary." t)
207 (eval-and-compile
208   (ti::macrof-version-bug-report
209    "tinybuffer.el"
210    "tinybuffer"
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)))
220
221 ;;}}}
222 ;;{{{ code: functions
223
224 ;;; ########################################################### &Funcs ###
225
226 ;;; ----------------------------------------------------------------------
227 ;;;
228 (defun tinybuffer-install-default-bindings ()
229   "Define default global keys."
230   (interactive)
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))
235
236 ;;; ----------------------------------------------------------------------
237 ;;;
238 (defun tinybuffer-start-list  (buffer-pointer list)
239   "Let BUFFER-POINTER be first and arrange LIST."
240   (let* ((start (memq buffer-pointer list))
241          (rev   (reverse list))
242          before
243          ret)
244     ;; Basic idea is this, say pointer is at B
245     ;; list:   A B C D
246     ;; start:    B C D
247     ;; rev     D C B A
248     ;; before      B A  --> take cdr --> A
249     ;;
250     ;; ret     start + before = B C D A
251     ;;
252     (unless start
253       (error "No such elt in list %s" buffer-pointer))
254     (setq before (cdr-safe (memq buffer-pointer rev)))
255     (setq ret start)
256     (if before
257         (union (reverse start) before))
258     ret))
259
260 ;;; ----------------------------------------------------------------------
261 ;;;
262 (defun tinybuffer-buffer-filter (&optional blist)
263   "Filter BLIST, which defaults to `buffer-list'.
264 References:
265   `tinybuffer-:ignore-regexp'"
266   (let* (ret)
267     (dolist (elt (or blist (buffer-list))  )
268       (if (not (string-match tinybuffer-:ignore-regexp
269                              (buffer-name elt)))
270           (push elt ret)))
271     ret))
272
273 ;;; ----------------------------------------------------------------------
274 ;;;
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))))
278     (setq
279      list
280      (if reverse
281          (sort list
282                (function
283                 (lambda (a b)
284                   (string< (buffer-name b) (buffer-name a)))))
285        (sort list
286              (function
287               (lambda (a b)
288                 (string< (buffer-name a) (buffer-name b)))))))
289     list))
290
291 ;;; ----------------------------------------------------------------------
292 ;;;
293 (defun tinybuffer-sort-buffer-list  (&optional reverse blist)
294   "Sort buffer list, optionally REVERSE and use BLIST."
295   (let* (sorted
296          part
297          list)
298     (setq sorted (tinybuffer-sort-buffer-list-1 blist reverse))
299
300     ;;  What is this? Okay, you see, when we sort the buffer list...
301     ;;     A B C D E F G
302     ;;         ^
303     ;;     #############  'sorted' holds all
304     ;;           %%%%%%%  'part'   contains only these
305     ;;
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.
308     ;;
309     ;;  So, to get past G, we have to make list in following way:
310     ;;
311     ;;      @@@@@@ =  %%%%%    ############
312     ;;      list   =  'part' + 'sorted
313     ;;
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.
317
318     (when (setq part (memq (current-buffer) sorted))
319       (setq list (cdr part))
320       (ti::nconc list sorted))
321     sorted))
322
323 ;;; ----------------------------------------------------------------------
324 ;;;
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)
329          list go)
330     (cond
331      (tinybuffer-:sort-flag
332       (setq list (tinybuffer-sort-buffer-list reverse)))
333      (reverse
334       (setq list (reverse (buffer-list))))
335      (t
336       (setq list (buffer-list))))
337
338     (setq list (delq (current-buffer) list))
339
340     (dolist (buffer list)
341       (unless (string-match re (buffer-name buffer))
342         (setq go buffer)                ;Stop and select it
343         (return)))
344
345     (if (null go)
346         (message
347          "TinyBuffer: No buffers to circulate; see `tinybuffer-:ignore-regexp'"))
348
349     (if go
350         (switch-to-buffer go))))
351
352 ;;; ----------------------------------------------------------------------
353 ;;;
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
359                     (current-buffer)
360                     (tinybuffer-sort-buffer-list-1 list))))
361     (setq tinybuffer-:buffer-list list)))
362
363 ;;; ----------------------------------------------------------------------
364 ;;;
365 (defmacro tinybuffer-iswitch-next ()
366   "Return next buffer in list."
367   (`
368    (let* ((first (car tinybuffer-:buffer-list))
369           (rest  (cdr tinybuffer-:buffer-list))
370           (ret   (car rest)))
371      (setq list rest)
372      (ti::nconc list first)                     ;add to the end
373      (setq tinybuffer-:buffer-list list)        ;update list
374      ret)))
375
376 ;;; ----------------------------------------------------------------------
377 ;;;
378 (defmacro tinybuffer-iswitch-previous ()
379   "Return previous buffer in list."
380   (`
381    (let* ((rev   (reverse tinybuffer-:buffer-list))
382           (last  (car rev))
383           (rest  (reverse (cdr rev)))
384           (ret   last))
385      (setq list rest)
386      (push last list)                           ;add to the end
387      (setq tinybuffer-:buffer-list list)        ;update list
388      ret)))
389
390 ;;}}}
391 ;;{{{ code: interactive
392
393 ;;; ----------------------------------------------------------------------
394 ;;;
395 ;;;###autoload
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.
399
400 Note:
401
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.
404
405 References:
406
407   `tinybuffer-:iswitch-to-buffer-keys'    keys to scroll buffer list"
408   (interactive)
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))
415          (str       (buffer-name))
416          (loop      t)
417          dir
418          fmt
419          list
420          buffer
421          mode
422          ch)
423
424     (tinybuffer-init-buffer-list)
425
426     (while loop
427
428       (setq mode
429             (with-current-buffer (get-buffer str)
430               (cond
431                ((eq major-mode 'dired-mode)
432                 (format "<dired> %s"
433                         (symbol-value 'dired-directory)))
434                (t
435                 (format "<%s>" (symbol-name major-mode))))))
436
437       (when show-dir
438         (setq dir (or (buffer-file-name (get-buffer str))
439                       nil)))
440
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.
444       ;;
445       ;;  FILE      DIR
446
447       (setq fmt
448             (if (and str (< (+ (length str) (length dir)) 55))
449                 "TinyIswich: %-20s %s %s"
450               "TinyIswich: %s %s %s"))
451
452       (unless dir
453         (setq dir  mode
454               mode nil))
455
456       (setq ch (ti::read-char-safe-until
457                 (format fmt str (or dir "") (or mode "" ))))
458
459 ;;;      (ti::d! str buffer (char= ch key-back) (char= ch key-fw) go-list)
460
461       (cond
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))
467         (setq loop nil)))
468
469       (if buffer
470           (setq str (buffer-name buffer))))
471
472     (if (and ch
473              buffer
474              (not (ti::char-in-list-case ch quit-list)))
475         (switch-to-buffer buffer))))
476
477 ;;; ----------------------------------------------------------------------
478 ;;;
479 ;;;###autoload
480 (defun tinybuffer-previous-buffer ()
481   "Switch to previous buffer in current window."
482   (interactive)
483   (tinybuffer-buffer-list-next 'reverse))
484
485 ;;; ----------------------------------------------------------------------
486 ;;;
487 ;;;###autoload
488 (defun tinybuffer-next-buffer ()
489   "Switch to the other buffer (2nd in list-buffer) in current window."
490   (interactive)
491   (bury-buffer (current-buffer))
492   (tinybuffer-buffer-list-next))
493
494 ;;; ----------------------------------------------------------------------
495 ;;;
496 (defun tinybuffer-sort-mode-toggle ()
497   "Sort mode on/off."
498   (interactive)
499   (setq tinybuffer-:sort-flag (not tinybuffer-:sort-flag))
500   (message (concat "TinyBuffer: sort mode "
501                    (if tinybuffer-:sort-flag
502                        "on"
503                      "off"))))
504
505 ;;}}}
506
507 (provide   'tinybuffer)
508 (run-hooks 'tinybuffer-:load-hook)
509
510 ;;; tinybuffer.el ends here