packages feed

hsc3-0.2: emacs/hsc3.el

;; hsc3.el - (c) rohan drape, 2006-2008

;; This mode is implemented as a derivation of `haskell' mode,
;; indentation and font locking is courtesy that mode.  The
;; inter-process communication is courtesy `comint'.  The symbol at
;; point acquisition is courtesy `thingatpt'.  The directory search
;; facilities are courtesy `find-lisp'.

(require 'scheme)
(require 'comint)
(require 'thingatpt)
(require 'find-lisp)

(defvar hsc3-buffer
  "*hsc3*"
  "*The name of the hsc3 process buffer (default=*hsc3*).")

(defvar hsc3-interpreter
  "ghci"
  "*The haskell interpter to use (default=ghci).")

(defvar hsc3-interpreter-arguments
  (list)
  "*Arguments to the haskell interpreter (default=none).")

(defvar hsc3-main-modules
  (list)
  "*Modules to load (using :l) into the haskell interpreter.")

(defvar hsc3-modules
  (list "Sound.OpenSoundControl"
	"Sound.SC3"
	"Data.List"
	"Control.Monad"
	"Control.Concurrent"
	"System.Directory"
	"System.Random")
  "*Modules to bring into scope (using :m +) into the haskell interpreter.")

(defvar hsc3-help-directory
  nil
  "*The directory containing the help files (default=nil).")

(defvar hsc3-literate-p
  t
  "*Flag to indicate if we are in literate mode (default=t).")

(make-variable-buffer-local 'hsc3-literate-p)

(defun hsc3-unlit (s)
  "Remove bird literate marks"
  (replace-regexp-in-string "^> " "" s))

(defun hsc3-intersperse (e l)
  (if (null l)
      '()
    (cons e (cons (car l) (hsc3-intersperse e (cdr l))))))

(defun hsc3-start-haskell ()
  "Start haskell."
  (interactive)
  (if (comint-check-proc hsc3-buffer)
      (error "An hsc3 process is already running")
    (apply
     'make-comint
     "hsc3"
     hsc3-interpreter
     nil
     hsc3-interpreter-arguments)
    (hsc3-see-output))
  (if (not (null hsc3-main-modules))
      (hsc3-send-string
       (apply 'concat (cons ":l " (hsc3-intersperse " " hsc3-main-modules)))))
  (if (not (null hsc3-modules))
      (hsc3-send-string
       (apply 'concat (cons ":m + " (hsc3-intersperse " " hsc3-modules))))))

(defun hsc3-see-output ()
  "Show haskell output."
  (interactive)
  (when (comint-check-proc hsc3-buffer)
    (delete-other-windows)
    (split-window-vertically)
    (with-current-buffer hsc3-buffer
      (let ((window (display-buffer (current-buffer))))
	(goto-char (point-max))
	(save-selected-window
	  (set-window-point window (point-max)))))))

(defun hsc3-quit-haskell ()
  "Quit haskell."
  (interactive)
  (kill-buffer hsc3-buffer)
  (delete-other-windows))

(defun hsc3-help ()
  "Lookup up the name at point in the Help files."
  (interactive)
  (mapc (lambda (filename)
	  (find-file-other-window filename))
	(find-lisp-find-files hsc3-help-directory
			      (concat "^"
				      (thing-at-point 'symbol)
				      "\\.help\\.lhs"))))

(defun hsc3-send-string (s)
  (if (comint-check-proc hsc3-buffer)
      (comint-send-string hsc3-buffer (concat s "\n"))
    (error "no hsc3 process running?")))

(defun hsc3-transform-and-store (f s)
  "Transform example text into compilable form."
  (with-temp-file f
    (mapc (lambda (module)
	    (insert (concat "import " module "\n")))
	  (append hsc3-main-modules hsc3-modules))
    (insert "main = do\n")
    (insert (if hsc3-literate-p (hsc3-unlit s) s))))

(defun hsc3-run-line ()
  "Send the current line to the interpreter."
  (interactive)
  (let* ((s (buffer-substring (line-beginning-position)
			      (line-end-position)))
	 (s* (if hsc3-literate-p
		 (hsc3-unlit s)
	       s)))
    (hsc3-send-string s*)))

(defun hsc3-run-multiple-lines ()
  "Send the current region to the interpreter as a single line."
  (interactive)
  (let* ((s (buffer-substring-no-properties (region-beginning)
					    (region-end)))
	 (s* (if hsc3-literate-p
		 (hsc3-unlit s)
	       s)))
    (hsc3-send-string (replace-regexp-in-string "\n" " " s*))))

(defun hsc3-run-region ()
  "Place the region in a do block and compile."
  (interactive)
  (hsc3-transform-and-store
   "/tmp/hsc3.hs"
   (buffer-substring-no-properties (region-beginning) (region-end)))
  (hsc3-send-string ":load \"/tmp/hsc3.hs\"")
  (hsc3-send-string "main"))

(defun hsc3-load-buffer ()
  "Load the current buffer."
  (interactive)
  (save-buffer)
  (hsc3-send-string (format ":load \"%s\"" buffer-file-name)))

(defun hsc3-run-main ()
  "Run current main."
  (interactive)
  (hsc3-send-string "main"))

(defun hsc3-interrupt-haskell ()
  (interactive)
  (if (comint-check-proc hsc3-buffer)
      (with-current-buffer hsc3-buffer
	(interrupt-process (get-buffer-process (current-buffer))))
    (error "no hsc3 process running?")))

(defun hsc3-reset-scsynth ()
  "Reset"
  (interactive)
  (hsc3-send-string "withSC3 reset"))

(defun hsc3-status-scsynth ()
  "Status"
  (interactive)
  (hsc3-send-string "withSC3 serverStatus >>= mapM putStrLn"))

(defun hsc3-quit-scsynth ()
  "Quit"
  (interactive)
  (hsc3-send-string "withSC3 (\fd -> send fd quit)"))

(defvar hsc3-mode-map nil
  "Haskell SuperCollider keymap.")

(defun hsc3-mode-keybindings (map)
  "Haskell SuperCollider keybindings."
  (define-key map [?\C-c ?\C-s] 'hsc3-start-haskell)
  (define-key map [?\C-c ?\C-g] 'hsc3-see-output)
  (define-key map [?\C-c ?\C-x] 'hsc3-quit-haskell)
  (define-key map [?\C-c ?\C-k] 'hsc3-reset-scsynth)
  (define-key map [?\C-c ?\C-w] 'hsc3-status-scsynth)
  (define-key map [?\C-c ?\C-c] 'hsc3-run-line)
  (define-key map [?\C-c ?\C-e] 'hsc3-run-multiple-lines)
  (define-key map [?\C-c ?\C-r] 'hsc3-run-region)
  (define-key map [?\C-c ?\C-l] 'hsc3-load-buffer)
  (define-key map [?\C-c ?\C-i] 'hsc3-interrupt-haskell)
  (define-key map [?\C-c ?\C-m] 'hsc3-run-main)
  (define-key map [?\C-c ?\C-o] 'hsc3-quit-scsynth)
  (define-key map [?\C-c ?\C-h] 'hsc3-help))

(defun turn-on-hsc3-keybindings ()
  "Haskell SuperCollider keybindings in the local map."
  (local-set-key [?\C-c ?\C-s] 'hsc3-start-haskell)
  (local-set-key [?\C-c ?\C-g] 'hsc3-see-output)
  (local-set-key [?\C-c ?\C-x] 'hsc3-quit-haskell)
  (local-set-key [?\C-c ?\C-k] 'hsc3-reset-scsynth)
  (local-set-key [?\C-c ?\C-w] 'hsc3-status-scsynth)
  (local-set-key [?\C-c ?\C-c] 'hsc3-run-line)
  (local-set-key [?\C-c ?\C-e] 'hsc3-run-multiple-lines)
  (local-set-key [?\C-c ?\C-r] 'hsc3-run-region)
  (local-set-key [?\C-c ?\C-l] 'hsc3-load-buffer)
  (local-set-key [?\C-c ?\C-i] 'hsc3-interrupt-haskell)
  (local-set-key [?\C-c ?\C-m] 'hsc3-run-main)
  (local-set-key [?\C-c ?\C-o] 'hsc3-quit-scsynth)
  (local-set-key [?\C-c ?\C-h] 'hsc3-help))

(defun hsc3-mode-menu (map)
  "Haskell SuperCollider menu."
  (define-key map [menu-bar hsc3]
    (cons "Haskell-SuperCollider" (make-sparse-keymap "Haskell-SuperCollider")))
  (define-key map [menu-bar hsc3 help]
    (cons "Help" (make-sparse-keymap "Help")))
  (define-key map [menu-bar hsc3 help hsc3]
    '("Haskell SuperCollider help" . hsc3-help))
  (define-key map [menu-bar hsc3 expression]
    (cons "Expression" (make-sparse-keymap "Expression")))
  (define-key map [menu-bar hsc3 expression load-buffer]
    '("Load buffer" . hsc3-load-buffer))
  (define-key map [menu-bar hsc3 expression run-main]
    '("Run main" . hsc3-run-main))
  (define-key map [menu-bar hsc3 expression run-region]
    '("Run region" . hsc3-run-region))
  (define-key map [menu-bar hsc3 expression run-multiple-lines]
    '("Run multiple lines" . hsc3-run-multiple-lines))
  (define-key map [menu-bar hsc3 expression run-line]
    '("Run line" . hsc3-run-line))
  (define-key map [menu-bar hsc3 scsynth]
    (cons "SCSynth" (make-sparse-keymap "SCSynth")))
  (define-key map [menu-bar hsc3 scsynth quit]
    '("Quit scsynth" . hsc3-quit-scsynth))
  (define-key map [menu-bar hsc3 scsynth status]
    '("Display status" . hsc3-status-scsynth))
  (define-key map [menu-bar hsc3 scsynth reset]
    '("Reset scsynth" . hsc3-reset-scsynth))
  (define-key map [menu-bar hsc3 haskell]
    (cons "Haskell" (make-sparse-keymap "Haskell")))
  (define-key map [menu-bar hsc3 haskell quit-haskell]
    '("Quit haskell" . hsc3-quit-haskell))
  (define-key map [menu-bar hsc3 haskell see-output]
    '("See output" . hsc3-see-output))
  (define-key map [menu-bar hsc3 haskell start-haskell]
    '("Start haskell" . hsc3-start-haskell)))

(if hsc3-mode-map
    ()
  (let ((map (make-sparse-keymap "Haskell-SuperCollider")))
    (hsc3-mode-keybindings map)
    (hsc3-mode-menu map)
    (setq hsc3-mode-map map)))

(define-derived-mode
  literate-hsc3-mode
  hsc3-mode
  "Literate Haskell SuperCollider"
  "Major mode for interacting with an inferior haskell process."
  (setq hsc3-literate-p t)
  (setq haskell-literate 'bird)
  (turn-on-font-lock))

(add-to-list 'auto-mode-alist '("\\.lhs$" . literate-hsc3-mode))

(define-derived-mode
  hsc3-mode
  haskell-mode
  "Haskell SuperCollider"
  "Major mode for interacting with an inferior haskell process."
  (setq hsc3-literate-p nil)
  (turn-on-font-lock))

(add-to-list 'auto-mode-alist '("\\.hs$" . hsc3-mode))

(provide 'hsc3)