javascriptとemacsで対話的に開発がしたい(html使わないです><)

schemeでプログラムを書くとき、少し関数を書いてから実行して結果をみるとかいうことをよくします。
そんなことをjavascriptでもしたいと思いました。
なので、てきとーにelispを書いてみました。

準備

javascriptインタプリタが必要す。(eg.spidermonkey)
spidermonkeyをインストールする場合(ubuntu)

sudo aptitude install spidermonkey-bin

使い方

最初にC-c Sを入力.(javascriptのshellが立ち上がる)
その後は以下の関数が使えるようになる。

  • C-c C-c :js-output/comment
    • 現在カーソルがある行を実行する
    • (regionが指定されている場合はregion内のコードを実行する)
    • 行末に結果を出力する*1
  • C-c C-l :js-send-buffer
    • 現在のバッファの内容をshellに読み込ませる
  • C-c C-e :js-sesnd-region
    • region内のコードをshellに読み込ませる

コード

js2-modeを使っています。
ecmascript-modeとかjavascript-modeの人はadd-hookのところを変えてください)

;;comintとの通信用?の関数
(defun ish-get-last-output (prompt buffer-name)
  (let ((n (length prompt))
	(oldbuf (current-buffer)))
    (save-excursion
      (set-buffer buffer-name)
      (goto-char (point-max))
      (previous-line 1)
      (buffer-substring-no-properties (+ n (point-at-bol)) (point-at-eol)))))
	    
(defun ish-output/comment (comment send-func get-last-output-func)
  (funcall send-func)
  (sleep-for 0 1)
  (when (progn (beginning-of-line)
	       (re-search-forward comment (point-at-eol) t 1))
    (delete-region (point) (point-at-eol)))
  (let ((str
	 (save-excursion
	   (end-of-line) 
	   (funcall get-last-output-func))))
    (end-of-line)
    (insert (format "%s => %s" comment str))))
;;

(defvar js-program "js")
(defvar js-shell "*js*")

;;shellの起動
(defun js-other-window ()   (interactive)
  (unless (get-buffer js-shell)
    (switch-to-buffer-other-window   (get-buffer-create js-shell))
    (comint-run js-program)))

    
(defun js-get-last-output () (interactive)
;;;   (print (concat "content:" (ish-get-last-output "js>" js-shell))))
  (ish-get-last-output "js>" js-shell))

(defun js-send-region (beg end) (interactive "r")
  (comint-send-string (get-process "js") 
		      (concat (buffer-substring-no-properties beg end) "\n")))

(defun js-send-string (str)
  (comint-send-string (get-process "js") str))

(defun js-send-line () (interactive)
  (js-send-region  (point-at-bol) (point-at-eol)))

(defun js-send-buffer () (interactive)
  (js-send-region (point-min) (point-max))
  (js-send-string "\"ok\"\n")
)

(defun js-output/comment () (interactive)
  (let ((send-func 
	 (if (region-active-p)
	   (lambda ()  (js-send-region (region-beginning) (region-end)))
	   (lambda () (js-send-line)))))
  (ish-output/comment "//" send-func (lambda () (js-get-last-output)))))

(add-hook 'js2-mode-hook
	  (lambda () 
	    (define-key js2-mode-map "\C-cS" 'js-other-window)
	    (define-key js2-mode-map "\C-c\C-c" 'js-output/comment)
	    ;(define-key js2-mode-map "\C-c\C-e" 'js-send-region')
	    (define-key js2-mode-map "\C-c\C-l" 'js-send-buffer)
))

追記

ish-output/commentはmacroにしたほうがいいかも。
そうすると、lambdaで包まなくてもすむようになるし。

追記2

C-c C-eはjs2-modeの関数とかぶって使えない(どんなキーバインドが良いんだろ?)

*1:「1 + 3 ////=>4」みたいな感じです。