(hexl-address-region):
[bpt/emacs.git] / lisp / desktop.el
CommitLineData
6343ed61
RS
1;;; desktop.el --- save partial status of Emacs when killed
2
0d30b337
TTN
3;; Copyright (C) 1993, 1994, 1995, 1997, 2000, 2001, 2002, 2003,
4;; 2004, 2005 Free Software Foundation, Inc.
6343ed61
RS
5
6;; Author: Morten Welinder <terra@diku.dk>
247c3649 7;; Maintainter: Lars Hansen <larsh@soem.dk>
3ea96cac 8;; Keywords: convenience
b6105c76 9;; Favourite-brand-of-beer: None, I hate beer.
6343ed61
RS
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it 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;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b578f267 24;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
6343ed61
RS
27
28;;; Commentary:
29
0b9bd504
RS
30;; Save the Desktop, i.e.,
31;; - some global variables
32;; - the list of buffers with associated files. For each buffer also
33;; - the major mode
34;; - the default directory
35;; - the point
36;; - the mark & mark-active
37;; - buffer-read-only
ec4c6f22 38;; - some local variables
6343ed61 39
c5b31c4d
LH
40;; To use this, use customize to turn on desktop-save-mode or add the
41;; following line somewhere in your .emacs file:
0b9bd504 42;;
c5b31c4d 43;; (desktop-save-mode 1)
0b9bd504 44;;
c5b31c4d
LH
45;; For further usage information, look at the section
46;; "Saving Emacs Sessions" in the GNU Emacs Manual.
47
48;; When the desktop module is loaded, the function `desktop-kill' is
49;; added to the `kill-emacs-hook'. This function is responsible for
50;; saving the desktop when Emacs is killed. Furthermore an anonymous
51;; function is added to the `after-init-hook'. This function is
52;; responsible for loading the desktop when Emacs is started.
ec4c6f22 53
47640244
GM
54;; Some words on minor modes: Most minor modes are controlled by
55;; buffer-local variables, which have a standard save / restore
56;; mechanism. To handle all minor modes, we take the following
57;; approach: (1) check whether the variable name from
58;; `minor-mode-alist' is also a function; and (2) use translation
59;; table `desktop-minor-mode-table' in the case where the two names
60;; are not the same.
61
ec4c6f22
RS
62;; By the way: don't use desktop.el to customize Emacs -- the file .emacs
63;; in your home directory is used for that. Saving global default values
64;; for buffers is an example of misuse.
65
0b9bd504
RS
66;; PLEASE NOTE: The kill ring can be saved as specified by the variable
67;; `desktop-globals-to-save' (by default it isn't). This may result in saving
68;; things you did not mean to keep. Use M-x desktop-clear RET.
ec4c6f22 69
fa379636
KH
70;; Thanks to hetrick@phys.uva.nl (Jim Hetrick) for useful ideas.
71;; avk@rtsg.mot.com (Andrew V. Klein) for a dired tip.
72;; chris@tecc.co.uk (Chris Boucher) for a mark tip.
73;; f89-kam@nada.kth.se (Klas Mellbourn) for a mh-e tip.
74;; kifer@sbkifer.cs.sunysb.edu (M. Kifer) for a bug hunt.
f4c73d07 75;; treese@lcs.mit.edu (Win Treese) for ange-ftp tips.
253406b9 76;; pot@cnuce.cnr.it (Francesco Potorti`) for misc. tips.
0b9bd504
RS
77;; ---------------------------------------------------------------------------
78;; TODO:
79;;
80;; Save window configuration.
81;; Recognize more minor modes.
82;; Save mark rings.
6343ed61
RS
83
84;;; Code:
85
4a2fce7a 86(defvar desktop-file-version "206"
05f1c4ec 87 "Version number of desktop file format.
4a2fce7a
RS
88Written into the desktop file and used at desktop read to provide
89backward compatibility.")
90
ec4c6f22 91;; ----------------------------------------------------------------------------
0b9bd504
RS
92;; USER OPTIONS -- settings you might want to play with.
93;; ----------------------------------------------------------------------------
bbf5eb28
RS
94
95(defgroup desktop nil
96 "Save status of Emacs when you exit."
97 :group 'frames)
98
c5b31c4d
LH
99;;;###autoload
100(define-minor-mode desktop-save-mode
101 "Toggle desktop saving mode.
102With numeric ARG, turn desktop saving on if ARG is positive, off
103otherwise. See variable `desktop-save' for a description of when the
104desktop is saved."
105 :global t
106 :group 'desktop)
107
108;; Maintained for backward compatibility
b89c5a72
JB
109(define-obsolete-variable-alias 'desktop-enable
110 'desktop-save-mode "22.1")
813dbb2d 111
4a2fce7a 112(defcustom desktop-save 'ask-if-new
c5b31c4d
LH
113 "*Specifies whether the desktop should be saved when it is killed.
114A desktop is killed when the user changes desktop or quits Emacs.
115Possible values are:
4a2fce7a
RS
116 t -- always save.
117 ask -- always ask.
118 ask-if-new -- ask if no desktop file exists, otherwise just save.
119 ask-if-exists -- ask if desktop file exists, otherwise don't save.
120 if-exists -- save if desktop file exists, otherwise don't save.
121 nil -- never save.
c5b31c4d 122The desktop is never saved when `desktop-save-mode' is nil.
cc8b76bf 123The variables `desktop-dirname' and `desktop-base-file-name'
c5b31c4d 124determine where the desktop is saved."
4a2fce7a
RS
125 :type '(choice
126 (const :tag "Always save" t)
127 (const :tag "Always ask" ask)
128 (const :tag "Ask if desktop file is new, else do save" ask-if-new)
129 (const :tag "Ask if desktop file exists, else don't save" ask-if-exists)
130 (const :tag "Save if desktop file exists, else don't" if-exists)
131 (const :tag "Never save" nil))
af61551b 132 :group 'desktop
bf247b6e 133 :version "22.1")
4a2fce7a
RS
134
135(defcustom desktop-base-file-name
fc715501 136 (convert-standard-filename ".emacs.desktop")
c5b31c4d 137 "Name of file for Emacs desktop, excluding the directory part."
813dbb2d
RS
138 :type 'file
139 :group 'desktop)
b89c5a72
JB
140(define-obsolete-variable-alias 'desktop-basefilename
141 'desktop-base-file-name "22.1")
6343ed61 142
4a2fce7a
RS
143(defcustom desktop-path '("." "~")
144 "List of directories to search for the desktop file.
145The base name of the file is specified in `desktop-base-file-name'."
146 :type '(repeat directory)
af61551b 147 :group 'desktop
bf247b6e 148 :version "22.1")
4a2fce7a 149
bbf5eb28 150(defcustom desktop-missing-file-warning nil
ebb39555
LH
151 "*If non-nil then `desktop-read' asks if a non-existent file should be recreated.
152Also pause for a moment to display message about errors signaled in
153`desktop-buffer-mode-handlers'.
154
155If nil, just print error messages in the message buffer."
bbf5eb28 156 :type 'boolean
af61551b 157 :group 'desktop
bf247b6e 158 :version "22.1")
6343ed61 159
4a2fce7a 160(defcustom desktop-no-desktop-file-hook nil
c5b31c4d 161 "Normal hook run when `desktop-read' can't find a desktop file.
8649a87b 162May be used to show a dired buffer."
4a2fce7a 163 :type 'hook
af61551b 164 :group 'desktop
bf247b6e 165 :version "22.1")
4a2fce7a
RS
166
167(defcustom desktop-after-read-hook nil
05f1c4ec 168 "Normal hook run after a successful `desktop-read'.
8649a87b 169May be used to show a buffer list."
4a2fce7a 170 :type 'hook
af61551b 171 :group 'desktop
bf247b6e 172 :version "22.1")
4a2fce7a
RS
173
174(defcustom desktop-save-hook nil
c5b31c4d 175 "Normal hook run before the desktop is saved in a desktop file.
4a2fce7a
RS
176This is useful for truncating history lists, for example."
177 :type 'hook
178 :group 'desktop)
179
340db502
LH
180(defcustom desktop-globals-to-save
181 '(desktop-missing-file-warning
182 tags-file-name
183 tags-table-list
184 search-ring
185 regexp-search-ring
186 register-alist)
c5b31c4d
LH
187 "List of global variables saved by `desktop-save'.
188An element may be variable name (a symbol) or a cons cell of the form
189\(VAR . MAX-SIZE), which means to truncate VAR's value to at most
190MAX-SIZE elements (if the value is a list) before saving the value.
4a2fce7a
RS
191Feature: Saving `kill-ring' implies saving `kill-ring-yank-pointer'."
192 :type '(repeat (restricted-sexp :match-alternatives (symbolp consp)))
193 :group 'desktop)
194
340db502
LH
195(defcustom desktop-globals-to-clear
196 '(kill-ring
197 kill-ring-yank-pointer
198 search-ring
199 search-ring-yank-pointer
200 regexp-search-ring
201 regexp-search-ring-yank-pointer)
150f39ee 202 "List of global variables that `desktop-clear' will clear.
4a2fce7a 203An element may be variable name (a symbol) or a cons cell of the form
cc8b76bf
JB
204\(VAR . FORM). Symbols are set to nil and for cons cells VAR is set
205to the value obtained by evaluating FORM."
4a2fce7a 206 :type '(repeat (restricted-sexp :match-alternatives (symbolp consp)))
af61551b 207 :group 'desktop
bf247b6e 208 :version "22.1")
4a2fce7a
RS
209
210(defcustom desktop-clear-preserve-buffers-regexp
4880a274 211 "^\\(\\*scratch\\*\\|\\*Messages\\*\\|\\*server\\*\\|\\*tramp/.+\\*\\)$"
340db502
LH
212 "Regexp identifying buffers that `desktop-clear' should not delete.
213See also `desktop-clear-preserve-buffers'."
4a2fce7a 214 :type 'regexp
af61551b 215 :group 'desktop
bf247b6e 216 :version "22.1")
4a2fce7a 217
c5b31c4d
LH
218(defcustom desktop-clear-preserve-buffers nil
219 "*List of buffer names that `desktop-clear' should not delete.
340db502 220See also `desktop-clear-preserve-buffers-regexp'."
4a2fce7a
RS
221 :type '(repeat string)
222 :group 'desktop)
340db502
LH
223
224(defcustom desktop-locals-to-save
225 '(desktop-locals-to-save ; Itself! Think it over.
226 truncate-lines
227 case-fold-search
228 case-replace
229 fill-column
230 overwrite-mode
231 change-log-default-name
232 line-number-mode
233 buffer-file-coding-system)
577ed2b2 234 "List of local variables to save for each buffer.
c5b31c4d
LH
235The variables are saved only when they really are local."
236 :type '(repeat symbol)
237 :group 'desktop)
de9e2828 238(make-variable-buffer-local 'desktop-locals-to-save)
ec4c6f22 239
0b9bd504 240;; We skip .log files because they are normally temporary.
a7acbbe4 241;; (ftp) files because they require passwords and whatnot.
0b9bd504 242;; TAGS files to save time (tags-file-name is saved instead).
bbf5eb28 243(defcustom desktop-buffers-not-to-save
4a2fce7a
RS
244 "\\(^nn\\.a[0-9]+\\|\\.log\\|(ftp)\\|^tags\\|^TAGS\\)$"
245 "Regexp identifying buffers that are to be excluded from saving."
246 :type 'regexp
247 :group 'desktop)
6343ed61 248
c5b31c4d 249;; Skip tramp and ange-ftp files
bbf5eb28 250(defcustom desktop-files-not-to-save
fa379636 251 "^/[^/:]*:"
bbf5eb28
RS
252 "Regexp identifying files whose buffers are to be excluded from saving."
253 :type 'regexp
254 :group 'desktop)
fa379636 255
80f9f3db
SM
256(defcustom desktop-modes-not-to-save nil
257 "List of major modes whose buffers should not be saved."
258 :type '(repeat symbol)
259 :group 'desktop)
260
4a2fce7a
RS
261(defcustom desktop-file-name-format 'absolute
262 "*Format in which desktop file names should be saved.
263Possible values are:
264 absolute -- Absolute file name.
265 tilde -- Relative to ~.
266 local -- Relative to directory of desktop file."
267 :type '(choice (const absolute) (const tilde) (const local))
af61551b 268 :group 'desktop
bf247b6e 269 :version "22.1")
569c754e 270
150f39ee
LH
271(defcustom desktop-restore-eager t
272 "Number of buffers to restore immediately.
273Remaining buffers are restored lazily (when Emacs is idle).
274If value is t, all buffers are restored immediately."
0ba9bc53 275 :type '(choice (const t) integer)
150f39ee 276 :group 'desktop
bf247b6e 277 :version "22.1")
150f39ee
LH
278
279(defcustom desktop-lazy-verbose t
280 "Verbose reporting of lazily created buffers."
281 :type 'boolean
282 :group 'desktop
bf247b6e 283 :version "22.1")
150f39ee
LH
284
285(defcustom desktop-lazy-idle-delay 5
286 "Idle delay before starting to create buffers.
287See `desktop-restore-eager'."
288 :type 'integer
289 :group 'desktop
bf247b6e 290 :version "22.1")
150f39ee 291
e5780ae1 292;;;###autoload
ebb39555
LH
293(defvar desktop-save-buffer nil
294 "When non-nil, save buffer status in desktop file.
e5780ae1 295This variable becomes buffer local when set.
ebb39555 296
b89c5a72
JB
297If the value is a function, it is called by `desktop-save' with argument
298DESKTOP-DIRNAME to obtain auxiliary information to save in the desktop
ebb39555 299file along with the state of the buffer for which it was called.
b6b70cda 300
c5b31c4d 301When file names are returned, they should be formatted using the call
e5780ae1 302\"(desktop-file-name FILE-NAME DESKTOP-DIRNAME)\".
4a2fce7a 303
e5780ae1 304Later, when `desktop-read' calls a function in `desktop-buffer-mode-handlers'
ebb39555
LH
305to restore the buffer, the auxiliary information is passed as the argument
306DESKTOP-BUFFER-MISC.")
307(make-variable-buffer-local 'desktop-save-buffer)
308(make-obsolete-variable 'desktop-buffer-modes-to-save
cc8b76bf 309 'desktop-save-buffer "22.1")
e5780ae1 310(make-obsolete-variable 'desktop-buffer-misc-functions
cc8b76bf 311 'desktop-save-buffer "22.1")
b6b70cda 312
340db502
LH
313(defcustom desktop-buffer-mode-handlers
314 '((dired-mode . dired-restore-desktop-buffer)
315 (rmail-mode . rmail-restore-desktop-buffer)
316 (mh-folder-mode . mh-restore-desktop-buffer)
317 (Info-mode . Info-restore-desktop-buffer))
e5780ae1 318 "Alist of major mode specific functions to restore a desktop buffer.
cc8b76bf 319Functions are called by `desktop-read'. List elements must have the form
55775448 320\(MAJOR-MODE . RESTORE-BUFFER-FUNCTION).
e5780ae1
LH
321
322Buffers with a major mode not specified here, are restored by the default
323handler `desktop-restore-file-buffer'.
324
55775448 325Handlers are called with argument list
4a2fce7a 326
9bcabb45 327 (DESKTOP-BUFFER-FILE-NAME DESKTOP-BUFFER-NAME DESKTOP-BUFFER-MISC)
e5780ae1
LH
328
329Furthermore, they may use the following variables:
330
331 desktop-file-version
4a2fce7a
RS
332 desktop-buffer-major-mode
333 desktop-buffer-minor-modes
334 desktop-buffer-point
335 desktop-buffer-mark
336 desktop-buffer-read-only
4a2fce7a
RS
337 desktop-buffer-locals
338
e5780ae1 339If a handler returns a buffer, then the saved mode settings
0a8c8225 340and variable values for that buffer are copied into it."
e5780ae1 341 :type 'alist
bbf5eb28 342 :group 'desktop)
ec4c6f22 343
e5780ae1
LH
344(put 'desktop-buffer-mode-handlers 'risky-local-variable t)
345(make-obsolete-variable 'desktop-buffer-handlers
cc8b76bf 346 'desktop-buffer-mode-handlers "22.1")
b6b70cda 347
47640244
GM
348(defcustom desktop-minor-mode-table
349 '((auto-fill-function auto-fill-mode)
350 (vc-mode nil))
351 "Table mapping minor mode variables to minor mode functions.
352Each entry has the form (NAME RESTORE-FUNCTION).
353NAME is the name of the buffer-local variable indicating that the minor
354mode is active. RESTORE-FUNCTION is the function to activate the minor mode.
355called. RESTORE-FUNCTION nil means don't try to restore the minor mode.
356Only minor modes for which the name of the buffer-local variable
7bfa55b3 357and the name of the minor mode function are different have to be added to
47640244
GM
358this table."
359 :type 'sexp
360 :group 'desktop)
361
0b9bd504
RS
362;; ----------------------------------------------------------------------------
363(defvar desktop-dirname nil
c5b31c4d 364 "The directory in which the desktop file should be saved.")
6343ed61
RS
365
366(defconst desktop-header
0b9bd504
RS
367";; --------------------------------------------------------------------------
368;; Desktop File for Emacs
369;; --------------------------------------------------------------------------
6343ed61 370" "*Header to place in Desktop file.")
de9e2828
RS
371
372(defvar desktop-delay-hook nil
373 "Hooks run after all buffers are loaded; intended for internal use.")
813dbb2d 374
0b9bd504 375;; ----------------------------------------------------------------------------
05f1c4ec 376(defun desktop-truncate (list n)
ec4c6f22 377 "Truncate LIST to at most N elements destructively."
05f1c4ec 378 (let ((here (nthcdr (1- n) list)))
ec4c6f22 379 (if (consp here)
de9e2828 380 (setcdr here nil))))
f9be4574 381
4a2fce7a 382;; ----------------------------------------------------------------------------
f9be4574
RS
383(defun desktop-clear ()
384 "Empty the Desktop.
c5b31c4d
LH
385This kills all buffers except for internal ones and those matching
386`desktop-clear-preserve-buffers-regexp' or listed in
387`desktop-clear-preserve-buffers'. Furthermore, it clears the
4a2fce7a 388variables listed in `desktop-globals-to-clear'."
6343ed61 389 (interactive)
150f39ee 390 (desktop-lazy-abort)
4a2fce7a
RS
391 (dolist (var desktop-globals-to-clear)
392 (if (symbolp var)
393 (eval `(setq-default ,var nil))
394 (eval `(setq-default ,(car var) ,(cdr var)))))
f9be4574
RS
395 (let ((buffers (buffer-list)))
396 (while buffers
4a2fce7a
RS
397 (let ((bufname (buffer-name (car buffers))))
398 (or
399 (null bufname)
400 (string-match desktop-clear-preserve-buffers-regexp bufname)
401 (member bufname desktop-clear-preserve-buffers)
402 ;; Don't kill buffers made for internal purposes.
3f32d6a3 403 (and (not (equal bufname "")) (eq (aref bufname 0) ?\s))
4a2fce7a 404 (kill-buffer (car buffers))))
f9be4574 405 (setq buffers (cdr buffers))))
b6105c76 406 (delete-other-windows))
4a2fce7a 407
0b9bd504 408;; ----------------------------------------------------------------------------
de9e2828 409(add-hook 'kill-emacs-hook 'desktop-kill)
ec4c6f22 410
6343ed61 411(defun desktop-kill ()
c5b31c4d 412 "If `desktop-save-mode' is non-nil, do what `desktop-save' says to do.
4a2fce7a
RS
413If the desktop should be saved and `desktop-dirname'
414is nil, ask the user where to save the desktop."
415 (when
416 (and
c5b31c4d 417 desktop-save-mode
73b0b745 418 (let ((exists (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))))
4a2fce7a 419 (or
d12d9396 420 (eq desktop-save t)
4a2fce7a
RS
421 (and exists (memq desktop-save '(ask-if-new if-exists)))
422 (and
423 (or
424 (memq desktop-save '(ask ask-if-new))
425 (and exists (eq desktop-save 'ask-if-exists)))
426 (y-or-n-p "Save desktop? ")))))
427 (unless desktop-dirname
428 (setq desktop-dirname
73b0b745
JB
429 (file-name-as-directory
430 (expand-file-name
431 (call-interactively
432 (lambda (dir) (interactive "DDirectory for desktop file: ") dir))))))
4a2fce7a
RS
433 (condition-case err
434 (desktop-save desktop-dirname)
435 (file-error
cc8b76bf 436 (unless (yes-or-no-p "Error while saving the desktop. Ignore? ")
4a2fce7a
RS
437 (signal (car err) (cdr err)))))))
438
0b9bd504 439;; ----------------------------------------------------------------------------
ed2f7fc8
RS
440(defun desktop-list* (&rest args)
441 (if (null (cdr args))
442 (car args)
443 (setq args (nreverse args))
444 (let ((value (cons (nth 1 args) (car args))))
445 (setq args (cdr (cdr args)))
446 (while args
447 (setq value (cons (car args) value))
448 (setq args (cdr args)))
449 value)))
450
4a2fce7a 451;; ----------------------------------------------------------------------------
05f1c4ec 452(defun desktop-internal-v2s (value)
577ed2b2
RS
453 "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE.
454TXT is a string that when read and evaluated yields value.
455QUOTE may be `may' (value may be quoted),
456`must' (values must be quoted), or nil (value may not be quoted)."
de9e2828 457 (cond
3f32d6a3 458 ((or (numberp value) (null value) (eq t value) (keywordp value))
05f1c4ec
JB
459 (cons 'may (prin1-to-string value)))
460 ((stringp value)
461 (let ((copy (copy-sequence value)))
95bf6435
RS
462 (set-text-properties 0 (length copy) nil copy)
463 ;; Get rid of text properties because we cannot read them
464 (cons 'may (prin1-to-string copy))))
05f1c4ec
JB
465 ((symbolp value)
466 (cons 'must (prin1-to-string value)))
467 ((vectorp value)
de9e2828
RS
468 (let* ((special nil)
469 (pass1 (mapcar
470 (lambda (el)
471 (let ((res (desktop-internal-v2s el)))
472 (if (null (car res))
473 (setq special t))
474 res))
05f1c4ec 475 value)))
de9e2828
RS
476 (if special
477 (cons nil (concat "(vector "
478 (mapconcat (lambda (el)
479 (if (eq (car el) 'must)
480 (concat "'" (cdr el))
481 (cdr el)))
482 pass1
483 " ")
484 ")"))
485 (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
05f1c4ec
JB
486 ((consp value)
487 (let ((p value)
b4929f75 488 newlist
ed2f7fc8 489 use-list*
b4929f75
RS
490 anynil)
491 (while (consp p)
492 (let ((q.txt (desktop-internal-v2s (car p))))
493 (or anynil (setq anynil (null (car q.txt))))
494 (setq newlist (cons q.txt newlist)))
495 (setq p (cdr p)))
496 (if p
497 (let ((last (desktop-internal-v2s p))
498 (el (car newlist)))
ed2f7fc8
RS
499 (or anynil (setq anynil (null (car last))))
500 (or anynil
501 (setq newlist (cons '(must . ".") newlist)))
502 (setq use-list* t)
503 (setq newlist (cons last newlist))))
b4929f75
RS
504 (setq newlist (nreverse newlist))
505 (if anynil
506 (cons nil
ed2f7fc8 507 (concat (if use-list* "(desktop-list* " "(list ")
b4929f75
RS
508 (mapconcat (lambda (el)
509 (if (eq (car el) 'must)
510 (concat "'" (cdr el))
511 (cdr el)))
512 newlist
513 " ")
514 ")"))
515 (cons 'must
516 (concat "(" (mapconcat 'cdr newlist " ") ")")))))
05f1c4ec 517 ((subrp value)
de9e2828 518 (cons nil (concat "(symbol-function '"
05f1c4ec 519 (substring (prin1-to-string value) 7 -1)
de9e2828 520 ")")))
05f1c4ec
JB
521 ((markerp value)
522 (let ((pos (prin1-to-string (marker-position value)))
523 (buf (prin1-to-string (buffer-name (marker-buffer value)))))
de9e2828
RS
524 (cons nil (concat "(let ((mk (make-marker)))"
525 " (add-hook 'desktop-delay-hook"
526 " (list 'lambda '() (list 'set-marker mk "
527 pos " (get-buffer " buf ")))) mk)"))))
528 (t ; save as text
fa379636 529 (cons 'may "\"Unprintable entity\""))))
de9e2828 530
4a2fce7a 531;; ----------------------------------------------------------------------------
05f1c4ec 532(defun desktop-value-to-string (value)
577ed2b2
RS
533 "Convert VALUE to a string that when read evaluates to the same value.
534Not all types of values are supported."
de9e2828
RS
535 (let* ((print-escape-newlines t)
536 (float-output-format nil)
05f1c4ec 537 (quote.txt (desktop-internal-v2s value))
de9e2828
RS
538 (quote (car quote.txt))
539 (txt (cdr quote.txt)))
540 (if (eq quote 'must)
541 (concat "'" txt)
542 txt)))
4a2fce7a 543
ec4c6f22 544;; ----------------------------------------------------------------------------
0e7c8611
RS
545(defun desktop-outvar (varspec)
546 "Output a setq statement for variable VAR to the desktop file.
547The argument VARSPEC may be the variable name VAR (a symbol),
548or a cons cell of the form (VAR . MAX-SIZE),
549which means to truncate VAR's value to at most MAX-SIZE elements
550\(if the value is a list) before saving the value."
551 (let (var size)
552 (if (consp varspec)
553 (setq var (car varspec) size (cdr varspec))
554 (setq var varspec))
555 (if (boundp var)
556 (progn
557 (if (and (integerp size)
558 (> size 0)
559 (listp (eval var)))
47640244 560 (desktop-truncate (eval var) size))
0e7c8611
RS
561 (insert "(setq "
562 (symbol-name var)
563 " "
564 (desktop-value-to-string (symbol-value var))
565 ")\n")))))
4a2fce7a 566
0b9bd504 567;; ----------------------------------------------------------------------------
ec4c6f22 568(defun desktop-save-buffer-p (filename bufname mode &rest dummy)
ebb39555 569 "Return t if buffer should have its state saved in the desktop file.
0b9bd504 570FILENAME is the visited file name, BUFNAME is the buffer name, and
6343ed61 571MODE is the major mode."
fa379636 572 (let ((case-fold-search nil))
80f9f3db 573 (and (not (string-match desktop-buffers-not-to-save bufname))
ebb39555
LH
574 (not (memq mode desktop-modes-not-to-save))
575 (or (and filename
576 (not (string-match desktop-files-not-to-save filename)))
577 (and (eq mode 'dired-mode)
578 (with-current-buffer bufname
579 (not (string-match desktop-files-not-to-save
580 default-directory))))
581 (and (null filename)
582 (with-current-buffer bufname desktop-save-buffer))))))
4a2fce7a 583
0b9bd504 584;; ----------------------------------------------------------------------------
4a2fce7a
RS
585(defun desktop-file-name (filename dirname)
586 "Convert FILENAME to format specified in `desktop-file-name-format'.
587DIRNAME must be the directory in which the desktop file will be saved."
588 (cond
589 ((not filename) nil)
590 ((eq desktop-file-name-format 'tilde)
591 (let ((relative-name (file-relative-name (expand-file-name filename) "~")))
592 (cond
593 ((file-name-absolute-p relative-name) relative-name)
594 ((string= "./" relative-name) "~/")
595 ((string= "." relative-name) "~")
596 (t (concat "~/" relative-name)))))
597 ((eq desktop-file-name-format 'local) (file-relative-name filename dirname))
598 (t (expand-file-name filename))))
e5714620 599
4a2fce7a 600;; ----------------------------------------------------------------------------
6343ed61 601(defun desktop-save (dirname)
c5b31c4d
LH
602 "Save the desktop in a desktop file.
603Parameter DIRNAME specifies where to save the desktop file.
604See also `desktop-base-file-name'."
6343ed61 605 (interactive "DDirectory to save desktop file in: ")
fa379636 606 (run-hooks 'desktop-save-hook)
7bcbf3c2 607 (setq dirname (file-name-as-directory (expand-file-name dirname)))
6343ed61 608 (save-excursion
73b0b745 609 (let ((filename (expand-file-name desktop-base-file-name dirname))
7bcbf3c2
JB
610 (info
611 (mapcar
340db502 612 #'(lambda (b)
7bcbf3c2
JB
613 (set-buffer b)
614 (list
615 (desktop-file-name (buffer-file-name) dirname)
616 (buffer-name)
617 major-mode
618 ;; minor modes
619 (let (ret)
7bfa55b3
LH
620 (mapc
621 #'(lambda (minor-mode)
7bcbf3c2 622 (and
7bfa55b3
LH
623 (boundp minor-mode)
624 (symbol-value minor-mode)
625 (let ((special (assq minor-mode desktop-minor-mode-table)))
626 (when (or special (functionp minor-mode))
627 (setq ret
628 (cons
629 (if special (cadr special) minor-mode)
630 ret))))))
7bcbf3c2
JB
631 (mapcar #'car minor-mode-alist))
632 ret)
633 (point)
634 (list (mark t) mark-active)
635 buffer-read-only
e5780ae1 636 ;; Auxiliary information
ebb39555
LH
637 (when (functionp desktop-save-buffer)
638 (funcall desktop-save-buffer dirname))
7bcbf3c2
JB
639 (let ((locals desktop-locals-to-save)
640 (loclist (buffer-local-variables))
641 (ll))
642 (while locals
643 (let ((here (assq (car locals) loclist)))
644 (if here
645 (setq ll (cons here ll))
646 (when (member (car locals) loclist)
647 (setq ll (cons (car locals) ll)))))
648 (setq locals (cdr locals)))
340db502 649 ll)))
7bcbf3c2 650 (buffer-list)))
150f39ee 651 (eager desktop-restore-eager)
7bcbf3c2 652 (buf (get-buffer-create "*desktop*")))
6343ed61
RS
653 (set-buffer buf)
654 (erase-buffer)
fa379636 655
4a2fce7a 656 (insert
aac42a1b 657 ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
4a2fce7a
RS
658 desktop-header
659 ";; Created " (current-time-string) "\n"
660 ";; Desktop file format version " desktop-file-version "\n"
661 ";; Emacs version " emacs-version "\n\n"
662 ";; Global section:\n")
63cd5734 663 (mapc (function desktop-outvar) desktop-globals-to-save)
6343ed61 664 (if (memq 'kill-ring desktop-globals-to-save)
4a2fce7a
RS
665 (insert
666 "(setq kill-ring-yank-pointer (nthcdr "
667 (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
668 " kill-ring))\n"))
6343ed61 669
4a2fce7a 670 (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
63cd5734 671 (mapc #'(lambda (l)
150f39ee
LH
672 (when (apply 'desktop-save-buffer-p l)
673 (insert "("
674 (if (or (not (integerp eager))
675 (unless (zerop eager)
676 (setq eager (1- eager))
677 t))
678 "desktop-create-buffer"
679 "desktop-append-buffer-args")
680 " "
681 desktop-file-version)
682 (mapc #'(lambda (e)
683 (insert "\n " (desktop-value-to-string e)))
684 l)
685 (insert ")\n\n")))
686 info)
6343ed61 687 (setq default-directory dirname)
4a2fce7a 688 (when (file-exists-p filename) (delete-file filename))
7642acca 689 (let ((coding-system-for-write 'emacs-mule))
4a2fce7a 690 (write-region (point-min) (point-max) filename nil 'nomessage))))
6343ed61 691 (setq desktop-dirname dirname))
4a2fce7a 692
0b9bd504 693;; ----------------------------------------------------------------------------
6343ed61 694(defun desktop-remove ()
c5b31c4d
LH
695 "Delete desktop file in `desktop-dirname'.
696This function also sets `desktop-dirname' to nil."
6343ed61 697 (interactive)
c5b31c4d
LH
698 (when desktop-dirname
699 (let ((filename (expand-file-name desktop-base-file-name desktop-dirname)))
700 (setq desktop-dirname nil)
701 (when (file-exists-p filename)
73b0b745 702 (delete-file filename)))))
c5b31c4d 703
150f39ee
LH
704(defvar desktop-buffer-args-list nil
705 "List of args for `desktop-create-buffer'.")
706
707(defvar desktop-lazy-timer nil)
708
0b9bd504 709;; ----------------------------------------------------------------------------
478653c9 710;;;###autoload
c5b31c4d
LH
711(defun desktop-read (&optional dirname)
712 "Read and process the desktop file in directory DIRNAME.
713Look for a desktop file in DIRNAME, or if DIRNAME is omitted, look in
714directories listed in `desktop-path'. If a desktop file is found, it
cc8b76bf 715is processed and `desktop-after-read-hook' is run. If no desktop file
c5b31c4d
LH
716is found, clear the desktop and run `desktop-no-desktop-file-hook'.
717This function is a no-op when Emacs is running in batch mode.
718It returns t if a desktop file was loaded, nil otherwise."
6343ed61 719 (interactive)
4a2fce7a 720 (unless noninteractive
c5b31c4d
LH
721 (setq desktop-dirname
722 (file-name-as-directory
723 (expand-file-name
724 (or
725 ;; If DIRNAME is specified, use it.
726 (and (< 0 (length dirname)) dirname)
727 ;; Otherwise search desktop file in desktop-path.
728 (let ((dirs desktop-path))
729 (while
730 (and
731 dirs
732 (not
733 (file-exists-p (expand-file-name desktop-base-file-name (car dirs)))))
734 (setq dirs (cdr dirs)))
735 (and dirs (car dirs)))
736 ;; If not found and `desktop-path' is non-nil, use its first element.
737 (and desktop-path (car desktop-path))
738 ;; Default: Home directory.
739 "~"))))
740 (if (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))
741 ;; Desktop file found, process it.
e5780ae1
LH
742 (let ((desktop-first-buffer nil)
743 (desktop-buffer-ok-count 0)
744 (desktop-buffer-fail-count 0))
150f39ee 745 (setq desktop-lazy-timer nil)
c5b31c4d
LH
746 ;; Evaluate desktop buffer.
747 (load (expand-file-name desktop-base-file-name desktop-dirname) t t t)
748 ;; `desktop-create-buffer' puts buffers at end of the buffer list.
749 ;; We want buffers existing prior to evaluating the desktop (and not reused)
750 ;; to be placed at the end of the buffer list, so we move them here.
48aa4dfc
LH
751 (mapc 'bury-buffer
752 (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
c5b31c4d
LH
753 (switch-to-buffer (car (buffer-list)))
754 (run-hooks 'desktop-delay-hook)
755 (setq desktop-delay-hook nil)
756 (run-hooks 'desktop-after-read-hook)
150f39ee 757 (message "Desktop: %d buffer%s restored%s%s."
e5780ae1
LH
758 desktop-buffer-ok-count
759 (if (= 1 desktop-buffer-ok-count) "" "s")
760 (if (< 0 desktop-buffer-fail-count)
761 (format ", %d failed to restore" desktop-buffer-fail-count)
150f39ee
LH
762 "")
763 (if desktop-buffer-args-list
764 (format ", %d to restore lazily"
765 (length desktop-buffer-args-list))
e5780ae1 766 ""))
c5b31c4d
LH
767 t)
768 ;; No desktop file found.
769 (desktop-clear)
770 (let ((default-directory desktop-dirname))
771 (run-hooks 'desktop-no-desktop-file-hook))
772 (message "No desktop file.")
773 nil)))
4a2fce7a 774
0b9bd504 775;; ----------------------------------------------------------------------------
c5b31c4d 776;; Maintained for backward compatibility
478653c9 777;;;###autoload
6343ed61 778(defun desktop-load-default ()
577ed2b2 779 "Load the `default' start-up library manually.
c5b31c4d 780Also inhibit further loading of it."
b89c5a72
JB
781 (unless inhibit-default-init ; safety check
782 (load "default" t t)
783 (setq inhibit-default-init t)))
784(make-obsolete 'desktop-load-default
785 'desktop-save-mode "22.1")
4a2fce7a
RS
786
787;; ----------------------------------------------------------------------------
788;;;###autoload
c5b31c4d
LH
789(defun desktop-change-dir (dirname)
790 "Change to desktop saved in DIRNAME.
791Kill the desktop as specified by variables `desktop-save-mode' and
792`desktop-save', then clear the desktop and load the desktop file in
793directory DIRNAME."
794 (interactive "DChange to directory: ")
795 (setq dirname (file-name-as-directory (expand-file-name dirname desktop-dirname)))
4a2fce7a
RS
796 (desktop-kill)
797 (desktop-clear)
c5b31c4d 798 (desktop-read dirname))
bf247b6e 799
c5b31c4d 800;; ----------------------------------------------------------------------------
4a2fce7a 801;;;###autoload
c5b31c4d
LH
802(defun desktop-save-in-desktop-dir ()
803 "Save the desktop in directory `desktop-dirname'."
4a2fce7a
RS
804 (interactive)
805 (if desktop-dirname
b89c5a72 806 (desktop-save desktop-dirname)
4a2fce7a
RS
807 (call-interactively 'desktop-save))
808 (message "Desktop saved in %s" desktop-dirname))
809
810;; ----------------------------------------------------------------------------
811;;;###autoload
812(defun desktop-revert ()
813 "Revert to the last loaded desktop."
814 (interactive)
c5b31c4d
LH
815 (unless desktop-dirname
816 (error "Unknown desktop directory"))
817 (unless (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))
818 (error "No desktop file found"))
819 (desktop-clear)
820 (desktop-read desktop-dirname))
4a2fce7a 821
0b9bd504 822;; ----------------------------------------------------------------------------
e5780ae1
LH
823(defun desktop-restore-file-buffer (desktop-buffer-file-name
824 desktop-buffer-name
825 desktop-buffer-misc)
826 "Restore a file buffer."
827 (eval-when-compile ; Just to silence the byte compiler
828 (defvar desktop-buffer-major-mode)
829 (defvar desktop-buffer-locals))
569c754e
RS
830 (if desktop-buffer-file-name
831 (if (or (file-exists-p desktop-buffer-file-name)
e5780ae1
LH
832 (let ((msg (format "Desktop: File \"%s\" no longer exists."
833 desktop-buffer-file-name)))
834 (if desktop-missing-file-warning
835 (y-or-n-p (concat msg " Re-create? "))
836 (message msg)
837 nil)))
bd382a27
LH
838 (let* ((auto-insert nil) ; Disable auto insertion
839 (coding-system-for-read
840 (or coding-system-for-read
841 (cdr (assq 'buffer-file-coding-system
842 desktop-buffer-locals))))
843 (buf (find-file-noselect desktop-buffer-file-name)))
80f9f3db
SM
844 (condition-case nil
845 (switch-to-buffer buf)
0a8c8225 846 (error (pop-to-buffer buf)))
84b538ec
JB
847 (and (not (eq major-mode desktop-buffer-major-mode))
848 (functionp desktop-buffer-major-mode)
849 (funcall desktop-buffer-major-mode))
0a8c8225 850 buf)
e5780ae1 851 nil)))
4a2fce7a 852
0b9bd504 853;; ----------------------------------------------------------------------------
eb982898
JL
854;; Create a buffer, load its file, set its mode, ...;
855;; called from Desktop file only.
1d500ca6 856
4a2fce7a
RS
857(eval-when-compile ; Just to silence the byte compiler
858 (defvar desktop-first-buffer) ;; Dynamically bound in `desktop-read'
859)
860
340db502
LH
861(defun desktop-create-buffer
862 (desktop-file-version
863 desktop-buffer-file-name
864 desktop-buffer-name
865 desktop-buffer-major-mode
866 desktop-buffer-minor-modes
867 desktop-buffer-point
868 desktop-buffer-mark
869 desktop-buffer-read-only
870 desktop-buffer-misc
871 &optional
872 desktop-buffer-locals)
e5780ae1
LH
873 ;; Just to silence the byte compiler. Bound locally in `desktop-read'.
874 (eval-when-compile
875 (defvar desktop-buffer-ok-count)
876 (defvar desktop-buffer-fail-count))
4a2fce7a
RS
877 ;; To make desktop files with relative file names possible, we cannot
878 ;; allow `default-directory' to change. Therefore we save current buffer.
879 (save-current-buffer
340db502
LH
880 (let ((buffer-list (buffer-list))
881 (result
882 (condition-case err
883 (funcall (or (cdr (assq desktop-buffer-major-mode
884 desktop-buffer-mode-handlers))
885 'desktop-restore-file-buffer)
886 desktop-buffer-file-name
887 desktop-buffer-name
888 desktop-buffer-misc)
889 (error
890 (message "Desktop: Can't load buffer %s: %s"
891 desktop-buffer-name
892 (error-message-string err))
893 (when desktop-missing-file-warning (sit-for 1))
894 nil))))
e5780ae1
LH
895 (if (bufferp result)
896 (setq desktop-buffer-ok-count (1+ desktop-buffer-ok-count))
897 (setq desktop-buffer-fail-count (1+ desktop-buffer-fail-count))
898 (setq result nil))
7bcbf3c2
JB
899 ;; Restore buffer list order with new buffer at end. Don't change
900 ;; the order for old desktop files (old desktop module behaviour).
4a2fce7a 901 (unless (< desktop-file-version 206)
48aa4dfc 902 (mapc 'bury-buffer buffer-list)
7bcbf3c2 903 (when result (bury-buffer result)))
4a2fce7a 904 (when result
7bcbf3c2
JB
905 (unless (or desktop-first-buffer (< desktop-file-version 206))
906 (setq desktop-first-buffer result))
4a2fce7a
RS
907 (set-buffer result)
908 (unless (equal (buffer-name) desktop-buffer-name)
909 (rename-buffer desktop-buffer-name))
910 ;; minor modes
340db502
LH
911 (cond ((equal '(t) desktop-buffer-minor-modes) ; backwards compatible
912 (auto-fill-mode 1))
913 ((equal '(nil) desktop-buffer-minor-modes) ; backwards compatible
914 (auto-fill-mode 0))
915 (t
63cd5734
EZ
916 (mapc #'(lambda (minor-mode)
917 (when (functionp minor-mode) (funcall minor-mode 1)))
918 desktop-buffer-minor-modes)))
4a2fce7a 919 ;; Even though point and mark are non-nil when written by `desktop-save'
73b0b745 920 ;; they may be modified by handlers wanting to set point or mark themselves.
e5780ae1
LH
921 (when desktop-buffer-point
922 (goto-char
923 (condition-case err
924 ;; Evaluate point. Thus point can be something like '(search-forward ...
925 (eval desktop-buffer-point)
926 (error (message "%s" (error-message-string err)) 1))))
4a2fce7a
RS
927 (when desktop-buffer-mark
928 (if (consp desktop-buffer-mark)
929 (progn
930 (set-mark (car desktop-buffer-mark))
931 (setq mark-active (car (cdr desktop-buffer-mark))))
932 (set-mark desktop-buffer-mark)))
933 ;; Never override file system if the file really is read-only marked.
934 (if desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
935 (while desktop-buffer-locals
936 (let ((this (car desktop-buffer-locals)))
937 (if (consp this)
938 ;; an entry of this form `(symbol . value)'
939 (progn
940 (make-local-variable (car this))
941 (set (car this) (cdr this)))
942 ;; an entry of the form `symbol'
943 (make-local-variable this)
944 (makunbound this)))
945 (setq desktop-buffer-locals (cdr desktop-buffer-locals)))))))
ec4c6f22 946
4a2fce7a 947;; ----------------------------------------------------------------------------
ec4c6f22 948;; Backward compatibility -- update parameters to 205 standards.
569c754e
RS
949(defun desktop-buffer (desktop-buffer-file-name desktop-buffer-name
950 desktop-buffer-major-mode
2699e23e 951 mim pt mk ro tl fc cfs cr desktop-buffer-misc)
569c754e 952 (desktop-create-buffer 205 desktop-buffer-file-name desktop-buffer-name
2699e23e
RS
953 desktop-buffer-major-mode (cdr mim) pt mk ro
954 desktop-buffer-misc
ec4c6f22
RS
955 (list (cons 'truncate-lines tl)
956 (cons 'fill-column fc)
957 (cons 'case-fold-search cfs)
958 (cons 'case-replace cr)
959 (cons 'overwrite-mode (car mim)))))
84b538ec 960
150f39ee 961(defun desktop-append-buffer-args (&rest args)
cc8b76bf 962 "Append ARGS at end of `desktop-buffer-args-list'.
150f39ee
LH
963ARGS must be an argument list for `desktop-create-buffer'."
964 (setq desktop-buffer-args-list (nconc desktop-buffer-args-list (list args)))
965 (unless desktop-lazy-timer
966 (setq desktop-lazy-timer
967 (run-with-idle-timer desktop-lazy-idle-delay t 'desktop-idle-create-buffers))))
968
969(defun desktop-lazy-create-buffer ()
970 "Pop args from `desktop-buffer-args-list', create buffer and bury it."
971 (when desktop-buffer-args-list
972 (let* ((remaining (length desktop-buffer-args-list))
973 (args (pop desktop-buffer-args-list))
974 (buffer-name (nth 2 args))
975 (msg (format "Desktop lazily opening %s (%s remaining)..."
976 buffer-name remaining)))
977 (when desktop-lazy-verbose
978 (message msg))
979 (let ((desktop-first-buffer nil)
980 (desktop-buffer-ok-count 0)
981 (desktop-buffer-fail-count 0))
982 (apply 'desktop-create-buffer args)
983 (run-hooks 'desktop-delay-hook)
984 (setq desktop-delay-hook nil)
985 (bury-buffer (get-buffer buffer-name))
986 (when desktop-lazy-verbose
987 (message "%s%s" msg (if (> desktop-buffer-ok-count 0) "done" "failed")))))))
988
989(defun desktop-idle-create-buffers ()
990 "Create buffers until the user does something, then stop.
991If there are no buffers left to create, kill the timer."
992 (let ((repeat 1))
993 (while (and repeat desktop-buffer-args-list)
994 (save-window-excursion
995 (desktop-lazy-create-buffer))
996 (setq repeat (sit-for 0.2))
997 (unless desktop-buffer-args-list
998 (cancel-timer desktop-lazy-timer)
999 (setq desktop-lazy-timer nil)
1000 (message "Lazy desktop load complete")
1001 (sit-for 3)
1002 (message "")))))
1003
1004(defun desktop-lazy-complete ()
1005 "Run the desktop load to completion."
1006 (interactive)
1007 (let ((desktop-lazy-verbose t))
1008 (while desktop-buffer-args-list
1009 (save-window-excursion
1010 (desktop-lazy-create-buffer)))
1011 (message "Lazy desktop load complete")))
1012
1013(defun desktop-lazy-abort ()
1014 "Abort lazy loading of the desktop."
1015 (interactive)
1016 (when desktop-lazy-timer
1017 (cancel-timer desktop-lazy-timer)
1018 (setq desktop-lazy-timer nil))
1019 (when desktop-buffer-args-list
1020 (setq desktop-buffer-args-list nil)
1021 (when (interactive-p)
1022 (message "Lazy desktop load aborted"))))
1023
0b9bd504 1024;; ----------------------------------------------------------------------------
c5b31c4d 1025;; When `desktop-save-mode' is non-nil and "--no-desktop" is not specified on the
4a2fce7a
RS
1026;; command line, we do the rest of what it takes to use desktop, but do it
1027;; after finishing loading the init file.
1028;; We cannot use `command-switch-alist' to process "--no-desktop" because these
1029;; functions are processed after `after-init-hook'.
1030(add-hook
1031 'after-init-hook
1032 '(lambda ()
1033 (let ((key "--no-desktop"))
4b217d46
LH
1034 (when (member key command-line-args)
1035 (setq command-line-args (delete key command-line-args))
1036 (setq desktop-save-mode nil)))
1037 (when desktop-save-mode (desktop-read))))
813dbb2d 1038
ab1d55ea 1039(provide 'desktop)
6343ed61 1040
ab5796a9 1041;;; arch-tag: 221907c3-1771-4fd3-9c2e-c6f700c6ede9
80f9f3db 1042;;; desktop.el ends here