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