home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / packages / em-config.el < prev    next >
Encoding:
Text File  |  1990-07-22  |  17.2 KB  |  547 lines

  1. ;From utkcs2!emory!mephisto!uflorida!novavax!weiner Mon Jul 16 12:22:56 EDT 1990
  2. ;Article 4617 of comp.emacs:
  3. ;Path: utkcs2!emory!mephisto!uflorida!novavax!weiner
  4. ;>From: weiner@novavax.UUCP (Bob &)
  5. ;Newsgroups: comp.emacs
  6. ;Subject: Re: Problem with rmail (maybe saveconf)
  7. ;Summary: em-config.el (posted here) solves the problem
  8. ;Message-ID: <WEINER.90Jul13013029@novavax.UUCP>
  9. ;Date: 13 Jul 90 05:30:29 GMT
  10. ;References: <933@progress.UUCP> <1990Jul9.141448.15783@talos.pm.com>
  11. ;Sender: weiner@novavax.UUCP
  12. ;Organization: Motorola Inc., Boynton Beach, FL
  13. ;Lines: 529
  14. ;In-reply-to: kjones@talos.pm.com's message of 9 Jul 90 14:14:48 GMT
  15. ;
  16. ;OK, for all you people trying to restore your GNU Emacs configuration
  17. ;between sessions, here is my most recent version of em-config.el that
  18. ;handles rmail and Info buffers properly, tries to save window
  19. ;configurations and works superbly with the 'ange-ftp' package, so that
  20. ;remote files may also be restored.
  21. ;
  22. ;Enjoy, and donate to the Free Software Foundation if you can, if you
  23. ;like it.  I've actually been using it (aside from one change today) for
  24. ;months and it does everything I and most people should need.
  25. ;Unfortunately, I've never had time to properly document the internals,
  26. ;but the user interface is simplistic.
  27.  
  28. ;;!emacs
  29. ;;
  30. ;; FILE:         em-config.el
  31. ;; SUMMARY:      Save and restore Emacs configurations between sessions.
  32. ;; USAGE:        GNU Emacs Lisp Library
  33. ;;
  34. ;; AUTHOR:       Bob Weiner
  35. ;; E-MAIL:       USENET:  weiner@novavax.UUCP
  36. ;;
  37. ;; ORIG-DATE:    22-Aug-89 at 16:07:48
  38. ;; LAST-MOD:     12-Jul-90 at 13:25:52 by Bob Weiner
  39. ;;
  40. ;; Copyright (C) 1989 Bob Weiner and Free Software Foundation, Inc.
  41. ;; Available for use and distribution under the same terms as GNU Emacs.
  42. ;;
  43. ;; This file is not part of GNU Emacs.
  44. ;;
  45. ;; DESCRIPTION:  
  46. ;;
  47. ;;   Currently saves only unmodified buffers visiting files.  Also
  48. ;;   saves only limited information about buffers.  It may not work
  49. ;;   properly with subsystem buffers that visit files.  It is best to
  50. ;;   kill these before saving a configuration.
  51. ;;
  52. ;;   The exceptions are that:
  53. ;;     An Info buffer is handled properly, so it need not be deleted.
  54. ;;     Any buffer in rmail-mode is not saved.  Thus, when 'rmail' is
  55. ;;     invoked it works properly.
  56. ;;
  57. ;;   To enable reading of Emacs state from last session at the start
  58. ;;   of a new session, put the following in your personal Emacs init file:
  59. ;;
  60. ;;          (load-library "em-config")
  61. ;;          (emc-restore)
  62. ;;
  63. ;;   Follow the above with this code to enable saving of Emacs state
  64. ;;   when quitting a session:
  65. ;;
  66. ;;         (defun save-buffers-kill-emacs (&optional arg)
  67. ;;            "Offer to save each buffer, save file configuration, then kill this Emacs fork.
  68. ;;         With prefix arg, silently save all file-visiting buffers, then kill."
  69. ;;           (interactive "P")
  70. ;;           (save-some-buffers arg t)
  71. ;;           (emc-save)
  72. ;;           (kill-emacs))
  73. ;;
  74. ;; DESCRIP-END.
  75.  
  76. (defconst emc-file "~/.em-config"
  77.   "Default file into which to save Emacs session data.")
  78.  
  79. (defconst emc-wconfigs-file "~/.em-wconfigs"
  80.   "Default file into which to save Emacs window configuration ring data.")
  81.  
  82. (defvar emc-start-file nil
  83.   "Last file to read in each time the default Emacs configuration is loaded.
  84. This makes it the first file seen by the user.")
  85.  
  86. ;;; User settings to control data that is saved and restored.  The restore
  87. ;;; will fail if any of these values are changed after a 'save' but before a
  88. ;;; 'restore'.  Always change values before a 'save'.
  89. ;;;
  90. (defconst emc-save-session-data-p t)
  91. (defconst emc-restore-session-data-p nil)
  92. (defconst emc-save-buffer-data-p t)
  93. (defconst emc-save-wconfig-ring-p t)
  94. (defconst emc-save-window-data-p t)
  95.  
  96. (defun emc-save (&optional file)
  97.   "Save Emacs configuration to optional FILE or 'emc-file'."
  98.   (interactive "FSave Emacs configuration to: ")
  99.   (run-hooks 'emc-save-hook)
  100.   (if (or (equal file "") (null file))
  101.       (setq file emc-file))
  102.   (let ((standard-output (set-buffer (find-file-noselect file)))
  103.     (hdr))
  104.     (erase-buffer)
  105.     (if emc-save-session-data-p
  106.     (progn (print "SESSION DATA")
  107.            (emc-save-session-data)))
  108.     (if emc-save-buffer-data-p
  109.     (progn (print "BUFFER DATA")
  110.            (emc-save-buffer-data)))
  111.     (if emc-save-window-data-p
  112.     (progn (set-buffer standard-output)
  113.            (save-excursion
  114.          (print "WINDOW DATA")
  115.          (emc-save-window-data))))
  116.     (if emc-save-wconfig-ring-p
  117.     (progn 
  118.       ;; Save ring of window configurations ahead of current window config.
  119.       (print "WINDOW CONFIG RING")
  120.       (emc-save-wconfig-ring)))
  121.     ;; Add (goto-char (point-max)) here before adding another 'emc-save'
  122.     ;; function call.
  123.     (set-buffer standard-output)
  124.     (save-buffer)
  125.     ))
  126.  
  127. (defun emc-restore (&optional file)
  128.   "Restore Emacs configuration from optional FILE or 'emc-file'.
  129. Adds buffers to current buffer list.  Returns t if the restore is successful
  130. or the restore file does not exist, otherwise returns a string indicating
  131. the restore failure point."
  132.   (interactive "fRestore Emacs configuration from: ")
  133.   (run-hooks 'emc-restore-hook)
  134.   (let ((hdr t))
  135.     (if (or (equal file "") (null file))
  136.     (setq file emc-file))
  137.     (if (file-exists-p file)
  138.     (let ((standard-input (set-buffer (find-file-noselect emc-file))))
  139.       (goto-char (point-min))
  140.       (if (and
  141.         (or (not emc-save-session-data-p)
  142.             (and (setq hdr "SESSION DATA")
  143.              (emc-valid-hdr-p hdr)
  144.              (emc-read-session-data)))
  145.         (or (not emc-restore-session-data-p)
  146.             (emc-restore-session-data))
  147.         (or (not emc-save-buffer-data-p)
  148.             (and (setq hdr "BUFFER DATA")
  149.              (emc-valid-hdr-p hdr)
  150.              (emc-restore-buffer-data)))
  151.         (if (or (< (screen-height) emc-screen-height)
  152.             (< (screen-width) emc-screen-width))
  153.             (progn (beep)
  154.                (message "Skipping window restores, screen size shrunk since saved session.")
  155.                (sit-for 2)
  156.                t)
  157.           ;; This should come before restore of window data.
  158.           (and (or (not emc-save-wconfig-ring-p)
  159.                (and (setq hdr "WINDOW CONFIG RING")
  160.                 (emc-valid-hdr-p hdr)
  161.                 (emc-restore-wconfig-ring)))
  162.                (or (not emc-save-window-data-p)
  163.                (and (setq hdr "WINDOW DATA")
  164.                 (emc-valid-hdr-p hdr)
  165.                 (emc-restore-window-data))))))
  166.           (progn (kill-buffer standard-input)
  167.              (and emc-start-file (equal file emc-file)
  168.               (file-exists-p emc-start-file)
  169.               (find-file emc-start-file))
  170.              (setq hdr t)))
  171.       ))
  172.     hdr))
  173.  
  174.  
  175. (defun emc-save-wconfigs (&optional file)
  176.   "Save Emacs window configuration ring to optional FILE or 'emc-wconfigs-file'."
  177.   (interactive "FSave Emacs window configuration ring to: ")
  178.   (run-hooks 'emc-save-hook)
  179.   (if (or (equal file "") (null file))
  180.       (setq file emc-wconfigs-file))
  181.   (let ((standard-output (set-buffer (find-file-noselect file)))
  182.     (hdr))
  183.     (erase-buffer)
  184.     (set-buffer standard-output)
  185.     (print "WINDOW CONFIG RING")
  186.     (emc-save-wconfig-ring)
  187.     ;; Add (goto-char (point-max)) here before adding another 'emc-save'
  188.     ;; function call.
  189.     (set-buffer standard-output)
  190.     (save-buffer)
  191.     ))
  192.  
  193. (defun emc-restore-wconfigs (&optional file)
  194.   "Restore Emacs window configuration ring from optional FILE or 'emc-wconfigs-file'.
  195. Returns t if the restore is successful or the restore file does not exist,
  196. otherwise returns a string indicating the restore failure point."
  197.   (interactive "fRestore Emacs window configuration ring from: ")
  198.   (run-hooks 'emc-restore-hook)
  199.   (let ((hdr t))
  200.     (if (or (equal file "") (null file))
  201.     (setq file emc-wconfigs-file))
  202.     (if (file-exists-p file)
  203.     (let ((standard-input (set-buffer (find-file-noselect emc-file))))
  204.       (goto-char (point-min))
  205.       (if (or (not emc-save-wconfig-ring-p)
  206.           (and (setq hdr "WINDOW CONFIG RING")
  207.                (emc-valid-hdr-p hdr)
  208.                (emc-restore-wconfig-ring)))
  209.           (progn (kill-buffer standard-input)
  210.              (setq hdr t))
  211.         )))
  212.     hdr))
  213.  
  214.  
  215. (defun emc-valid-hdr-p (hdr)
  216.   (equal (read) hdr))
  217.  
  218. (defun buffer-major-mode= (buf mode)
  219.   (eq (cdr (assq 'major-mode (buffer-local-variables buf))) mode))
  220.  
  221. (defun emc-save-buffer-data ()
  222.   ;; Save only buffers visiting files; skip some in special modes.
  223.   (let ((buf-list (mapcar '(lambda (buf)
  224.                  (let ((bn (buffer-name buf)))
  225.                    (if (and (not (buffer-major-mode= buf 'Info-mode))
  226.                     (or (null (buffer-file-name buf))
  227.                         (string-match "^[ \*].*\*$" bn)
  228.                         (buffer-major-mode= buf 'rmail-mode)
  229.                         (equal bn (buffer-name
  230.                             standard-output))))
  231.                    nil
  232.                  bn)))
  233.               (nreverse (buffer-list)))))
  234.     (print buf-list)
  235.     (mapcar '(lambda (buf)
  236.            (if (null buf)
  237.            nil
  238.          (set-buffer buf)
  239.          (if (eq major-mode 'Info-mode)
  240.              (progn (print major-mode)
  241.                 (print Info-current-file)
  242.                 (print Info-current-node)
  243.                 (print (point)))
  244.            (print major-mode)
  245.            (print (buffer-name))
  246.            (print (buffer-file-name))
  247.            (print buffer-read-only)
  248.            (print (point))
  249.            ;; t if buffer is narrowed
  250.            (print (if (or (/= (point-min) 1)
  251.                   (/= (point-max) (1+ (buffer-size)))) t))
  252.            (print (point-min))
  253.            (print (point-max))
  254.            )))
  255.         buf-list)
  256.     ))
  257.  
  258. (defun emc-restore-buffer-data ()
  259.   (let ((buf-list (read))
  260.     (buf-name) (file) (mode)
  261.     (buf-read-only) (point)
  262.     (point-min) (point-max)
  263.     (narrowed-p)
  264.     (mark-list))
  265.     (mapcar '(lambda (buf)
  266.            (if (null buf)
  267.            nil
  268.          (setq mode (read))
  269.          (if (eq mode 'Info-mode)
  270.              (progn (info)
  271.                 (Info-find-node (read) (read))
  272.                 (goto-char (read)))
  273.            (setq buf-name (read)
  274.              file (read)
  275.              buf-read-only (read)
  276.              point (read)
  277.              narrowed-p (read) ;; t if buffer was narrowed
  278.              point-min (read)
  279.              point-max (read))
  280.            (if (or (file-exists-p file)
  281.                (and (featurep 'ange-ftp)
  282.                 (string-match 
  283.                   (if (boundp 'ange-ftp-path-user-exp)
  284.                       ;; Old style
  285.                       (concat "^" ange-ftp-path-user-exp
  286.                           ange-ftp-path-host-exp
  287.                           ange-ftp-path-path-exp)
  288.                       ;; New style
  289.                     (concat "^" (car ange-ftp-path-format)))
  290.                   file)))
  291.                (progn (find-file file)
  292.                   (or (get-buffer buf-name)
  293.                   (rename-buffer buf-name))
  294.                   (setq buffer-read-only buf-read-only)
  295.                   (and mode (funcall mode))
  296.                   (if (<= point (point-max))
  297.                   (goto-char point))
  298.                   (if (and narrowed-p
  299.                        (or (< point-max (point-max))
  300.                        (> point-min (point-min))))
  301.                   (narrow-to-region point-min point-max)))))))
  302.         buf-list)
  303.     ;;
  304.     ;; Might want to do stuff and set buffer local variables from src/buffer.c.
  305.     ;;
  306.     )
  307.   t)
  308.  
  309. ;;;
  310. ;;; Often won't work properly when used with an external window system.
  311. ;;; 
  312. (defun emc-save-session-data ()
  313.   (print (screen-height))
  314.   (print (screen-width))
  315.   )
  316.  
  317. (defun emc-read-session-data ()
  318.   (setq emc-screen-height (read))
  319.   (setq emc-screen-width (read))
  320.   t)
  321.  
  322. ;; Must only be called after 'emc-read-session-data'.
  323. (defun emc-restore-session-data ()
  324.   (set-screen-height emc-screen-height)
  325.   (set-screen-width emc-screen-width)
  326.   t)
  327.  
  328. ;;;
  329. ;;; Should be done after current window configuration is saved but output of
  330. ;;; this function should be written in save file AHEAD of the output from
  331. ;;; current window configuration.  This allows the current to be the last
  332. ;;; config restored, as one would desire.
  333. ;;;
  334. (defun emc-save-wconfig-ring ()
  335.   ;; Assumes point is at proper position in standard-output.
  336.   (if (or (not (featurep 'wconfig)) (null wconfig-ring))
  337.       (print 0) ;; No wconfigs stored.
  338.     (let ((wconfig-list (reverse wconfig-ring)))
  339.       (print (length wconfig-list))
  340.       (mapcar
  341.     '(lambda (w)
  342.        (set-window-configuration w)
  343.        (emc-save-window-data))
  344.     wconfig-list))))
  345.  
  346. ;;;
  347. ;;; Should be done before last current window configuration is restored.
  348. ;;;
  349. (defun emc-restore-wconfig-ring ()
  350.   (let ((n (read))) ;; Number of window-configurations saved.
  351.     (if (<= n 0)
  352.     nil
  353.       (if (not (featurep 'wconfig)) (load "wconfig"))
  354.       (setq wconfig-ring nil) ;; Clear out any window configurations.
  355.       (if (> n wconfig-ring-max) (setq wconfig-ring-max n))
  356.       (while (> n 0)
  357.     (setq n (1- n))
  358.     (emc-restore-window-data)
  359.     (wconfig-ring-save))))
  360.   t)
  361.  
  362.  
  363. (defun emc-save-window-data ()
  364.   (goto-char (point-max))
  365.   (let ((wind-list (nreverse (emc-window-list 1))))
  366.     (print (length wind-list))
  367.     (mapcar
  368.       '(lambda (w)
  369.      (select-window w)
  370.      (let* ((bn (buffer-name))
  371.         (buf (get-buffer bn)))
  372.        (if (or (string-match "^[ \*].*\*$" bn)
  373.            (buffer-major-mode= buf 'rmail-mode)
  374.            (equal bn (buffer-name standard-output))
  375.            (and (not (buffer-major-mode= buf 'Info-mode))
  376.             (null (buffer-file-name buf))))
  377.            (print nil)
  378.          (print (buffer-file-name))
  379.          (print (buffer-name))
  380.          (print (window-edges))
  381.          (print (window-point))
  382.          (print (window-start))
  383.          (print (window-hscroll))
  384.          )))
  385.       wind-list)))
  386.  
  387. (defun emc-read-window-data ()
  388.   (let* ((num-windows (read))
  389.      (n num-windows)
  390.      (wind-list)
  391.      (buf-name) (file)
  392.      (window-edges) (window-point) (window-start) (window-hscroll))
  393.     (while (> n 0)
  394.       (setq n (1- n)
  395.         file (read))
  396.       (if (null file)
  397.       nil
  398.     (setq buf-name (read))
  399.     (if (file-exists-p file)
  400.         (progn (setq window-edges (read))
  401.            (setq window-point (read))
  402.            (setq window-start (read))
  403.            (setq window-hscroll (read)))
  404.       ;; Skip irrelevant data.
  405.       (read) (read) (read) (read)))
  406.       (setq wind-list (cons (list window-edges file buf-name window-point
  407.                   window-start window-hscroll) wind-list))
  408.       )
  409.     wind-list))
  410.  
  411.  
  412. ;; WORK IN PROGRESS
  413.  
  414. ;;window-edges:
  415. ;;Return a list of the edge coordinates of WINDOW.
  416. ;;(LEFT TOP RIGHT BOTTOM), all relative to 0, 0 at top left corner of screen.
  417. ;;RIGHT is one more than the rightmost column used by WINDOW,
  418. ;;and BOTTOM is one more than the bottommost row used by WINDOW
  419. ;; and its mode-line.
  420.  
  421. (defun emc-calc-window-splits ()
  422.   (let* ((wind-list (emc-read-window-data))
  423.      (wind-tops) (winds-per-top))
  424.     ;; Compute set of unique window top positions.
  425.     (mapcar '(lambda (w)
  426.            (set-cons wind-tops (car (cdr (car w))))
  427.            wind-list))
  428.     ;; Order tops in descending order
  429.     (setq wind-tops (sort wind-tops '>))
  430.     ;; Calc num of windows at each top position (num horiz windows)
  431.     (setq winds-per-top
  432.       (mapcar '(lambda (wt)
  433.              (apply '+ (mapcar '(lambda (w)
  434.                       (if (= wt (car (cdr (car w)))) 1 0)
  435.                       wind-list))))
  436.           wind-tops))
  437.     (let ((wpt (apply '+ winds-per-top))
  438.       (winds (length wind-list)))
  439.       (if (/= wpt winds)
  440.       (error "Bug in 'emc-calc-window-splits', wpt = %s, winds = %s" wpt
  441.          winds))
  442.       (emc-split-windows-vert wind-tops)
  443.       )))
  444.  
  445.  
  446. ;; Remember windows that share a top dimension, do not have to share a bottom
  447. ;; one, so can't always do all vertical splitting before all horizontal.
  448.  
  449. (defun emc-split-windows-vert (wind-tops)
  450.   (let* ((n (length wind-tops))
  451.      (i 0)
  452.      (window-min-height 2)
  453.      (prev-window-top 0) (top))
  454.     (delete-other-windows)
  455.     (while (< i n)
  456.       (split-window)
  457.       ;; If top edge of window is less than previous, then size
  458.       ;; current window.
  459.       (setq top (nth i wind-tops))
  460.       (if (<= top prev-window-top)
  461.       (progn (shrink-window (- (window-height)
  462.                    ;; NOT DONE
  463.                    (- (nth 3 window-edges) top)))
  464.          (other-window 1))
  465.     (let ((owin (selected-window)))
  466.       (other-window 1)
  467.       (shrink-window (- (window-height)
  468.                 (- (nth 3 window-edges) top)))
  469.       (select-window owin)))
  470.       (setq prev-window-top top)
  471.       (setq i (1+ i))
  472.       )))
  473.  
  474.  
  475. ;;;
  476. ;;; Not finished; have to figure out how to set vertical window edges properly.
  477. ;;; Therefore does not yet handle side-by-side window splits.
  478. ;;;
  479. (defun emc-restore-window-data ()
  480.   (let* ((num-windows (read))
  481.      (n num-windows)
  482.      (buf-name) (file) (window-edges)
  483.      (window-min-height 2)
  484.      (prev-window-top 0))
  485.     (delete-other-windows)
  486.     (while (> n 0)
  487.       (setq n (1- n)
  488.         file (read))
  489.       (if (null file)
  490.       nil
  491.     (setq buf-name (read))
  492.     (if (file-exists-p file)
  493.         (progn (find-file file)
  494.            (or (get-buffer buf-name)
  495.                (rename-buffer buf-name))
  496.            (setq window-edges (read))
  497.            (set-window-point (selected-window) (read))
  498.            (set-window-start (selected-window) (read))
  499.            (set-window-hscroll (selected-window) (read))
  500.            ;; Set top and bottom window edges here.
  501.            (if (= n 0)
  502.                nil
  503.              (split-window)
  504.              ;; If top edge of window is less than previous then size
  505.              ;; current window.
  506.              (let ((top (nth 1 window-edges)))
  507.                (if (> top prev-window-top)
  508.                (let ((owin (selected-window)))
  509.                  (other-window 1)
  510.                  (shrink-window (- (window-height)
  511.                            (- (nth 3 window-edges) top)))
  512.                  (select-window owin)) 
  513.              (shrink-window (- (window-height)
  514.                        (- (nth 3 window-edges) top)))
  515.              (other-window 1))
  516.                (setq prev-window-top top))))
  517.       ;; Skip irrelevant data.
  518.       (read) (read) (read) (read)))
  519.       ))
  520.   t)
  521.  
  522.  
  523. ;;;
  524. ;;; Copyright (C) 1987, 1988 Kyle E. Jones
  525. ;;;
  526. (defun emc-window-list (&optional mini)
  527.   "Returns a list of Lisp window objects for all Emacs windows.
  528. Optional first arg MINIBUF t means include the minibuffer window
  529. in the list, even if it is not active.  If MINIBUF is neither t
  530. nor nil it means to not count the minibuffer window even if it is active."
  531.   (let* ((first-window (next-window (previous-window (selected-window)) mini))
  532.      (windows (cons first-window nil))
  533.      (current-cons windows)
  534.      (w (next-window first-window mini)))
  535.     (while (not (eq w first-window))
  536.       (setq current-cons (setcdr current-cons (cons w nil)))
  537.       (setq w (next-window w mini)))
  538.     windows))
  539.  
  540.  
  541. (provide 'em-config)
  542. ;--
  543. ;Bob Weiner        Usenet:   ...!gatech!uflorida!novavax!weiner
  544. ;                  Internet: weiner%novavax@bikini.cis.ufl.edu
  545.  
  546.  
  547.