]> git.donarmstrong.com Git - lib.git/blob - emacs_el/tiny-tools/tiny/tinychist.el
add tiny-tools
[lib.git] / emacs_el / tiny-tools / tiny / tinychist.el
1 ;;; tinychist.el --- Command history save/restore utility
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 tinychist-version.
13 ;; Look at the code with folding.el
14
15 ;; COPYRIGHT NOTICE
16 ;;
17 ;; This program is free software; you can redistribute it and/or modify it
18 ;; under the terms of the GNU General Public License as published by the Free
19 ;; Software Foundation; either version 2 of the License, or (at your option)
20 ;; any later version.
21 ;;
22 ;; This program is distributed in the hope that it will be useful, but
23 ;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
24 ;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
25 ;; for more details.
26 ;;
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with program; see the file COPYING. If not, write to the
29 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
30 ;; Boston, MA 02110-1301, USA.
31 ;;
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
33
34 ;;}}}
35 ;;{{{ Install
36
37 ;; ....................................................... &t-install ...
38 ;;  Put this file on your Emacs-Lisp load path, add following into your
39 ;;  ~/.emacs startup file.
40 ;;
41 ;;      (require 'tinychist)
42 ;;
43 ;;  This package also installs function `tinychist-command-history-load' in
44 ;;  `kill-emacs-hook' or if no such hook exist, you will be warned that
45 ;;  other means must be used. Now, whenever you start emacs, name it with
46 ;;  this option.
47 ;;
48 ;;      -name <TITLE>           e.g. -name Mail
49 ;;
50 ;;  and the history file associated with "Mail emacs" is loaded.
51 ;;  If you have any questions, use this function
52 ;;
53 ;;      M-x tinychist-submit-bug-report
54
55 ;;}}}
56 ;;{{{ Docs
57
58 ;; ..................................................... &t-commentary ...
59
60 ;;; Commentary:
61
62 ;;  Preface, apr 1996
63 ;;
64 ;;      In newsgroup post to gnu.emacs.help there was discussion about
65 ;;      saving and restoring emacs command history between session. Fred G.
66 ;;      Athearn <fga@maple.sover.net> sent a help message to a person
67 ;;      asking for it describing how to print out command-history and
68 ;;      saving it into a file with `C-x' `C-w'. This little package tries
69 ;;      to automate everything, so that when you load it, it will
70 ;;      automatically load command history for the right emacs and when you
71 ;;      exit emacs, the command history is saved to disk.
72 ;;
73 ;;  Overview of features
74 ;;
75 ;;      o   Save and restore emacs command history
76 ;;      o   Suport simultaneous emacs sessions, different history for
77 ;;          each one. Eg. "-name Mail" is associated with "mail" emacs history,
78 ;;          "-name News" is associated with "news" history. This trick works
79 ;;          on non-windowed tty too, since the switch is evaluated and cached
80 ;;          internally in those cases.
81 ;;
82 ;;  Default save file from -name parameter
83 ;;
84 ;;      The default save name of command history file is extracted from
85 ;;      the frame parameter. It is quite customary that people have
86 ;;      several emacs open in their X display, each one dedicated to
87 ;;      specific task.
88 ;;
89 ;;      The key here is, that you should make a habit of naming your
90 ;;      emacs by task when you start it:
91 ;;
92 ;;          % emacs -name Mail &        # My mail emacs
93 ;;          % emacs -name C++1 &        # My C++ project 1
94 ;;          % emacs -name News          # for news reading
95 ;;
96 ;;      This effectively sets the frame's name to "-name" parameter's
97 ;;      value. But old emacs versions are a little picky about the order of
98 ;;      command line options, please look at the info pages in which order
99 ;;      you must specify additional arguments. (info pages, Node:Initial
100 ;;      Options) For non-windowed environment, this trick doesn't quite
101 ;;      work out of the box, because emacs doesn't accept the name option
102 ;;      at all. Let's try to start fresh emacs to an xterm, not to separate
103 ;;      frame and see what happens. Order of the options is important here.
104 ;;
105 ;;          % emacs -nw -q -name Mail  &
106 ;;
107 ;;      What happens, is that you get two new buffers: "-name" and "Mail",
108 ;;      and this is not what we intended. If we ask the frame name in this
109 ;;      emacs, it says "terminal" or something similar. What we do instead,
110 ;;      is ,that we install our own command line handler in non-windowed
111 ;;      emacs and then we're able to intercept the "-name" option and it's
112 ;;      parameter. When the emacs is killed, we then again look at the
113 ;;      cached "-name" option to derive the save file postfix. If you're
114 ;;      interested in adding your own command line option, see function
115 ;;      ti::add-command-line-arg in tinylibm.el
116 ;;
117 ;;  How it works
118 ;;
119 ;;      Your emacs must support `kill-emacs-hook', so that the command
120 ;;      history can be saved automatically when you exit emacs. If you have
121 ;;      older than 19.xx, or other emacs that doesn't support the variable,
122 ;;      there is alternative example which replaces you emacs exit. See at
123 ;;      the end of file for examples. When emacs loads, the
124 ;;      `tinychist-load' is runs (see installation) and correct
125 ;;      `command-history' file is loaded.
126 ;;
127 ;;  File saving/loading
128 ;;
129 ;;      If you want to save/load the history session manually, you can call
130 ;;      function
131 ;;
132 ;;          M-x tinychist-command-history-save
133 ;;
134 ;;  About using the frame name
135 ;;
136 ;;      I know it's a bit risky to rely on the frame name's first word,
137 ;;      that it designates the purpose of your emacs. After all you _can_
138 ;;      change the frame name to whatever you want in side emacs. But in
139 ;;      general, the name labels your emacs and gives visible clue in X
140 ;;      where the particular emacs is used to. (in non-X you can't see the
141 ;;      name parameter at all, but I don't think that's a problem.)
142 ;;
143 ;;  See also
144 ;;
145 ;;      See file *chistory.el* in your emacs distribution, how to configure
146 ;;      some parameters. E.g.:
147 ;;
148 ;;          M-x command-history-mode
149 ;;          (setq list-command-history-max 50)
150
151 ;;}}}
152
153 ;;; Change Log:
154
155 ;;; Code:
156
157 ;;{{{ setup: require
158
159 ;;  Require not needed
160 ;; (require 'chistory)
161
162 (require 'tinylibm)
163
164 (eval-when-compile (ti::package-use-dynamic-compilation))
165
166 (ti::package-defgroup-tiny TinyChist tinychist-: extensions
167   "Automatic `command-history' save and restore utility.
168   Overview of features
169
170         o   Saving and restoring emacs command history
171         o   Supports simultaneous emacs sessions, different history for
172             each one. E.g. '-name Mail' is associated with 'mail' emacs history,
173             '-name News' is associated with 'news' history. This trick works
174             on non-windowed tty too, since the switch is evaluated and cached
175             internally in those cases.")
176
177 ;;}}}
178 ;;{{{ setup: variables
179
180 ;;; ......................................................... &v-hooks ...
181
182 (defcustom tinychist-:load-hook '(tinychist-install)
183   "*Hook run when file is loaded."
184   :type  'hook
185   :group 'TinyChist)
186
187 ;;; ........................................................ &v-public ...
188 ;;; user configurable
189
190 (defcustom tinychist-:file-prefix  (ti::package-config-file-prefix "tinychist-")
191   "*File prefix for command history files."
192   :type  'file
193   :group 'TinyChist)
194
195 (defcustom tinychist-:non-x-function 'tinychist-non-x-name
196   "*Function which return history file postfix in non-X Emacs."
197   :type  'function
198   :group 'TinyChist)
199
200 ;;; ....................................................... &v-private ...
201 ;;; do not touch this
202
203 (defvar tinychist-:load-flag  nil
204   "Non-nil means that flag is t when history has been loaded.")
205
206 ;;; ....................................................... &v-version ...
207
208 ;;;###autoload (autoload 'tinychist-version "tinychist" "Display commentary." t)
209 (eval-and-compile
210   (ti::macrof-version-bug-report
211    "tinychist.el"
212    "tinychist"
213    tinychist-:version-id
214    "$Id: tinychist.el,v 2.44 2007/05/07 10:50:07 jaalto Exp $"
215    '(tinychist-:version-id
216      tinychist-:load-hook
217      tinychist-:load-flag
218      tinychist-:file-prefix
219      tinychist-:non-x-function)))
220
221 ;;}}}
222
223 ;;; ########################################################### &Funcs ###
224
225 ;;{{{ code: misc
226
227 ;;; ----------------------------------------------------------------------
228 ;;; - If you don't use -name in X, then the frame name looks like
229 ;;;   EMACS-RUN-COMMAND@site.some.com
230 ;;;
231 (defsubst tinychist-get-frame-name ()
232   "Return first word of frame. "
233   (let (ptr)
234     (or (and (boundp 'command-line-args)
235              (setq ptr (member "-name" command-line-args))
236              (nth 1 ptr))
237         (ti::string-match "^\\([^\t ]+\\)" 1
238                           (frame-parameter (selected-frame) 'name)))))
239
240 ;;; ----------------------------------------------------------------------
241 ;;;
242 (defun tinychist-get-command-line-cache ()
243   "Return option's -name ARG, if it was cached in non window env."
244   (let* (ret)
245     ;; (("-name" . ignore) ("TITLE" . ignore) ..)
246     (dolist (elt command-switch-alist)
247       (when (string= "-name" (car elt))
248         (setq ret (car elt))
249         (return)))
250     ret))
251
252 ;;; ----------------------------------------------------------------------
253 ;;; - This is just an sample, you propably want to modify this
254 ;;;   to your env.
255 ;;;
256 (defun tinychist-non-x-name ()
257   "Return command history filename postfix.
258 Snoop what files are currently loaded into emacs. Eg. if RMAIL is found from
259 emacs, it is supposed that this emacs handles \"mail\"."
260   (let* (elt)
261     ;; The order is important, because first one matched is used
262     (cond
263      ((setq elt (tinychist-get-command-line-cache))
264       ;; If this is non-window emacs, check if this option is "cached" by
265       ;; this package.
266       elt)
267      ((ti::dolist-buffer-list
268        (string-match "VM$\\|RMAIL$\\|MH$" (buffer-name)) 'temp-buffers)
269       "mail")
270      ((ti::dolist-buffer-list (string-match "gnus" (buffer-name)) 'temp-buffers)
271       "news")
272      (t
273       "def"))))
274
275 ;;; ----------------------------------------------------------------------
276 ;;;
277 (defun tinychist-get-file-name  ()
278   "Return command history filename."
279   (let* ((pfx    (expand-file-name tinychist-:file-prefix))
280          frame)
281     (if (ti::compat-window-system)
282         (setq frame  (tinychist-get-frame-name))
283       (setq frame (funcall tinychist-:non-x-function)))
284     (concat pfx frame)))
285
286 ;;; ----------------------------------------------------------------------
287 ;;;
288 (defun tinychist-install (&optional uninstall)
289   "Install the package. optionally UNINSTALL."
290   (interactive)
291   (let* ()
292     (if uninstall
293         (remove-hook 'kill-emacs-hook 'tinychist-command-history-load)
294       (cond
295        ((boundp 'kill-emacs-hook)
296         (add-hook 'kill-emacs-hook 'tinychist-command-history-load))
297
298        ;;  Too bad; this emacs does not have kill-emacs-hook,
299        ;;  Check if the default exit function is bound to default key
300        ;;  and notify user that he should add custom exit function there.
301
302        ((memq (lookup-key global-map "\C-x\C-c")
303               '(save-buffers-kill-emacs))
304
305         (read-from-minibuffer
306          (concat
307           "Cannot auto-install. Use manual key binding C-xC-c installation."
308           " See source code of `tinychist-install'."))))
309
310       ;; load only once, prevent command-history wipe out
311       ;; if package is loaded multiple times
312
313       (if (null tinychist-:load-flag)
314           (tinychist-load)              ;Autoload the command history
315         (setq tinychist-:load-flag t)))))
316
317 ;;}}}
318 ;;{{{ code: main
319
320 ;;; ............................................................ &main ...
321
322 ;;; ----------------------------------------------------------------------
323 ;;; - I admit, this is a bit tricky thing to do, but it also demonstrates
324 ;;;   how you'd add new command line options
325 ;;;
326 ;;;      1. Install extra argument
327 ;;;      2. read the argument content and put it into list too
328 ;;;
329 (defun tinychist-load  ()
330   "Load appropriate `command-history' file by looking at command line option.
331 The `-name' option is used. This function runs when package is loaded."
332   (let* ((elt1                  (member "-name" command-line-args))
333          (elt2                  (car-safe (cdr elt1)))
334          (pfx                   (expand-file-name tinychist-:file-prefix))
335          (debug-on-error        t)      ;trigger errors
336          file)
337     (cond
338      ((not elt1)                             ;no option given
339       (setq file (tinychist-get-file-name))) ;guess which file to load.
340      (t
341       ;;  We know what user wants to load...
342       ;;  Prevent using this argument as buffer name. The "-name"
343       ;;  option is valid only in X
344       (unless (ti::compat-window-system)
345         (ti::add-command-line-arg elt2)         ;; this is put after
346         (ti::add-command-line-arg (car elt1)))  ;; this is put before
347       (setq file (concat pfx elt2))))
348     (if (and file (file-exists-p file))
349         (load file))))
350
351 ;;; ----------------------------------------------------------------------
352 ;;;
353 (defun tinychist-command-history-load  ()
354   "Load the saved `command-history' file."
355   (if command-history                   ;something to save ?
356       (tinychist-command-history-save (tinychist-get-file-name))))
357
358 ;;; ----------------------------------------------------------------------
359 ;;;
360 (defun tinychist-command-history-save  (file &optional load)
361   "Save history to FILE or optionally LOAD (prefix arg) command history."
362   (interactive "FFile: \nP")
363   (let* ((buffer         "*Command History*") ;see chistory.el
364          (debug-on-error t)                   ;trigger errors
365
366          ;;     XEmacs and Emacs don't agree with key commands,
367          ;;     so we leave them out from history
368          ;; (global-set-key
369          ;;  (quote [#<keypress-event control-C> #<keypress-event 1>]) ...
370
371          (re-incompatible "([^ ]+-key\\(-[^ ]+\\)* "))
372
373     (cond
374      (load
375       (load-file file))
376      (t
377       (list-command-history)
378       (with-current-buffer buffer
379         (setq buffer-read-only nil)
380         ;;  Format it
381         (ti::pmin)
382         (flush-lines re-incompatible)
383         (ti::pmin)
384         (insert
385          (concat
386           ";;\n"
387           ";; File: Emacs command history file\n"
388           ";; Desc: Load this with M-x load-file to reset the history to "
389           "these values\n"
390           ;;    This is for tinymy.el -- RCS compatible times tamp
391           ;;
392           ";; $\Docid: $\n"
393           "\n"))
394         (insert "(defconst command-history\n'(\n")
395         (ti::pmax)
396         (insert
397          (concat
398           "))\n\n"
399           ";;; " (file-name-nondirectory file) " ends here"))
400         (lisp-mode)
401         (indent-region (point-min) (point-max) nil)
402         ;; Update the times tamp [if function exist] and save the configuration
403         ;; See tinymy.el
404         (when (fboundp 'tinymy-file-stamp)
405           (ti::funcall 'tinymy-file-stamp))
406         (write-region (point-min) (point-max) file))
407
408       (ti::kill-buffer-safe buffer)))))
409
410 ;;}}}
411 ;;{{{ code: examples
412
413 ;;; ......................................................... &example ...
414 ;;; Draw region and extract code with tinylib.el / ti::package-rip-magic
415 ;;; This is Manual C-xC-x installation
416 ;;;
417 ;;; - If your Emacs doesn't have `kill-emacs-hook' then you
418 ;;;   need this manual installation example.
419 ;; -  Copy this into your emacs BEFORE the (require 'tinychist)
420
421 ;;* _
422 ;;* (global-set-key "\C-x\C-c" 'my-kill-emacs)
423 ;;* _
424 ;;* ;; This works for 18.57 and 19.xx, whereas the
425 ;;* ;; kill-emacs-query-functions doesn't, and this suffices for personal
426 ;;* ;; needs.
427 ;;* _
428 ;;* (defun my-kill-emacs (&optional arg)
429 ;;*   "Prevent accidental emacs kill. Calls save-buffers-kill-emacs"
430 ;;*   (interactive "P")
431 ;;*   (let* ((chist (tinychist-get-file-name))
432 ;;*          ch)
433 ;;*     (setq ch (ti::read-char-safe-until "Exit emacs y/[n]"))
434 ;;*     (when (char= ch ?y)
435 ;;*       (tinychist-command-history-save chist)
436 ;;*       (save-buffers-kill-emacs arg))))
437
438 ;;}}}
439 ;;{{{ code: install
440
441 ;;; .................................................... &auto-install ...
442
443 (provide   'tinychist)
444 (run-hooks 'tinychist-:load-hook)
445
446 ;;}}}
447
448 ;;; tinychist.el ends here