Add arch taglines
[bpt/emacs.git] / lisp / desktop.el
CommitLineData
6343ed61
RS
1;;; desktop.el --- save partial status of Emacs when killed
2
291e3b68
GM
3;; Copyright (C) 1993, 1994, 1995, 1997, 2000, 2001
4;; Free Software Foundation, Inc.
6343ed61
RS
5
6;; Author: Morten Welinder <terra@diku.dk>
16906a65 7;; Maintainter: Lars Hansen <larsh@math.ku.dk>
3ea96cac 8;; Keywords: convenience
b6105c76 9;; Favourite-brand-of-beer: None, I hate beer.
6343ed61
RS
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
15;; the Free Software Foundation; either version 2, or (at your option)
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
b578f267
EN
24;; along with GNU Emacs; see the file COPYING. If not, write to the
25;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
26;; Boston, MA 02111-1307, USA.
6343ed61
RS
27
28;;; Commentary:
29
0b9bd504
RS
30;; Save the Desktop, i.e.,
31;; - some global variables
32;; - the list of buffers with associated files. For each buffer also
33;; - the major mode
34;; - the default directory
35;; - the point
36;; - the mark & mark-active
37;; - buffer-read-only
ec4c6f22 38;; - some local variables
6343ed61 39
c5b31c4d
LH
40;; To use this, use customize to turn on desktop-save-mode or add the
41;; following line somewhere in your .emacs file:
0b9bd504 42;;
c5b31c4d 43;; (desktop-save-mode 1)
0b9bd504 44;;
c5b31c4d
LH
45;; For further usage information, look at the section
46;; "Saving Emacs Sessions" in the GNU Emacs Manual.
47
48;; When the desktop module is loaded, the function `desktop-kill' is
49;; added to the `kill-emacs-hook'. This function is responsible for
50;; saving the desktop when Emacs is killed. Furthermore an anonymous
51;; function is added to the `after-init-hook'. This function is
52;; responsible for loading the desktop when Emacs is started.
ec4c6f22 53
47640244
GM
54;; Some words on minor modes: Most minor modes are controlled by
55;; buffer-local variables, which have a standard save / restore
56;; mechanism. To handle all minor modes, we take the following
57;; approach: (1) check whether the variable name from
58;; `minor-mode-alist' is also a function; and (2) use translation
59;; table `desktop-minor-mode-table' in the case where the two names
60;; are not the same.
61
ec4c6f22
RS
62;; By the way: don't use desktop.el to customize Emacs -- the file .emacs
63;; in your home directory is used for that. Saving global default values
64;; for buffers is an example of misuse.
65
0b9bd504
RS
66;; PLEASE NOTE: The kill ring can be saved as specified by the variable
67;; `desktop-globals-to-save' (by default it isn't). This may result in saving
68;; things you did not mean to keep. Use M-x desktop-clear RET.
ec4c6f22 69
fa379636
KH
70;; Thanks to hetrick@phys.uva.nl (Jim Hetrick) for useful ideas.
71;; avk@rtsg.mot.com (Andrew V. Klein) for a dired tip.
72;; chris@tecc.co.uk (Chris Boucher) for a mark tip.
73;; f89-kam@nada.kth.se (Klas Mellbourn) for a mh-e tip.
74;; kifer@sbkifer.cs.sunysb.edu (M. Kifer) for a bug hunt.
f4c73d07 75;; treese@lcs.mit.edu (Win Treese) for ange-ftp tips.
253406b9 76;; pot@cnuce.cnr.it (Francesco Potorti`) for misc. tips.
0b9bd504
RS
77;; ---------------------------------------------------------------------------
78;; TODO:
79;;
80;; Save window configuration.
81;; Recognize more minor modes.
82;; Save mark rings.
6343ed61
RS
83
84;;; Code:
85
4a2fce7a 86(defvar desktop-file-version "206"
05f1c4ec 87 "Version number of desktop file format.
4a2fce7a
RS
88Written into the desktop file and used at desktop read to provide
89backward compatibility.")
90
ec4c6f22 91;; ----------------------------------------------------------------------------
0b9bd504
RS
92;; USER OPTIONS -- settings you might want to play with.
93;; ----------------------------------------------------------------------------
bbf5eb28
RS
94
95(defgroup desktop nil
96 "Save status of Emacs when you exit."
97 :group 'frames)
98
c5b31c4d
LH
99;;;###autoload
100(define-minor-mode desktop-save-mode
101 "Toggle desktop saving mode.
102With numeric ARG, turn desktop saving on if ARG is positive, off
103otherwise. See variable `desktop-save' for a description of when the
104desktop is saved."
105 :global t
106 :group 'desktop)
107
108;; Maintained for backward compatibility
109(defvaralias 'desktop-enable 'desktop-save-mode)
110(make-obsolete-variable 'desktop-enable 'desktop-save-mode)
813dbb2d 111
4a2fce7a 112(defcustom desktop-save 'ask-if-new
c5b31c4d
LH
113 "*Specifies whether the desktop should be saved when it is killed.
114A desktop is killed when the user changes desktop or quits Emacs.
115Possible values are:
4a2fce7a
RS
116 t -- always save.
117 ask -- always ask.
118 ask-if-new -- ask if no desktop file exists, otherwise just save.
119 ask-if-exists -- ask if desktop file exists, otherwise don't save.
120 if-exists -- save if desktop file exists, otherwise don't save.
121 nil -- never save.
c5b31c4d
LH
122The desktop is never saved when `desktop-save-mode' is nil.
123The variables `desktop-directory' and `desktop-base-file-name'
124determine where the desktop is saved."
4a2fce7a
RS
125 :type '(choice
126 (const :tag "Always save" t)
127 (const :tag "Always ask" ask)
128 (const :tag "Ask if desktop file is new, else do save" ask-if-new)
129 (const :tag "Ask if desktop file exists, else don't save" ask-if-exists)
130 (const :tag "Save if desktop file exists, else don't" if-exists)
131 (const :tag "Never save" nil))
132 :group 'desktop)
133
134(defcustom desktop-base-file-name
fc715501 135 (convert-standard-filename ".emacs.desktop")
c5b31c4d 136 "Name of file for Emacs desktop, excluding the directory part."
813dbb2d
RS
137 :type 'file
138 :group 'desktop)
7065d42f 139(defvaralias 'desktop-basefilename 'desktop-base-file-name)
6343ed61 140
4a2fce7a
RS
141(defcustom desktop-path '("." "~")
142 "List of directories to search for the desktop file.
143The base name of the file is specified in `desktop-base-file-name'."
144 :type '(repeat directory)
145 :group 'desktop)
146
bbf5eb28 147(defcustom desktop-missing-file-warning nil
ebb39555
LH
148 "*If non-nil then `desktop-read' asks if a non-existent file should be recreated.
149Also pause for a moment to display message about errors signaled in
150`desktop-buffer-mode-handlers'.
151
152If nil, just print error messages in the message buffer."
bbf5eb28
RS
153 :type 'boolean
154 :group 'desktop)
6343ed61 155
4a2fce7a 156(defcustom desktop-no-desktop-file-hook nil
c5b31c4d 157 "Normal hook run when `desktop-read' can't find a desktop file.
4a2fce7a
RS
158May e.g. be used to show a dired buffer."
159 :type 'hook
160 :group 'desktop)
161
162(defcustom desktop-after-read-hook nil
05f1c4ec 163 "Normal hook run after a successful `desktop-read'.
4a2fce7a
RS
164May e.g. be used to show a buffer list."
165 :type 'hook
166 :group 'desktop)
167
168(defcustom desktop-save-hook nil
c5b31c4d 169 "Normal hook run before the desktop is saved in a desktop file.
4a2fce7a
RS
170This is useful for truncating history lists, for example."
171 :type 'hook
172 :group 'desktop)
173
174(defcustom desktop-globals-to-save '(
175 desktop-missing-file-warning
176 tags-file-name
177 tags-table-list
178 search-ring
179 regexp-search-ring
180 register-alist)
c5b31c4d
LH
181 "List of global variables saved by `desktop-save'.
182An element may be variable name (a symbol) or a cons cell of the form
183\(VAR . MAX-SIZE), which means to truncate VAR's value to at most
184MAX-SIZE elements (if the value is a list) before saving the value.
4a2fce7a
RS
185Feature: Saving `kill-ring' implies saving `kill-ring-yank-pointer'."
186 :type '(repeat (restricted-sexp :match-alternatives (symbolp consp)))
187 :group 'desktop)
188
189(defcustom desktop-globals-to-clear '(
190 kill-ring
191 kill-ring-yank-pointer
192 search-ring
193 search-ring-yank-pointer
194 regexp-search-ring
195 regexp-search-ring-yank-pointer)
c5b31c4d 196 "List of global variables to clear by `desktop-clear'.
4a2fce7a
RS
197An element may be variable name (a symbol) or a cons cell of the form
198\(VAR . FORM). Symbols are set to nil and for cons cells VAR is set
199to the value obtained by evaluateing FORM."
200 :type '(repeat (restricted-sexp :match-alternatives (symbolp consp)))
201 :group 'desktop)
202
203(defcustom desktop-clear-preserve-buffers-regexp
c5b31c4d 204 "^\\(\\*scratch\\*\\|\\*Messages\\*\\|\\*tramp/.+\\*\\)$"
4a2fce7a
RS
205 "Regexp identifying buffers that `desktop-clear' should not delete."
206 :type 'regexp
207 :group 'desktop)
208
209;; Maintained for backward compatibility
c5b31c4d
LH
210(defcustom desktop-clear-preserve-buffers nil
211 "*List of buffer names that `desktop-clear' should not delete.
05f1c4ec 212This variable is maintained for backward compatibility only."
4a2fce7a
RS
213 :type '(repeat string)
214 :group 'desktop)
c5b31c4d
LH
215(make-obsolete-variable 'desktop-clear-preserve-buffers
216 'desktop-clear-preserve-buffers-regexp)
4a2fce7a 217
c5b31c4d 218(defcustom desktop-locals-to-save '(
4a2fce7a
RS
219 desktop-locals-to-save ; Itself! Think it over.
220 truncate-lines
221 case-fold-search
222 case-replace
223 fill-column
224 overwrite-mode
225 change-log-default-name
bd382a27
LH
226 line-number-mode
227 buffer-file-coding-system)
577ed2b2 228 "List of local variables to save for each buffer.
c5b31c4d
LH
229The variables are saved only when they really are local."
230 :type '(repeat symbol)
231 :group 'desktop)
de9e2828 232(make-variable-buffer-local 'desktop-locals-to-save)
ec4c6f22 233
0b9bd504 234;; We skip .log files because they are normally temporary.
a7acbbe4 235;; (ftp) files because they require passwords and whatnot.
0b9bd504 236;; TAGS files to save time (tags-file-name is saved instead).
bbf5eb28 237(defcustom desktop-buffers-not-to-save
4a2fce7a
RS
238 "\\(^nn\\.a[0-9]+\\|\\.log\\|(ftp)\\|^tags\\|^TAGS\\)$"
239 "Regexp identifying buffers that are to be excluded from saving."
240 :type 'regexp
241 :group 'desktop)
6343ed61 242
c5b31c4d 243;; Skip tramp and ange-ftp files
bbf5eb28 244(defcustom desktop-files-not-to-save
fa379636 245 "^/[^/:]*:"
bbf5eb28
RS
246 "Regexp identifying files whose buffers are to be excluded from saving."
247 :type 'regexp
248 :group 'desktop)
fa379636 249
80f9f3db
SM
250(defcustom desktop-modes-not-to-save nil
251 "List of major modes whose buffers should not be saved."
252 :type '(repeat symbol)
253 :group 'desktop)
254
4a2fce7a
RS
255(defcustom desktop-file-name-format 'absolute
256 "*Format in which desktop file names should be saved.
257Possible values are:
258 absolute -- Absolute file name.
259 tilde -- Relative to ~.
260 local -- Relative to directory of desktop file."
261 :type '(choice (const absolute) (const tilde) (const local))
bbf5eb28 262 :group 'desktop)
569c754e 263
e5780ae1 264;;;###autoload
ebb39555
LH
265(defvar desktop-save-buffer nil
266 "When non-nil, save buffer status in desktop file.
e5780ae1 267This variable becomes buffer local when set.
ebb39555
LH
268
269If the value is a function, it called by `desktop-save' with argument
270DESKTOP-DIRNAME to obtain auxiliary information to saved in the desktop
271file along with the state of the buffer for which it was called.
b6b70cda 272
c5b31c4d 273When file names are returned, they should be formatted using the call
e5780ae1 274\"(desktop-file-name FILE-NAME DESKTOP-DIRNAME)\".
4a2fce7a 275
e5780ae1 276Later, when `desktop-read' calls a function in `desktop-buffer-mode-handlers'
ebb39555
LH
277to restore the buffer, the auxiliary information is passed as the argument
278DESKTOP-BUFFER-MISC.")
279(make-variable-buffer-local 'desktop-save-buffer)
280(make-obsolete-variable 'desktop-buffer-modes-to-save
281 'desktop-save-buffer)
e5780ae1 282(make-obsolete-variable 'desktop-buffer-misc-functions
ebb39555 283 'desktop-save-buffer)
b6b70cda 284
e5780ae1
LH
285(defcustom desktop-buffer-mode-handlers '(
286 (dired-mode . dired-restore-desktop-buffer)
287 (rmail-mode . rmail-restore-desktop-buffer)
288 (mh-folder-mode . mh-restore-desktop-buffer)
289 (Info-mode . Info-restore-desktop-buffer))
290 "Alist of major mode specific functions to restore a desktop buffer.
291Functions are called by `desktop-read'. List elements must have the form
55775448 292\(MAJOR-MODE . RESTORE-BUFFER-FUNCTION).
e5780ae1
LH
293
294Buffers with a major mode not specified here, are restored by the default
295handler `desktop-restore-file-buffer'.
296
55775448 297Handlers are called with argument list
4a2fce7a 298
9bcabb45 299 (DESKTOP-BUFFER-FILE-NAME DESKTOP-BUFFER-NAME DESKTOP-BUFFER-MISC)
e5780ae1
LH
300
301Furthermore, they may use the following variables:
302
303 desktop-file-version
4a2fce7a
RS
304 desktop-buffer-major-mode
305 desktop-buffer-minor-modes
306 desktop-buffer-point
307 desktop-buffer-mark
308 desktop-buffer-read-only
4a2fce7a
RS
309 desktop-buffer-locals
310
e5780ae1 311If a handler returns a buffer, then the saved mode settings
0a8c8225 312and variable values for that buffer are copied into it."
e5780ae1 313 :type 'alist
bbf5eb28 314 :group 'desktop)
ec4c6f22 315
e5780ae1
LH
316(put 'desktop-buffer-mode-handlers 'risky-local-variable t)
317(make-obsolete-variable 'desktop-buffer-handlers
318 'desktop-buffer-mode-handlers)
b6b70cda 319
47640244
GM
320(defcustom desktop-minor-mode-table
321 '((auto-fill-function auto-fill-mode)
322 (vc-mode nil))
323 "Table mapping minor mode variables to minor mode functions.
324Each entry has the form (NAME RESTORE-FUNCTION).
325NAME is the name of the buffer-local variable indicating that the minor
326mode is active. RESTORE-FUNCTION is the function to activate the minor mode.
327called. RESTORE-FUNCTION nil means don't try to restore the minor mode.
328Only minor modes for which the name of the buffer-local variable
7bfa55b3 329and the name of the minor mode function are different have to be added to
47640244
GM
330this table."
331 :type 'sexp
332 :group 'desktop)
333
0b9bd504
RS
334;; ----------------------------------------------------------------------------
335(defvar desktop-dirname nil
c5b31c4d 336 "The directory in which the desktop file should be saved.")
6343ed61
RS
337
338(defconst desktop-header
0b9bd504
RS
339";; --------------------------------------------------------------------------
340;; Desktop File for Emacs
341;; --------------------------------------------------------------------------
6343ed61 342" "*Header to place in Desktop file.")
de9e2828
RS
343
344(defvar desktop-delay-hook nil
345 "Hooks run after all buffers are loaded; intended for internal use.")
813dbb2d 346
0b9bd504 347;; ----------------------------------------------------------------------------
05f1c4ec 348(defun desktop-truncate (list n)
ec4c6f22 349 "Truncate LIST to at most N elements destructively."
05f1c4ec 350 (let ((here (nthcdr (1- n) list)))
ec4c6f22 351 (if (consp here)
de9e2828 352 (setcdr here nil))))
f9be4574 353
4a2fce7a 354;; ----------------------------------------------------------------------------
f9be4574
RS
355(defun desktop-clear ()
356 "Empty the Desktop.
c5b31c4d
LH
357This kills all buffers except for internal ones and those matching
358`desktop-clear-preserve-buffers-regexp' or listed in
359`desktop-clear-preserve-buffers'. Furthermore, it clears the
4a2fce7a 360variables listed in `desktop-globals-to-clear'."
6343ed61 361 (interactive)
4a2fce7a
RS
362 (dolist (var desktop-globals-to-clear)
363 (if (symbolp var)
364 (eval `(setq-default ,var nil))
365 (eval `(setq-default ,(car var) ,(cdr var)))))
f9be4574
RS
366 (let ((buffers (buffer-list)))
367 (while buffers
4a2fce7a
RS
368 (let ((bufname (buffer-name (car buffers))))
369 (or
370 (null bufname)
371 (string-match desktop-clear-preserve-buffers-regexp bufname)
372 (member bufname desktop-clear-preserve-buffers)
373 ;; Don't kill buffers made for internal purposes.
374 (and (not (equal bufname "")) (eq (aref bufname 0) ?\ ))
375 (kill-buffer (car buffers))))
f9be4574 376 (setq buffers (cdr buffers))))
b6105c76 377 (delete-other-windows))
4a2fce7a 378
0b9bd504 379;; ----------------------------------------------------------------------------
de9e2828 380(add-hook 'kill-emacs-hook 'desktop-kill)
ec4c6f22 381
6343ed61 382(defun desktop-kill ()
c5b31c4d 383 "If `desktop-save-mode' is non-nil, do what `desktop-save' says to do.
4a2fce7a
RS
384If the desktop should be saved and `desktop-dirname'
385is nil, ask the user where to save the desktop."
386 (when
387 (and
c5b31c4d 388 desktop-save-mode
73b0b745 389 (let ((exists (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))))
4a2fce7a 390 (or
d12d9396 391 (eq desktop-save t)
4a2fce7a
RS
392 (and exists (memq desktop-save '(ask-if-new if-exists)))
393 (and
394 (or
395 (memq desktop-save '(ask ask-if-new))
396 (and exists (eq desktop-save 'ask-if-exists)))
397 (y-or-n-p "Save desktop? ")))))
398 (unless desktop-dirname
399 (setq desktop-dirname
73b0b745
JB
400 (file-name-as-directory
401 (expand-file-name
402 (call-interactively
403 (lambda (dir) (interactive "DDirectory for desktop file: ") dir))))))
4a2fce7a
RS
404 (condition-case err
405 (desktop-save desktop-dirname)
406 (file-error
407 (unless (yes-or-no-p "Error while saving the desktop. Ignore? ")
408 (signal (car err) (cdr err)))))))
409
0b9bd504 410;; ----------------------------------------------------------------------------
ed2f7fc8
RS
411(defun desktop-list* (&rest args)
412 (if (null (cdr args))
413 (car args)
414 (setq args (nreverse args))
415 (let ((value (cons (nth 1 args) (car args))))
416 (setq args (cdr (cdr args)))
417 (while args
418 (setq value (cons (car args) value))
419 (setq args (cdr args)))
420 value)))
421
4a2fce7a 422;; ----------------------------------------------------------------------------
05f1c4ec 423(defun desktop-internal-v2s (value)
577ed2b2
RS
424 "Convert VALUE to a pair (QUOTE . TXT); (eval (read TXT)) gives VALUE.
425TXT is a string that when read and evaluated yields value.
426QUOTE may be `may' (value may be quoted),
427`must' (values must be quoted), or nil (value may not be quoted)."
de9e2828 428 (cond
05f1c4ec
JB
429 ((or (numberp value) (null value) (eq t value))
430 (cons 'may (prin1-to-string value)))
431 ((stringp value)
432 (let ((copy (copy-sequence value)))
95bf6435
RS
433 (set-text-properties 0 (length copy) nil copy)
434 ;; Get rid of text properties because we cannot read them
435 (cons 'may (prin1-to-string copy))))
05f1c4ec
JB
436 ((symbolp value)
437 (cons 'must (prin1-to-string value)))
438 ((vectorp value)
de9e2828
RS
439 (let* ((special nil)
440 (pass1 (mapcar
441 (lambda (el)
442 (let ((res (desktop-internal-v2s el)))
443 (if (null (car res))
444 (setq special t))
445 res))
05f1c4ec 446 value)))
de9e2828
RS
447 (if special
448 (cons nil (concat "(vector "
449 (mapconcat (lambda (el)
450 (if (eq (car el) 'must)
451 (concat "'" (cdr el))
452 (cdr el)))
453 pass1
454 " ")
455 ")"))
456 (cons 'may (concat "[" (mapconcat 'cdr pass1 " ") "]")))))
05f1c4ec
JB
457 ((consp value)
458 (let ((p value)
b4929f75 459 newlist
ed2f7fc8 460 use-list*
b4929f75
RS
461 anynil)
462 (while (consp p)
463 (let ((q.txt (desktop-internal-v2s (car p))))
464 (or anynil (setq anynil (null (car q.txt))))
465 (setq newlist (cons q.txt newlist)))
466 (setq p (cdr p)))
467 (if p
468 (let ((last (desktop-internal-v2s p))
469 (el (car newlist)))
ed2f7fc8
RS
470 (or anynil (setq anynil (null (car last))))
471 (or anynil
472 (setq newlist (cons '(must . ".") newlist)))
473 (setq use-list* t)
474 (setq newlist (cons last newlist))))
b4929f75
RS
475 (setq newlist (nreverse newlist))
476 (if anynil
477 (cons nil
ed2f7fc8 478 (concat (if use-list* "(desktop-list* " "(list ")
b4929f75
RS
479 (mapconcat (lambda (el)
480 (if (eq (car el) 'must)
481 (concat "'" (cdr el))
482 (cdr el)))
483 newlist
484 " ")
485 ")"))
486 (cons 'must
487 (concat "(" (mapconcat 'cdr newlist " ") ")")))))
05f1c4ec 488 ((subrp value)
de9e2828 489 (cons nil (concat "(symbol-function '"
05f1c4ec 490 (substring (prin1-to-string value) 7 -1)
de9e2828 491 ")")))
05f1c4ec
JB
492 ((markerp value)
493 (let ((pos (prin1-to-string (marker-position value)))
494 (buf (prin1-to-string (buffer-name (marker-buffer value)))))
de9e2828
RS
495 (cons nil (concat "(let ((mk (make-marker)))"
496 " (add-hook 'desktop-delay-hook"
497 " (list 'lambda '() (list 'set-marker mk "
498 pos " (get-buffer " buf ")))) mk)"))))
499 (t ; save as text
fa379636 500 (cons 'may "\"Unprintable entity\""))))
de9e2828 501
4a2fce7a 502;; ----------------------------------------------------------------------------
05f1c4ec 503(defun desktop-value-to-string (value)
577ed2b2
RS
504 "Convert VALUE to a string that when read evaluates to the same value.
505Not all types of values are supported."
de9e2828
RS
506 (let* ((print-escape-newlines t)
507 (float-output-format nil)
05f1c4ec 508 (quote.txt (desktop-internal-v2s value))
de9e2828
RS
509 (quote (car quote.txt))
510 (txt (cdr quote.txt)))
511 (if (eq quote 'must)
512 (concat "'" txt)
513 txt)))
4a2fce7a 514
ec4c6f22 515;; ----------------------------------------------------------------------------
0e7c8611
RS
516(defun desktop-outvar (varspec)
517 "Output a setq statement for variable VAR to the desktop file.
518The argument VARSPEC may be the variable name VAR (a symbol),
519or a cons cell of the form (VAR . MAX-SIZE),
520which means to truncate VAR's value to at most MAX-SIZE elements
521\(if the value is a list) before saving the value."
522 (let (var size)
523 (if (consp varspec)
524 (setq var (car varspec) size (cdr varspec))
525 (setq var varspec))
526 (if (boundp var)
527 (progn
528 (if (and (integerp size)
529 (> size 0)
530 (listp (eval var)))
47640244 531 (desktop-truncate (eval var) size))
0e7c8611
RS
532 (insert "(setq "
533 (symbol-name var)
534 " "
535 (desktop-value-to-string (symbol-value var))
536 ")\n")))))
4a2fce7a 537
0b9bd504 538;; ----------------------------------------------------------------------------
ec4c6f22 539(defun desktop-save-buffer-p (filename bufname mode &rest dummy)
ebb39555 540 "Return t if buffer should have its state saved in the desktop file.
0b9bd504 541FILENAME is the visited file name, BUFNAME is the buffer name, and
6343ed61 542MODE is the major mode."
fa379636 543 (let ((case-fold-search nil))
80f9f3db 544 (and (not (string-match desktop-buffers-not-to-save bufname))
ebb39555
LH
545 (not (memq mode desktop-modes-not-to-save))
546 (or (and filename
547 (not (string-match desktop-files-not-to-save filename)))
548 (and (eq mode 'dired-mode)
549 (with-current-buffer bufname
550 (not (string-match desktop-files-not-to-save
551 default-directory))))
552 (and (null filename)
553 (with-current-buffer bufname desktop-save-buffer))))))
4a2fce7a 554
0b9bd504 555;; ----------------------------------------------------------------------------
4a2fce7a
RS
556(defun desktop-file-name (filename dirname)
557 "Convert FILENAME to format specified in `desktop-file-name-format'.
558DIRNAME must be the directory in which the desktop file will be saved."
559 (cond
560 ((not filename) nil)
561 ((eq desktop-file-name-format 'tilde)
562 (let ((relative-name (file-relative-name (expand-file-name filename) "~")))
563 (cond
564 ((file-name-absolute-p relative-name) relative-name)
565 ((string= "./" relative-name) "~/")
566 ((string= "." relative-name) "~")
567 (t (concat "~/" relative-name)))))
568 ((eq desktop-file-name-format 'local) (file-relative-name filename dirname))
569 (t (expand-file-name filename))))
e5714620 570
4a2fce7a 571;; ----------------------------------------------------------------------------
6343ed61 572(defun desktop-save (dirname)
c5b31c4d
LH
573 "Save the desktop in a desktop file.
574Parameter DIRNAME specifies where to save the desktop file.
575See also `desktop-base-file-name'."
6343ed61 576 (interactive "DDirectory to save desktop file in: ")
fa379636 577 (run-hooks 'desktop-save-hook)
7bcbf3c2 578 (setq dirname (file-name-as-directory (expand-file-name dirname)))
6343ed61 579 (save-excursion
73b0b745 580 (let ((filename (expand-file-name desktop-base-file-name dirname))
7bcbf3c2
JB
581 (info
582 (mapcar
583 (function
584 (lambda (b)
585 (set-buffer b)
586 (list
587 (desktop-file-name (buffer-file-name) dirname)
588 (buffer-name)
589 major-mode
590 ;; minor modes
591 (let (ret)
7bfa55b3
LH
592 (mapc
593 #'(lambda (minor-mode)
7bcbf3c2 594 (and
7bfa55b3
LH
595 (boundp minor-mode)
596 (symbol-value minor-mode)
597 (let ((special (assq minor-mode desktop-minor-mode-table)))
598 (when (or special (functionp minor-mode))
599 (setq ret
600 (cons
601 (if special (cadr special) minor-mode)
602 ret))))))
7bcbf3c2
JB
603 (mapcar #'car minor-mode-alist))
604 ret)
605 (point)
606 (list (mark t) mark-active)
607 buffer-read-only
e5780ae1 608 ;; Auxiliary information
ebb39555
LH
609 (when (functionp desktop-save-buffer)
610 (funcall desktop-save-buffer dirname))
7bcbf3c2
JB
611 (let ((locals desktop-locals-to-save)
612 (loclist (buffer-local-variables))
613 (ll))
614 (while locals
615 (let ((here (assq (car locals) loclist)))
616 (if here
617 (setq ll (cons here ll))
618 (when (member (car locals) loclist)
619 (setq ll (cons (car locals) ll)))))
620 (setq locals (cdr locals)))
621 ll))))
622 (buffer-list)))
623 (buf (get-buffer-create "*desktop*")))
6343ed61
RS
624 (set-buffer buf)
625 (erase-buffer)
fa379636 626
4a2fce7a
RS
627 (insert
628 ";; -*- coding: emacs-mule; -*-\n"
629 desktop-header
630 ";; Created " (current-time-string) "\n"
631 ";; Desktop file format version " desktop-file-version "\n"
632 ";; Emacs version " emacs-version "\n\n"
633 ";; Global section:\n")
6343ed61
RS
634 (mapcar (function desktop-outvar) desktop-globals-to-save)
635 (if (memq 'kill-ring desktop-globals-to-save)
4a2fce7a
RS
636 (insert
637 "(setq kill-ring-yank-pointer (nthcdr "
638 (int-to-string (- (length kill-ring) (length kill-ring-yank-pointer)))
639 " kill-ring))\n"))
6343ed61 640
4a2fce7a 641 (insert "\n;; Buffer section -- buffers listed in same order as in buffer list:\n")
de9e2828 642 (mapcar
4a2fce7a
RS
643 (function
644 (lambda (l)
645 (if (apply 'desktop-save-buffer-p l)
646 (progn
647 (insert "(desktop-create-buffer " desktop-file-version)
648 (mapcar
649 (function
650 (lambda (e)
651 (insert "\n " (desktop-value-to-string e))))
652 l)
653 (insert ")\n\n")))))
654 info)
6343ed61 655 (setq default-directory dirname)
4a2fce7a 656 (when (file-exists-p filename) (delete-file filename))
7642acca 657 (let ((coding-system-for-write 'emacs-mule))
4a2fce7a 658 (write-region (point-min) (point-max) filename nil 'nomessage))))
6343ed61 659 (setq desktop-dirname dirname))
4a2fce7a 660
0b9bd504 661;; ----------------------------------------------------------------------------
6343ed61 662(defun desktop-remove ()
c5b31c4d
LH
663 "Delete desktop file in `desktop-dirname'.
664This function also sets `desktop-dirname' to nil."
6343ed61 665 (interactive)
c5b31c4d
LH
666 (when desktop-dirname
667 (let ((filename (expand-file-name desktop-base-file-name desktop-dirname)))
668 (setq desktop-dirname nil)
669 (when (file-exists-p filename)
73b0b745 670 (delete-file filename)))))
c5b31c4d 671
0b9bd504 672;; ----------------------------------------------------------------------------
478653c9 673;;;###autoload
c5b31c4d
LH
674(defun desktop-read (&optional dirname)
675 "Read and process the desktop file in directory DIRNAME.
676Look for a desktop file in DIRNAME, or if DIRNAME is omitted, look in
677directories listed in `desktop-path'. If a desktop file is found, it
678is processed and `desktop-after-read-hook' is run. If no desktop file
679is found, clear the desktop and run `desktop-no-desktop-file-hook'.
680This function is a no-op when Emacs is running in batch mode.
681It returns t if a desktop file was loaded, nil otherwise."
6343ed61 682 (interactive)
4a2fce7a 683 (unless noninteractive
c5b31c4d
LH
684 (setq desktop-dirname
685 (file-name-as-directory
686 (expand-file-name
687 (or
688 ;; If DIRNAME is specified, use it.
689 (and (< 0 (length dirname)) dirname)
690 ;; Otherwise search desktop file in desktop-path.
691 (let ((dirs desktop-path))
692 (while
693 (and
694 dirs
695 (not
696 (file-exists-p (expand-file-name desktop-base-file-name (car dirs)))))
697 (setq dirs (cdr dirs)))
698 (and dirs (car dirs)))
699 ;; If not found and `desktop-path' is non-nil, use its first element.
700 (and desktop-path (car desktop-path))
701 ;; Default: Home directory.
702 "~"))))
703 (if (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))
704 ;; Desktop file found, process it.
e5780ae1
LH
705 (let ((desktop-first-buffer nil)
706 (desktop-buffer-ok-count 0)
707 (desktop-buffer-fail-count 0))
c5b31c4d
LH
708 ;; Evaluate desktop buffer.
709 (load (expand-file-name desktop-base-file-name desktop-dirname) t t t)
710 ;; `desktop-create-buffer' puts buffers at end of the buffer list.
711 ;; We want buffers existing prior to evaluating the desktop (and not reused)
712 ;; to be placed at the end of the buffer list, so we move them here.
713 (mapcar 'bury-buffer
714 (nreverse (cdr (memq desktop-first-buffer (nreverse (buffer-list))))))
715 (switch-to-buffer (car (buffer-list)))
716 (run-hooks 'desktop-delay-hook)
717 (setq desktop-delay-hook nil)
718 (run-hooks 'desktop-after-read-hook)
e5780ae1
LH
719 (message "Desktop: %d buffer%s restored%s."
720 desktop-buffer-ok-count
721 (if (= 1 desktop-buffer-ok-count) "" "s")
722 (if (< 0 desktop-buffer-fail-count)
723 (format ", %d failed to restore" desktop-buffer-fail-count)
724 ""))
c5b31c4d
LH
725 t)
726 ;; No desktop file found.
727 (desktop-clear)
728 (let ((default-directory desktop-dirname))
729 (run-hooks 'desktop-no-desktop-file-hook))
730 (message "No desktop file.")
731 nil)))
4a2fce7a 732
0b9bd504 733;; ----------------------------------------------------------------------------
c5b31c4d 734;; Maintained for backward compatibility
478653c9 735;;;###autoload
6343ed61 736(defun desktop-load-default ()
577ed2b2 737 "Load the `default' start-up library manually.
c5b31c4d 738Also inhibit further loading of it."
0b9bd504 739 (if (not inhibit-default-init) ; safety check
6343ed61
RS
740 (progn
741 (load "default" t t)
742 (setq inhibit-default-init t))))
c5b31c4d 743(make-obsolete 'desktop-load-default 'desktop-save-mode)
4a2fce7a
RS
744
745;; ----------------------------------------------------------------------------
746;;;###autoload
c5b31c4d
LH
747(defun desktop-change-dir (dirname)
748 "Change to desktop saved in DIRNAME.
749Kill the desktop as specified by variables `desktop-save-mode' and
750`desktop-save', then clear the desktop and load the desktop file in
751directory DIRNAME."
752 (interactive "DChange to directory: ")
753 (setq dirname (file-name-as-directory (expand-file-name dirname desktop-dirname)))
4a2fce7a
RS
754 (desktop-kill)
755 (desktop-clear)
c5b31c4d
LH
756 (desktop-read dirname))
757
758;; ----------------------------------------------------------------------------
4a2fce7a 759;;;###autoload
c5b31c4d
LH
760(defun desktop-save-in-desktop-dir ()
761 "Save the desktop in directory `desktop-dirname'."
4a2fce7a
RS
762 (interactive)
763 (if desktop-dirname
764 (desktop-save desktop-dirname)
765 (call-interactively 'desktop-save))
766 (message "Desktop saved in %s" desktop-dirname))
767
768;; ----------------------------------------------------------------------------
769;;;###autoload
770(defun desktop-revert ()
771 "Revert to the last loaded desktop."
772 (interactive)
c5b31c4d
LH
773 (unless desktop-dirname
774 (error "Unknown desktop directory"))
775 (unless (file-exists-p (expand-file-name desktop-base-file-name desktop-dirname))
776 (error "No desktop file found"))
777 (desktop-clear)
778 (desktop-read desktop-dirname))
4a2fce7a 779
0b9bd504 780;; ----------------------------------------------------------------------------
e5780ae1
LH
781(defun desktop-restore-file-buffer (desktop-buffer-file-name
782 desktop-buffer-name
783 desktop-buffer-misc)
784 "Restore a file buffer."
785 (eval-when-compile ; Just to silence the byte compiler
786 (defvar desktop-buffer-major-mode)
787 (defvar desktop-buffer-locals))
569c754e
RS
788 (if desktop-buffer-file-name
789 (if (or (file-exists-p desktop-buffer-file-name)
e5780ae1
LH
790 (let ((msg (format "Desktop: File \"%s\" no longer exists."
791 desktop-buffer-file-name)))
792 (if desktop-missing-file-warning
793 (y-or-n-p (concat msg " Re-create? "))
794 (message msg)
795 nil)))
bd382a27
LH
796 (let* ((auto-insert nil) ; Disable auto insertion
797 (coding-system-for-read
798 (or coding-system-for-read
799 (cdr (assq 'buffer-file-coding-system
800 desktop-buffer-locals))))
801 (buf (find-file-noselect desktop-buffer-file-name)))
80f9f3db
SM
802 (condition-case nil
803 (switch-to-buffer buf)
0a8c8225 804 (error (pop-to-buffer buf)))
84b538ec
JB
805 (and (not (eq major-mode desktop-buffer-major-mode))
806 (functionp desktop-buffer-major-mode)
807 (funcall desktop-buffer-major-mode))
0a8c8225 808 buf)
e5780ae1 809 nil)))
4a2fce7a 810
0b9bd504 811;; ----------------------------------------------------------------------------
eb982898
JL
812;; Create a buffer, load its file, set its mode, ...;
813;; called from Desktop file only.
1d500ca6 814
4a2fce7a
RS
815(eval-when-compile ; Just to silence the byte compiler
816 (defvar desktop-first-buffer) ;; Dynamically bound in `desktop-read'
817)
818
819(defun desktop-create-buffer (
820 desktop-file-version
821 desktop-buffer-file-name
822 desktop-buffer-name
823 desktop-buffer-major-mode
824 desktop-buffer-minor-modes
825 desktop-buffer-point
826 desktop-buffer-mark
827 desktop-buffer-read-only
828 desktop-buffer-misc
829 &optional
830 desktop-buffer-locals)
e5780ae1
LH
831 ;; Just to silence the byte compiler. Bound locally in `desktop-read'.
832 (eval-when-compile
833 (defvar desktop-buffer-ok-count)
834 (defvar desktop-buffer-fail-count))
4a2fce7a
RS
835 ;; To make desktop files with relative file names possible, we cannot
836 ;; allow `default-directory' to change. Therefore we save current buffer.
837 (save-current-buffer
838 (let (
839 (buffer-list (buffer-list))
e5780ae1
LH
840 (result
841 (condition-case err
842 (funcall (or (cdr (assq desktop-buffer-major-mode desktop-buffer-mode-handlers))
843 'desktop-restore-file-buffer)
844 desktop-buffer-file-name
845 desktop-buffer-name
846 desktop-buffer-misc)
847 (error
848 (message "Desktop: Can't load buffer %s: %s"
849 desktop-buffer-name (error-message-string err))
850 (when desktop-missing-file-warning (sit-for 1))
851 nil)))
4a2fce7a 852 )
e5780ae1
LH
853 (if (bufferp result)
854 (setq desktop-buffer-ok-count (1+ desktop-buffer-ok-count))
855 (setq desktop-buffer-fail-count (1+ desktop-buffer-fail-count))
856 (setq result nil))
4a2fce7a 857 (unless (bufferp result) (setq result nil))
7bcbf3c2
JB
858 ;; Restore buffer list order with new buffer at end. Don't change
859 ;; the order for old desktop files (old desktop module behaviour).
4a2fce7a 860 (unless (< desktop-file-version 206)
7bcbf3c2
JB
861 (mapcar 'bury-buffer buffer-list)
862 (when result (bury-buffer result)))
4a2fce7a 863 (when result
7bcbf3c2
JB
864 (unless (or desktop-first-buffer (< desktop-file-version 206))
865 (setq desktop-first-buffer result))
4a2fce7a
RS
866 (set-buffer result)
867 (unless (equal (buffer-name) desktop-buffer-name)
868 (rename-buffer desktop-buffer-name))
869 ;; minor modes
870 (cond (
871 ;; backwards compatible
872 (equal '(t) desktop-buffer-minor-modes)
873 (auto-fill-mode 1))(
874 (equal '(nil) desktop-buffer-minor-modes)
875 (auto-fill-mode 0))(
876 t
877 (mapcar
878 #'(lambda (minor-mode)
879 (when (functionp minor-mode) (funcall minor-mode 1)))
880 desktop-buffer-minor-modes)))
881 ;; Even though point and mark are non-nil when written by `desktop-save'
73b0b745 882 ;; they may be modified by handlers wanting to set point or mark themselves.
e5780ae1
LH
883 (when desktop-buffer-point
884 (goto-char
885 (condition-case err
886 ;; Evaluate point. Thus point can be something like '(search-forward ...
887 (eval desktop-buffer-point)
888 (error (message "%s" (error-message-string err)) 1))))
4a2fce7a
RS
889 (when desktop-buffer-mark
890 (if (consp desktop-buffer-mark)
891 (progn
892 (set-mark (car desktop-buffer-mark))
893 (setq mark-active (car (cdr desktop-buffer-mark))))
894 (set-mark desktop-buffer-mark)))
895 ;; Never override file system if the file really is read-only marked.
896 (if desktop-buffer-read-only (setq buffer-read-only desktop-buffer-read-only))
897 (while desktop-buffer-locals
898 (let ((this (car desktop-buffer-locals)))
899 (if (consp this)
900 ;; an entry of this form `(symbol . value)'
901 (progn
902 (make-local-variable (car this))
903 (set (car this) (cdr this)))
904 ;; an entry of the form `symbol'
905 (make-local-variable this)
906 (makunbound this)))
907 (setq desktop-buffer-locals (cdr desktop-buffer-locals)))))))
ec4c6f22 908
4a2fce7a 909;; ----------------------------------------------------------------------------
ec4c6f22 910;; Backward compatibility -- update parameters to 205 standards.
569c754e
RS
911(defun desktop-buffer (desktop-buffer-file-name desktop-buffer-name
912 desktop-buffer-major-mode
2699e23e 913 mim pt mk ro tl fc cfs cr desktop-buffer-misc)
569c754e 914 (desktop-create-buffer 205 desktop-buffer-file-name desktop-buffer-name
2699e23e
RS
915 desktop-buffer-major-mode (cdr mim) pt mk ro
916 desktop-buffer-misc
ec4c6f22
RS
917 (list (cons 'truncate-lines tl)
918 (cons 'fill-column fc)
919 (cons 'case-fold-search cfs)
920 (cons 'case-replace cr)
921 (cons 'overwrite-mode (car mim)))))
84b538ec 922
0b9bd504 923;; ----------------------------------------------------------------------------
c5b31c4d 924;; When `desktop-save-mode' is non-nil and "--no-desktop" is not specified on the
4a2fce7a
RS
925;; command line, we do the rest of what it takes to use desktop, but do it
926;; after finishing loading the init file.
927;; We cannot use `command-switch-alist' to process "--no-desktop" because these
928;; functions are processed after `after-init-hook'.
929(add-hook
930 'after-init-hook
931 '(lambda ()
932 (let ((key "--no-desktop"))
933 (if (member key command-line-args)
934 (delete key command-line-args)
c5b31c4d 935 (when desktop-save-mode (desktop-read))))))
813dbb2d 936
ab1d55ea 937(provide 'desktop)
6343ed61 938
ab5796a9 939;;; arch-tag: 221907c3-1771-4fd3-9c2e-c6f700c6ede9
80f9f3db 940;;; desktop.el ends here