comparison lisp/packages/desktop.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
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; desktop.el --- save partial status of Emacs when killed
2
3 ;; Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
4
5 ;; Author: Morten Welinder <terra@diku.dk>
6 ;; Version: 2.09? RMS has an obnoxious tendency to remove version
7 ;; numbers from packages, and he did in this case.
8 ;; Keywords: customization
9 ;; Favourite-brand-of-beer: None, I hate beer.
10
11 ;; This file is part of XEmacs.
12
13 ;; XEmacs is free software; you can redistribute it and/or modify it
14 ;; under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; XEmacs is distributed in the hope that it will be useful, but
19 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 ;; General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with XEmacs; see the file COPYING. If not, write to the Free
25 ;; Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26
27 ;;; Synched up with: FSF 19.30.
28
29 ;;; Commentary:
30
31 ;; Save the Desktop, i.e.,
32 ;; - some global variables
33 ;; - the list of buffers with associated files. For each buffer also
34 ;; - the major mode
35 ;; - the default directory
36 ;; - the point
37 ;; - the mark & mark-active
38 ;; - buffer-read-only
39 ;; - some local variables
40
41 ;; To use this, first put these three lines in the bottom of your .emacs
42 ;; file (the later the better):
43 ;;
44 ;; (load "desktop")
45 ;; (desktop-load-default)
46 ;; (desktop-read)
47 ;;
48 ;; Between the second and the third line you may wish to add something that
49 ;; updates the variables `desktop-globals-to-save' and/or
50 ;; `desktop-locals-to-save'. If for instance you want to save the local
51 ;; variable `foobar' for every buffer in which it is local, you could add
52 ;; the line
53 ;;
54 ;; (setq desktop-locals-to-save (cons 'foobar desktop-locals-to-save))
55 ;;
56 ;; To avoid saving excessive amounts of data you may also wish to add
57 ;; something like the following
58 ;;
59 ;; (add-hook 'kill-emacs-hook
60 ;; '(lambda ()
61 ;; (desktop-truncate search-ring 3)
62 ;; (desktop-truncate regexp-search-ring 3)))
63 ;;
64 ;; which will make sure that no more than three search items are saved. You
65 ;; must place this line *after* the (load "desktop") line. See also the
66 ;; variable desktop-save-hook.
67
68 ;; Start Emacs in the root directory of your "project". The desktop saver
69 ;; is inactive by default. You activate it by M-x desktop-save RET. When
70 ;; you exit the next time the above data will be saved. This ensures that
71 ;; all the files you were editing will be reloaded the next time you start
72 ;; Emacs from the same directory and that points will be set where you
73 ;; left them. If you save a desktop file in your home directory it will
74 ;; act as a default desktop when you start Emacs from a directory that
75 ;; doesn't have its own. I never do this, but you may want to.
76
77 ;; By the way: don't use desktop.el to customize Emacs -- the file .emacs
78 ;; in your home directory is used for that. Saving global default values
79 ;; for buffers is an example of misuse.
80
81 ;; PLEASE NOTE: The kill ring can be saved as specified by the variable
82 ;; `desktop-globals-to-save' (by default it isn't). This may result in saving
83 ;; things you did not mean to keep. Use M-x desktop-clear RET.
84
85 ;; Thanks to hetrick@phys.uva.nl (Jim Hetrick) for useful ideas.
86 ;; avk@rtsg.mot.com (Andrew V. Klein) for a dired tip.
87 ;; chris@tecc.co.uk (Chris Boucher) for a mark tip.
88 ;; f89-kam@nada.kth.se (Klas Mellbourn) for a mh-e tip.
89 ;; kifer@sbkifer.cs.sunysb.edu (M. Kifer) for a bug hunt.
90 ;; treese@lcs.mit.edu (Win Treese) for ange-ftp tips.
91 ;; ---------------------------------------------------------------------------
92 ;; TODO:
93 ;;
94 ;; Save window configuration.
95 ;; Recognize more minor modes.
96 ;; Save mark rings.
97 ;; Start-up with buffer-menu???
98
99 ;;; Code:
100
101 ;; Make the compilation more silent
102 (eval-when-compile
103 ;; We use functions from these modules
104 ;; We can't (require 'mh-e) since that wants to load something.
105 (mapcar 'require '(info dired reporter)))
106 ;; ----------------------------------------------------------------------------
107 ;; USER OPTIONS -- settings you might want to play with.
108 ;; ----------------------------------------------------------------------------
109 (defconst desktop-basefilename
110 (if (or (eq system-type 'ms-dos) (eq system-type 'windows-nt))
111 "emacs.dsk" ; Ms-Dos does not support multiple dots in file name
112 ".emacs.desktop")
113 "File for Emacs desktop, not including the directory name.")
114
115 (defvar desktop-missing-file-warning t
116 "*If non-nil then desktop warns when a file no longer exists.
117 Otherwise it simply ignores that file.")
118
119 (defvar desktop-globals-to-save
120 (list 'desktop-missing-file-warning
121 ;; Feature: saving kill-ring implies saving kill-ring-yank-pointer
122 ;; 'kill-ring
123 'tags-file-name
124 'tags-table-list
125 'search-ring
126 'regexp-search-ring
127 'register-alist
128 ;; 'desktop-globals-to-save ; Itself!
129 )
130 "List of global variables to save when killing Emacs.
131 An element may be variable name (a symbol)
132 or a cons cell of the form (VAR . MAX-SIZE),
133 which means to truncate VAR's value to at most MAX-SIZE elements
134 \(if the value is a list) before saving the value.")
135
136 (defvar desktop-locals-to-save
137 (list 'desktop-locals-to-save ; Itself! Think it over.
138 'truncate-lines
139 'case-fold-search
140 'case-replace
141 'fill-column
142 'overwrite-mode
143 'change-log-default-name
144 'line-number-mode
145 )
146 "List of local variables to save for each buffer.
147 The variables are saved only when they really are local.")
148 (make-variable-buffer-local 'desktop-locals-to-save)
149
150 ;; We skip .log files because they are normally temporary.
151 ;; (ftp) files because they require passwords and whatsnot.
152 ;; TAGS files to save time (tags-file-name is saved instead).
153 (defvar desktop-buffers-not-to-save
154 "\\(^nn\\.a[0-9]+\\|\\.log\\|(ftp)\\|^tags\\|^TAGS\\)$"
155 "Regexp identifying buffers that are to be excluded from saving.")
156
157 ;; Skip ange-ftp files
158 (defvar desktop-files-not-to-save
159 "^/[^/:]*:"
160 "Regexp identifying files whose buffers are to be excluded from saving.")
161
162 (defvar desktop-buffer-handlers
163 '(desktop-buffer-dired
164 desktop-buffer-rmail
165 desktop-buffer-mh
166 desktop-buffer-info
167 desktop-buffer-file)
168 "*List of functions to call in order to create a buffer.
169 The functions are called without explicit parameters but may access
170 the the major mode as `mam', the file name as `fn', the buffer name as
171 `bn', the default directory as `dd'. If some function returns non-nil
172 no further functions are called. If the function returns t then the
173 buffer is considered created.")
174
175 (defvar desktop-create-buffer-form "(desktop-create-buffer 205"
176 "Opening of form for creation of new buffers.")
177
178 (defvar desktop-save-hook nil
179 "Hook run before saving the desktop to allow you to cut history lists and
180 the like shorter.")
181 ;; ----------------------------------------------------------------------------
182 (defvar desktop-dirname nil
183 "The directory in which the current desktop file resides.")
184
185 (defconst desktop-header
186 ";; --------------------------------------------------------------------------
187 ;; Desktop File for Emacs
188 ;; --------------------------------------------------------------------------
189 " "*Header to place in Desktop file.")
190
191 (defvar desktop-delay-hook nil
192 "Hooks run after all buffers are loaded; intended for internal use.")
193 ;; ----------------------------------------------------------------------------
194 (defun desktop-truncate (l n)
195 "Truncate LIST to at most N elements destructively."
196 (let ((here (nthcdr (1- n) l)))
197 (if (consp here)
198 (setcdr here nil))))
199 ;; ----------------------------------------------------------------------------
200 (defun desktop-clear () "Empty the Desktop."
201 (interactive)
202 (setq kill-ring nil
203 kill-ring-yank-pointer nil
204 search-ring nil
205 search-ring-yank-pointer nil
206 regexp-search-ring nil
207 regexp-search-ring-yank-pointer nil)
208 (mapcar (function (lambda (x)
209 ;; XEmacs change
210 (if (not (equal (buffer-name x) "*Warnings*"))
211 (kill-buffer x)))) (buffer-list))
212 (delete-other-windows))
213 ;; ----------------------------------------------------------------------------
214 (add-hook 'kill-emacs-hook 'desktop-kill)
215
216 (defun desktop-kill ()
217 (if desktop-dirname
218 (condition-case err
219 (desktop-save desktop-dirname)
220 (file-error
221 (if (yes-or-no-p "Error while saving the desktop. Quit anyway? ")
222 nil
223 (signal (car err) (cdr err)))))))
224 ;; ----------------------------------------------------------------------------
225 (defun desktop-internal-v2s (val)
226 "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE.
227 TXT is a string that when read and evaluated yields value.
228 QUOTE may be `may' (value may be quoted),
229 `must' (values must be quoted), or nil (value may not be quoted)."
230 (cond
231 ((or (numberp val) (null val) (eq t val))
232 (cons 'may (prin1-to-string val)))
233 ((stringp val)
234 (let ((copy (copy-sequence val)))
235 (set-text-properties 0 (length copy) nil copy)
236 ;; Get rid of text properties because we cannot read them
237 (cons 'may (prin1-to-string copy))))
238 ((symbolp val)
239 (cons 'must (prin1-to-string val)))
240 ((vectorp val)
241 (let* ((special nil)
242 (pass1 (mapcar
243 (lambda (el)
244 (let ((res (desktop-internal-v2s el)))
245 (if (null (car res))
246 (setq special t))
247 res))
248 val)))
249 (if special
250 (cons nil (concat "(vector "
251 (mapconcat (lambda (el)
252 (if (eq (car el) 'must)
253 (concat "'" (cdr el))
254 (cdr el)))
255 pass1
256 " ")
257 ")"))
258 (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
259 ((consp val)
260 (let ((p val)
261 newlist
262 anynil)
263 (while (consp p)
264 (let ((q.txt (desktop-internal-v2s (car p))))
265 (or anynil (setq anynil (null (car q.txt))))
266 (setq newlist (cons q.txt newlist)))
267 (setq p (cdr p)))
268 (if p
269 (let ((last (desktop-internal-v2s p))
270 (el (car newlist)))
271 (setcar newlist
272 (if (or anynil (setq anynil (null (car last))))
273 (cons nil
274 (concat "(cons "
275 (if (eq (car el) 'must) "'" "")
276 (cdr el)
277 " "
278 (if (eq (car last) 'must) "'" "")
279 (cdr last)
280 ")"))
281 (cons 'must
282 (concat (cdr el) " . " (cdr last)))))))
283 (setq newlist (nreverse newlist))
284 (if anynil
285 (cons nil
286 (concat "(list "
287 (mapconcat (lambda (el)
288 (if (eq (car el) 'must)
289 (concat "'" (cdr el))
290 (cdr el)))
291 newlist
292 " ")
293 ")"))
294 (cons 'must
295 (concat "(" (mapconcat 'cdr newlist " ") ")")))))
296 ((subrp val)
297 (cons nil (concat "(symbol-function '"
298 (substring (prin1-to-string val) 7 -1)
299 ")")))
300 ((markerp val)
301 (let ((pos (prin1-to-string (marker-position val)))
302 (buf (prin1-to-string (buffer-name (marker-buffer val)))))
303 (cons nil (concat "(let ((mk (make-marker)))"
304 " (add-hook 'desktop-delay-hook"
305 " (list 'lambda '() (list 'set-marker mk "
306 pos " (get-buffer " buf ")))) mk)"))))
307 (t ; save as text
308 (cons 'may "\"Unprintable entity\""))))
309
310 (defun desktop-value-to-string (val)
311 "Convert VALUE to a string that when read evaluates to the same value.
312 Not all types of values are supported."
313 (let* ((print-escape-newlines t)
314 (float-output-format nil)
315 (quote.txt (desktop-internal-v2s val))
316 (quote (car quote.txt))
317 (txt (cdr quote.txt)))
318 (if (eq quote 'must)
319 (concat "'" txt)
320 txt)))
321 ;; ----------------------------------------------------------------------------
322 (defun desktop-outvar (varspec)
323 "Output a setq statement for variable VAR to the desktop file.
324 The argument VARSPEC may be the variable name VAR (a symbol),
325 or a cons cell of the form (VAR . MAX-SIZE),
326 which means to truncate VAR's value to at most MAX-SIZE elements
327 \(if the value is a list) before saving the value."
328 (let (var size)
329 (if (consp varspec)
330 (setq var (car varspec) size (cdr varspec))
331 (setq var varspec))
332 (if (boundp var)
333 (progn
334 (if (and (integerp size)
335 (> size 0)
336 (listp (eval var)))
337 (desktop-truncate (eval var) size))
338 (insert "(setq "
339 (symbol-name var)
340 " "
341 (desktop-value-to-string (symbol-value var))
342 ")\n")))))
343 ;; ----------------------------------------------------------------------------
344 (defun desktop-save-buffer-p (filename bufname mode &rest dummy)
345 "Return t if the desktop should record a particular buffer for next startup.
346 FILENAME is the visited file name, BUFNAME is the buffer name, and
347 MODE is the major mode."
348 (let ((case-fold-search nil))
349 (or (and filename
350 (not (string-match desktop-buffers-not-to-save bufname))
351 (not (string-match desktop-files-not-to-save filename)))
352 (and (eq mode 'dired-mode)
353 (save-excursion
354 (set-buffer (get-buffer bufname))
355 (not (string-match desktop-files-not-to-save
356 default-directory))))
357 (and (null filename)
358 (memq mode '(Info-mode rmail-mode))))))
359 ;; ----------------------------------------------------------------------------
360 (defun desktop-save (dirname)
361 "Save the Desktop file. Parameter DIRNAME specifies where to save desktop."
362 (interactive "DDirectory to save desktop file in: ")
363 (run-hooks 'desktop-save-hook)
364 (save-excursion
365 (let ((filename (expand-file-name
366 (concat dirname desktop-basefilename)))
367 (info (nreverse
368 (mapcar
369 (function (lambda (b)
370 (set-buffer b)
371 (list
372 (buffer-file-name)
373 (buffer-name)
374 major-mode
375 (list ; list explaining minor modes
376 (not (null auto-fill-function)))
377 (point)
378 (list (mark t) ;; mark-active
379 (not (null (mark)))) ; XEmacs
380 buffer-read-only
381 (cond ((eq major-mode 'Info-mode)
382 (list Info-current-file
383 Info-current-node))
384 ((eq major-mode 'dired-mode)
385 (cons
386 (expand-file-name dired-directory)
387 (cdr
388 (nreverse
389 (mapcar
390 (function car)
391 dired-subdir-alist))))))
392 (let ((locals desktop-locals-to-save)
393 (loclist (buffer-local-variables))
394 (ll))
395 (while locals
396 (let ((here (assq (car locals) loclist)))
397 (if here
398 (setq ll (cons here ll))
399 (if (member (car locals) loclist)
400 (setq ll (cons (car locals) ll)))))
401 (setq locals (cdr locals)))
402 ll)
403 )))
404 (buffer-list))))
405 (buf (get-buffer-create "*desktop*")))
406 (set-buffer buf)
407 (erase-buffer)
408
409 (insert desktop-header
410 ";; Created " (current-time-string) "\n"
411 ";; Emacs version " emacs-version "\n\n"
412 ";; Global section:\n")
413 (mapcar (function desktop-outvar) desktop-globals-to-save)
414 (if (memq 'kill-ring desktop-globals-to-save)
415 (insert "(setq kill-ring-yank-pointer (nthcdr "
416 (int-to-string
417 (- (length kill-ring) (length kill-ring-yank-pointer)))
418 " kill-ring))\n"))
419
420 (insert "\n;; Buffer section:\n")
421 (mapcar
422 (function (lambda (l)
423 (if (apply 'desktop-save-buffer-p l)
424 (progn
425 (insert desktop-create-buffer-form)
426 (mapcar
427 (function (lambda (e)
428 (insert "\n "
429 (desktop-value-to-string e))))
430 l)
431 (insert ")\n\n")))))
432 info)
433 (setq default-directory dirname)
434 (if (file-exists-p filename) (delete-file filename))
435 (write-region (point-min) (point-max) filename nil 'nomessage)))
436 (setq desktop-dirname dirname))
437 ;; ----------------------------------------------------------------------------
438 (defun desktop-remove ()
439 "Delete the Desktop file and inactivate the desktop system."
440 (interactive)
441 (if desktop-dirname
442 (let ((filename (concat desktop-dirname desktop-basefilename)))
443 (setq desktop-dirname nil)
444 (if (file-exists-p filename)
445 (delete-file filename)))))
446 ;; ----------------------------------------------------------------------------
447 (defun desktop-read ()
448 "Read the Desktop file and the files it specifies."
449 (interactive)
450 (let ((filename))
451 (if (file-exists-p (concat "./" desktop-basefilename))
452 (setq desktop-dirname (expand-file-name "./"))
453 (if (file-exists-p (concat "~/" desktop-basefilename))
454 (setq desktop-dirname (expand-file-name "~/"))
455 (setq desktop-dirname nil)))
456 (if desktop-dirname
457 (progn
458 (load (concat desktop-dirname desktop-basefilename) t t t)
459 (run-hooks 'desktop-delay-hook)
460 (message "Desktop loaded."))
461 (desktop-clear))))
462 ;; ----------------------------------------------------------------------------
463 (defun desktop-load-default ()
464 "Load the `default' start-up library manually.
465 Also inhibit further loading of it. Call this from your `.emacs' file
466 to provide correct modes for autoloaded files."
467 (if (not inhibit-default-init) ; safety check
468 (progn
469 (load "default" t t)
470 (setq inhibit-default-init t))))
471 ;; ----------------------------------------------------------------------------
472 ;; Note: the following functions use the dynamic variable binding in Lisp.
473 ;;
474 (defun desktop-buffer-info () "Load an info file."
475 (if (eq 'Info-mode mam)
476 (progn
477 (require 'info)
478 (Info-find-node (nth 0 misc) (nth 1 misc))
479 t)))
480 ;; ----------------------------------------------------------------------------
481 (defun desktop-buffer-rmail () "Load an RMAIL file."
482 (if (eq 'rmail-mode mam)
483 (condition-case error
484 (progn (rmail-input fn) t)
485 (file-locked
486 (kill-buffer (current-buffer))
487 'ignored))))
488 ;; ----------------------------------------------------------------------------
489 (defun desktop-buffer-mh () "Load a folder in the mh system."
490 (if (eq 'mh-folder-mode mam)
491 (progn
492 (require 'mh-e)
493 (mh-find-path)
494 (mh-visit-folder bn)
495 t)))
496 ;; ----------------------------------------------------------------------------
497 (defun desktop-buffer-dired () "Load a directory using dired."
498 (if (eq 'dired-mode mam)
499 (if (file-directory-p (file-name-directory (car misc)))
500 (progn
501 (dired (car misc))
502 (mapcar 'dired-insert-subdir (cdr misc))
503 t)
504 (message "Directory %s no longer exists." (car misc))
505 (sit-for 1)
506 'ignored)))
507 ;; ----------------------------------------------------------------------------
508 (defun desktop-buffer-file () "Load a file."
509 (if fn
510 (if (or (file-exists-p fn)
511 (and desktop-missing-file-warning
512 (y-or-n-p (format
513 "File \"%s\" no longer exists. Re-create? "
514 fn))))
515 (progn (find-file fn) t)
516 'ignored)))
517 ;; ----------------------------------------------------------------------------
518 ;; Create a buffer, load its file, set is mode, ...; called from Desktop file
519 ;; only.
520 (defun desktop-create-buffer (ver fn bn mam mim pt mk ro misc &optional locals)
521 (let ((hlist desktop-buffer-handlers)
522 (result)
523 (handler))
524 (while (and (not result) hlist)
525 (setq handler (car hlist))
526 (setq result (funcall handler))
527 (setq hlist (cdr hlist)))
528 (if (eq result t)
529 (progn
530 (if (not (equal (buffer-name) bn))
531 (rename-buffer bn))
532 (auto-fill-mode (if (nth 0 mim) 1 0))
533 (goto-char pt)
534 (if (consp mk)
535 (progn
536 (set-mark (car mk))
537 ;; (setq mark-active (car (cdr mk))))
538 (if (car (cdr mk)) (zmacs-activate-region))) ; XEmacs
539 (set-mark mk))
540 ;; Never override file system if the file really is read-only marked.
541 (if ro (setq buffer-read-only ro))
542 (while locals
543 (let ((this (car locals)))
544 (if (consp this)
545 ;; an entry of this form `(symbol . value)'
546 (progn
547 (make-local-variable (car this))
548 (set (car this) (cdr this)))
549 ;; an entry of the form `symbol'
550 (make-local-variable this)
551 (makunbound this)))
552 (setq locals (cdr locals)))
553 ))))
554
555 ;; Backward compatibility -- update parameters to 205 standards.
556 (defun desktop-buffer (fn bn mam mim pt mk ro tl fc cfs cr misc)
557 (desktop-create-buffer 205 fn bn mam (cdr mim) pt mk ro misc
558 (list (cons 'truncate-lines tl)
559 (cons 'fill-column fc)
560 (cons 'case-fold-search cfs)
561 (cons 'case-replace cr)
562 (cons 'overwrite-mode (car mim)))))
563 ;; ----------------------------------------------------------------------------
564 (provide 'desktop)
565
566 ;; desktop.el ends here.