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