X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/f6218c044cd430badeb1ad12142b305c228a5c76..aef88a00d364bbb208acff2d9b66b2a1eb6cf8f5:/lisp/desktop.el diff --git a/lisp/desktop.el b/lisp/desktop.el index 4328b8e383..a042828e19 100644 --- a/lisp/desktop.el +++ b/lisp/desktop.el @@ -1,7 +1,7 @@ ;;; desktop.el --- save partial status of Emacs when killed ;; Copyright (C) 1993, 1994, 1995, 1997, 2000, 2001, 2002, 2003, -;; 2004, 2005, 2006, 2007 Free Software Foundation, Inc. +;; 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. ;; Author: Morten Welinder ;; Keywords: convenience @@ -11,7 +11,7 @@ ;; GNU Emacs is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation; either version 2, or (at your option) +;; the Free Software Foundation; either version 3, or (at your option) ;; any later version. ;; GNU Emacs is distributed in the hope that it will be useful, @@ -45,9 +45,9 @@ ;; "Saving Emacs Sessions" in the GNU Emacs Manual. ;; When the desktop module is loaded, the function `desktop-kill' is -;; added to the `kill-emacs-hook'. This function is responsible for +;; added to the `kill-emacs-hook'. This function is responsible for ;; saving the desktop when Emacs is killed. Furthermore an anonymous -;; function is added to the `after-init-hook'. This function is +;; function is added to the `after-init-hook'. This function is ;; responsible for loading the desktop when Emacs is started. ;; Special handling. @@ -55,12 +55,12 @@ ;; Variables `desktop-buffer-mode-handlers' and `desktop-minor-mode-handlers' ;; are supplied to handle special major and minor modes respectively. ;; `desktop-buffer-mode-handlers' is an alist of major mode specific functions -;; to restore a desktop buffer. Elements must have the form +;; to restore a desktop buffer. Elements must have the form ;; ;; (MAJOR-MODE . RESTORE-BUFFER-FUNCTION). ;; ;; Functions listed are called by `desktop-create-buffer' when `desktop-read' -;; evaluates the desktop file. Buffers with a major mode not specified here, +;; evaluates the desktop file. Buffers with a major mode not specified here, ;; are restored by the default handler `desktop-restore-file-buffer'. ;; `desktop-minor-mode-handlers' is an alist of functions to restore ;; non-standard minor modes. Elements must have the form @@ -85,7 +85,7 @@ ;; '(bar-mode . bar-desktop-restore)) ;; in the module itself, and make shure that the mode function is -;; autoloaded. See the docstrings of `desktop-buffer-mode-handlers' and +;; autoloaded. See the docstrings of `desktop-buffer-mode-handlers' and ;; `desktop-minor-mode-handlers' for more info. ;; Minor modes. @@ -100,7 +100,7 @@ ;; The variables `desktop-minor-mode-table' and `desktop-minor-mode-handlers' ;; are used to handle non-conventional minor modes. `desktop-save' uses ;; `desktop-minor-mode-table' to map minor mode variables to minor mode -;; functions before writing `desktop-minor-modes'. If a minor mode has a +;; functions before writing `desktop-minor-modes'. If a minor mode has a ;; variable name that is different form its function name, an entry ;; (NAME RESTORE-FUNCTION) @@ -162,6 +162,10 @@ and function `desktop-read' for details." (define-obsolete-variable-alias 'desktop-enable 'desktop-save-mode "22.1") +(defun desktop-save-mode-off () + "Disable `desktop-save-mode'. Provided for use in hooks." + (desktop-save-mode 0)) + (defcustom desktop-save 'ask-if-new "*Specifies whether the desktop should be saved when it is killed. A desktop is killed when the user changes desktop or quits Emacs. @@ -186,6 +190,22 @@ determine where the desktop is saved." :group 'desktop :version "22.1") +(defcustom desktop-load-locked-desktop 'ask + "Specifies whether the desktop should be loaded if locked. +Possible values are: + t -- load anyway. + nil -- don't load. + ask -- ask the user. +If the value is nil, or `ask' and the user chooses not to load the desktop, +the normal hook `desktop-not-loaded-hook' is run." + :type + '(choice + (const :tag "Load anyway" t) + (const :tag "Don't load" nil) + (const :tag "Ask the user" ask)) + :group 'desktop + :version "22.2") + (defcustom desktop-base-file-name (convert-standard-filename ".emacs.desktop") "Name of file for Emacs desktop, excluding the directory part." @@ -194,6 +214,13 @@ determine where the desktop is saved." (define-obsolete-variable-alias 'desktop-basefilename 'desktop-base-file-name "22.1") +(defcustom desktop-base-lock-name + (convert-standard-filename ".emacs.desktop.lock") + "Name of lock file for Emacs desktop, excluding the directory part." + :type 'file + :group 'desktop + :version "22.2") + (defcustom desktop-path '("." "~") "List of directories to search for the desktop file. The base name of the file is specified in `desktop-base-file-name'." @@ -219,6 +246,15 @@ May be used to show a dired buffer." :group 'desktop :version "22.1") +(defcustom desktop-not-loaded-hook nil + "Normal hook run when the user declines to re-use a desktop file. +Run in the directory in which the desktop file was found. +May be used to deal with accidental multiple Emacs jobs." + :type 'hook + :group 'desktop + :options '(desktop-save-mode-off save-buffers-kill-emacs) + :version "22.2") + (defcustom desktop-after-read-hook nil "Normal hook run after a successful `desktop-read'. May be used to show a buffer list." @@ -423,7 +459,7 @@ Furthermore the major mode function must be autoloaded.") Each entry has the form (NAME RESTORE-FUNCTION). NAME is the name of the buffer-local variable indicating that the minor mode is active. RESTORE-FUNCTION is the function to activate the minor mode. -called. RESTORE-FUNCTION nil means don't try to restore the minor mode. +RESTORE-FUNCTION nil means don't try to restore the minor mode. Only minor modes for which the name of the buffer-local variable and the name of the minor mode function are different have to be added to this table. See also `desktop-minor-mode-handlers'." @@ -486,6 +522,11 @@ See also `desktop-minor-mode-table'.") DIRNAME omitted or nil means use `desktop-dirname'." (expand-file-name desktop-base-file-name (or dirname desktop-dirname))) +(defun desktop-full-lock-name (&optional dirname) + "Return the full name of the desktop lock file in DIRNAME. +DIRNAME omitted or nil means use `desktop-dirname'." + (expand-file-name desktop-base-lock-name (or dirname desktop-dirname))) + (defconst desktop-header ";; -------------------------------------------------------------------------- ;; Desktop File for Emacs @@ -495,12 +536,45 @@ DIRNAME omitted or nil means use `desktop-dirname'." (defvar desktop-delay-hook nil "Hooks run after all buffers are loaded; intended for internal use.") +;; ---------------------------------------------------------------------------- +;; Desktop file conflict detection +(defvar desktop-file-modtime nil + "When the desktop file was last modified to the knowledge of this Emacs. +Used to detect desktop file conflicts.") + +(defun desktop-owner (&optional dirname) + "Return the PID of the Emacs process that owns the desktop file in DIRNAME. +Return nil if no desktop file found or no Emacs process is using it. +DIRNAME omitted or nil means use `desktop-dirname'." + (let (owner) + (and (file-exists-p (desktop-full-lock-name dirname)) + (condition-case nil + (with-temp-buffer + (insert-file-contents-literally (desktop-full-lock-name dirname)) + (goto-char (point-min)) + (setq owner (read (current-buffer))) + (integerp owner)) + (error nil)) + owner))) + +(defun desktop-claim-lock (&optional dirname) + "Record this Emacs process as the owner of the desktop file in DIRNAME. +DIRNAME omitted or nil means use `desktop-dirname'." + (write-region (number-to-string (emacs-pid)) nil + (desktop-full-lock-name dirname))) + +(defun desktop-release-lock (&optional dirname) + "Remove the lock file for the desktop in DIRNAME. +DIRNAME omitted or nil means use `desktop-dirname'." + (let ((file (desktop-full-lock-name dirname))) + (when (file-exists-p file) (delete-file file)))) + ;; ---------------------------------------------------------------------------- (defun desktop-truncate (list n) "Truncate LIST to at most N elements destructively." (let ((here (nthcdr (1- n) list))) - (if (consp here) - (setcdr here nil)))) + (when (consp here) + (setcdr here nil)))) ;; ---------------------------------------------------------------------------- ;;;###autoload @@ -513,7 +587,7 @@ Furthermore, it clears the variables listed in `desktop-globals-to-clear'." (desktop-lazy-abort) (dolist (var desktop-globals-to-clear) (if (symbolp var) - (eval `(setq-default ,var nil)) + (eval `(setq-default ,var nil)) (eval `(setq-default ,(car var) ,(cdr var))))) (let ((buffers (buffer-list)) (preserve-regexp (concat "^\\(" @@ -552,14 +626,14 @@ is nil, ask the user where to save the desktop." (setq desktop-dirname (file-name-as-directory (expand-file-name - (call-interactively - (lambda (dir) - (interactive "DDirectory for desktop file: ") dir)))))) + (read-directory-name "Directory for desktop file: " nil nil t))))) (condition-case err - (desktop-save desktop-dirname) + (desktop-save desktop-dirname t) (file-error (unless (yes-or-no-p "Error while saving the desktop. Ignore? ") - (signal (car err) (cdr err))))))) + (signal (car err) (cdr err)))))) + ;; If we own it, we don't anymore. + (when (eq (emacs-pid) (desktop-owner)) (desktop-release-lock))) ;; ---------------------------------------------------------------------------- (defun desktop-list* (&rest args) @@ -573,6 +647,48 @@ is nil, ask the user where to save the desktop." (setq args (cdr args))) value))) +;; ---------------------------------------------------------------------------- +(defun desktop-buffer-info (buffer) + (set-buffer buffer) + (list + ;; base name of the buffer; replaces the buffer name if managed by uniquify + (and (fboundp 'uniquify-buffer-base-name) (uniquify-buffer-base-name)) + ;; basic information + (desktop-file-name (buffer-file-name) desktop-dirname) + (buffer-name) + major-mode + ;; minor modes + (let (ret) + (mapc + #'(lambda (minor-mode) + (and (boundp minor-mode) + (symbol-value minor-mode) + (let* ((special (assq minor-mode desktop-minor-mode-table)) + (value (cond (special (cadr special)) + ((functionp minor-mode) minor-mode)))) + (when value (add-to-list 'ret value))))) + (mapcar #'car minor-mode-alist)) + ret) + ;; point and mark, and read-only status + (point) + (list (mark t) mark-active) + buffer-read-only + ;; auxiliary information + (when (functionp desktop-save-buffer) + (funcall desktop-save-buffer desktop-dirname)) + ;; local variables + (let ((locals desktop-locals-to-save) + (loclist (buffer-local-variables)) + (ll)) + (while locals + (let ((here (assq (car locals) loclist))) + (if here + (setq ll (cons here ll)) + (when (member (car locals) loclist) + (setq ll (cons (car locals) ll))))) + (setq locals (cdr locals))) + ll))) + ;; ---------------------------------------------------------------------------- (defun desktop-internal-v2s (value) "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE. @@ -580,78 +696,77 @@ TXT is a string that when read and evaluated yields value. QUOTE may be `may' (value may be quoted), `must' (values must be quoted), or nil (value may not be quoted)." (cond - ((or (numberp value) (null value) (eq t value) (keywordp value)) - (cons 'may (prin1-to-string value))) - ((stringp value) - (let ((copy (copy-sequence value))) - (set-text-properties 0 (length copy) nil copy) - ;; Get rid of text properties because we cannot read them - (cons 'may (prin1-to-string copy)))) - ((symbolp value) - (cons 'must (prin1-to-string value))) - ((vectorp value) - (let* ((special nil) - (pass1 (mapcar - (lambda (el) - (let ((res (desktop-internal-v2s el))) - (if (null (car res)) - (setq special t)) - res)) - value))) - (if special - (cons nil (concat "(vector " - (mapconcat (lambda (el) - (if (eq (car el) 'must) - (concat "'" (cdr el)) - (cdr el))) - pass1 - " ") - ")")) - (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]"))))) - ((consp value) - (let ((p value) - newlist - use-list* - anynil) - (while (consp p) - (let ((q.txt (desktop-internal-v2s (car p)))) - (or anynil (setq anynil (null (car q.txt)))) - (setq newlist (cons q.txt newlist))) - (setq p (cdr p))) - (if p - (let ((last (desktop-internal-v2s p)) - (el (car newlist))) - (or anynil (setq anynil (null (car last)))) - (or anynil - (setq newlist (cons '(must . ".") newlist))) - (setq use-list* t) - (setq newlist (cons last newlist)))) - (setq newlist (nreverse newlist)) - (if anynil - (cons nil - (concat (if use-list* "(desktop-list* " "(list ") - (mapconcat (lambda (el) - (if (eq (car el) 'must) - (concat "'" (cdr el)) - (cdr el))) - newlist - " ") - ")")) - (cons 'must - (concat "(" (mapconcat 'cdr newlist " ") ")"))))) - ((subrp value) - (cons nil (concat "(symbol-function '" - (substring (prin1-to-string value) 7 -1) - ")"))) - ((markerp value) - (let ((pos (prin1-to-string (marker-position value))) - (buf (prin1-to-string (buffer-name (marker-buffer value))))) - (cons nil (concat "(let ((mk (make-marker)))" - " (add-hook 'desktop-delay-hook" - " (list 'lambda '() (list 'set-marker mk " - pos " (get-buffer " buf ")))) mk)")))) - (t ; save as text - (cons 'may "\"Unprintable entity\"")))) + ((or (numberp value) (null value) (eq t value) (keywordp value)) + (cons 'may (prin1-to-string value))) + ((stringp value) + (let ((copy (copy-sequence value))) + (set-text-properties 0 (length copy) nil copy) + ;; Get rid of text properties because we cannot read them + (cons 'may (prin1-to-string copy)))) + ((symbolp value) + (cons 'must (prin1-to-string value))) + ((vectorp value) + (let* ((special nil) + (pass1 (mapcar + (lambda (el) + (let ((res (desktop-internal-v2s el))) + (if (null (car res)) + (setq special t)) + res)) + value))) + (if special + (cons nil (concat "(vector " + (mapconcat (lambda (el) + (if (eq (car el) 'must) + (concat "'" (cdr el)) + (cdr el))) + pass1 + " ") + ")")) + (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]"))))) + ((consp value) + (let ((p value) + newlist + use-list* + anynil) + (while (consp p) + (let ((q.txt (desktop-internal-v2s (car p)))) + (or anynil (setq anynil (null (car q.txt)))) + (setq newlist (cons q.txt newlist))) + (setq p (cdr p))) + (if p + (let ((last (desktop-internal-v2s p))) + (or anynil (setq anynil (null (car last)))) + (or anynil + (setq newlist (cons '(must . ".") newlist))) + (setq use-list* t) + (setq newlist (cons last newlist)))) + (setq newlist (nreverse newlist)) + (if anynil + (cons nil + (concat (if use-list* "(desktop-list* " "(list ") + (mapconcat (lambda (el) + (if (eq (car el) 'must) + (concat "'" (cdr el)) + (cdr el))) + newlist + " ") + ")")) + (cons 'must + (concat "(" (mapconcat 'cdr newlist " ") ")"))))) + ((subrp value) + (cons nil (concat "(symbol-function '" + (substring (prin1-to-string value) 7 -1) + ")"))) + ((markerp value) + (let ((pos (prin1-to-string (marker-position value))) + (buf (prin1-to-string (buffer-name (marker-buffer value))))) + (cons nil (concat "(let ((mk (make-marker)))" + " (add-hook 'desktop-delay-hook" + " (list 'lambda '() (list 'set-marker mk " + pos " (get-buffer " buf ")))) mk)")))) + (t ; save as text + (cons 'may "\"Unprintable entity\"")))) ;; ---------------------------------------------------------------------------- (defun desktop-value-to-string (value) @@ -677,17 +792,16 @@ which means to truncate VAR's value to at most MAX-SIZE elements (if (consp varspec) (setq var (car varspec) size (cdr varspec)) (setq var varspec)) - (if (boundp var) - (progn - (if (and (integerp size) - (> size 0) - (listp (eval var))) - (desktop-truncate (eval var) size)) - (insert "(setq " - (symbol-name var) - " " - (desktop-value-to-string (symbol-value var)) - ")\n"))))) + (when (boundp var) + (when (and (integerp size) + (> size 0) + (listp (eval var))) + (desktop-truncate (eval var) size)) + (insert "(setq " + (symbol-name var) + " " + (desktop-value-to-string (symbol-value var)) + ")\n")))) ;; ---------------------------------------------------------------------------- (defun desktop-save-buffer-p (filename bufname mode &rest dummy) @@ -725,91 +839,74 @@ DIRNAME must be the directory in which the desktop file will be saved." ;; ---------------------------------------------------------------------------- ;;;###autoload -(defun desktop-save (dirname) +(defun desktop-save (dirname &optional release) "Save the desktop in a desktop file. Parameter DIRNAME specifies where to save the desktop file. +Optional parameter RELEASE says whether we're done with this desktop. See also `desktop-base-file-name'." (interactive "DDirectory to save desktop file in: ") - (run-hooks 'desktop-save-hook) - (setq dirname (file-name-as-directory (expand-file-name dirname))) + (setq desktop-dirname (file-name-as-directory (expand-file-name dirname))) (save-excursion - (let ((filename (desktop-full-file-name dirname)) - (info - (mapcar - #'(lambda (b) - (set-buffer b) - (list - (desktop-file-name (buffer-file-name) dirname) - (buffer-name) - major-mode - ;; minor modes - (let (ret) - (mapc - #'(lambda (minor-mode) - (and - (boundp minor-mode) - (symbol-value minor-mode) - (let* ((special (assq minor-mode desktop-minor-mode-table)) - (value (cond (special (cadr special)) - ((functionp minor-mode) minor-mode)))) - (when value (add-to-list 'ret value))))) - (mapcar #'car minor-mode-alist)) - ret) - (point) - (list (mark t) mark-active) - buffer-read-only - ;; Auxiliary information - (when (functionp desktop-save-buffer) - (funcall desktop-save-buffer dirname)) - (let ((locals desktop-locals-to-save) - (loclist (buffer-local-variables)) - (ll)) - (while locals - (let ((here (assq (car locals) loclist))) - (if here - (setq ll (cons here ll)) - (when (member (car locals) loclist) - (setq ll (cons (car locals) ll))))) - (setq locals (cdr locals))) - ll))) - (buffer-list))) - (eager desktop-restore-eager)) - (with-temp-buffer - (insert - ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n" - desktop-header - ";; Created " (current-time-string) "\n" - ";; Desktop file format version " desktop-file-version "\n" - ";; Emacs version " emacs-version "\n\n" - ";; Global section:\n") - (mapc (function desktop-outvar) desktop-globals-to-save) - (if (memq 'kill-ring desktop-globals-to-save) - (insert - "(setq kill-ring-yank-pointer (nthcdr " - (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer))) - " kill-ring))\n")) - - (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n") - (mapc #'(lambda (l) - (when (apply 'desktop-save-buffer-p l) - (insert "(" - (if (or (not (integerp eager)) - (unless (zerop eager) - (setq eager (1- eager)) - t)) - "desktop-create-buffer" - "desktop-append-buffer-args") - " " - desktop-file-version) - (mapc #'(lambda (e) - (insert "\n " (desktop-value-to-string e))) - l) - (insert ")\n\n"))) - info) - (setq default-directory dirname) - (let ((coding-system-for-write 'emacs-mule)) - (write-region (point-min) (point-max) filename nil 'nomessage))))) - (setq desktop-dirname dirname)) + (let ((eager desktop-restore-eager) + (new-modtime (nth 5 (file-attributes (desktop-full-file-name))))) + (when + (or (not new-modtime) ; nothing to overwrite + (equal desktop-file-modtime new-modtime) + (yes-or-no-p (if desktop-file-modtime + (if (> (float-time new-modtime) (float-time desktop-file-modtime)) + "Desktop file is more recent than the one loaded. Save anyway? " + "Desktop file isn't the one loaded. Overwrite it? ") + "Current desktop was not loaded from a file. Overwrite this desktop file? ")) + (unless release (error "Desktop file conflict"))) + + ;; If we're done with it, release the lock. + ;; Otherwise, claim it if it's unclaimed or if we created it. + (if release + (desktop-release-lock) + (unless (and new-modtime (desktop-owner)) (desktop-claim-lock))) + + (with-temp-buffer + (insert + ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n" + desktop-header + ";; Created " (current-time-string) "\n" + ";; Desktop file format version " desktop-file-version "\n" + ";; Emacs version " emacs-version "\n") + (save-excursion (run-hooks 'desktop-save-hook)) + (goto-char (point-max)) + (insert "\n;; Global section:\n") + (mapc (function desktop-outvar) desktop-globals-to-save) + (when (memq 'kill-ring desktop-globals-to-save) + (insert + "(setq kill-ring-yank-pointer (nthcdr " + (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer))) + " kill-ring))\n")) + + (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n") + (dolist (l (mapcar 'desktop-buffer-info (buffer-list))) + (let ((base (pop l))) + (when (apply 'desktop-save-buffer-p l) + (insert "(" + (if (or (not (integerp eager)) + (if (zerop eager) + nil + (setq eager (1- eager)))) + "desktop-create-buffer" + "desktop-append-buffer-args") + " " + desktop-file-version) + ;; If there's a non-empty base name, we save it instead of the buffer name + (when (and base (not (string= base ""))) + (setcar (nthcdr 1 l) base)) + (dolist (e l) + (insert "\n " (desktop-value-to-string e))) + (insert ")\n\n")))) + + (setq default-directory desktop-dirname) + (let ((coding-system-for-write 'emacs-mule)) + (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage)) + ;; We remember when it was modified (which is presumably just now). + (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))))))) ;; ---------------------------------------------------------------------------- ;;;###autoload @@ -858,35 +955,56 @@ It returns t if a desktop file was loaded, nil otherwise." ;; Default: Home directory. "~")))) (if (file-exists-p (desktop-full-file-name)) - ;; Desktop file found, process it. - (let ((desktop-first-buffer nil) - (desktop-buffer-ok-count 0) - (desktop-buffer-fail-count 0) - ;; Avoid desktop saving during evaluation of desktop buffer. - (desktop-save nil)) - (desktop-lazy-abort) - ;; Evaluate desktop buffer. - (load (desktop-full-file-name) t t t) - ;; `desktop-create-buffer' puts buffers at end of the buffer list. - ;; We want buffers existing prior to evaluating the desktop (and not reused) - ;; to be placed at the end of the buffer list, so we move them here. - (mapc 'bury-buffer - (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list)))))) - (switch-to-buffer (car (buffer-list))) - (run-hooks 'desktop-delay-hook) - (setq desktop-delay-hook nil) - (run-hooks 'desktop-after-read-hook) - (message "Desktop: %d buffer%s restored%s%s." - desktop-buffer-ok-count - (if (= 1 desktop-buffer-ok-count) "" "s") - (if (< 0 desktop-buffer-fail-count) - (format ", %d failed to restore" desktop-buffer-fail-count) - "") - (if desktop-buffer-args-list - (format ", %d to restore lazily" - (length desktop-buffer-args-list)) - "")) - t) + ;; Desktop file found, but is it already in use? + (let ((desktop-first-buffer nil) + (desktop-buffer-ok-count 0) + (desktop-buffer-fail-count 0) + (owner (desktop-owner)) + ;; Avoid desktop saving during evaluation of desktop buffer. + (desktop-save nil)) + (if (and owner + (memq desktop-load-locked-desktop '(nil ask)) + (or (null desktop-load-locked-desktop) + (not (y-or-n-p (format "Warning: desktop file appears to be in use by PID %s.\n\ +Using it may cause conflicts. Use it anyway? " owner))))) + (let ((default-directory desktop-dirname)) + (setq desktop-dirname nil) + (run-hooks 'desktop-not-loaded-hook) + (unless desktop-dirname + (message "Desktop file in use; not loaded."))) + (desktop-lazy-abort) + ;; Evaluate desktop buffer and remember when it was modified. + (load (desktop-full-file-name) t t t) + (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))) + ;; If it wasn't already, mark it as in-use, to bother other + ;; desktop instances. + (unless owner + (condition-case nil + (desktop-claim-lock) + (file-error (message "Couldn't record use of desktop file") + (sit-for 1)))) + + ;; `desktop-create-buffer' puts buffers at end of the buffer list. + ;; We want buffers existing prior to evaluating the desktop (and + ;; not reused) to be placed at the end of the buffer list, so we + ;; move them here. + (mapc 'bury-buffer + (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list)))))) + (switch-to-buffer (car (buffer-list))) + (run-hooks 'desktop-delay-hook) + (setq desktop-delay-hook nil) + (run-hooks 'desktop-after-read-hook) + (message "Desktop: %d buffer%s restored%s%s." + desktop-buffer-ok-count + (if (= 1 desktop-buffer-ok-count) "" "s") + (if (< 0 desktop-buffer-fail-count) + (format ", %d failed to restore" desktop-buffer-fail-count) + "") + (if desktop-buffer-args-list + (format ", %d to restore lazily" + (length desktop-buffer-args-list)) + "")) + t)) ;; No desktop file found. (desktop-clear) (let ((default-directory desktop-dirname)) @@ -941,36 +1059,35 @@ directory DIRNAME." (desktop-clear) (desktop-read desktop-dirname)) +(defvar desktop-buffer-major-mode) +(defvar desktop-buffer-locals) ;; ---------------------------------------------------------------------------- (defun desktop-restore-file-buffer (desktop-buffer-file-name desktop-buffer-name desktop-buffer-misc) "Restore a file buffer." - (eval-when-compile ; Just to silence the byte compiler - (defvar desktop-buffer-major-mode) - (defvar desktop-buffer-locals)) - (if desktop-buffer-file-name - (if (or (file-exists-p desktop-buffer-file-name) - (let ((msg (format "Desktop: File \"%s\" no longer exists." - desktop-buffer-file-name))) - (if desktop-missing-file-warning - (y-or-n-p (concat msg " Re-create buffer? ")) - (message "%s" msg) - nil))) - (let* ((auto-insert nil) ; Disable auto insertion - (coding-system-for-read - (or coding-system-for-read - (cdr (assq 'buffer-file-coding-system - desktop-buffer-locals)))) - (buf (find-file-noselect desktop-buffer-file-name))) - (condition-case nil - (switch-to-buffer buf) - (error (pop-to-buffer buf))) - (and (not (eq major-mode desktop-buffer-major-mode)) - (functionp desktop-buffer-major-mode) - (funcall desktop-buffer-major-mode)) - buf) - nil))) + (when desktop-buffer-file-name + (if (or (file-exists-p desktop-buffer-file-name) + (let ((msg (format "Desktop: File \"%s\" no longer exists." + desktop-buffer-file-name))) + (if desktop-missing-file-warning + (y-or-n-p (concat msg " Re-create buffer? ")) + (message "%s" msg) + nil))) + (let* ((auto-insert nil) ; Disable auto insertion + (coding-system-for-read + (or coding-system-for-read + (cdr (assq 'buffer-file-coding-system + desktop-buffer-locals)))) + (buf (find-file-noselect desktop-buffer-file-name))) + (condition-case nil + (switch-to-buffer buf) + (error (pop-to-buffer buf))) + (and (not (eq major-mode desktop-buffer-major-mode)) + (functionp desktop-buffer-major-mode) + (funcall desktop-buffer-major-mode)) + buf) + nil))) (defun desktop-load-file (function) "Load the file where auto loaded FUNCTION is defined." @@ -985,8 +1102,12 @@ directory DIRNAME." ;; called from Desktop file only. ;; Just to silence the byte compiler. -(eval-when-compile - (defvar desktop-first-buffer)) ; Dynamically bound in `desktop-read' + +(defvar desktop-first-buffer) ; Dynamically bound in `desktop-read' + +;; Bound locally in `desktop-read'. +(defvar desktop-buffer-ok-count) +(defvar desktop-buffer-fail-count) (defun desktop-create-buffer (desktop-file-version @@ -1000,10 +1121,6 @@ directory DIRNAME." desktop-buffer-misc &optional desktop-buffer-locals) - ;; Just to silence the byte compiler. Bound locally in `desktop-read'. - (eval-when-compile - (defvar desktop-buffer-ok-count) - (defvar desktop-buffer-fail-count)) ;; To make desktop files with relative file names possible, we cannot ;; allow `default-directory' to change. Therefore we save current buffer. (save-current-buffer @@ -1038,45 +1155,46 @@ directory DIRNAME." (setq desktop-first-buffer result)) (set-buffer result) (unless (equal (buffer-name) desktop-buffer-name) - (rename-buffer desktop-buffer-name)) + (rename-buffer desktop-buffer-name t)) ;; minor modes (cond ((equal '(t) desktop-buffer-minor-modes) ; backwards compatible (auto-fill-mode 1)) ((equal '(nil) desktop-buffer-minor-modes) ; backwards compatible (auto-fill-mode 0)) (t - (mapc #'(lambda (minor-mode) - ;; Give minor mode module a chance to add a handler. - (desktop-load-file minor-mode) - (let ((handler (cdr (assq minor-mode desktop-minor-mode-handlers)))) - (if handler - (funcall handler desktop-buffer-locals) - (when (functionp minor-mode) - (funcall minor-mode 1))))) - desktop-buffer-minor-modes))) - ;; Even though point and mark are non-nil when written by `desktop-save', - ;; they may be modified by handlers wanting to set point or mark themselves. + (dolist (minor-mode desktop-buffer-minor-modes) + ;; Give minor mode module a chance to add a handler. + (desktop-load-file minor-mode) + (let ((handler (cdr (assq minor-mode desktop-minor-mode-handlers)))) + (if handler + (funcall handler desktop-buffer-locals) + (when (functionp minor-mode) + (funcall minor-mode 1))))))) + ;; Even though point and mark are non-nil when written by + ;; `desktop-save', they may be modified by handlers wanting to set + ;; point or mark themselves. (when desktop-buffer-point (goto-char (condition-case err - ;; Evaluate point. Thus point can be something like '(search-forward ... + ;; Evaluate point. Thus point can be something like + ;; '(search-forward ... (eval desktop-buffer-point) (error (message "%s" (error-message-string err)) 1)))) (when desktop-buffer-mark (if (consp desktop-buffer-mark) - (progn - (set-mark (car desktop-buffer-mark)) - (setq mark-active (car (cdr desktop-buffer-mark)))) + (progn + (set-mark (car desktop-buffer-mark)) + (setq mark-active (car (cdr desktop-buffer-mark)))) (set-mark desktop-buffer-mark))) ;; Never override file system if the file really is read-only marked. - (if desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only)) + (when desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only)) (while desktop-buffer-locals (let ((this (car desktop-buffer-locals))) (if (consp this) - ;; an entry of this form `(symbol . value)' - (progn - (make-local-variable (car this)) - (set (car this) (cdr this))) + ;; an entry of this form `(symbol . value)' + (progn + (make-local-variable (car this)) + (set (car this) (cdr this))) ;; an entry of the form `symbol' (make-local-variable this) (makunbound this))) @@ -1167,14 +1285,16 @@ If there are no buffers left to create, kill the timer." ;; functions are processed after `after-init-hook'. (add-hook 'after-init-hook - '(lambda () + (lambda () (let ((key "--no-desktop")) (when (member key command-line-args) (setq command-line-args (delete key command-line-args)) (setq desktop-save-mode nil))) - (when desktop-save-mode (desktop-read)))) + (when desktop-save-mode + (desktop-read) + (setq inhibit-startup-screen t)))) (provide 'desktop) -;;; arch-tag: 221907c3-1771-4fd3-9c2e-c6f700c6ede9 +;; arch-tag: 221907c3-1771-4fd3-9c2e-c6f700c6ede9 ;;; desktop.el ends here