[plt-scheme] running an MzScheme debugger in Emacs?

From: Noel Welsh (noelwelsh at gmail.com)
Date: Fri Apr 25 06:11:12 EDT 2008

On Fri, Apr 25, 2008 at 10:52 AM, Benjamin L. Russell
<dekudekuplex at yahoo.com> wrote:
>  Unfortunately, it seems that MzScheme does not run a debugger in Emacs, and that Emacs cannot run an inferior interactive MIT Scheme mode in Windows, but only in UNIX, because their inferior interactive Scheme mode relies on UNIX signals, which are not present in Windows.
...
>  In sum, there seem to be the following options:
...
8) Use a different process that doesn't rely on a debugger

I think there are a fair number of people who don't miss a debugger,
myself included.  My process is this:

 - Every file foo.ss has a corresponding test file foo-test.ss.
 - Every time I have a query about the semantics of any code in foo.ss
I write a test
 - I run the tests with an Emacs command (see below)

Occasionally I'll need to debug.  In this case I just use printf
statements, which I delete when I've finished.  This works well for
most code.  The only times I've had problems is when I'm writing
statistical code -- here testing is difficult, as results are not
deterministic, and inspecting output is useful.

HTH,
Noel

PS: This is my Elisp code for integrating SchemeUnit w/ Quack and Emacs:


;;; Copyright Noel Welsh 2006
;;; Released under Lesser GNU Public Licence

;;; Useful (X)Emacs commands for SchemeUnit

(provide 'schemeunit)

;;; Definitions

(defvar macintosh-p
  "t if running on a Macintosh (Darwin)"
  (progn
    (save-current-buffer
      (save-excursion
	(set-buffer (get-buffer-create "*Shell Command Output*"))
	(erase-buffer)
	(shell-command "uname")
	(string= (buffer-string) "Darwin\n")))))

(defvar schemeunit-read-directory-name
  (if (fboundp 'read-directory-name)
      #'read-directory-name
    #'read-file-name))

(defun schemeunit-split-path (path)
      "Explode a search path into a list of strings. The path
components are separated with the characters specified with
`path-separator', except on Macintosh (Darwin) where paths are split
on the / character."
      (if macintosh-p
	  (split-string path (regexp-quote "/"))
	(split-string path (regexp-quote path-separator))))


(defvar schemeunit-test-require-spec
  "(require (planet \"test.ss\" (\"schematics\" \"schemeunit.plt\" 2)))")

(defvar schemeunit-text-ui-require-spec
  "(require (planet \"text-ui.ss\" (\"schematics\" \"schemeunit.plt\" 2)))")

(defun schemeunit-lowest-directory (directory)
  "Return just the lowest directory in the given path as a STRING"
  (first
   (last
    (schemeunit-split-path directory)
    2)))

(defun schemeunit-current-directory ()
  "Return the name of the current directory as a STRING"
  (schemeunit-lowest-directory default-directory))

(defun schemeunit-all-tests-file-name (directory-name)
  "Given the current directory as a STRING, return the name
   of the file that accumulates all the tests as a STRING"
  (concat (schemeunit-all-tests-suite-name directory-name) ".ss"))

(defun schemeunit-all-tests-suite-name (directory-name)
  "Given the current directory as a STRING, return the name
   of the suite that accumulates all the tests as a STRING"
  (concat "all-"
	  (if (string= directory-name "src")
		""
	      (concat directory-name "-"))
	  "tests"))

(defconst schemeunit-run-tests-file-name
  "run-tests.ss"
  "The name of the file that runs all the tests; a STRING")

(defun schemeunit-make-file (file-name skeleton)
  (find-file file-name)
  (if (file-exists-p file-name)
      (message (format "File %s already exists" file-name))
    (progn (auto-insert)
	   (funcall skeleton)
	   (save-buffer))))

(defun schemeunit-directory->run-tests-file-name (directory-name)
  (concat directory-name schemeunit-run-tests-file-name))

(defun schemeunit-directory->all-tests-file-name (directory-name)
  (schemeunit-all-tests-file-name
   (schemeunit-lowest-directory directory-name)))

(defun schemeunit-file->module-name (file-name)
  (car (split-string (file-name-nondirectory file-name) "\\.")))

(defun schemeunit-guess-code-file (file-name)
  (concat (schemeunit-guess-code-module file-name) ".ss"))

(defun schemeunit-guess-test-file (file-name)
  (concat (file-name-sans-extension file-name) "-test.ss"))

(defun schemeunit-guess-code-module (file-name)
  (let* ((file (file-name-nondirectory file-name))
	 (end (string-match "-test" file)))
    (substring file 0 end)))

;;; Skeletons

(require 'skeleton)

(define-skeleton
  schemeunit-run-tests-skel
  "Inserts template code for the file that runs all tests in the
   current directory"
  nil
  schemeunit-test-require-spec \n
  schemeunit-text-ui-require-spec \n
  "(require \"" (schemeunit-all-tests-file-name
(schemeunit-current-directory)) "\")" \n
  \n
  "(test/text-ui " (schemeunit-all-tests-suite-name
(schemeunit-current-directory)) ")" \n)

(define-skeleton
  schemeunit-all-tests-skel
  "Inserts template code for the file that accumulates all tests
   in the current directory"
  nil
  "(module " (schemeunit-all-tests-suite-name
(schemeunit-current-directory)) " mzscheme" \n
  \n
  schemeunit-test-require-spec \n
  "(provide " (schemeunit-all-tests-suite-name
(schemeunit-current-directory)) ")" \n
  \n
  "(define " (schemeunit-all-tests-suite-name (schemeunit-current-directory)) \n
  "(test-suite " \n
  "\"" (schemeunit-all-tests-suite-name (schemeunit-current-directory)) "\"" \n
  ";; add suites here" \n
  "))" \n
  ")" \n)

(define-skeleton
  schemeunit-tests-skel
  "Inserts template code for a file containing a test suite"
  nil
  "(module " (schemeunit-file->module-name (buffer-file-name)) " mzscheme" \n
  \n
  schemeunit-test-require-spec \n
  "(require \"" (schemeunit-guess-code-file (buffer-file-name)) "\")" \n
  \n
  "(provide " (schemeunit-guess-code-module (buffer-file-name)) "-tests)" \n
  \n
  "(define " (schemeunit-guess-code-module (buffer-file-name)) "-tests" \n
  "(test-suite" \n
  "\"All tests for " (schemeunit-guess-code-module (buffer-file-name)) "\"" \n
  ";;test cases here" \n
  "))" \n
  ")" \n)


;;; User Functions

(defun schemeunit-make-run-tests-file ()
  "Make the file that runs all the tests"
  (interactive)
  (let* ((directory-name
	  (funcall schemeunit-read-directory-name
	   (format "Directory for %s (Default %S): "
		   schemeunit-run-tests-file-name
		   default-directory)
	   default-directory
	   default-directory))
	 (file-name
	  (schemeunit-directory->run-tests-file-name
	   directory-name)))
    (schemeunit-make-file file-name #'schemeunit-run-tests-skel)))

(defun schemeunit-make-all-tests-file ()
  "Make the file that accumulates all the tests"
  (interactive)
  (let* ((directory-name
	  (funcall schemeunit-read-directory-name
	   (format "Directory for %s (Default %S): "
		   (schemeunit-directory->all-tests-file-name default-directory)
		   default-directory)
	   default-directory
	   default-directory))
	 (file-name
	  (schemeunit-directory->all-tests-file-name
	   directory-name)))
    (schemeunit-make-file file-name #'schemeunit-all-tests-skel)))

(defun schemeunit-fill-test-suite ()
  "Fill the current buffer with the skeleton of a test suite"
  (interactive)
  (schemeunit-tests-skel))

(defun schemeunit-make-all-files ()
  "Make the files to use SchemeUnit in a project.  This function
   creates a file to accumulate all the tests for a project and
   a file to run the tests."
  (interactive)
  (let ((directory-name
	 (funcall schemeunit-read-directory-name
	  (format "Directory for tests (Default %S): "
		  default-directory)
	  default-directory
	  default-directory)))
    (schemeunit-make-file
     (schemeunit-directory->run-tests-file-name directory-name)
     #'schemeunit-run-tests-skel)
    (schemeunit-make-file
     (schemeunit-directory->all-tests-file-name directory-name)
     #'schemeunit-all-tests-skel)))

(defun schemeunit-run-tests (command)
  "Run SchemeUnit tests via compile (so normal compile commands
   work on the output).  Command is a STRING"
  (interactive
   (list (read-from-minibuffer
	  "Command to run tests: "
	  "mzscheme -t 'run-tests.ss'")))
  (compile command))


(defun schemeunit-insinuate-quack ()
  "Add key bindings for SchemeUnit to Quack
   Defines the following keys (with your Quack prefix; default is C-c C-q):
      c  -- Run SchemeUnit tests (schemeunit-run-tests; think 'compile')
      a  -- Make SchemeUnit files (schemeunit-make-all-files; think 'all')
      i  -- Fill the current buffer with a test suite skeleton
(schemeunit-fill-test-suite; think 'insert')"
  (interactive)
  (require 'quack)
  (define-key quack-scheme-mode-keymap "c" 'schemeunit-run-tests)
  (define-key quack-scheme-mode-keymap "a" 'schemeunit-make-all-files)
  (define-key quack-scheme-mode-keymap "i" 'schemeunit-fill-test-suite))


Posted on the users mailing list.