diff lisp/packages/saveconf.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ac2d302a0011
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/packages/saveconf.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,282 @@
+;;; Save Emacs buffer and window configuration between editing sessions.
+;;; Copyright (C) 1987, 1988, 1989 Kyle E. Jones
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 1, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; A copy of the GNU General Public License can be obtained from the
+;;; program's author (send electronic mail to kyle@cs.odu.edu) or from
+;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
+;;; 02139, USA.
+;;;
+;;; Send bug reports to kyle@cs.odu.edu.
+
+;;; Synched up with: Not in FSF.
+
+;; This package of functions gives Emacs the ability to remember which
+;; files were being visited, the windows that were on them, and the
+;; value of point in their buffers the last Emacs session in the same
+;; directory.  This is an emulation of an old Gosling Emacs feature.
+;;
+;; The relevant commands are save-context and recover-context.
+;;
+;; Most of the time you'll want an Emacs session's context saved even if
+;; you choose not to recover it later.  To avoid having to manually
+;; M-x save-context at each emacs exit, put the line:
+;;    (setq auto-save-and-recover-context t)
+;; in your .emacs or in default.el in the lisp directory of the Emacs
+;; distribution.  The context will then automatically be saved when
+;; Emacs exits.
+;;
+;; By default only the contexts of visible buffers (buffers with windows
+;; on them) are saved.  Setting the variable save-buffer-context to t
+;; causes the contexts of all buffers to be saved.
+;;
+;; Put this file in the "lisp" directory of the emacs distribution in a
+;; file called saveconf.el.  Byte-compile it.
+;;
+;; There are two ways to use this package.
+;;   1) Put the line
+;;       (require 'saveconf)
+;;      in the file site-init.el in the lisp directory of the Emacs
+;;      directory and rebuild Emacs.  If you get the "Pure Lisp storage
+;;      exhausted" error message when rebuilding Emacs, increase PURESIZE
+;;      in src/config.h by about 30000 bytes and try again.  It's almost
+;;      certain that this will happen to you so you might as well increase
+;;      PURESIZE beforehand.
+;;
+;;      This is the preferred mode of operation because it allows the
+;;      package to become part of Emacs' startup sequence and automatically
+;;      restore context in a directory if Emacs is invoked without any
+;;      command line arguments.
+;;
+;;   2) Put these lines
+;;       (require 'saveconf)
+;;       (if (null (cdr command-line-args))
+;;           (setq inihibit-startup-message (recover-context)))
+;;      at the end of your .emacs file or the default.el file in the
+;;      lisp directory of the Emacs distribution.  This causes the
+;;      context saved in the current directory to be recovered whenever
+;;      Emacs is invoked without any arguments.
+
+(provide 'saveconf)
+
+(defconst save-context-version "Norma Jean"
+  "A unique string which is placed at the beginning of every saved context
+file.  If the string at the beginning of the context file doesn't match the
+value of this variable the `recover-context' command will ignore the file's
+contents.")
+
+(defvar auto-save-and-recover-context nil
+  "*If non-nil the `save-context' command will always be run before Emacs is
+exited.  Also upon Emacs startup, if this variable is non-nil and Emacs is
+passed no command line arguments, `recover-context' will be run.")
+
+(defvar save-buffer-context nil
+  "*If non-nil the `save-context' command will save the context
+of buffers that are visiting files, as well as the contexts of buffers
+that have windows.")
+
+(defvar save-context-predicate
+  (function (lambda (w)
+	      (and (buffer-file-name (window-buffer w))
+		   (not (string-match "^\\(/usr\\)?/tmp/"
+				      (buffer-file-name (window-buffer w)))))))
+  "*Value is a predicate function which determines which windows' contexts
+are saved.  When the `save-context' command is invoked, this function will
+be called once for each existing Emacs window.  The function should accept
+one argument which will be a window object, and should return non-nil if
+the window's context should be saved.")
+
+
+;; kill-emacs' function definition must be saved
+(if (not (fboundp 'just-kill-emacs))
+    (fset 'just-kill-emacs (symbol-function 'kill-emacs)))
+
+;; Make Emacs call recover-context at startup if appropriate.
+(setq top-level
+      (list 'let '((starting-up (not command-line-processed)))
+	    (list 'prog1
+		  top-level
+		  '(and starting-up auto-save-and-recover-context
+			(null (cdr command-line-args)) (recover-context)))))
+
+(defun kill-emacs (&optional query)
+  "End this Emacs session.
+Prefix ARG or optional first ARG non-nil means exit with no questions asked,
+even if there are unsaved buffers.  If Emacs is running non-interactively
+and ARG is an integer, then Emacs exits with ARG as its exit code.
+
+If the variable `auto-save-and-restore-context' is non-nil,
+the function save-context will be called first."
+  (interactive "P")
+  ;; check the purify flag.  try to save only if this is a dumped Emacs.
+  ;; saving context from a undumped Emacs caused a NULL pointer to be
+  ;; referenced through.  I'm not sure why.
+  (if (and auto-save-and-recover-context (null purify-flag))
+      (save-context))
+  (just-kill-emacs query))
+
+(defun save-context ()
+  "Save context of all Emacs windows (files visited and position of point).
+The information goes into a file called .emacs_<username> in the directory
+where the Emacs session was started.  The context can be recovered with the
+`recover-context' command, provided you are in the same directory where
+the context was saved.
+
+If the variable `save-buffer-context' is non-nil, the context of all buffers
+visiting files will be saved as well.
+
+Window sizes and shapes are not saved, since these may not be recoverable
+on terminals with a different number of rows and columns."
+  (interactive)
+  (condition-case error-data
+      (let (context-buffer mark save-file-name)
+	(setq save-file-name (concat (original-working-directory)
+				     ".emacs_" (user-login-name)))
+	(if (not (file-writable-p save-file-name))
+	    (if (file-writable-p (original-working-directory))
+		(error "context is write-protected, %s" save-file-name)
+	      (error "can't access directory, %s"
+		     (original-working-directory))))
+	;;
+	;; set up a buffer for the saved context information
+	;; Note that we can't set the visited file yet, because by
+	;; giving the buffer a file to visit we are making it
+	;; eligible to have it's context saved.
+	;;
+	(setq context-buffer (get-buffer-create " *Context Info*"))
+	(set-buffer context-buffer)
+	(erase-buffer)
+	(set-buffer-modified-p nil)
+	;;
+	;; record the context information
+	;;
+	(mapcar
+	 (function
+	  (lambda (w)
+	    (cond ((funcall save-context-predicate w)
+		   (prin1 (buffer-file-name (window-buffer w)) context-buffer)
+		   (princ " " context-buffer)
+		   (prin1 (window-point w) context-buffer)
+		   (princ "\n" context-buffer)))))
+	 (window-list))
+	
+	;;
+	;; nil is the data sentinel.  We will insert it later if we
+	;; need it but for now just remember where the last line of
+	;; window context ended.
+	;;
+	(setq mark (point))
+
+	;;
+	;; If `save-buffer-context' is non-nil we save buffer contexts.
+	;;
+	(if save-buffer-context
+	    (mapcar
+	     (function
+	      (lambda (b)
+		(set-buffer b)
+		(cond (buffer-file-name
+		       (prin1 buffer-file-name context-buffer)
+		       (princ " " context-buffer)
+		       (prin1 (point) context-buffer)
+		       (princ "\n" context-buffer)))))
+	     (buffer-list)))
+
+	;;
+	;; If the context-buffer contains information, we add the version
+	;;   string and sentinels, and write out the saved context.
+	;; If the context-buffer is empty, we don't create a file at all.
+	;; If there's an old saved context in this directory we attempt
+	;;   to delete it.
+	;;
+	(cond ((buffer-modified-p context-buffer)
+	       (set-buffer context-buffer)
+	       (setq buffer-offer-save nil)
+	       ;; sentinel for EOF
+	       (insert "nil\n")
+	       ;; sentinel for end of window contexts
+	       (goto-char mark)
+	       (insert "nil\n")
+	       ;; version string
+	       (goto-char (point-min))
+	       (prin1 save-context-version context-buffer)
+	       (insert "\n\n")
+	       ;; so kill-buffer won't need confirmation later
+	       (set-buffer-modified-p nil)
+	       ;; save it
+	       (write-region (point-min) (point-max) save-file-name
+			     nil 'quiet))
+	      (t (condition-case data
+		     (delete-file save-file-name) (error nil))))
+
+	(kill-buffer context-buffer))
+    (error nil)))
+
+(defun recover-context ()
+  "Recover an Emacs context saved by `save-context' command.
+Files that were visible in windows when the context was saved are visited and
+point is set in each window to what is was when the context was saved."
+  (interactive)
+  (condition-case error-data
+      ;;
+      ;; Set up some local variables.
+      ;;
+      (let (sexpr context-buffer recover-file-name)
+	(setq recover-file-name (concat (original-working-directory)
+					".emacs_" (user-login-name)))
+	(if (not (file-readable-p recover-file-name))
+	    (error "can't access context, %s" recover-file-name))
+	;;
+	;; create a temp buffer and copy the saved context into it.
+	;;
+	(setq context-buffer (get-buffer-create " *Recovered Context*"))
+	(set-buffer context-buffer)
+	(erase-buffer)
+	(insert-file-contents recover-file-name nil)
+	;; so kill-buffer won't need confirmation later
+	(set-buffer-modified-p nil)
+	;;
+	;; If it's empty forget it.
+	;;
+	(if (zerop (buffer-size))
+	    (error "context file is empty, %s" recover-file-name))
+	;;
+	;; check the version and make sure it matches ours
+	;;
+	(setq sexpr (read context-buffer))
+	(if (not (equal sexpr save-context-version))
+	    (error "version string incorrect, %s" sexpr))
+	;;
+	;; Recover the window contexts
+	;;
+	(while (setq sexpr (read context-buffer))
+	  (select-window (get-largest-window))
+	  (if (buffer-file-name)
+	      (split-window))
+	  (other-window 1)
+	  (find-file sexpr)
+	  (goto-char (read context-buffer)))
+	;;
+	;; Recover buffer contexts, if any.
+	;;
+	(while (setq sexpr (read context-buffer))
+	  (set-buffer (find-file-noselect sexpr))
+	  (goto-char (read context-buffer)))
+	(bury-buffer "*scratch*")
+	(kill-buffer context-buffer)
+	t )
+    (error nil)))
+	 
+(defun original-working-directory ()
+  (save-excursion
+    (set-buffer (get-buffer-create "*scratch*"))
+    default-directory))