* lisp/desktop.el (desktop-save): Default to saving in .emacs.d,
[bpt/emacs.git] / lisp / desktop.el
1 ;;; desktop.el --- save partial status of Emacs when killed -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 1993-1995, 1997, 2000-2013 Free Software Foundation, Inc.
4
5 ;; Author: Morten Welinder <terra@diku.dk>
6 ;; Keywords: convenience
7 ;; Favorite-brand-of-beer: None, I hate beer.
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software: you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation, either version 3 of the License, or
14 ;; (at your option) any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24 ;;; Commentary:
25
26 ;; Save the Desktop, i.e.,
27 ;; - some global variables
28 ;; - the list of buffers with associated files. For each buffer also
29 ;; - the major mode
30 ;; - the default directory
31 ;; - the point
32 ;; - the mark & mark-active
33 ;; - buffer-read-only
34 ;; - some local variables
35 ;; - frame and window configuration
36
37 ;; To use this, use customize to turn on desktop-save-mode or add the
38 ;; following line somewhere in your init file:
39 ;;
40 ;; (desktop-save-mode 1)
41 ;;
42 ;; For further usage information, look at the section
43 ;; (info "(emacs)Saving Emacs Sessions") in the GNU Emacs Manual.
44
45 ;; When the desktop module is loaded, the function `desktop-kill' is
46 ;; added to the `kill-emacs-hook'. This function is responsible for
47 ;; saving the desktop when Emacs is killed. Furthermore an anonymous
48 ;; function is added to the `after-init-hook'. This function is
49 ;; responsible for loading the desktop when Emacs is started.
50
51 ;; Special handling.
52 ;; -----------------
53 ;; Variables `desktop-buffer-mode-handlers' and `desktop-minor-mode-handlers'
54 ;; are supplied to handle special major and minor modes respectively.
55 ;; `desktop-buffer-mode-handlers' is an alist of major mode specific functions
56 ;; to restore a desktop buffer. Elements must have the form
57 ;;
58 ;; (MAJOR-MODE . RESTORE-BUFFER-FUNCTION).
59 ;;
60 ;; Functions listed are called by `desktop-create-buffer' when `desktop-read'
61 ;; evaluates the desktop file. Buffers with a major mode not specified here,
62 ;; are restored by the default handler `desktop-restore-file-buffer'.
63 ;; `desktop-minor-mode-handlers' is an alist of functions to restore
64 ;; non-standard minor modes. Elements must have the form
65 ;;
66 ;; (MINOR-MODE . RESTORE-FUNCTION).
67 ;;
68 ;; Functions are called by `desktop-create-buffer' to restore minor modes.
69 ;; Minor modes not specified here, are restored by the standard minor mode
70 ;; function. If you write a module that defines a major or minor mode that
71 ;; needs a special handler, then place code like
72
73 ;; (defun foo-restore-desktop-buffer
74 ;; ...
75 ;; (add-to-list 'desktop-buffer-mode-handlers
76 ;; '(foo-mode . foo-restore-desktop-buffer))
77
78 ;; or
79
80 ;; (defun bar-desktop-restore
81 ;; ...
82 ;; (add-to-list 'desktop-minor-mode-handlers
83 ;; '(bar-mode . bar-desktop-restore))
84
85 ;; in the module itself, and make sure that the mode function is
86 ;; autoloaded. See the docstrings of `desktop-buffer-mode-handlers' and
87 ;; `desktop-minor-mode-handlers' for more info.
88
89 ;; Minor modes.
90 ;; ------------
91 ;; Conventional minor modes (see node "Minor Mode Conventions" in the elisp
92 ;; manual) are handled in the following way:
93 ;; When `desktop-save' saves the state of a buffer to the desktop file, it
94 ;; saves as `desktop-minor-modes' the list of names of those variables in
95 ;; `minor-mode-alist' that have a non-nil value.
96 ;; When `desktop-create' restores the buffer, each of the symbols in
97 ;; `desktop-minor-modes' is called as function with parameter 1.
98 ;; The variables `desktop-minor-mode-table' and `desktop-minor-mode-handlers'
99 ;; are used to handle non-conventional minor modes. `desktop-save' uses
100 ;; `desktop-minor-mode-table' to map minor mode variables to minor mode
101 ;; functions before writing `desktop-minor-modes'. If a minor mode has a
102 ;; variable name that is different form its function name, an entry
103
104 ;; (NAME RESTORE-FUNCTION)
105
106 ;; should be added to `desktop-minor-mode-table'. If a minor mode should not
107 ;; be restored, RESTORE-FUNCTION should be set to nil. `desktop-create' uses
108 ;; `desktop-minor-mode-handlers' to lookup minor modes that needs a restore
109 ;; function different from the usual minor mode function.
110 ;; ---------------------------------------------------------------------------
111
112 ;; By the way: don't use desktop.el to customize Emacs -- the file .emacs
113 ;; in your home directory is used for that. Saving global default values
114 ;; for buffers is an example of misuse.
115
116 ;; PLEASE NOTE: The kill ring can be saved as specified by the variable
117 ;; `desktop-globals-to-save' (by default it isn't). This may result in saving
118 ;; things you did not mean to keep. Use M-x desktop-clear RET.
119
120 ;; Thanks to hetrick@phys.uva.nl (Jim Hetrick) for useful ideas.
121 ;; avk@rtsg.mot.com (Andrew V. Klein) for a dired tip.
122 ;; chris@tecc.co.uk (Chris Boucher) for a mark tip.
123 ;; f89-kam@nada.kth.se (Klas Mellbourn) for a mh-e tip.
124 ;; kifer@sbkifer.cs.sunysb.edu (M. Kifer) for a bug hunt.
125 ;; treese@lcs.mit.edu (Win Treese) for ange-ftp tips.
126 ;; pot@cnuce.cnr.it (Francesco Potorti`) for misc. tips.
127 ;; ---------------------------------------------------------------------------
128 ;; TODO:
129 ;;
130 ;; Recognize more minor modes.
131 ;; Save mark rings.
132
133 ;;; Code:
134
135 (require 'cl-lib)
136 (require 'frameset)
137
138 (defvar desktop-file-version "206"
139 "Version number of desktop file format.
140 Written into the desktop file and used at desktop read to provide
141 backward compatibility.")
142
143 ;; ----------------------------------------------------------------------------
144 ;; USER OPTIONS -- settings you might want to play with.
145 ;; ----------------------------------------------------------------------------
146
147 (defgroup desktop nil
148 "Save status of Emacs when you exit."
149 :group 'frames)
150
151 ;; Maintained for backward compatibility
152 (define-obsolete-variable-alias 'desktop-enable 'desktop-save-mode "22.1")
153 ;;;###autoload
154 (define-minor-mode desktop-save-mode
155 "Toggle desktop saving (Desktop Save mode).
156 With a prefix argument ARG, enable Desktop Save mode if ARG is
157 positive, and disable it otherwise. If called from Lisp, enable
158 the mode if ARG is omitted or nil.
159
160 If Desktop Save mode is enabled, the state of Emacs is saved from
161 one session to another. See variable `desktop-save' and function
162 `desktop-read' for details."
163 :global t
164 :group 'desktop)
165
166 (defun desktop-save-mode-off ()
167 "Disable `desktop-save-mode'. Provided for use in hooks."
168 (desktop-save-mode 0))
169
170 (defcustom desktop-save 'ask-if-new
171 "Specifies whether the desktop should be saved when it is killed.
172 A desktop is killed when the user changes desktop or quits Emacs.
173 Possible values are:
174 t -- always save.
175 ask -- always ask.
176 ask-if-new -- ask if no desktop file exists, otherwise just save.
177 ask-if-exists -- ask if desktop file exists, otherwise don't save.
178 if-exists -- save if desktop file exists, otherwise don't save.
179 nil -- never save.
180 The desktop is never saved when `desktop-save-mode' is nil.
181 The variables `desktop-dirname' and `desktop-base-file-name'
182 determine where the desktop is saved."
183 :type
184 '(choice
185 (const :tag "Always save" t)
186 (const :tag "Always ask" ask)
187 (const :tag "Ask if desktop file is new, else do save" ask-if-new)
188 (const :tag "Ask if desktop file exists, else don't save" ask-if-exists)
189 (const :tag "Save if desktop file exists, else don't" if-exists)
190 (const :tag "Never save" nil))
191 :group 'desktop
192 :version "22.1")
193
194 (defcustom desktop-auto-save-timeout nil
195 "Number of seconds between auto-saves of the desktop.
196 Zero or nil means disable timer-based auto-saving."
197 :type '(choice (const :tag "Off" nil)
198 (integer :tag "Seconds"))
199 :set (lambda (symbol value)
200 (set-default symbol value)
201 (ignore-errors (desktop-auto-save-set-timer)))
202 :group 'desktop
203 :version "24.4")
204
205 (defcustom desktop-load-locked-desktop 'ask
206 "Specifies whether the desktop should be loaded if locked.
207 Possible values are:
208 t -- load anyway.
209 nil -- don't load.
210 ask -- ask the user.
211 If the value is nil, or `ask' and the user chooses not to load the desktop,
212 the normal hook `desktop-not-loaded-hook' is run."
213 :type
214 '(choice
215 (const :tag "Load anyway" t)
216 (const :tag "Don't load" nil)
217 (const :tag "Ask the user" ask))
218 :group 'desktop
219 :version "22.2")
220
221 (define-obsolete-variable-alias 'desktop-basefilename
222 'desktop-base-file-name "22.1")
223
224 (defcustom desktop-base-file-name
225 (convert-standard-filename ".emacs.desktop")
226 "Name of file for Emacs desktop, excluding the directory part."
227 :type 'file
228 :group 'desktop)
229
230 (defcustom desktop-base-lock-name
231 (convert-standard-filename ".emacs.desktop.lock")
232 "Name of lock file for Emacs desktop, excluding the directory part."
233 :type 'file
234 :group 'desktop
235 :version "22.2")
236
237 (defcustom desktop-path (list user-emacs-directory "~")
238 "List of directories to search for the desktop file.
239 The base name of the file is specified in `desktop-base-file-name'."
240 :type '(repeat directory)
241 :group 'desktop
242 :version "23.2") ; user-emacs-directory added
243
244 (defcustom desktop-missing-file-warning nil
245 "If non-nil, offer to recreate the buffer of a deleted file.
246 Also pause for a moment to display message about errors signaled in
247 `desktop-buffer-mode-handlers'.
248
249 If nil, just print error messages in the message buffer."
250 :type 'boolean
251 :group 'desktop
252 :version "22.1")
253
254 (defcustom desktop-no-desktop-file-hook nil
255 "Normal hook run when `desktop-read' can't find a desktop file.
256 Run in the directory in which the desktop file was sought.
257 May be used to show a dired buffer."
258 :type 'hook
259 :group 'desktop
260 :version "22.1")
261
262 (defcustom desktop-not-loaded-hook nil
263 "Normal hook run when the user declines to re-use a desktop file.
264 Run in the directory in which the desktop file was found.
265 May be used to deal with accidental multiple Emacs jobs."
266 :type 'hook
267 :group 'desktop
268 :options '(desktop-save-mode-off save-buffers-kill-emacs)
269 :version "22.2")
270
271 (defcustom desktop-after-read-hook nil
272 "Normal hook run after a successful `desktop-read'.
273 May be used to show a buffer list."
274 :type 'hook
275 :group 'desktop
276 :options '(list-buffers)
277 :version "22.1")
278
279 (defcustom desktop-save-hook nil
280 "Normal hook run before the desktop is saved in a desktop file.
281 Run with the desktop buffer current with only the header present.
282 May be used to add to the desktop code or to truncate history lists,
283 for example."
284 :type 'hook
285 :group 'desktop)
286
287 (defcustom desktop-globals-to-save
288 '(desktop-missing-file-warning
289 tags-file-name
290 tags-table-list
291 search-ring
292 regexp-search-ring
293 register-alist
294 file-name-history)
295 "List of global variables saved by `desktop-save'.
296 An element may be variable name (a symbol) or a cons cell of the form
297 \(VAR . MAX-SIZE), which means to truncate VAR's value to at most
298 MAX-SIZE elements (if the value is a list) before saving the value.
299 Feature: Saving `kill-ring' implies saving `kill-ring-yank-pointer'."
300 :type '(repeat (restricted-sexp :match-alternatives (symbolp consp)))
301 :group 'desktop)
302
303 (defcustom desktop-globals-to-clear
304 '(kill-ring
305 kill-ring-yank-pointer
306 search-ring
307 search-ring-yank-pointer
308 regexp-search-ring
309 regexp-search-ring-yank-pointer)
310 "List of global variables that `desktop-clear' will clear.
311 An element may be variable name (a symbol) or a cons cell of the form
312 \(VAR . FORM). Symbols are set to nil and for cons cells VAR is set
313 to the value obtained by evaluating FORM."
314 :type '(repeat (restricted-sexp :match-alternatives (symbolp consp)))
315 :group 'desktop
316 :version "22.1")
317
318 (defcustom desktop-clear-preserve-buffers
319 '("\\*scratch\\*" "\\*Messages\\*" "\\*server\\*" "\\*tramp/.+\\*"
320 "\\*Warnings\\*")
321 "List of buffers that `desktop-clear' should not delete.
322 Each element is a regular expression. Buffers with a name matched by any of
323 these won't be deleted."
324 :version "23.3" ; added Warnings - bug#6336
325 :type '(repeat string)
326 :group 'desktop)
327
328 ;;;###autoload
329 (defcustom desktop-locals-to-save
330 '(desktop-locals-to-save ; Itself! Think it over.
331 truncate-lines
332 case-fold-search
333 case-replace
334 fill-column
335 overwrite-mode
336 change-log-default-name
337 line-number-mode
338 column-number-mode
339 size-indication-mode
340 buffer-file-coding-system
341 indent-tabs-mode
342 tab-width
343 indicate-buffer-boundaries
344 indicate-empty-lines
345 show-trailing-whitespace)
346 "List of local variables to save for each buffer.
347 The variables are saved only when they really are local. Conventional minor
348 modes are restored automatically; they should not be listed here."
349 :type '(repeat symbol)
350 :group 'desktop)
351
352 (defcustom desktop-buffers-not-to-save nil
353 "Regexp identifying buffers that are to be excluded from saving."
354 :type '(choice (const :tag "None" nil)
355 regexp)
356 :version "23.2" ; set to nil
357 :group 'desktop)
358
359 ;; Skip tramp and ange-ftp files
360 (defcustom desktop-files-not-to-save
361 "\\(^/[^/:]*:\\|(ftp)$\\)"
362 "Regexp identifying files whose buffers are to be excluded from saving."
363 :type '(choice (const :tag "None" nil)
364 regexp)
365 :group 'desktop)
366
367 ;; We skip TAGS files to save time (tags-file-name is saved instead).
368 (defcustom desktop-modes-not-to-save
369 '(tags-table-mode)
370 "List of major modes whose buffers should not be saved."
371 :type '(repeat symbol)
372 :group 'desktop)
373
374 (defcustom desktop-restore-frames t
375 "When non-nil, save frames to desktop file."
376 :type 'boolean
377 :group 'desktop
378 :version "24.4")
379
380 (defcustom desktop-restore-in-current-display nil
381 "If t, frames are restored in the current display.
382 If nil, frames are restored, if possible, in their original displays.
383 If `delete', frames on other displays are deleted instead of restored."
384 :type '(choice (const :tag "Restore in current display" t)
385 (const :tag "Restore in original display" nil)
386 (const :tag "Delete frames in other displays" 'delete))
387 :group 'desktop
388 :version "24.4")
389
390 (defcustom desktop-restore-forces-onscreen t
391 "If t, offscreen frames are restored onscreen instead.
392 If `:all', frames that are partially offscreen are also forced onscreen.
393 NOTE: Checking of frame boundaries is only approximate and can fail
394 to reliably detect frames whose onscreen/offscreen state depends on a
395 few pixels, especially near the right / bottom borders of the screen."
396 :type '(choice (const :tag "Only fully offscreen frames" t)
397 (const :tag "Also partially offscreen frames" :all)
398 (const :tag "Do not force frames onscreen" nil))
399 :group 'desktop
400 :version "24.4")
401
402 (defcustom desktop-restore-reuses-frames t
403 "If t, restoring frames reuses existing frames.
404 If nil, existing frames are deleted.
405 If `:keep', existing frames are kept and not reused."
406 :type '(choice (const :tag "Reuse existing frames" t)
407 (const :tag "Delete existing frames" nil)
408 (const :tag "Keep existing frames" :keep))
409 :group 'desktop
410 :version "24.4")
411
412 (defcustom desktop-file-name-format 'absolute
413 "Format in which desktop file names should be saved.
414 Possible values are:
415 absolute -- Absolute file name.
416 tilde -- Relative to ~.
417 local -- Relative to directory of desktop file."
418 :type '(choice (const absolute) (const tilde) (const local))
419 :group 'desktop
420 :version "22.1")
421
422 (defcustom desktop-restore-eager t
423 "Number of buffers to restore immediately.
424 Remaining buffers are restored lazily (when Emacs is idle).
425 If value is t, all buffers are restored immediately."
426 :type '(choice (const t) integer)
427 :group 'desktop
428 :version "22.1")
429
430 (defcustom desktop-lazy-verbose t
431 "Verbose reporting of lazily created buffers."
432 :type 'boolean
433 :group 'desktop
434 :version "22.1")
435
436 (defcustom desktop-lazy-idle-delay 5
437 "Idle delay before starting to create buffers.
438 See `desktop-restore-eager'."
439 :type 'integer
440 :group 'desktop
441 :version "22.1")
442
443 ;;;###autoload
444 (defvar-local desktop-save-buffer nil
445 "When non-nil, save buffer status in desktop file.
446
447 If the value is a function, it is called by `desktop-save' with argument
448 DESKTOP-DIRNAME to obtain auxiliary information to save in the desktop
449 file along with the state of the buffer for which it was called.
450
451 When file names are returned, they should be formatted using the call
452 \"(desktop-file-name FILE-NAME DESKTOP-DIRNAME)\".
453
454 Later, when `desktop-read' evaluates the desktop file, auxiliary information
455 is passed as the argument DESKTOP-BUFFER-MISC to functions in
456 `desktop-buffer-mode-handlers'.")
457 (make-obsolete-variable 'desktop-buffer-modes-to-save
458 'desktop-save-buffer "22.1")
459 (make-obsolete-variable 'desktop-buffer-misc-functions
460 'desktop-save-buffer "22.1")
461
462 ;;;###autoload
463 (defvar desktop-buffer-mode-handlers nil
464 "Alist of major mode specific functions to restore a desktop buffer.
465 Functions listed are called by `desktop-create-buffer' when `desktop-read'
466 evaluates the desktop file. List elements must have the form
467
468 (MAJOR-MODE . RESTORE-BUFFER-FUNCTION).
469
470 Buffers with a major mode not specified here, are restored by the default
471 handler `desktop-restore-file-buffer'.
472
473 Handlers are called with argument list
474
475 (DESKTOP-BUFFER-FILE-NAME DESKTOP-BUFFER-NAME DESKTOP-BUFFER-MISC)
476
477 Furthermore, they may use the following variables:
478
479 desktop-file-version
480 desktop-buffer-major-mode
481 desktop-buffer-minor-modes
482 desktop-buffer-point
483 desktop-buffer-mark
484 desktop-buffer-read-only
485 desktop-buffer-locals
486
487 If a handler returns a buffer, then the saved mode settings
488 and variable values for that buffer are copied into it.
489
490 Modules that define a major mode that needs a special handler should contain
491 code like
492
493 (defun foo-restore-desktop-buffer
494 ...
495 (add-to-list 'desktop-buffer-mode-handlers
496 '(foo-mode . foo-restore-desktop-buffer))
497
498 Furthermore the major mode function must be autoloaded.")
499
500 ;;;###autoload
501 (put 'desktop-buffer-mode-handlers 'risky-local-variable t)
502 (make-obsolete-variable 'desktop-buffer-handlers
503 'desktop-buffer-mode-handlers "22.1")
504
505 (defcustom desktop-minor-mode-table
506 '((auto-fill-function auto-fill-mode)
507 (vc-mode nil)
508 (vc-dired-mode nil)
509 (erc-track-minor-mode nil)
510 (savehist-mode nil))
511 "Table mapping minor mode variables to minor mode functions.
512 Each entry has the form (NAME RESTORE-FUNCTION).
513 NAME is the name of the buffer-local variable indicating that the minor
514 mode is active. RESTORE-FUNCTION is the function to activate the minor mode.
515 RESTORE-FUNCTION nil means don't try to restore the minor mode.
516 Only minor modes for which the name of the buffer-local variable
517 and the name of the minor mode function are different have to be added to
518 this table. See also `desktop-minor-mode-handlers'."
519 :type 'sexp
520 :group 'desktop)
521
522 ;;;###autoload
523 (defvar desktop-minor-mode-handlers nil
524 "Alist of functions to restore non-standard minor modes.
525 Functions are called by `desktop-create-buffer' to restore minor modes.
526 List elements must have the form
527
528 (MINOR-MODE . RESTORE-FUNCTION).
529
530 Minor modes not specified here, are restored by the standard minor mode
531 function.
532
533 Handlers are called with argument list
534
535 (DESKTOP-BUFFER-LOCALS)
536
537 Furthermore, they may use the following variables:
538
539 desktop-file-version
540 desktop-buffer-file-name
541 desktop-buffer-name
542 desktop-buffer-major-mode
543 desktop-buffer-minor-modes
544 desktop-buffer-point
545 desktop-buffer-mark
546 desktop-buffer-read-only
547 desktop-buffer-misc
548
549 When a handler is called, the buffer has been created and the major mode has
550 been set, but local variables listed in desktop-buffer-locals has not yet been
551 created and set.
552
553 Modules that define a minor mode that needs a special handler should contain
554 code like
555
556 (defun foo-desktop-restore
557 ...
558 (add-to-list 'desktop-minor-mode-handlers
559 '(foo-mode . foo-desktop-restore))
560
561 Furthermore the minor mode function must be autoloaded.
562
563 See also `desktop-minor-mode-table'.")
564
565 ;;;###autoload
566 (put 'desktop-minor-mode-handlers 'risky-local-variable t)
567
568 ;; ----------------------------------------------------------------------------
569 (defvar desktop-dirname nil
570 "The directory in which the desktop file should be saved.")
571
572 (defun desktop-full-file-name (&optional dirname)
573 "Return the full name of the desktop file in DIRNAME.
574 DIRNAME omitted or nil means use `desktop-dirname'."
575 (expand-file-name desktop-base-file-name (or dirname desktop-dirname)))
576
577 (defun desktop-full-lock-name (&optional dirname)
578 "Return the full name of the desktop lock file in DIRNAME.
579 DIRNAME omitted or nil means use `desktop-dirname'."
580 (expand-file-name desktop-base-lock-name (or dirname desktop-dirname)))
581
582 (defconst desktop-header
583 ";; --------------------------------------------------------------------------
584 ;; Desktop File for Emacs
585 ;; --------------------------------------------------------------------------
586 " "*Header to place in Desktop file.")
587
588 (defvar desktop-delay-hook nil
589 "Hooks run after all buffers are loaded; intended for internal use.")
590
591 (defvar desktop-file-checksum nil
592 "Checksum of the last auto-saved contents of the desktop file.
593 Used to avoid writing contents unchanged between auto-saves.")
594
595 (defvar desktop-saved-frameset nil
596 "Saved state of all frames.
597 Only valid during frame saving & restoring; intended for internal use.")
598
599 ;; ----------------------------------------------------------------------------
600 ;; Desktop file conflict detection
601 (defvar desktop-file-modtime nil
602 "When the desktop file was last modified to the knowledge of this Emacs.
603 Used to detect desktop file conflicts.")
604
605 (defun desktop-owner (&optional dirname)
606 "Return the PID of the Emacs process that owns the desktop file in DIRNAME.
607 Return nil if no desktop file found or no Emacs process is using it.
608 DIRNAME omitted or nil means use `desktop-dirname'."
609 (let (owner
610 (file (desktop-full-lock-name dirname)))
611 (and (file-exists-p file)
612 (ignore-errors
613 (with-temp-buffer
614 (insert-file-contents-literally file)
615 (goto-char (point-min))
616 (setq owner (read (current-buffer)))
617 (integerp owner)))
618 owner)))
619
620 (defun desktop-claim-lock (&optional dirname)
621 "Record this Emacs process as the owner of the desktop file in DIRNAME.
622 DIRNAME omitted or nil means use `desktop-dirname'."
623 (write-region (number-to-string (emacs-pid)) nil
624 (desktop-full-lock-name dirname)))
625
626 (defun desktop-release-lock (&optional dirname)
627 "Remove the lock file for the desktop in DIRNAME.
628 DIRNAME omitted or nil means use `desktop-dirname'."
629 (let ((file (desktop-full-lock-name dirname)))
630 (when (file-exists-p file) (delete-file file))))
631
632 ;; ----------------------------------------------------------------------------
633 (defun desktop-truncate (list n)
634 "Truncate LIST to at most N elements destructively."
635 (let ((here (nthcdr (1- n) list)))
636 (when (consp here)
637 (setcdr here nil))))
638
639 ;; ----------------------------------------------------------------------------
640 ;;;###autoload
641 (defun desktop-clear ()
642 "Empty the Desktop.
643 This kills all buffers except for internal ones and those with names matched by
644 a regular expression in the list `desktop-clear-preserve-buffers'.
645 Furthermore, it clears the variables listed in `desktop-globals-to-clear'.
646 When called interactively and `desktop-restore-frames' is non-nil, it also
647 deletes all frames except the selected one (and its minibuffer frame,
648 if different)."
649 (interactive)
650 (desktop-lazy-abort)
651 (dolist (var desktop-globals-to-clear)
652 (if (symbolp var)
653 (eval `(setq-default ,var nil))
654 (eval `(setq-default ,(car var) ,(cdr var)))))
655 (let ((preserve-regexp (concat "^\\("
656 (mapconcat (lambda (regexp)
657 (concat "\\(" regexp "\\)"))
658 desktop-clear-preserve-buffers
659 "\\|")
660 "\\)$")))
661 (dolist (buffer (buffer-list))
662 (let ((bufname (buffer-name buffer)))
663 (unless (or (eq (aref bufname 0) ?\s) ;; Don't kill internal buffers
664 (string-match-p preserve-regexp bufname))
665 (kill-buffer buffer)))))
666 (delete-other-windows)
667 (when (and desktop-restore-frames
668 ;; Non-interactive calls to desktop-clear happen before desktop-read
669 ;; which already takes care of frame restoration and deletion.
670 (called-interactively-p 'any))
671 (let* ((this (selected-frame))
672 (mini (window-frame (minibuffer-window this)))) ; in case they differ
673 (dolist (frame (sort (frame-list) #'frameset-minibufferless-first-p))
674 (condition-case err
675 (unless (or (eq frame this)
676 (eq frame mini)
677 (frame-parameter frame 'desktop-dont-clear))
678 (delete-frame frame))
679 (error
680 (delay-warning 'desktop (error-message-string err))))))))
681
682 ;; ----------------------------------------------------------------------------
683 (unless noninteractive
684 (add-hook 'kill-emacs-hook 'desktop-kill))
685
686 (defun desktop-kill ()
687 "If `desktop-save-mode' is non-nil, do what `desktop-save' says to do.
688 If the desktop should be saved and `desktop-dirname'
689 is nil, ask the user where to save the desktop."
690 (when (and desktop-save-mode
691 (let ((exists (file-exists-p (desktop-full-file-name))))
692 (or (eq desktop-save t)
693 (and exists (eq desktop-save 'if-exists))
694 ;; If it exists, but we aren't using it, we are going
695 ;; to ask for a new directory below.
696 (and exists desktop-dirname (eq desktop-save 'ask-if-new))
697 (and
698 (or (memq desktop-save '(ask ask-if-new))
699 (and exists (eq desktop-save 'ask-if-exists)))
700 (y-or-n-p "Save desktop? ")))))
701 (unless desktop-dirname
702 (setq desktop-dirname
703 (file-name-as-directory
704 (expand-file-name
705 (read-directory-name "Directory for desktop file: " nil nil t)))))
706 (condition-case err
707 (desktop-save desktop-dirname t)
708 (file-error
709 (unless (yes-or-no-p "Error while saving the desktop. Ignore? ")
710 (signal (car err) (cdr err))))))
711 ;; If we own it, we don't anymore.
712 (when (eq (emacs-pid) (desktop-owner)) (desktop-release-lock)))
713
714 ;; ----------------------------------------------------------------------------
715 (defun desktop-list* (&rest args)
716 (and args (apply #'cl-list* args)))
717
718 ;; ----------------------------------------------------------------------------
719 (defun desktop-buffer-info (buffer)
720 (set-buffer buffer)
721 (list
722 ;; base name of the buffer; replaces the buffer name if managed by uniquify
723 (and (fboundp 'uniquify-buffer-base-name) (uniquify-buffer-base-name))
724 ;; basic information
725 (desktop-file-name (buffer-file-name) desktop-dirname)
726 (buffer-name)
727 major-mode
728 ;; minor modes
729 (let (ret)
730 (mapc
731 #'(lambda (minor-mode)
732 (and (boundp minor-mode)
733 (symbol-value minor-mode)
734 (let* ((special (assq minor-mode desktop-minor-mode-table))
735 (value (cond (special (cadr special))
736 ((functionp minor-mode) minor-mode))))
737 (when value (add-to-list 'ret value)))))
738 (mapcar #'car minor-mode-alist))
739 ret)
740 ;; point and mark, and read-only status
741 (point)
742 (list (mark t) mark-active)
743 buffer-read-only
744 ;; auxiliary information
745 (when (functionp desktop-save-buffer)
746 (funcall desktop-save-buffer desktop-dirname))
747 ;; local variables
748 (let ((loclist (buffer-local-variables))
749 (ll nil))
750 (dolist (local desktop-locals-to-save)
751 (let ((here (assq local loclist)))
752 (cond (here
753 (push here ll))
754 ((member local loclist)
755 (push local ll)))))
756 ll)))
757
758 ;; ----------------------------------------------------------------------------
759 (defun desktop--v2s (value)
760 "Convert VALUE to a pair (QUOTE . SEXP); (eval SEXP) gives VALUE.
761 SEXP is an sexp that when evaluated yields VALUE.
762 QUOTE may be `may' (value may be quoted),
763 `must' (value must be quoted), or nil (value must not be quoted)."
764 (cond
765 ((or (numberp value) (null value) (eq t value) (keywordp value))
766 (cons 'may value))
767 ((stringp value)
768 (let ((copy (copy-sequence value)))
769 (set-text-properties 0 (length copy) nil copy)
770 ;; Get rid of text properties because we cannot read them.
771 (cons 'may copy)))
772 ((symbolp value)
773 (cons 'must value))
774 ((vectorp value)
775 (let* ((pass1 (mapcar #'desktop--v2s value))
776 (special (assq nil pass1)))
777 (if special
778 (cons nil `(vector
779 ,@(mapcar (lambda (el)
780 (if (eq (car el) 'must)
781 `',(cdr el) (cdr el)))
782 pass1)))
783 (cons 'may `[,@(mapcar #'cdr pass1)]))))
784 ((consp value)
785 (let ((p value)
786 newlist
787 use-list*)
788 (while (consp p)
789 (let ((q.sexp (desktop--v2s (car p))))
790 (push q.sexp newlist))
791 (setq p (cdr p)))
792 (when p
793 (let ((last (desktop--v2s p)))
794 (setq use-list* t)
795 (push last newlist)))
796 (if (assq nil newlist)
797 (cons nil
798 `(,(if use-list* 'desktop-list* 'list)
799 ,@(mapcar (lambda (el)
800 (if (eq (car el) 'must)
801 `',(cdr el) (cdr el)))
802 (nreverse newlist))))
803 (cons 'must
804 `(,@(mapcar #'cdr
805 (nreverse (if use-list* (cdr newlist) newlist)))
806 ,@(if use-list* (cdar newlist)))))))
807 ((subrp value)
808 (cons nil `(symbol-function
809 ',(intern-soft (substring (prin1-to-string value) 7 -1)))))
810 ((markerp value)
811 (let ((pos (marker-position value))
812 (buf (buffer-name (marker-buffer value))))
813 (cons nil
814 `(let ((mk (make-marker)))
815 (add-hook 'desktop-delay-hook
816 `(lambda ()
817 (set-marker ,mk ,,pos (get-buffer ,,buf))))
818 mk))))
819 (t ; Save as text.
820 (cons 'may "Unprintable entity"))))
821
822 ;; ----------------------------------------------------------------------------
823 (defun desktop-value-to-string (value)
824 "Convert VALUE to a string that when read evaluates to the same value.
825 Not all types of values are supported."
826 (let* ((print-escape-newlines t)
827 (float-output-format nil)
828 (quote.sexp (desktop--v2s value))
829 (quote (car quote.sexp))
830 (txt
831 (let ((print-quoted t))
832 (prin1-to-string (cdr quote.sexp)))))
833 (if (eq quote 'must)
834 (concat "'" txt)
835 txt)))
836
837 ;; ----------------------------------------------------------------------------
838 (defun desktop-outvar (varspec)
839 "Output a setq statement for variable VAR to the desktop file.
840 The argument VARSPEC may be the variable name VAR (a symbol),
841 or a cons cell of the form (VAR . MAX-SIZE),
842 which means to truncate VAR's value to at most MAX-SIZE elements
843 \(if the value is a list) before saving the value."
844 (let (var size)
845 (if (consp varspec)
846 (setq var (car varspec) size (cdr varspec))
847 (setq var varspec))
848 (when (boundp var)
849 (when (and (integerp size)
850 (> size 0)
851 (listp (eval var)))
852 (desktop-truncate (eval var) size))
853 (insert "(setq "
854 (symbol-name var)
855 " "
856 (desktop-value-to-string (symbol-value var))
857 ")\n"))))
858
859 ;; ----------------------------------------------------------------------------
860 (defun desktop-save-buffer-p (filename bufname mode &rest _dummy)
861 "Return t if buffer should have its state saved in the desktop file.
862 FILENAME is the visited file name, BUFNAME is the buffer name, and
863 MODE is the major mode.
864 \n\(fn FILENAME BUFNAME MODE)"
865 (let ((case-fold-search nil)
866 dired-skip)
867 (and (not (and (stringp desktop-buffers-not-to-save)
868 (not filename)
869 (string-match-p desktop-buffers-not-to-save bufname)))
870 (not (memq mode desktop-modes-not-to-save))
871 ;; FIXME this is broken if desktop-files-not-to-save is nil.
872 (or (and filename
873 (stringp desktop-files-not-to-save)
874 (not (string-match-p desktop-files-not-to-save filename)))
875 (and (memq mode '(dired-mode vc-dir-mode))
876 (with-current-buffer bufname
877 (not (setq dired-skip
878 (string-match-p desktop-files-not-to-save
879 default-directory)))))
880 (and (null filename)
881 (null dired-skip) ; bug#5755
882 (with-current-buffer bufname desktop-save-buffer))))))
883
884 ;; ----------------------------------------------------------------------------
885 (defun desktop-file-name (filename dirname)
886 "Convert FILENAME to format specified in `desktop-file-name-format'.
887 DIRNAME must be the directory in which the desktop file will be saved."
888 (cond
889 ((not filename) nil)
890 ((eq desktop-file-name-format 'tilde)
891 (let ((relative-name (file-relative-name (expand-file-name filename) "~")))
892 (cond
893 ((file-name-absolute-p relative-name) relative-name)
894 ((string= "./" relative-name) "~/")
895 ((string= "." relative-name) "~")
896 (t (concat "~/" relative-name)))))
897 ((eq desktop-file-name-format 'local) (file-relative-name filename dirname))
898 (t (expand-file-name filename))))
899
900
901 ;; ----------------------------------------------------------------------------
902 (defun desktop--check-dont-save (frame)
903 (not (frame-parameter frame 'desktop-dont-save)))
904
905 (defconst desktop--app-id `(desktop . ,desktop-file-version))
906
907 (defun desktop-save-frameset ()
908 "Save the state of existing frames in `desktop-saved-frameset'.
909 Frames with a non-nil `desktop-dont-save' parameter are not saved."
910 (setq desktop-saved-frameset
911 (and desktop-restore-frames
912 (frameset-save nil
913 :app desktop--app-id
914 :name (concat user-login-name "@" system-name)
915 :predicate #'desktop--check-dont-save))))
916
917 ;;;###autoload
918 (defun desktop-save (dirname &optional release auto-save)
919 "Save the desktop in a desktop file.
920 Parameter DIRNAME specifies where to save the desktop file.
921 Optional parameter RELEASE says whether we're done with this desktop.
922 If AUTO-SAVE is non-nil, compare the saved contents to the one last saved,
923 and don't save the buffer if they are the same."
924 (interactive (list
925 ;; Or should we just use (car desktop-path)?
926 (let ((default (if (member "." desktop-path)
927 default-directory
928 user-emacs-directory)))
929 (read-directory-name "Directory to save desktop file in: "
930 default default t))))
931 (setq desktop-dirname (file-name-as-directory (expand-file-name dirname)))
932 (save-excursion
933 (let ((eager desktop-restore-eager)
934 (new-modtime (nth 5 (file-attributes (desktop-full-file-name)))))
935 (when
936 (or (not new-modtime) ; nothing to overwrite
937 (equal desktop-file-modtime new-modtime)
938 (yes-or-no-p (if desktop-file-modtime
939 (if (> (float-time new-modtime) (float-time desktop-file-modtime))
940 "Desktop file is more recent than the one loaded. Save anyway? "
941 "Desktop file isn't the one loaded. Overwrite it? ")
942 "Current desktop was not loaded from a file. Overwrite this desktop file? "))
943 (unless release (error "Desktop file conflict")))
944
945 ;; If we're done with it, release the lock.
946 ;; Otherwise, claim it if it's unclaimed or if we created it.
947 (if release
948 (desktop-release-lock)
949 (unless (and new-modtime (desktop-owner)) (desktop-claim-lock)))
950
951 (with-temp-buffer
952 (insert
953 ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
954 desktop-header
955 ";; Created " (current-time-string) "\n"
956 ";; Desktop file format version " desktop-file-version "\n"
957 ";; Emacs version " emacs-version "\n")
958 (save-excursion (run-hooks 'desktop-save-hook))
959 (goto-char (point-max))
960 (insert "\n;; Global section:\n")
961 ;; Called here because we save the window/frame state as a global
962 ;; variable for compatibility with previous Emacsen.
963 (desktop-save-frameset)
964 (unless (memq 'desktop-saved-frameset desktop-globals-to-save)
965 (desktop-outvar 'desktop-saved-frameset))
966 (mapc (function desktop-outvar) desktop-globals-to-save)
967 (setq desktop-saved-frameset nil) ; after saving desktop-globals-to-save
968 (when (memq 'kill-ring desktop-globals-to-save)
969 (insert
970 "(setq kill-ring-yank-pointer (nthcdr "
971 (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
972 " kill-ring))\n"))
973
974 (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
975 (dolist (l (mapcar 'desktop-buffer-info (buffer-list)))
976 (let ((base (pop l)))
977 (when (apply 'desktop-save-buffer-p l)
978 (insert "("
979 (if (or (not (integerp eager))
980 (if (zerop eager)
981 nil
982 (setq eager (1- eager))))
983 "desktop-create-buffer"
984 "desktop-append-buffer-args")
985 " "
986 desktop-file-version)
987 ;; If there's a non-empty base name, we save it instead of the buffer name
988 (when (and base (not (string= base "")))
989 (setcar (nthcdr 1 l) base))
990 (dolist (e l)
991 (insert "\n " (desktop-value-to-string e)))
992 (insert ")\n\n"))))
993
994 (setq default-directory desktop-dirname)
995 ;; If auto-saving, avoid writing if nothing has changed since the last write.
996 ;; Don't check 300 characters of the header that contains the timestamp.
997 (let ((checksum (and auto-save (md5 (current-buffer)
998 (+ (point-min) 300) (point-max)
999 'emacs-mule))))
1000 (unless (and auto-save (equal checksum desktop-file-checksum))
1001 (let ((coding-system-for-write 'emacs-mule))
1002 (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage))
1003 (setq desktop-file-checksum checksum)
1004 ;; We remember when it was modified (which is presumably just now).
1005 (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))))))))))
1006
1007 ;; ----------------------------------------------------------------------------
1008 ;;;###autoload
1009 (defun desktop-remove ()
1010 "Delete desktop file in `desktop-dirname'.
1011 This function also sets `desktop-dirname' to nil."
1012 (interactive)
1013 (when desktop-dirname
1014 (let ((filename (desktop-full-file-name)))
1015 (setq desktop-dirname nil)
1016 (when (file-exists-p filename)
1017 (delete-file filename)))))
1018
1019 (defvar desktop-buffer-args-list nil
1020 "List of args for `desktop-create-buffer'.")
1021
1022 (defvar desktop-lazy-timer nil)
1023
1024 ;; ----------------------------------------------------------------------------
1025 (defun desktop-restoring-frameset-p ()
1026 "True if calling `desktop-restore-frameset' will actually restore it."
1027 (and desktop-restore-frames desktop-saved-frameset t))
1028
1029 (defun desktop-restore-frameset ()
1030 "Restore the state of a set of frames.
1031 This function depends on the value of `desktop-saved-frameset'
1032 being set (usually, by reading it from the desktop)."
1033 (when (desktop-restoring-frameset-p)
1034 (frameset-restore desktop-saved-frameset
1035 :reuse-frames desktop-restore-reuses-frames
1036 :force-display desktop-restore-in-current-display
1037 :force-onscreen desktop-restore-forces-onscreen)))
1038
1039 ;; Just to silence the byte compiler.
1040 ;; Dynamically bound in `desktop-read'.
1041 (defvar desktop-first-buffer)
1042 (defvar desktop-buffer-ok-count)
1043 (defvar desktop-buffer-fail-count)
1044
1045 ;;;###autoload
1046 (defun desktop-read (&optional dirname)
1047 "Read and process the desktop file in directory DIRNAME.
1048 Look for a desktop file in DIRNAME, or if DIRNAME is omitted, look in
1049 directories listed in `desktop-path'. If a desktop file is found, it
1050 is processed and `desktop-after-read-hook' is run. If no desktop file
1051 is found, clear the desktop and run `desktop-no-desktop-file-hook'.
1052 This function is a no-op when Emacs is running in batch mode.
1053 It returns t if a desktop file was loaded, nil otherwise."
1054 (interactive)
1055 (unless noninteractive
1056 (setq desktop-dirname
1057 (file-name-as-directory
1058 (expand-file-name
1059 (or
1060 ;; If DIRNAME is specified, use it.
1061 (and (< 0 (length dirname)) dirname)
1062 ;; Otherwise search desktop file in desktop-path.
1063 (let ((dirs desktop-path))
1064 (while (and dirs
1065 (not (file-exists-p
1066 (desktop-full-file-name (car dirs)))))
1067 (setq dirs (cdr dirs)))
1068 (and dirs (car dirs)))
1069 ;; If not found and `desktop-path' is non-nil, use its first element.
1070 (and desktop-path (car desktop-path))
1071 ;; Default: .emacs.d.
1072 user-emacs-directory))))
1073 (if (file-exists-p (desktop-full-file-name))
1074 ;; Desktop file found, but is it already in use?
1075 (let ((desktop-first-buffer nil)
1076 (desktop-buffer-ok-count 0)
1077 (desktop-buffer-fail-count 0)
1078 (owner (desktop-owner))
1079 ;; Avoid desktop saving during evaluation of desktop buffer.
1080 (desktop-save nil))
1081 (if (and owner
1082 (memq desktop-load-locked-desktop '(nil ask))
1083 (or (null desktop-load-locked-desktop)
1084 (daemonp)
1085 (not (y-or-n-p (format "Warning: desktop file appears to be in use by PID %s.\n\
1086 Using it may cause conflicts. Use it anyway? " owner)))))
1087 (let ((default-directory desktop-dirname))
1088 (setq desktop-dirname nil)
1089 (run-hooks 'desktop-not-loaded-hook)
1090 (unless desktop-dirname
1091 (message "Desktop file in use; not loaded.")))
1092 (desktop-lazy-abort)
1093 ;; Evaluate desktop buffer and remember when it was modified.
1094 (load (desktop-full-file-name) t t t)
1095 (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))
1096 ;; If it wasn't already, mark it as in-use, to bother other
1097 ;; desktop instances.
1098 (unless owner
1099 (condition-case nil
1100 (desktop-claim-lock)
1101 (file-error (message "Couldn't record use of desktop file")
1102 (sit-for 1))))
1103
1104 (unless (desktop-restoring-frameset-p)
1105 ;; `desktop-create-buffer' puts buffers at end of the buffer list.
1106 ;; We want buffers existing prior to evaluating the desktop (and
1107 ;; not reused) to be placed at the end of the buffer list, so we
1108 ;; move them here.
1109 (mapc 'bury-buffer
1110 (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
1111 (switch-to-buffer (car (buffer-list))))
1112 (run-hooks 'desktop-delay-hook)
1113 (setq desktop-delay-hook nil)
1114 (desktop-restore-frameset)
1115 (run-hooks 'desktop-after-read-hook)
1116 (message "Desktop: %s%d buffer%s restored%s%s."
1117 (if desktop-saved-frameset
1118 (let ((fn (length (frameset-states desktop-saved-frameset))))
1119 (format "%d frame%s, "
1120 fn (if (= fn 1) "" "s")))
1121 "")
1122 desktop-buffer-ok-count
1123 (if (= 1 desktop-buffer-ok-count) "" "s")
1124 (if (< 0 desktop-buffer-fail-count)
1125 (format ", %d failed to restore" desktop-buffer-fail-count)
1126 "")
1127 (if desktop-buffer-args-list
1128 (format ", %d to restore lazily"
1129 (length desktop-buffer-args-list))
1130 ""))
1131 (unless (desktop-restoring-frameset-p)
1132 ;; Bury the *Messages* buffer to not reshow it when burying
1133 ;; the buffer we switched to above.
1134 (when (buffer-live-p (get-buffer "*Messages*"))
1135 (bury-buffer "*Messages*"))
1136 ;; Clear all windows' previous and next buffers, these have
1137 ;; been corrupted by the `switch-to-buffer' calls in
1138 ;; `desktop-restore-file-buffer' (bug#11556). This is a
1139 ;; brute force fix and should be replaced by a more subtle
1140 ;; strategy eventually.
1141 (walk-window-tree (lambda (window)
1142 (set-window-prev-buffers window nil)
1143 (set-window-next-buffers window nil))))
1144 (setq desktop-saved-frameset nil)
1145 t))
1146 ;; No desktop file found.
1147 (desktop-clear)
1148 (let ((default-directory desktop-dirname))
1149 (run-hooks 'desktop-no-desktop-file-hook))
1150 (message "No desktop file.")
1151 nil)))
1152
1153 ;; ----------------------------------------------------------------------------
1154 ;; Maintained for backward compatibility
1155 ;;;###autoload
1156 (defun desktop-load-default ()
1157 "Load the `default' start-up library manually.
1158 Also inhibit further loading of it."
1159 (declare (obsolete desktop-save-mode "22.1"))
1160 (unless inhibit-default-init ; safety check
1161 (load "default" t t)
1162 (setq inhibit-default-init t)))
1163
1164 ;; ----------------------------------------------------------------------------
1165 ;;;###autoload
1166 (defun desktop-change-dir (dirname)
1167 "Change to desktop saved in DIRNAME.
1168 Kill the desktop as specified by variables `desktop-save-mode' and
1169 `desktop-save', then clear the desktop and load the desktop file in
1170 directory DIRNAME."
1171 (interactive "DChange to directory: ")
1172 (setq dirname (file-name-as-directory (expand-file-name dirname desktop-dirname)))
1173 (desktop-kill)
1174 (desktop-clear)
1175 (desktop-read dirname))
1176
1177 ;; ----------------------------------------------------------------------------
1178 ;;;###autoload
1179 (defun desktop-save-in-desktop-dir ()
1180 "Save the desktop in directory `desktop-dirname'."
1181 (interactive)
1182 (if desktop-dirname
1183 (desktop-save desktop-dirname)
1184 (call-interactively 'desktop-save))
1185 (message "Desktop saved in %s" (abbreviate-file-name desktop-dirname)))
1186
1187 ;; ----------------------------------------------------------------------------
1188 ;; Auto-Saving.
1189 (defvar desktop-auto-save-timer nil)
1190
1191 (defun desktop-auto-save ()
1192 "Save the desktop periodically.
1193 Called by the timer created in `desktop-auto-save-set-timer'."
1194 (when (and desktop-save-mode
1195 (integerp desktop-auto-save-timeout)
1196 (> desktop-auto-save-timeout 0)
1197 ;; Avoid desktop saving during lazy loading.
1198 (not desktop-lazy-timer)
1199 ;; Save only to own desktop file.
1200 (eq (emacs-pid) (desktop-owner))
1201 desktop-dirname)
1202 (desktop-save desktop-dirname nil t))
1203 (desktop-auto-save-set-timer))
1204
1205 (defun desktop-auto-save-set-timer ()
1206 "Reset the auto-save timer.
1207 Cancel any previous timer. When `desktop-auto-save-timeout' is a positive
1208 integer, start a new timer to call `desktop-auto-save' in that many seconds."
1209 (when desktop-auto-save-timer
1210 (cancel-timer desktop-auto-save-timer)
1211 (setq desktop-auto-save-timer nil))
1212 (when (and (integerp desktop-auto-save-timeout)
1213 (> desktop-auto-save-timeout 0))
1214 (setq desktop-auto-save-timer
1215 (run-with-timer desktop-auto-save-timeout nil
1216 'desktop-auto-save))))
1217
1218 ;; ----------------------------------------------------------------------------
1219 ;;;###autoload
1220 (defun desktop-revert ()
1221 "Revert to the last loaded desktop."
1222 (interactive)
1223 (unless desktop-dirname
1224 (error "Unknown desktop directory"))
1225 (unless (file-exists-p (desktop-full-file-name))
1226 (error "No desktop file found"))
1227 (desktop-clear)
1228 (desktop-read desktop-dirname))
1229
1230 (defvar desktop-buffer-major-mode)
1231 (defvar desktop-buffer-locals)
1232 (defvar auto-insert) ; from autoinsert.el
1233 ;; ----------------------------------------------------------------------------
1234 (defun desktop-restore-file-buffer (buffer-filename
1235 _buffer-name
1236 _buffer-misc)
1237 "Restore a file buffer."
1238 (when buffer-filename
1239 (if (or (file-exists-p buffer-filename)
1240 (let ((msg (format "Desktop: File \"%s\" no longer exists."
1241 buffer-filename)))
1242 (if desktop-missing-file-warning
1243 (y-or-n-p (concat msg " Re-create buffer? "))
1244 (message "%s" msg)
1245 nil)))
1246 (let* ((auto-insert nil) ; Disable auto insertion
1247 (coding-system-for-read
1248 (or coding-system-for-read
1249 (cdr (assq 'buffer-file-coding-system
1250 desktop-buffer-locals))))
1251 (buf (find-file-noselect buffer-filename)))
1252 (condition-case nil
1253 (switch-to-buffer buf)
1254 (error (pop-to-buffer buf)))
1255 (and (not (eq major-mode desktop-buffer-major-mode))
1256 (functionp desktop-buffer-major-mode)
1257 (funcall desktop-buffer-major-mode))
1258 buf)
1259 nil)))
1260
1261 (defun desktop-load-file (function)
1262 "Load the file where auto loaded FUNCTION is defined."
1263 (when (fboundp function)
1264 (autoload-do-load (symbol-function function) function)))
1265
1266 ;; ----------------------------------------------------------------------------
1267 ;; Create a buffer, load its file, set its mode, ...;
1268 ;; called from Desktop file only.
1269
1270 (defun desktop-create-buffer
1271 (file-version
1272 buffer-filename
1273 buffer-name
1274 buffer-majormode
1275 buffer-minormodes
1276 buffer-point
1277 buffer-mark
1278 buffer-readonly
1279 buffer-misc
1280 &optional
1281 buffer-locals)
1282
1283 (let ((desktop-file-version file-version)
1284 (desktop-buffer-file-name buffer-filename)
1285 (desktop-buffer-name buffer-name)
1286 (desktop-buffer-major-mode buffer-majormode)
1287 (desktop-buffer-minor-modes buffer-minormodes)
1288 (desktop-buffer-point buffer-point)
1289 (desktop-buffer-mark buffer-mark)
1290 (desktop-buffer-read-only buffer-readonly)
1291 (desktop-buffer-misc buffer-misc)
1292 (desktop-buffer-locals buffer-locals))
1293 ;; To make desktop files with relative file names possible, we cannot
1294 ;; allow `default-directory' to change. Therefore we save current buffer.
1295 (save-current-buffer
1296 ;; Give major mode module a chance to add a handler.
1297 (desktop-load-file desktop-buffer-major-mode)
1298 (let ((buffer-list (buffer-list))
1299 (result
1300 (condition-case-unless-debug err
1301 (funcall (or (cdr (assq desktop-buffer-major-mode
1302 desktop-buffer-mode-handlers))
1303 'desktop-restore-file-buffer)
1304 desktop-buffer-file-name
1305 desktop-buffer-name
1306 desktop-buffer-misc)
1307 (error
1308 (message "Desktop: Can't load buffer %s: %s"
1309 desktop-buffer-name
1310 (error-message-string err))
1311 (when desktop-missing-file-warning (sit-for 1))
1312 nil))))
1313 (if (bufferp result)
1314 (setq desktop-buffer-ok-count (1+ desktop-buffer-ok-count))
1315 (setq desktop-buffer-fail-count (1+ desktop-buffer-fail-count))
1316 (setq result nil))
1317 ;; Restore buffer list order with new buffer at end. Don't change
1318 ;; the order for old desktop files (old desktop module behavior).
1319 (unless (< desktop-file-version 206)
1320 (mapc 'bury-buffer buffer-list)
1321 (when result (bury-buffer result)))
1322 (when result
1323 (unless (or desktop-first-buffer (< desktop-file-version 206))
1324 (setq desktop-first-buffer result))
1325 (set-buffer result)
1326 (unless (equal (buffer-name) desktop-buffer-name)
1327 (rename-buffer desktop-buffer-name t))
1328 ;; minor modes
1329 (cond ((equal '(t) desktop-buffer-minor-modes) ; backwards compatible
1330 (auto-fill-mode 1))
1331 ((equal '(nil) desktop-buffer-minor-modes) ; backwards compatible
1332 (auto-fill-mode 0))
1333 (t
1334 (dolist (minor-mode desktop-buffer-minor-modes)
1335 ;; Give minor mode module a chance to add a handler.
1336 (desktop-load-file minor-mode)
1337 (let ((handler (cdr (assq minor-mode desktop-minor-mode-handlers))))
1338 (if handler
1339 (funcall handler desktop-buffer-locals)
1340 (when (functionp minor-mode)
1341 (funcall minor-mode 1)))))))
1342 ;; Even though point and mark are non-nil when written by
1343 ;; `desktop-save', they may be modified by handlers wanting to set
1344 ;; point or mark themselves.
1345 (when desktop-buffer-point
1346 (goto-char
1347 (condition-case err
1348 ;; Evaluate point. Thus point can be something like
1349 ;; '(search-forward ...
1350 (eval desktop-buffer-point)
1351 (error (message "%s" (error-message-string err)) 1))))
1352 (when desktop-buffer-mark
1353 (if (consp desktop-buffer-mark)
1354 (progn
1355 (set-mark (car desktop-buffer-mark))
1356 (setq mark-active (car (cdr desktop-buffer-mark))))
1357 (set-mark desktop-buffer-mark)))
1358 ;; Never override file system if the file really is read-only marked.
1359 (when desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
1360 (dolist (this desktop-buffer-locals)
1361 (if (consp this)
1362 ;; an entry of this form `(symbol . value)'
1363 (progn
1364 (make-local-variable (car this))
1365 (set (car this) (cdr this)))
1366 ;; an entry of the form `symbol'
1367 (make-local-variable this)
1368 (makunbound this))))))))
1369
1370 ;; ----------------------------------------------------------------------------
1371 ;; Backward compatibility -- update parameters to 205 standards.
1372 (defun desktop-buffer (buffer-filename buffer-name buffer-majormode
1373 mim pt mk ro tl fc cfs cr buffer-misc)
1374 (desktop-create-buffer 205 buffer-filename buffer-name
1375 buffer-majormode (cdr mim) pt mk ro
1376 buffer-misc
1377 (list (cons 'truncate-lines tl)
1378 (cons 'fill-column fc)
1379 (cons 'case-fold-search cfs)
1380 (cons 'case-replace cr)
1381 (cons 'overwrite-mode (car mim)))))
1382
1383 (defun desktop-append-buffer-args (&rest args)
1384 "Append ARGS at end of `desktop-buffer-args-list'.
1385 ARGS must be an argument list for `desktop-create-buffer'."
1386 (setq desktop-buffer-args-list (nconc desktop-buffer-args-list (list args)))
1387 (unless desktop-lazy-timer
1388 (setq desktop-lazy-timer
1389 (run-with-idle-timer desktop-lazy-idle-delay t 'desktop-idle-create-buffers))))
1390
1391 (defun desktop-lazy-create-buffer ()
1392 "Pop args from `desktop-buffer-args-list', create buffer and bury it."
1393 (when desktop-buffer-args-list
1394 (let* ((remaining (length desktop-buffer-args-list))
1395 (args (pop desktop-buffer-args-list))
1396 (buffer-name (nth 2 args))
1397 (msg (format "Desktop lazily opening %s (%s remaining)..."
1398 buffer-name remaining)))
1399 (when desktop-lazy-verbose
1400 (message "%s" msg))
1401 (let ((desktop-first-buffer nil)
1402 (desktop-buffer-ok-count 0)
1403 (desktop-buffer-fail-count 0))
1404 (apply 'desktop-create-buffer args)
1405 (run-hooks 'desktop-delay-hook)
1406 (setq desktop-delay-hook nil)
1407 (bury-buffer (get-buffer buffer-name))
1408 (when desktop-lazy-verbose
1409 (message "%s%s" msg (if (> desktop-buffer-ok-count 0) "done" "failed")))))))
1410
1411 (defun desktop-idle-create-buffers ()
1412 "Create buffers until the user does something, then stop.
1413 If there are no buffers left to create, kill the timer."
1414 (let ((repeat 1))
1415 (while (and repeat desktop-buffer-args-list)
1416 (save-window-excursion
1417 (desktop-lazy-create-buffer))
1418 (setq repeat (sit-for 0.2))
1419 (unless desktop-buffer-args-list
1420 (cancel-timer desktop-lazy-timer)
1421 (setq desktop-lazy-timer nil)
1422 (message "Lazy desktop load complete")
1423 (sit-for 3)
1424 (message "")))))
1425
1426 (defun desktop-lazy-complete ()
1427 "Run the desktop load to completion."
1428 (interactive)
1429 (let ((desktop-lazy-verbose t))
1430 (while desktop-buffer-args-list
1431 (save-window-excursion
1432 (desktop-lazy-create-buffer)))
1433 (message "Lazy desktop load complete")))
1434
1435 (defun desktop-lazy-abort ()
1436 "Abort lazy loading of the desktop."
1437 (interactive)
1438 (when desktop-lazy-timer
1439 (cancel-timer desktop-lazy-timer)
1440 (setq desktop-lazy-timer nil))
1441 (when desktop-buffer-args-list
1442 (setq desktop-buffer-args-list nil)
1443 (when (called-interactively-p 'interactive)
1444 (message "Lazy desktop load aborted"))))
1445
1446 ;; ----------------------------------------------------------------------------
1447 ;; When `desktop-save-mode' is non-nil and "--no-desktop" is not specified on the
1448 ;; command line, we do the rest of what it takes to use desktop, but do it
1449 ;; after finishing loading the init file.
1450 ;; We cannot use `command-switch-alist' to process "--no-desktop" because these
1451 ;; functions are processed after `after-init-hook'.
1452 (add-hook
1453 'after-init-hook
1454 (lambda ()
1455 (let ((key "--no-desktop"))
1456 (when (member key command-line-args)
1457 (setq command-line-args (delete key command-line-args))
1458 (setq desktop-save-mode nil)))
1459 (when desktop-save-mode
1460 (desktop-read)
1461 (desktop-auto-save-set-timer)
1462 (setq inhibit-startup-screen t))))
1463
1464 ;; So we can restore vc-dir buffers.
1465 (autoload 'vc-dir-mode "vc-dir" nil t)
1466
1467 (provide 'desktop)
1468
1469 ;;; desktop.el ends here