[slime-devel] save SLIME session (and more)

Walter C. Pelissero slime at pelissero.de
Mon Sep 27 18:14:44 UTC 2004


In SLIME I've been missing the possibility to save my hacking
sessions.  I turn off my PC every night (my PC's ACPI doesn't work
reliably) and the next day I have to spend half an hour to reload
everything and figure out what package/system dependency I've screwed
up the day before.  Educative exercise, but admittedly annoying.

The following patch adds this feature.  It implements the "save" (or
"freeze") shortcut and can interact nicely with Emacs desktop.el.
User can type ",save" or click "REPL->Save session" and get a lisp
image saved in the ~/.slime directory.  To recover a previously saved
session just do "M-x slime-recover-session" instead of "M-x slime".
If the customisable variable slime-part-of-desktop is true, the saving
and recovering is done automatically upon exit/start of Emacs
(provided you use desktop.el).

It has been tested on SBCL and CMUCL.  In CLISP there seem to be some
communications problems during the recovery, that I haven't been able
to track down.  Those are probably due to the fact that CLISP uses a
different communication style than CMUCL and SBCL; just a guess.

Unfortunately the following patch contains an unrelated modification I
wanted to propose to the mailing list some days ago.  The message got
bounced by the mailing list server (I wasn't subscribed).  Just to
clarify what is what, my message went more or less like this:

 > I personally find a bit annoying the fact that the "compiler notes"
 > window opens up always at half the frame height, even if the list
 > to display is very short.  I'd really appreciate if the window
 > popped up to a more modest size and expand as necessary (toggling
 > the tree branches).
 > 
 > Something like the following patch would keep the window at most half
 > the frame's height, although starting at much smaller size.
 > 
 > Index: slime.el
 > ===================================================================
 > RCS file: /project/slime/cvsroot/slime/slime.el,v
 > retrieving revision 1.390
 > diff -c -r1.390 slime.el
 > *** slime.el	13 Aug 2004 21:23:53 -0000	1.390
 > --- slime.el	27 Aug 2004 22:59:40 -0000
 > ***************
 > *** 3322,3328 ****
 >             (slime-tree-insert tree "")
 >             (insert "\n")))
 >         (setq buffer-read-only t)
 > !       (goto-char (point-min)))))
 >   
 >   (defun slime-alistify (list key test)
 >     "Partition the elements of LIST into an alist.  KEY extracts the key
 > --- 3322,3329 ----
 >             (slime-tree-insert tree "")
 >             (insert "\n")))
 >         (setq buffer-read-only t)
 > !       (goto-char (point-min))
 > !       (fit-window-to-buffer nil (window-height)))))
 >   
 >   (defun slime-alistify (list key test)
 >     "Partition the elements of LIST into an alist.  KEY extracts the key
 > ***************
 > *** 3536,3542 ****
 >       (backward-char 1)
 >       (slime-tree-insert tree prefix)
 >       (delete-char 1)
 > !     (goto-char start-mark)))
 >   
 >   
 >   ;;;;; Adding a single compiler note
 > --- 3537,3544 ----
 >       (backward-char 1)
 >       (slime-tree-insert tree prefix)
 >       (delete-char 1)
 > !     (goto-char start-mark)
 > !     (fit-window-to-buffer nil (/ (frame-height) 2) (window-height))))
 >   
 >   
 >   ;;;;; Adding a single compiler note
 > 

Here is the whole lot:

Index: slime.el
===================================================================
RCS file: /project/slime/cvsroot/slime/slime.el,v
retrieving revision 1.390
diff -u -r1.390 slime.el
--- slime.el	13 Aug 2004 21:23:53 -0000	1.390
+++ slime.el	27 Sep 2004 17:18:44 -0000
@@ -69,6 +69,8 @@
 (defun* slime-setup (&key autodoc typeout-frame)
   "Setup Emacs so that lisp-mode buffers always use SLIME."
   (add-hook 'lisp-mode-hook 'slime-lisp-mode-hook)
+  (add-hook 'desktop-save-hook 'slime-desktop-save-hook)
+  (add-hook 'desktop-after-read-hook 'slime-desktop-after-read-hook)
   (when typeout-frame
     (add-hook 'slime-connected-hook 'slime-ensure-typeout-frame))
   (setq slime-use-autodoc-mode autodoc))
@@ -130,6 +132,12 @@
   :type 'boolean
   :group 'slime-ui)
 
+(defcustom slime-part-of-desktop t
+  "If non-nil, SLIME sessions get saved and restored together
+with Emacs desktop."
+  :type 'boolean
+  :group 'slime-ui)
+
 ;;;;; slime-lisp
 
 (defgroup slime-lisp nil
@@ -742,6 +750,7 @@
       [ "Send Input"             slime-repl-return ,C ]
       [ "Close and Send Input "  slime-repl-closing-return ,C ]
       [ "Interrupt Lisp process" slime-interrupt ,C ]
+      [ "Save session"           slime-save-session ,C ]
       "--"
       [ "Previous Input"         slime-repl-previous-input t ]
       [ "Next Input"             slime-repl-previous-input t ]
@@ -1199,6 +1208,36 @@
       (add-hook 'slime-connected-hook hook)
       (slime))))
 
+(defun slime-guess-image-switch (lisp-program-name)
+  (cdr (assoc (file-name-sans-extension (file-name-nondirectory lisp-program-name))
+              '(("lisp" . "-noinit -core")
+                ("sbcl" . "-userinit /dev/null -sysinit /dev/null -core")
+                ("clisp" . "-norc -M")))))
+
+(defun slime-session-image-name (lisp-program-name)
+  (expand-file-name (concat "~/.slime/"
+                            (file-name-sans-extension (file-name-nondirectory lisp-program-name))
+                            ".core")))
+
+(defun slime-restore-session ()
+  "Restart Slime on a previously saved Lisp image.  It's user's
+responsability to make sure that the saved image contains a swank
+that is compatible with the version of Slime she is running.
+That is, do a rm ~/.slime/*.core after upgrading Slime."
+  (interactive)
+  (unless (get-buffer-process (get-buffer "*inferior-lisp*"))
+    (let ((image-name (slime-session-image-name inferior-lisp-program)))
+      (if (file-exists-p image-name)
+          (let ((inferior-lisp-program (format "%s %s %s"
+                                               inferior-lisp-program
+                                               (slime-guess-image-switch inferior-lisp-program)
+                                               image-name)))
+            (call-interactively 'inferior-lisp)
+            (set-process-query-on-exit-flag (inferior-lisp-proc)
+                                            (not slime-kill-without-query-p))
+            (slime-inferior-connect))
+        (error "No session has been saved for %S" inferior-lisp-program)))))
+
 ;;;;; Start inferior lisp
 ;;;
 ;;; Here is the protocol for starting SLIME:
@@ -2966,6 +3005,10 @@
               (slime-kill-all-buffers)))
   (:one-liner "Quit the lisp and close all SLIME buffers."))
 
+(defslime-repl-shortcut slime-repl-save-session ("save" "freeze")
+  (:handler #'slime-save-session)
+  (:one-liner "Save Lisp image and quit closing all SLIME buffers."))
+
 (defslime-repl-shortcut slime-repl-defparameter ("defparameter" "!")
   (:handler (lambda (name value)
               (interactive (list (slime-read-symbol-name "Name (symbol): " t)
@@ -3322,7 +3365,8 @@
           (slime-tree-insert tree "")
           (insert "\n")))
       (setq buffer-read-only t)
-      (goto-char (point-min)))))
+      (goto-char (point-min))
+      (fit-window-to-buffer nil (window-height)))))
 
 (defun slime-alistify (list key test)
   "Partition the elements of LIST into an alist.  KEY extracts the key
@@ -3536,7 +3580,8 @@
     (backward-char 1)
     (slime-tree-insert tree prefix)
     (delete-char 1)
-    (goto-char start-mark)))
+    (goto-char start-mark)
+    (fit-window-to-buffer nil (/ (frame-height) 2) (window-height))))
 
 
 ;;;;; Adding a single compiler note
@@ -7855,6 +7900,31 @@
 (defun sldb-xemacs-post-command-hook ()
   (when (get-text-property (point) 'point-entered)
     (funcall (get-text-property (point) 'point-entered))))
+
+(defun slime-save-session ()
+  "Save and kill the current Lisp hacking session.  To restore
+the session use slime-restore-session instead of just the slime
+command."
+  (interactive)
+  (when (slime-connected-p)
+    (let ((process (inferior-lisp-proc)))
+      (slime-eval-async `(swank:save-session-image-and-die ,(slime-session-image-name inferior-lisp-program)))
+      ;; wait untill inferior lisp dies
+      (while (eq 'run (process-status process))
+        (sleep-for 1))
+      (slime-kill-all-buffers))))
+
+(defun slime-desktop-save-hook ()
+  "Run upon saving the Emacs desktop.  See desktop-save-hook."
+  (when (and slime-part-of-desktop
+             (get-buffer-process (get-buffer "*inferior-lisp*")))
+    (slime-save-session)))
+
+(defun slime-desktop-after-read-hook ()
+  (when (and slime-part-of-desktop
+             (file-exists-p (slime-session-image-name inferior-lisp-program))
+             (y-or-n-p "Restore SLIME session? "))
+    (slime-restore-session)))
 
 
 ;;;; Finishing up
Index: swank.lisp
===================================================================
RCS file: /project/slime/cvsroot/slime/swank.lisp,v
retrieving revision 1.222
diff -u -r1.222 swank.lisp
--- swank.lisp	13 Aug 2004 16:14:13 -0000	1.222
+++ swank.lisp	27 Sep 2004 17:19:26 -0000
@@ -2762,6 +2762,13 @@
 
 (add-hook *pre-reply-hook* 'sync-indentation-to-emacs)
 
+(defslimefun save-session-image-and-die (lisp-image)
+  "Save Lisp image in LISP-IMAGE file and die."
+  #+cmu (extensions:save-lisp lisp-image)
+  #+sbcl (sb-ext:save-lisp-and-die lisp-image)
+  #+clisp (ext:saveinitmem lisp-image)
+  #+clisp (ext:quit))
+
 ;;; Local Variables:
 ;;; eval: (font-lock-add-keywords 'lisp-mode '(("(\\(defslimefun\\)\\s +\\(\\(\\w\\|\\s_\\)+\\)"  (1 font-lock-keyword-face) (2 font-lock-function-name-face))))
 ;;; End:


-- 
walter pelissero
http://www.pelissero.de




More information about the slime-devel mailing list