aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuanma Barranquero2007-06-12 09:11:31 +0000
committerJuanma Barranquero2007-06-12 09:11:31 +0000
commite88110dbfc271c45802d97a4c518e276d6e42b0d (patch)
treef95a58dd523153a24b4478910d65a193af029d3d
parent3f7194edd4c350e7e1cbc72aa4868e58157211aa (diff)
downloademacs-e88110dbfc271c45802d97a4c518e276d6e42b0d.tar.gz
emacs-e88110dbfc271c45802d97a4c518e276d6e42b0d.zip
(desktop-save-mode-off): New function.
(desktop-base-lock-name, desktop-not-loaded-hook): New variables. (desktop-full-lock-name, desktop-file-modtime, desktop-owner, desktop-claim-lock, desktop-release-lock): New functions. (desktop-kill): Tell `desktop-save' that this is the last save. Release the lock afterwards. (desktop-buffer-info): New function. (desktop-save): Use it. Run `desktop-save-hook' where the doc says to. Detect conflicts, and manage the lock. (desktop-read): Detect conflicts. Manage the lock.
-rw-r--r--lisp/ChangeLog13
-rw-r--r--lisp/desktop.el318
2 files changed, 221 insertions, 110 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index f28aac05b4f..b135c84b2cf 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,16 @@
12007-06-12 Davis Herring <herring@lanl.gov>
2
3 * desktop.el (desktop-save-mode-off): New function.
4 (desktop-base-lock-name, desktop-not-loaded-hook): New variables.
5 (desktop-full-lock-name, desktop-file-modtime, desktop-owner)
6 (desktop-claim-lock, desktop-release-lock): New functions.
7 (desktop-kill): Tell `desktop-save' that this is the last save.
8 Release the lock afterwards.
9 (desktop-buffer-info): New function.
10 (desktop-save): Use it. Run `desktop-save-hook' where the doc
11 says to. Detect conflicts, and manage the lock.
12 (desktop-read): Detect conflicts. Manage the lock.
13
12007-06-12 Stefan Monnier <monnier@iro.umontreal.ca> 142007-06-12 Stefan Monnier <monnier@iro.umontreal.ca>
2 15
3 * emulation/tpu-mapper.el (tpu-emacs-map-key): Use new keymap names. 16 * emulation/tpu-mapper.el (tpu-emacs-map-key): Use new keymap names.
diff --git a/lisp/desktop.el b/lisp/desktop.el
index e44e943db3e..83a68c88f4b 100644
--- a/lisp/desktop.el
+++ b/lisp/desktop.el
@@ -162,6 +162,10 @@ and function `desktop-read' for details."
162(define-obsolete-variable-alias 'desktop-enable 162(define-obsolete-variable-alias 'desktop-enable
163 'desktop-save-mode "22.1") 163 'desktop-save-mode "22.1")
164 164
165(defun desktop-save-mode-off ()
166 "Disable `desktop-save-mode'. Provided for use in hooks."
167 (desktop-save-mode 0))
168
165(defcustom desktop-save 'ask-if-new 169(defcustom desktop-save 'ask-if-new
166 "*Specifies whether the desktop should be saved when it is killed. 170 "*Specifies whether the desktop should be saved when it is killed.
167A desktop is killed when the user changes desktop or quits Emacs. 171A desktop is killed when the user changes desktop or quits Emacs.
@@ -194,6 +198,13 @@ determine where the desktop is saved."
194(define-obsolete-variable-alias 'desktop-basefilename 198(define-obsolete-variable-alias 'desktop-basefilename
195 'desktop-base-file-name "22.1") 199 'desktop-base-file-name "22.1")
196 200
201(defcustom desktop-base-lock-name
202 (convert-standard-filename ".emacs.desktop.lock")
203 "Name of lock file for Emacs desktop, excluding the directory part."
204 :type 'file
205 :group 'desktop
206 :version "23.1")
207
197(defcustom desktop-path '("." "~") 208(defcustom desktop-path '("." "~")
198 "List of directories to search for the desktop file. 209 "List of directories to search for the desktop file.
199The base name of the file is specified in `desktop-base-file-name'." 210The base name of the file is specified in `desktop-base-file-name'."
@@ -219,6 +230,15 @@ May be used to show a dired buffer."
219 :group 'desktop 230 :group 'desktop
220 :version "22.1") 231 :version "22.1")
221 232
233(defcustom desktop-not-loaded-hook nil
234 "Normal hook run when the user declines to re-use a desktop file.
235Run in the directory in which the desktop file was found.
236May be used to deal with accidental multiple Emacs jobs."
237 :type 'hook
238 :group 'desktop
239 :options '(desktop-save-mode-off save-buffers-kill-emacs)
240 :version "23.1")
241
222(defcustom desktop-after-read-hook nil 242(defcustom desktop-after-read-hook nil
223 "Normal hook run after a successful `desktop-read'. 243 "Normal hook run after a successful `desktop-read'.
224May be used to show a buffer list." 244May be used to show a buffer list."
@@ -486,6 +506,11 @@ See also `desktop-minor-mode-table'.")
486DIRNAME omitted or nil means use `desktop-dirname'." 506DIRNAME omitted or nil means use `desktop-dirname'."
487 (expand-file-name desktop-base-file-name (or dirname desktop-dirname))) 507 (expand-file-name desktop-base-file-name (or dirname desktop-dirname)))
488 508
509(defun desktop-full-lock-name (&optional dirname)
510 "Return the full name of the desktop lock file in DIRNAME.
511DIRNAME omitted or nil means use `desktop-dirname'."
512 (expand-file-name desktop-base-lock-name (or dirname desktop-dirname)))
513
489(defconst desktop-header 514(defconst desktop-header
490";; -------------------------------------------------------------------------- 515";; --------------------------------------------------------------------------
491;; Desktop File for Emacs 516;; Desktop File for Emacs
@@ -496,6 +521,39 @@ DIRNAME omitted or nil means use `desktop-dirname'."
496 "Hooks run after all buffers are loaded; intended for internal use.") 521 "Hooks run after all buffers are loaded; intended for internal use.")
497 522
498;; ---------------------------------------------------------------------------- 523;; ----------------------------------------------------------------------------
524;; Desktop file conflict detection
525(defvar desktop-file-modtime nil
526 "When the desktop file was last modified to the knowledge of this Emacs.
527Used to detect desktop file conflicts.")
528
529(defun desktop-owner (&optional dirname)
530 "Return the PID of the Emacs process that owns the desktop file in DIRNAME.
531Return nil if no desktop file found or no Emacs process is using it.
532DIRNAME omitted or nil means use `desktop-dirname'."
533 (let (owner)
534 (and (file-exists-p (desktop-full-lock-name dirname))
535 (condition-case nil
536 (with-temp-buffer
537 (insert-file-contents-literally (desktop-full-lock-name dirname))
538 (goto-char (point-min))
539 (setq owner (read (current-buffer)))
540 (integerp owner))
541 (error nil))
542 owner)))
543
544(defun desktop-claim-lock (&optional dirname)
545 "Record this Emacs process as the owner of the desktop file in DIRNAME.
546DIRNAME omitted or nil means use `desktop-dirname'."
547 (write-region (number-to-string (emacs-pid)) nil
548 (desktop-full-lock-name dirname)))
549
550(defun desktop-release-lock (&optional dirname)
551 "Remove the lock file for the desktop in DIRNAME.
552DIRNAME omitted or nil means use `desktop-dirname'."
553 (let ((file (desktop-full-lock-name dirname)))
554 (when (file-exists-p file) (delete-file file))))
555
556;; ----------------------------------------------------------------------------
499(defun desktop-truncate (list n) 557(defun desktop-truncate (list n)
500 "Truncate LIST to at most N elements destructively." 558 "Truncate LIST to at most N elements destructively."
501 (let ((here (nthcdr (1- n) list))) 559 (let ((here (nthcdr (1- n) list)))
@@ -556,10 +614,12 @@ is nil, ask the user where to save the desktop."
556 (lambda (dir) 614 (lambda (dir)
557 (interactive "DDirectory for desktop file: ") dir)))))) 615 (interactive "DDirectory for desktop file: ") dir))))))
558 (condition-case err 616 (condition-case err
559 (desktop-save desktop-dirname) 617 (desktop-save desktop-dirname t)
560 (file-error 618 (file-error
561 (unless (yes-or-no-p "Error while saving the desktop. Ignore? ") 619 (unless (yes-or-no-p "Error while saving the desktop. Ignore? ")
562 (signal (car err) (cdr err))))))) 620 (signal (car err) (cdr err))))))
621 ;; If we own it, we don't anymore.
622 (when (eq (emacs-pid) (desktop-owner)) (desktop-release-lock)))
563 623
564;; ---------------------------------------------------------------------------- 624;; ----------------------------------------------------------------------------
565(defun desktop-list* (&rest args) 625(defun desktop-list* (&rest args)
@@ -574,6 +634,46 @@ is nil, ask the user where to save the desktop."
574 value))) 634 value)))
575 635
576;; ---------------------------------------------------------------------------- 636;; ----------------------------------------------------------------------------
637(defun desktop-buffer-info (buffer)
638 (set-buffer buffer)
639 (list
640 ;; basic information
641 (desktop-file-name (buffer-file-name) dirname)
642 (buffer-name)
643 major-mode
644 ;; minor modes
645 (let (ret)
646 (mapc
647 #'(lambda (minor-mode)
648 (and (boundp minor-mode)
649 (symbol-value minor-mode)
650 (let* ((special (assq minor-mode desktop-minor-mode-table))
651 (value (cond (special (cadr special))
652 ((functionp minor-mode) minor-mode))))
653 (when value (add-to-list 'ret value)))))
654 (mapcar #'car minor-mode-alist))
655 ret)
656 ;; point and mark, and read-only status
657 (point)
658 (list (mark t) mark-active)
659 buffer-read-only
660 ;; auxiliary information
661 (when (functionp desktop-save-buffer)
662 (funcall desktop-save-buffer dirname))
663 ;; local variables
664 (let ((locals desktop-locals-to-save)
665 (loclist (buffer-local-variables))
666 (ll))
667 (while locals
668 (let ((here (assq (car locals) loclist)))
669 (if here
670 (setq ll (cons here ll))
671 (when (member (car locals) loclist)
672 (setq ll (cons (car locals) ll)))))
673 (setq locals (cdr locals)))
674 ll)))
675
676;; ----------------------------------------------------------------------------
577(defun desktop-internal-v2s (value) 677(defun desktop-internal-v2s (value)
578 "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE. 678 "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE.
579TXT is a string that when read and evaluated yields value. 679TXT is a string that when read and evaluated yields value.
@@ -724,90 +824,70 @@ DIRNAME must be the directory in which the desktop file will be saved."
724 824
725;; ---------------------------------------------------------------------------- 825;; ----------------------------------------------------------------------------
726;;;###autoload 826;;;###autoload
727(defun desktop-save (dirname) 827(defun desktop-save (dirname &optional release)
728 "Save the desktop in a desktop file. 828 "Save the desktop in a desktop file.
729Parameter DIRNAME specifies where to save the desktop file. 829Parameter DIRNAME specifies where to save the desktop file.
830Optional parameter RELEASE says whether we're done with this desktop.
730See also `desktop-base-file-name'." 831See also `desktop-base-file-name'."
731 (interactive "DDirectory to save desktop file in: ") 832 (interactive "DDirectory to save desktop file in: ")
732 (run-hooks 'desktop-save-hook) 833 (setq desktop-dirname (file-name-as-directory (expand-file-name dirname)))
733 (setq dirname (file-name-as-directory (expand-file-name dirname)))
734 (save-excursion 834 (save-excursion
735 (let ((filename (desktop-full-file-name dirname)) 835 (let ((eager desktop-restore-eager)
736 (info 836 (new-modtime (nth 5 (file-attributes (desktop-full-file-name)))))
737 (mapcar 837 (when
738 #'(lambda (b) 838 (or (not new-modtime) ; nothing to overwrite
739 (set-buffer b) 839 (equal desktop-file-modtime new-modtime)
740 (list 840 (yes-or-no-p (if desktop-file-modtime
741 (desktop-file-name (buffer-file-name) dirname) 841 (if (> (float-time new-modtime) (float-time desktop-file-modtime))
742 (buffer-name) 842 "Desktop file is more recent than the one loaded. Save anyway? "
743 major-mode 843 "Desktop file isn't the one loaded. Overwrite it? ")
744 ;; minor modes 844 "Current desktop was not loaded from a file. Overwrite this desktop file? "))
745 (let (ret) 845 (unless release (error "Desktop file conflict")))
746 (mapc 846
747 #'(lambda (minor-mode) 847 ;; If we're done with it, release the lock.
748 (and 848 ;; Otherwise, claim it if it's unclaimed or if we created it.
749 (boundp minor-mode) 849 (if release
750 (symbol-value minor-mode) 850 (desktop-release-lock)
751 (let* ((special (assq minor-mode desktop-minor-mode-table)) 851 (unless (and new-modtime (desktop-owner)) (desktop-claim-lock)))
752 (value (cond (special (cadr special)) 852
753 ((functionp minor-mode) minor-mode)))) 853 (with-temp-buffer
754 (when value (add-to-list 'ret value))))) 854 (insert
755 (mapcar #'car minor-mode-alist)) 855 ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
756 ret) 856 desktop-header
757 (point) 857 ";; Created " (current-time-string) "\n"
758 (list (mark t) mark-active) 858 ";; Desktop file format version " desktop-file-version "\n"
759 buffer-read-only 859 ";; Emacs version " emacs-version "\n")
760 ;; Auxiliary information 860 (save-excursion (run-hooks 'desktop-save-hook))
761 (when (functionp desktop-save-buffer) 861 (goto-char (point-max))
762 (funcall desktop-save-buffer dirname)) 862 (insert "\n;; Global section:\n")
763 (let ((locals desktop-locals-to-save) 863 (mapc (function desktop-outvar) desktop-globals-to-save)
764 (loclist (buffer-local-variables)) 864 (when (memq 'kill-ring desktop-globals-to-save)
765 (ll)) 865 (insert
766 (while locals 866 "(setq kill-ring-yank-pointer (nthcdr "
767 (let ((here (assq (car locals) loclist))) 867 (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
768 (if here 868 " kill-ring))\n"))
769 (setq ll (cons here ll)) 869
770 (when (member (car locals) loclist) 870 (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
771 (setq ll (cons (car locals) ll))))) 871 (dolist (l (mapcar 'desktop-buffer-info (buffer-list)))
772 (setq locals (cdr locals))) 872 (when (apply 'desktop-save-buffer-p l)
773 ll))) 873 (insert "("
774 (buffer-list))) 874 (if (or (not (integerp eager))
775 (eager desktop-restore-eager)) 875 (if (zerop eager)
776 (with-temp-buffer 876 nil
777 (insert 877 (setq eager (1- eager))))
778 ";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n" 878 "desktop-create-buffer"
779 desktop-header 879 "desktop-append-buffer-args")
780 ";; Created " (current-time-string) "\n" 880 " "
781 ";; Desktop file format version " desktop-file-version "\n" 881 desktop-file-version)
782 ";; Emacs version " emacs-version "\n\n" 882 (dolist (e l)
783 ";; Global section:\n") 883 (insert "\n " (desktop-value-to-string e)))
784 (dolist (varspec desktop-globals-to-save) 884 (insert ")\n\n")))
785 (desktop-outvar varspec)) 885
786 (if (memq 'kill-ring desktop-globals-to-save) 886 (setq default-directory dirname)
787 (insert 887 (let ((coding-system-for-write 'emacs-mule))
788 "(setq kill-ring-yank-pointer (nthcdr " 888 (write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage))
789 (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer))) 889 ;; We remember when it was modified (which is presumably just now).
790 " kill-ring))\n")) 890 (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))))))))
791
792 (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
793 (dolist (l info)
794 (when (apply 'desktop-save-buffer-p l)
795 (insert "("
796 (if (or (not (integerp eager))
797 (unless (zerop eager)
798 (setq eager (1- eager))
799 t))
800 "desktop-create-buffer"
801 "desktop-append-buffer-args")
802 " "
803 desktop-file-version)
804 (dolist (e l)
805 (insert "\n " (desktop-value-to-string e)))
806 (insert ")\n\n")))
807 (setq default-directory dirname)
808 (let ((coding-system-for-write 'emacs-mule))
809 (write-region (point-min) (point-max) filename nil 'nomessage)))))
810 (setq desktop-dirname dirname))
811 891
812;; ---------------------------------------------------------------------------- 892;; ----------------------------------------------------------------------------
813;;;###autoload 893;;;###autoload
@@ -856,35 +936,53 @@ It returns t if a desktop file was loaded, nil otherwise."
856 ;; Default: Home directory. 936 ;; Default: Home directory.
857 "~")))) 937 "~"))))
858 (if (file-exists-p (desktop-full-file-name)) 938 (if (file-exists-p (desktop-full-file-name))
859 ;; Desktop file found, process it. 939 ;; Desktop file found, but is it already in use?
860 (let ((desktop-first-buffer nil) 940 (let ((desktop-first-buffer nil)
861 (desktop-buffer-ok-count 0) 941 (desktop-buffer-ok-count 0)
862 (desktop-buffer-fail-count 0) 942 (desktop-buffer-fail-count 0)
863 ;; Avoid desktop saving during evaluation of desktop buffer. 943 (owner (desktop-owner))
864 (desktop-save nil)) 944 ;; Avoid desktop saving during evaluation of desktop buffer.
865 (desktop-lazy-abort) 945 (desktop-save nil))
866 ;; Evaluate desktop buffer. 946 (if (and owner
867 (load (desktop-full-file-name) t t t) 947 (not (y-or-n-p (format "Warning: desktop file appears to be in use by PID %s.\n\
868 ;; `desktop-create-buffer' puts buffers at end of the buffer list. 948Using it may cause conflicts. Use it anyway? " owner))))
869 ;; We want buffers existing prior to evaluating the desktop (and not reused) 949 (progn (setq desktop-dirname nil)
870 ;; to be placed at the end of the buffer list, so we move them here. 950 (let ((default-directory desktop-dirname))
871 (mapc 'bury-buffer 951 (run-hooks 'desktop-not-loaded-hook))
872 (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list)))))) 952 (message "Desktop file in use; not loaded."))
873 (switch-to-buffer (car (buffer-list))) 953 (desktop-lazy-abort)
874 (run-hooks 'desktop-delay-hook) 954 ;; Evaluate desktop buffer and remember when it was modified.
875 (setq desktop-delay-hook nil) 955 (load (desktop-full-file-name) t t t)
876 (run-hooks 'desktop-after-read-hook) 956 (setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))
877 (message "Desktop: %d buffer%s restored%s%s." 957 ;; If it wasn't already, mark it as in-use, to bother other
878 desktop-buffer-ok-count 958 ;; desktop instances.
879 (if (= 1 desktop-buffer-ok-count) "" "s") 959 (unless owner
880 (if (< 0 desktop-buffer-fail-count) 960 (condition-case nil
881 (format ", %d failed to restore" desktop-buffer-fail-count) 961 (desktop-claim-lock)
882 "") 962 (file-error (message "Couldn't record use of desktop file")
883 (if desktop-buffer-args-list 963 (sit-for 1))))
884 (format ", %d to restore lazily" 964
885 (length desktop-buffer-args-list)) 965 ;; `desktop-create-buffer' puts buffers at end of the buffer list.
886 "")) 966 ;; We want buffers existing prior to evaluating the desktop (and
887 t) 967 ;; not reused) to be placed at the end of the buffer list, so we
968 ;; move them here.
969 (mapc 'bury-buffer
970 (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
971 (switch-to-buffer (car (buffer-list)))
972 (run-hooks 'desktop-delay-hook)
973 (setq desktop-delay-hook nil)
974 (run-hooks 'desktop-after-read-hook)
975 (message "Desktop: %d buffer%s restored%s%s."
976 desktop-buffer-ok-count
977 (if (= 1 desktop-buffer-ok-count) "" "s")
978 (if (< 0 desktop-buffer-fail-count)
979 (format ", %d failed to restore" desktop-buffer-fail-count)
980 "")
981 (if desktop-buffer-args-list
982 (format ", %d to restore lazily"
983 (length desktop-buffer-args-list))
984 ""))
985 t))
888 ;; No desktop file found. 986 ;; No desktop file found.
889 (desktop-clear) 987 (desktop-clear)
890 (let ((default-directory desktop-dirname)) 988 (let ((default-directory desktop-dirname))