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: