1 ;;; tinychist.el --- Command history save/restore utility
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 tinychist-version.
13 ;; Look at the code with folding.el
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)
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
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.
32 ;; Visit <http://www.gnu.org/copyleft/gpl.html> for more information
37 ;; ....................................................... &t-install ...
38 ;; Put this file on your Emacs-Lisp load path, add following into your
39 ;; ~/.emacs startup file.
41 ;; (require 'tinychist)
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
48 ;; -name <TITLE> e.g. -name Mail
50 ;; and the history file associated with "Mail emacs" is loaded.
51 ;; If you have any questions, use this function
53 ;; M-x tinychist-submit-bug-report
58 ;; ..................................................... &t-commentary ...
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.
73 ;; Overview of features
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.
82 ;; Default save file from -name parameter
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
89 ;; The key here is, that you should make a habit of naming your
90 ;; emacs by task when you start it:
92 ;; % emacs -name Mail & # My mail emacs
93 ;; % emacs -name C++1 & # My C++ project 1
94 ;; % emacs -name News # for news reading
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.
105 ;; % emacs -nw -q -name Mail &
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
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.
127 ;; File saving/loading
129 ;; If you want to save/load the history session manually, you can call
132 ;; M-x tinychist-command-history-save
134 ;; About using the frame name
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.)
145 ;; See file *chistory.el* in your emacs distribution, how to configure
146 ;; some parameters. E.g.:
148 ;; M-x command-history-mode
149 ;; (setq list-command-history-max 50)
159 ;; Require not needed
160 ;; (require 'chistory)
164 (eval-when-compile (ti::package-use-dynamic-compilation))
166 (ti::package-defgroup-tiny TinyChist tinychist-: extensions
167 "Automatic `command-history' save and restore utility.
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.")
178 ;;{{{ setup: variables
180 ;;; ......................................................... &v-hooks ...
182 (defcustom tinychist-:load-hook '(tinychist-install)
183 "*Hook run when file is loaded."
187 ;;; ........................................................ &v-public ...
188 ;;; user configurable
190 (defcustom tinychist-:file-prefix (ti::package-config-file-prefix "tinychist-")
191 "*File prefix for command history files."
195 (defcustom tinychist-:non-x-function 'tinychist-non-x-name
196 "*Function which return history file postfix in non-X Emacs."
200 ;;; ....................................................... &v-private ...
201 ;;; do not touch this
203 (defvar tinychist-:load-flag nil
204 "Non-nil means that flag is t when history has been loaded.")
206 ;;; ....................................................... &v-version ...
208 ;;;###autoload (autoload 'tinychist-version "tinychist" "Display commentary." t)
210 (ti::macrof-version-bug-report
213 tinychist-:version-id
214 "$Id: tinychist.el,v 2.44 2007/05/07 10:50:07 jaalto Exp $"
215 '(tinychist-:version-id
218 tinychist-:file-prefix
219 tinychist-:non-x-function)))
223 ;;; ########################################################### &Funcs ###
227 ;;; ----------------------------------------------------------------------
228 ;;; - If you don't use -name in X, then the frame name looks like
229 ;;; EMACS-RUN-COMMAND@site.some.com
231 (defsubst tinychist-get-frame-name ()
232 "Return first word of frame. "
234 (or (and (boundp 'command-line-args)
235 (setq ptr (member "-name" command-line-args))
237 (ti::string-match "^\\([^\t ]+\\)" 1
238 (frame-parameter (selected-frame) 'name)))))
240 ;;; ----------------------------------------------------------------------
242 (defun tinychist-get-command-line-cache ()
243 "Return option's -name ARG, if it was cached in non window env."
245 ;; (("-name" . ignore) ("TITLE" . ignore) ..)
246 (dolist (elt command-switch-alist)
247 (when (string= "-name" (car elt))
252 ;;; ----------------------------------------------------------------------
253 ;;; - This is just an sample, you propably want to modify this
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\"."
261 ;; The order is important, because first one matched is used
263 ((setq elt (tinychist-get-command-line-cache))
264 ;; If this is non-window emacs, check if this option is "cached" by
267 ((ti::dolist-buffer-list
268 (string-match "VM$\\|RMAIL$\\|MH$" (buffer-name)) 'temp-buffers)
270 ((ti::dolist-buffer-list (string-match "gnus" (buffer-name)) 'temp-buffers)
275 ;;; ----------------------------------------------------------------------
277 (defun tinychist-get-file-name ()
278 "Return command history filename."
279 (let* ((pfx (expand-file-name tinychist-:file-prefix))
281 (if (ti::compat-window-system)
282 (setq frame (tinychist-get-frame-name))
283 (setq frame (funcall tinychist-:non-x-function)))
286 ;;; ----------------------------------------------------------------------
288 (defun tinychist-install (&optional uninstall)
289 "Install the package. optionally UNINSTALL."
293 (remove-hook 'kill-emacs-hook 'tinychist-command-history-load)
295 ((boundp 'kill-emacs-hook)
296 (add-hook 'kill-emacs-hook 'tinychist-command-history-load))
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.
302 ((memq (lookup-key global-map "\C-x\C-c")
303 '(save-buffers-kill-emacs))
305 (read-from-minibuffer
307 "Cannot auto-install. Use manual key binding C-xC-c installation."
308 " See source code of `tinychist-install'."))))
310 ;; load only once, prevent command-history wipe out
311 ;; if package is loaded multiple times
313 (if (null tinychist-:load-flag)
314 (tinychist-load) ;Autoload the command history
315 (setq tinychist-:load-flag t)))))
320 ;;; ............................................................ &main ...
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
326 ;;; 1. Install extra argument
327 ;;; 2. read the argument content and put it into list too
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
338 ((not elt1) ;no option given
339 (setq file (tinychist-get-file-name))) ;guess which file to load.
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))
351 ;;; ----------------------------------------------------------------------
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))))
358 ;;; ----------------------------------------------------------------------
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
366 ;; XEmacs and Emacs don't agree with key commands,
367 ;; so we leave them out from history
369 ;; (quote [#<keypress-event control-C> #<keypress-event 1>]) ...
371 (re-incompatible "([^ ]+-key\\(-[^ ]+\\)* "))
377 (list-command-history)
378 (with-current-buffer buffer
379 (setq buffer-read-only nil)
382 (flush-lines re-incompatible)
387 ";; File: Emacs command history file\n"
388 ";; Desc: Load this with M-x load-file to reset the history to "
390 ;; This is for tinymy.el -- RCS compatible times tamp
394 (insert "(defconst command-history\n'(\n")
399 ";;; " (file-name-nondirectory file) " ends here"))
401 (indent-region (point-min) (point-max) nil)
402 ;; Update the times tamp [if function exist] and save the configuration
404 (when (fboundp 'tinymy-file-stamp)
405 (ti::funcall 'tinymy-file-stamp))
406 (write-region (point-min) (point-max) file))
408 (ti::kill-buffer-safe buffer)))))
413 ;;; ......................................................... &example ...
414 ;;; Draw region and extract code with tinylib.el / ti::package-rip-magic
415 ;;; This is Manual C-xC-x installation
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)
422 ;;* (global-set-key "\C-x\C-c" 'my-kill-emacs)
424 ;;* ;; This works for 18.57 and 19.xx, whereas the
425 ;;* ;; kill-emacs-query-functions doesn't, and this suffices for personal
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))
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))))
441 ;;; .................................................... &auto-install ...
444 (run-hooks 'tinychist-:load-hook)
448 ;;; tinychist.el ends here