0
|
1 ;;; Save Emacs buffer and window configuration between editing sessions.
|
|
2 ;;; Copyright (C) 1987, 1988, 1989 Kyle E. Jones
|
|
3 ;;;
|
|
4 ;;; This program is free software; you can redistribute it and/or modify
|
|
5 ;;; it under the terms of the GNU General Public License as published by
|
|
6 ;;; the Free Software Foundation; either version 1, or (at your option)
|
|
7 ;;; any later version.
|
|
8 ;;;
|
|
9 ;;; This program is distributed in the hope that it will be useful,
|
|
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12 ;;; GNU General Public License for more details.
|
|
13 ;;;
|
|
14 ;;; A copy of the GNU General Public License can be obtained from the
|
|
15 ;;; program's author (send electronic mail to kyle@cs.odu.edu) or from
|
|
16 ;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
|
|
17 ;;; 02139, USA.
|
|
18 ;;;
|
|
19 ;;; Send bug reports to kyle@cs.odu.edu.
|
|
20
|
|
21 ;;; Synched up with: Not in FSF.
|
|
22
|
|
23 ;; This package of functions gives Emacs the ability to remember which
|
|
24 ;; files were being visited, the windows that were on them, and the
|
|
25 ;; value of point in their buffers the last Emacs session in the same
|
|
26 ;; directory. This is an emulation of an old Gosling Emacs feature.
|
|
27 ;;
|
|
28 ;; The relevant commands are save-context and recover-context.
|
|
29 ;;
|
|
30 ;; Most of the time you'll want an Emacs session's context saved even if
|
|
31 ;; you choose not to recover it later. To avoid having to manually
|
|
32 ;; M-x save-context at each emacs exit, put the line:
|
|
33 ;; (setq auto-save-and-recover-context t)
|
|
34 ;; in your .emacs or in default.el in the lisp directory of the Emacs
|
|
35 ;; distribution. The context will then automatically be saved when
|
|
36 ;; Emacs exits.
|
|
37 ;;
|
|
38 ;; By default only the contexts of visible buffers (buffers with windows
|
|
39 ;; on them) are saved. Setting the variable save-buffer-context to t
|
|
40 ;; causes the contexts of all buffers to be saved.
|
|
41 ;;
|
|
42 ;; Put this file in the "lisp" directory of the emacs distribution in a
|
|
43 ;; file called saveconf.el. Byte-compile it.
|
|
44 ;;
|
|
45 ;; There are two ways to use this package.
|
|
46 ;; 1) Put the line
|
|
47 ;; (require 'saveconf)
|
|
48 ;; in the file site-init.el in the lisp directory of the Emacs
|
|
49 ;; directory and rebuild Emacs. If you get the "Pure Lisp storage
|
|
50 ;; exhausted" error message when rebuilding Emacs, increase PURESIZE
|
|
51 ;; in src/config.h by about 30000 bytes and try again. It's almost
|
|
52 ;; certain that this will happen to you so you might as well increase
|
|
53 ;; PURESIZE beforehand.
|
|
54 ;;
|
|
55 ;; This is the preferred mode of operation because it allows the
|
|
56 ;; package to become part of Emacs' startup sequence and automatically
|
|
57 ;; restore context in a directory if Emacs is invoked without any
|
|
58 ;; command line arguments.
|
|
59 ;;
|
|
60 ;; 2) Put these lines
|
|
61 ;; (require 'saveconf)
|
|
62 ;; (if (null (cdr command-line-args))
|
|
63 ;; (setq inihibit-startup-message (recover-context)))
|
|
64 ;; at the end of your .emacs file or the default.el file in the
|
|
65 ;; lisp directory of the Emacs distribution. This causes the
|
|
66 ;; context saved in the current directory to be recovered whenever
|
|
67 ;; Emacs is invoked without any arguments.
|
|
68
|
|
69 (provide 'saveconf)
|
|
70
|
|
71 (defconst save-context-version "Norma Jean"
|
|
72 "A unique string which is placed at the beginning of every saved context
|
|
73 file. If the string at the beginning of the context file doesn't match the
|
|
74 value of this variable the `recover-context' command will ignore the file's
|
|
75 contents.")
|
|
76
|
|
77 (defvar auto-save-and-recover-context nil
|
|
78 "*If non-nil the `save-context' command will always be run before Emacs is
|
|
79 exited. Also upon Emacs startup, if this variable is non-nil and Emacs is
|
|
80 passed no command line arguments, `recover-context' will be run.")
|
|
81
|
|
82 (defvar save-buffer-context nil
|
|
83 "*If non-nil the `save-context' command will save the context
|
|
84 of buffers that are visiting files, as well as the contexts of buffers
|
|
85 that have windows.")
|
|
86
|
|
87 (defvar save-context-predicate
|
|
88 (function (lambda (w)
|
|
89 (and (buffer-file-name (window-buffer w))
|
|
90 (not (string-match "^\\(/usr\\)?/tmp/"
|
|
91 (buffer-file-name (window-buffer w)))))))
|
|
92 "*Value is a predicate function which determines which windows' contexts
|
|
93 are saved. When the `save-context' command is invoked, this function will
|
|
94 be called once for each existing Emacs window. The function should accept
|
|
95 one argument which will be a window object, and should return non-nil if
|
|
96 the window's context should be saved.")
|
|
97
|
|
98
|
|
99 ;; kill-emacs' function definition must be saved
|
|
100 (if (not (fboundp 'just-kill-emacs))
|
|
101 (fset 'just-kill-emacs (symbol-function 'kill-emacs)))
|
|
102
|
|
103 ;; Make Emacs call recover-context at startup if appropriate.
|
|
104 (setq top-level
|
|
105 (list 'let '((starting-up (not command-line-processed)))
|
|
106 (list 'prog1
|
|
107 top-level
|
|
108 '(and starting-up auto-save-and-recover-context
|
|
109 (null (cdr command-line-args)) (recover-context)))))
|
|
110
|
|
111 (defun kill-emacs (&optional query)
|
|
112 "End this Emacs session.
|
|
113 Prefix ARG or optional first ARG non-nil means exit with no questions asked,
|
|
114 even if there are unsaved buffers. If Emacs is running non-interactively
|
|
115 and ARG is an integer, then Emacs exits with ARG as its exit code.
|
|
116
|
|
117 If the variable `auto-save-and-restore-context' is non-nil,
|
|
118 the function save-context will be called first."
|
|
119 (interactive "P")
|
|
120 ;; check the purify flag. try to save only if this is a dumped Emacs.
|
|
121 ;; saving context from a undumped Emacs caused a NULL pointer to be
|
|
122 ;; referenced through. I'm not sure why.
|
|
123 (if (and auto-save-and-recover-context (null purify-flag))
|
|
124 (save-context))
|
|
125 (just-kill-emacs query))
|
|
126
|
|
127 (defun save-context ()
|
|
128 "Save context of all Emacs windows (files visited and position of point).
|
|
129 The information goes into a file called .emacs_<username> in the directory
|
|
130 where the Emacs session was started. The context can be recovered with the
|
|
131 `recover-context' command, provided you are in the same directory where
|
|
132 the context was saved.
|
|
133
|
|
134 If the variable `save-buffer-context' is non-nil, the context of all buffers
|
|
135 visiting files will be saved as well.
|
|
136
|
|
137 Window sizes and shapes are not saved, since these may not be recoverable
|
|
138 on terminals with a different number of rows and columns."
|
|
139 (interactive)
|
|
140 (condition-case error-data
|
|
141 (let (context-buffer mark save-file-name)
|
|
142 (setq save-file-name (concat (original-working-directory)
|
|
143 ".emacs_" (user-login-name)))
|
|
144 (if (not (file-writable-p save-file-name))
|
|
145 (if (file-writable-p (original-working-directory))
|
|
146 (error "context is write-protected, %s" save-file-name)
|
|
147 (error "can't access directory, %s"
|
|
148 (original-working-directory))))
|
|
149 ;;
|
|
150 ;; set up a buffer for the saved context information
|
|
151 ;; Note that we can't set the visited file yet, because by
|
|
152 ;; giving the buffer a file to visit we are making it
|
|
153 ;; eligible to have it's context saved.
|
|
154 ;;
|
|
155 (setq context-buffer (get-buffer-create " *Context Info*"))
|
|
156 (set-buffer context-buffer)
|
|
157 (erase-buffer)
|
|
158 (set-buffer-modified-p nil)
|
|
159 ;;
|
|
160 ;; record the context information
|
|
161 ;;
|
|
162 (mapcar
|
|
163 (function
|
|
164 (lambda (w)
|
|
165 (cond ((funcall save-context-predicate w)
|
|
166 (prin1 (buffer-file-name (window-buffer w)) context-buffer)
|
|
167 (princ " " context-buffer)
|
|
168 (prin1 (window-point w) context-buffer)
|
|
169 (princ "\n" context-buffer)))))
|
|
170 (window-list))
|
|
171
|
|
172 ;;
|
|
173 ;; nil is the data sentinel. We will insert it later if we
|
|
174 ;; need it but for now just remember where the last line of
|
|
175 ;; window context ended.
|
|
176 ;;
|
|
177 (setq mark (point))
|
|
178
|
|
179 ;;
|
|
180 ;; If `save-buffer-context' is non-nil we save buffer contexts.
|
|
181 ;;
|
|
182 (if save-buffer-context
|
|
183 (mapcar
|
|
184 (function
|
|
185 (lambda (b)
|
|
186 (set-buffer b)
|
|
187 (cond (buffer-file-name
|
|
188 (prin1 buffer-file-name context-buffer)
|
|
189 (princ " " context-buffer)
|
|
190 (prin1 (point) context-buffer)
|
|
191 (princ "\n" context-buffer)))))
|
|
192 (buffer-list)))
|
|
193
|
|
194 ;;
|
|
195 ;; If the context-buffer contains information, we add the version
|
|
196 ;; string and sentinels, and write out the saved context.
|
|
197 ;; If the context-buffer is empty, we don't create a file at all.
|
|
198 ;; If there's an old saved context in this directory we attempt
|
|
199 ;; to delete it.
|
|
200 ;;
|
|
201 (cond ((buffer-modified-p context-buffer)
|
|
202 (set-buffer context-buffer)
|
|
203 (setq buffer-offer-save nil)
|
|
204 ;; sentinel for EOF
|
|
205 (insert "nil\n")
|
|
206 ;; sentinel for end of window contexts
|
|
207 (goto-char mark)
|
|
208 (insert "nil\n")
|
|
209 ;; version string
|
|
210 (goto-char (point-min))
|
|
211 (prin1 save-context-version context-buffer)
|
|
212 (insert "\n\n")
|
|
213 ;; so kill-buffer won't need confirmation later
|
|
214 (set-buffer-modified-p nil)
|
|
215 ;; save it
|
|
216 (write-region (point-min) (point-max) save-file-name
|
|
217 nil 'quiet))
|
|
218 (t (condition-case data
|
|
219 (delete-file save-file-name) (error nil))))
|
|
220
|
|
221 (kill-buffer context-buffer))
|
|
222 (error nil)))
|
|
223
|
|
224 (defun recover-context ()
|
|
225 "Recover an Emacs context saved by `save-context' command.
|
|
226 Files that were visible in windows when the context was saved are visited and
|
|
227 point is set in each window to what is was when the context was saved."
|
|
228 (interactive)
|
|
229 (condition-case error-data
|
|
230 ;;
|
|
231 ;; Set up some local variables.
|
|
232 ;;
|
|
233 (let (sexpr context-buffer recover-file-name)
|
|
234 (setq recover-file-name (concat (original-working-directory)
|
|
235 ".emacs_" (user-login-name)))
|
|
236 (if (not (file-readable-p recover-file-name))
|
|
237 (error "can't access context, %s" recover-file-name))
|
|
238 ;;
|
|
239 ;; create a temp buffer and copy the saved context into it.
|
|
240 ;;
|
|
241 (setq context-buffer (get-buffer-create " *Recovered Context*"))
|
|
242 (set-buffer context-buffer)
|
|
243 (erase-buffer)
|
|
244 (insert-file-contents recover-file-name nil)
|
|
245 ;; so kill-buffer won't need confirmation later
|
|
246 (set-buffer-modified-p nil)
|
|
247 ;;
|
|
248 ;; If it's empty forget it.
|
|
249 ;;
|
|
250 (if (zerop (buffer-size))
|
|
251 (error "context file is empty, %s" recover-file-name))
|
|
252 ;;
|
|
253 ;; check the version and make sure it matches ours
|
|
254 ;;
|
|
255 (setq sexpr (read context-buffer))
|
|
256 (if (not (equal sexpr save-context-version))
|
|
257 (error "version string incorrect, %s" sexpr))
|
|
258 ;;
|
|
259 ;; Recover the window contexts
|
|
260 ;;
|
|
261 (while (setq sexpr (read context-buffer))
|
|
262 (select-window (get-largest-window))
|
|
263 (if (buffer-file-name)
|
|
264 (split-window))
|
|
265 (other-window 1)
|
|
266 (find-file sexpr)
|
|
267 (goto-char (read context-buffer)))
|
|
268 ;;
|
|
269 ;; Recover buffer contexts, if any.
|
|
270 ;;
|
|
271 (while (setq sexpr (read context-buffer))
|
2
|
272 (set-buffer (find-file-noselect sexpr t))
|
|
273 (if (zerop (buffer-size))
|
|
274 (kill-buffer (current-buffer))
|
|
275 (goto-char (read context-buffer))))
|
0
|
276 (bury-buffer "*scratch*")
|
|
277 (kill-buffer context-buffer)
|
|
278 t )
|
|
279 (error nil)))
|
|
280
|
|
281 (defun original-working-directory ()
|
|
282 (save-excursion
|
|
283 (set-buffer (get-buffer-create "*scratch*"))
|
|
284 default-directory))
|