496
537
"Hooks run after all buffers are loaded; intended for internal use.")
498
539
;; ----------------------------------------------------------------------------
540
;; Desktop file conflict detection
541
(defvar desktop-file-modtime nil
542
"When the desktop file was last modified to the knowledge of this Emacs.
543
Used to detect desktop file conflicts.")
545
(defun desktop-owner (&optional dirname)
546
"Return the PID of the Emacs process that owns the desktop file in DIRNAME.
547
Return nil if no desktop file found or no Emacs process is using it.
548
DIRNAME omitted or nil means use `desktop-dirname'."
550
(and (file-exists-p (desktop-full-lock-name dirname))
553
(insert-file-contents-literally (desktop-full-lock-name dirname))
554
(goto-char (point-min))
555
(setq owner (read (current-buffer)))
560
(defun desktop-claim-lock (&optional dirname)
561
"Record this Emacs process as the owner of the desktop file in DIRNAME.
562
DIRNAME omitted or nil means use `desktop-dirname'."
563
(write-region (number-to-string (emacs-pid)) nil
564
(desktop-full-lock-name dirname)))
566
(defun desktop-release-lock (&optional dirname)
567
"Remove the lock file for the desktop in DIRNAME.
568
DIRNAME omitted or nil means use `desktop-dirname'."
569
(let ((file (desktop-full-lock-name dirname)))
570
(when (file-exists-p file) (delete-file file))))
572
;; ----------------------------------------------------------------------------
499
573
(defun desktop-truncate (list n)
500
574
"Truncate LIST to at most N elements destructively."
501
575
(let ((here (nthcdr (1- n) list)))
505
579
;; ----------------------------------------------------------------------------
576
650
;; ----------------------------------------------------------------------------
651
(defun desktop-buffer-info (buffer)
655
(desktop-file-name (buffer-file-name) dirname)
661
#'(lambda (minor-mode)
662
(and (boundp minor-mode)
663
(symbol-value minor-mode)
664
(let* ((special (assq minor-mode desktop-minor-mode-table))
665
(value (cond (special (cadr special))
666
((functionp minor-mode) minor-mode))))
667
(when value (add-to-list 'ret value)))))
668
(mapcar #'car minor-mode-alist))
670
;; point and mark, and read-only status
672
(list (mark t) mark-active)
674
;; auxiliary information
675
(when (functionp desktop-save-buffer)
676
(funcall desktop-save-buffer dirname))
678
(let ((locals desktop-locals-to-save)
679
(loclist (buffer-local-variables))
682
(let ((here (assq (car locals) loclist)))
684
(setq ll (cons here ll))
685
(when (member (car locals) loclist)
686
(setq ll (cons (car locals) ll)))))
687
(setq locals (cdr locals)))
690
;; ----------------------------------------------------------------------------
577
691
(defun desktop-internal-v2s (value)
578
692
"Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE.
579
693
TXT is a string that when read and evaluated yields value.
580
694
QUOTE may be `may' (value may be quoted),
581
695
`must' (values must be quoted), or nil (value may not be quoted)."
583
((or (numberp value) (null value) (eq t value) (keywordp value))
584
(cons 'may (prin1-to-string value)))
586
(let ((copy (copy-sequence value)))
587
(set-text-properties 0 (length copy) nil copy)
588
;; Get rid of text properties because we cannot read them
589
(cons 'may (prin1-to-string copy))))
591
(cons 'must (prin1-to-string value)))
596
(let ((res (desktop-internal-v2s el)))
602
(cons nil (concat "(vector "
603
(mapconcat (lambda (el)
604
(if (eq (car el) 'must)
605
(concat "'" (cdr el))
610
(cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
617
(let ((q.txt (desktop-internal-v2s (car p))))
618
(or anynil (setq anynil (null (car q.txt))))
619
(setq newlist (cons q.txt newlist)))
622
(let ((last (desktop-internal-v2s p)))
623
(or anynil (setq anynil (null (car last))))
625
(setq newlist (cons '(must . ".") newlist)))
627
(setq newlist (cons last newlist))))
628
(setq newlist (nreverse newlist))
631
(concat (if use-list* "(desktop-list* " "(list ")
632
(mapconcat (lambda (el)
633
(if (eq (car el) 'must)
634
(concat "'" (cdr el))
640
(concat "(" (mapconcat 'cdr newlist " ") ")")))))
642
(cons nil (concat "(symbol-function '"
643
(substring (prin1-to-string value) 7 -1)
646
(let ((pos (prin1-to-string (marker-position value)))
647
(buf (prin1-to-string (buffer-name (marker-buffer value)))))
648
(cons nil (concat "(let ((mk (make-marker)))"
649
" (add-hook 'desktop-delay-hook"
650
" (list 'lambda '() (list 'set-marker mk "
651
pos " (get-buffer " buf ")))) mk)"))))
653
(cons 'may "\"Unprintable entity\""))))
697
((or (numberp value) (null value) (eq t value) (keywordp value))
698
(cons 'may (prin1-to-string value)))
700
(let ((copy (copy-sequence value)))
701
(set-text-properties 0 (length copy) nil copy)
702
;; Get rid of text properties because we cannot read them
703
(cons 'may (prin1-to-string copy))))
705
(cons 'must (prin1-to-string value)))
710
(let ((res (desktop-internal-v2s el)))
716
(cons nil (concat "(vector "
717
(mapconcat (lambda (el)
718
(if (eq (car el) 'must)
719
(concat "'" (cdr el))
724
(cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
731
(let ((q.txt (desktop-internal-v2s (car p))))
732
(or anynil (setq anynil (null (car q.txt))))
733
(setq newlist (cons q.txt newlist)))
736
(let ((last (desktop-internal-v2s p)))
737
(or anynil (setq anynil (null (car last))))
739
(setq newlist (cons '(must . ".") newlist)))
741
(setq newlist (cons last newlist))))
742
(setq newlist (nreverse newlist))
745
(concat (if use-list* "(desktop-list* " "(list ")
746
(mapconcat (lambda (el)
747
(if (eq (car el) 'must)
748
(concat "'" (cdr el))
754
(concat "(" (mapconcat 'cdr newlist " ") ")")))))
756
(cons nil (concat "(symbol-function '"
757
(substring (prin1-to-string value) 7 -1)
760
(let ((pos (prin1-to-string (marker-position value)))
761
(buf (prin1-to-string (buffer-name (marker-buffer value)))))
762
(cons nil (concat "(let ((mk (make-marker)))"
763
" (add-hook 'desktop-delay-hook"
764
" (list 'lambda '() (list 'set-marker mk "
765
pos " (get-buffer " buf ")))) mk)"))))
767
(cons 'may "\"Unprintable entity\""))))
655
769
;; ----------------------------------------------------------------------------
656
770
(defun desktop-value-to-string (value)
725
838
;; ----------------------------------------------------------------------------
727
(defun desktop-save (dirname)
840
(defun desktop-save (dirname &optional release)
728
841
"Save the desktop in a desktop file.
729
842
Parameter DIRNAME specifies where to save the desktop file.
843
Optional parameter RELEASE says whether we're done with this desktop.
730
844
See also `desktop-base-file-name'."
731
845
(interactive "DDirectory to save desktop file in: ")
732
(run-hooks 'desktop-save-hook)
733
(setq dirname (file-name-as-directory (expand-file-name dirname)))
846
(setq desktop-dirname (file-name-as-directory (expand-file-name dirname)))
735
(let ((filename (desktop-full-file-name dirname))
741
(desktop-file-name (buffer-file-name) dirname)
747
#'(lambda (minor-mode)
750
(symbol-value minor-mode)
751
(let* ((special (assq minor-mode desktop-minor-mode-table))
752
(value (cond (special (cadr special))
753
((functionp minor-mode) minor-mode))))
754
(when value (add-to-list 'ret value)))))
755
(mapcar #'car minor-mode-alist))
758
(list (mark t) mark-active)
760
;; Auxiliary information
761
(when (functionp desktop-save-buffer)
762
(funcall desktop-save-buffer dirname))
763
(let ((locals desktop-locals-to-save)
764
(loclist (buffer-local-variables))
767
(let ((here (assq (car locals) loclist)))
769
(setq ll (cons here ll))
770
(when (member (car locals) loclist)
771
(setq ll (cons (car locals) ll)))))
772
(setq locals (cdr locals)))
775
(eager desktop-restore-eager))
778
";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
780
";; Created " (current-time-string) "\n"
781
";; Desktop file format version " desktop-file-version "\n"
782
";; Emacs version " emacs-version "\n\n"
783
";; Global section:\n")
784
(dolist (varspec desktop-globals-to-save)
785
(desktop-outvar varspec))
786
(if (memq 'kill-ring desktop-globals-to-save)
788
"(setq kill-ring-yank-pointer (nthcdr "
789
(int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
792
(insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
794
(when (apply 'desktop-save-buffer-p l)
796
(if (or (not (integerp eager))
797
(unless (zerop eager)
798
(setq eager (1- eager))
800
"desktop-create-buffer"
801
"desktop-append-buffer-args")
803
desktop-file-version)
805
(insert "\n " (desktop-value-to-string e)))
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))
848
(let ((eager desktop-restore-eager)
849
(new-modtime (nth 5 (file-attributes (desktop-full-file-name)))))
851
(or (not new-modtime) ; nothing to overwrite
852
(equal desktop-file-modtime new-modtime)
853
(yes-or-no-p (if desktop-file-modtime
854
(if (> (float-time new-modtime) (float-time desktop-file-modtime))
855
"Desktop file is more recent than the one loaded. Save anyway? "
856
"Desktop file isn't the one loaded. Overwrite it? ")
857
"Current desktop was not loaded from a file. Overwrite this desktop file? "))
858
(unless release (error "Desktop file conflict")))
860
;; If we're done with it, release the lock.
861
;; Otherwise, claim it if it's unclaimed or if we created it.
863
(desktop-release-lock)
864
(unless (and new-modtime (desktop-owner)) (desktop-claim-lock)))
868
";; -*- mode: emacs-lisp; coding: emacs-mule; -*-\n"
870
";; Created " (current-time-string) "\n"
871
";; Desktop file format version " desktop-file-version "\n"
872
";; Emacs version " emacs-version "\n")
873
(save-excursion (run-hooks 'desktop-save-hook))
874
(goto-char (point-max))
875
(insert "\n;; Global section:\n")
876
(mapc (function desktop-outvar) desktop-globals-to-save)
877
(when (memq 'kill-ring desktop-globals-to-save)
879
"(setq kill-ring-yank-pointer (nthcdr "
880
(int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
883
(insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
884
(dolist (l (mapcar 'desktop-buffer-info (buffer-list)))
885
(when (apply 'desktop-save-buffer-p l)
887
(if (or (not (integerp eager))
890
(setq eager (1- eager))))
891
"desktop-create-buffer"
892
"desktop-append-buffer-args")
894
desktop-file-version)
896
(insert "\n " (desktop-value-to-string e)))
899
(setq default-directory dirname)
900
(let ((coding-system-for-write 'emacs-mule))
901
(write-region (point-min) (point-max) (desktop-full-file-name) nil 'nomessage))
902
;; We remember when it was modified (which is presumably just now).
903
(setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name)))))))))
812
905
;; ----------------------------------------------------------------------------
856
949
;; Default: Home directory.
858
951
(if (file-exists-p (desktop-full-file-name))
859
;; Desktop file found, process it.
860
(let ((desktop-first-buffer nil)
861
(desktop-buffer-ok-count 0)
862
(desktop-buffer-fail-count 0)
863
;; Avoid desktop saving during evaluation of desktop buffer.
866
;; Evaluate desktop buffer.
867
(load (desktop-full-file-name) t t t)
868
;; `desktop-create-buffer' puts buffers at end of the buffer list.
869
;; We want buffers existing prior to evaluating the desktop (and not reused)
870
;; to be placed at the end of the buffer list, so we move them here.
872
(nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
873
(switch-to-buffer (car (buffer-list)))
874
(run-hooks 'desktop-delay-hook)
875
(setq desktop-delay-hook nil)
876
(run-hooks 'desktop-after-read-hook)
877
(message "Desktop: %d buffer%s restored%s%s."
878
desktop-buffer-ok-count
879
(if (= 1 desktop-buffer-ok-count) "" "s")
880
(if (< 0 desktop-buffer-fail-count)
881
(format ", %d failed to restore" desktop-buffer-fail-count)
883
(if desktop-buffer-args-list
884
(format ", %d to restore lazily"
885
(length desktop-buffer-args-list))
952
;; Desktop file found, but is it already in use?
953
(let ((desktop-first-buffer nil)
954
(desktop-buffer-ok-count 0)
955
(desktop-buffer-fail-count 0)
956
(owner (desktop-owner))
957
;; Avoid desktop saving during evaluation of desktop buffer.
960
(memq desktop-load-locked-desktop '(nil ask))
961
(or (null desktop-load-locked-desktop)
962
(not (y-or-n-p (format "Warning: desktop file appears to be in use by PID %s.\n\
963
Using it may cause conflicts. Use it anyway? " owner)))))
965
(let ((default-directory desktop-dirname))
966
(run-hooks 'desktop-not-loaded-hook))
967
(setq desktop-dirname nil)
968
(message "Desktop file in use; not loaded."))
970
;; Evaluate desktop buffer and remember when it was modified.
971
(load (desktop-full-file-name) t t t)
972
(setq desktop-file-modtime (nth 5 (file-attributes (desktop-full-file-name))))
973
;; If it wasn't already, mark it as in-use, to bother other
974
;; desktop instances.
978
(file-error (message "Couldn't record use of desktop file")
981
;; `desktop-create-buffer' puts buffers at end of the buffer list.
982
;; We want buffers existing prior to evaluating the desktop (and
983
;; not reused) to be placed at the end of the buffer list, so we
986
(nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
987
(switch-to-buffer (car (buffer-list)))
988
(run-hooks 'desktop-delay-hook)
989
(setq desktop-delay-hook nil)
990
(run-hooks 'desktop-after-read-hook)
991
(message "Desktop: %d buffer%s restored%s%s."
992
desktop-buffer-ok-count
993
(if (= 1 desktop-buffer-ok-count) "" "s")
994
(if (< 0 desktop-buffer-fail-count)
995
(format ", %d failed to restore" desktop-buffer-fail-count)
997
(if desktop-buffer-args-list
998
(format ", %d to restore lazily"
999
(length desktop-buffer-args-list))
888
1002
;; No desktop file found.
890
1004
(let ((default-directory desktop-dirname))
946
1060
desktop-buffer-name
947
1061
desktop-buffer-misc)
948
1062
"Restore a file buffer."
949
(if desktop-buffer-file-name
950
(if (or (file-exists-p desktop-buffer-file-name)
951
(let ((msg (format "Desktop: File \"%s\" no longer exists."
952
desktop-buffer-file-name)))
953
(if desktop-missing-file-warning
954
(y-or-n-p (concat msg " Re-create buffer? "))
957
(let* ((auto-insert nil) ; Disable auto insertion
958
(coding-system-for-read
959
(or coding-system-for-read
960
(cdr (assq 'buffer-file-coding-system
961
desktop-buffer-locals))))
962
(buf (find-file-noselect desktop-buffer-file-name)))
964
(switch-to-buffer buf)
965
(error (pop-to-buffer buf)))
966
(and (not (eq major-mode desktop-buffer-major-mode))
967
(functionp desktop-buffer-major-mode)
968
(funcall desktop-buffer-major-mode))
1063
(when desktop-buffer-file-name
1064
(if (or (file-exists-p desktop-buffer-file-name)
1065
(let ((msg (format "Desktop: File \"%s\" no longer exists."
1066
desktop-buffer-file-name)))
1067
(if desktop-missing-file-warning
1068
(y-or-n-p (concat msg " Re-create buffer? "))
1071
(let* ((auto-insert nil) ; Disable auto insertion
1072
(coding-system-for-read
1073
(or coding-system-for-read
1074
(cdr (assq 'buffer-file-coding-system
1075
desktop-buffer-locals))))
1076
(buf (find-file-noselect desktop-buffer-file-name)))
1078
(switch-to-buffer buf)
1079
(error (pop-to-buffer buf)))
1080
(and (not (eq major-mode desktop-buffer-major-mode))
1081
(functionp desktop-buffer-major-mode)
1082
(funcall desktop-buffer-major-mode))
972
1086
(defun desktop-load-file (function)
973
1087
"Load the file where auto loaded FUNCTION is defined."
1062
1176
(error (message "%s" (error-message-string err)) 1))))
1063
1177
(when desktop-buffer-mark
1064
1178
(if (consp desktop-buffer-mark)
1066
(set-mark (car desktop-buffer-mark))
1067
(setq mark-active (car (cdr desktop-buffer-mark))))
1180
(set-mark (car desktop-buffer-mark))
1181
(setq mark-active (car (cdr desktop-buffer-mark))))
1068
1182
(set-mark desktop-buffer-mark)))
1069
1183
;; Never override file system if the file really is read-only marked.
1070
(if desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
1184
(when desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
1071
1185
(while desktop-buffer-locals
1072
1186
(let ((this (car desktop-buffer-locals)))
1073
1187
(if (consp this)
1074
;; an entry of this form `(symbol . value)'
1076
(make-local-variable (car this))
1077
(set (car this) (cdr this)))
1188
;; an entry of this form `(symbol . value)'
1190
(make-local-variable (car this))
1191
(set (car this) (cdr this)))
1078
1192
;; an entry of the form `symbol'
1079
1193
(make-local-variable this)
1080
1194
(makunbound this)))