LCOV - code coverage report
Current view: top level - lisp - desktop.el (source / functions) Hit Total Coverage
Test: tramp-tests-after.info Lines: 9 589 1.5 %
Date: 2017-08-30 10:12:24 Functions: 2 47 4.3 %

          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

Generated by: LCOV version 1.12