Ejacsのjs-consoleの履歴を保存する

EmacsJavaScriptが実行できるEjacsのjs-consoleに感動しつつ、js-console上でシェルやgdbのように履歴を保存できないのが不満だったので、ちょこっと作ってみた。なんてことはない。ただのスタックだ。履歴を遡るだけなら1個でいいのだが、履歴を遡っている途中で逆方向に戻りたくなった場合、もう1つスタックが必要だったので、2段構えになっている。

--- js-console.el.old	2008-11-19 00:03:53.000000000 +0900
+++ js-console.el	2008-11-18 23:58:01.000000000 +0900
@@ -44,6 +44,58 @@
 (defvar js-console-last-parsed-input nil)
 (make-variable-buffer-local 'js-console-last-parsed-input)
 
+;; expression history
+(defvar js-console-prev-history '())
+(defvar js-console-next-history '())
+
+(defun js-console-restore-history ()
+  "Restore input-expression history"
+  (setq js-console-prev-history
+  (append (reverse js-console-next-history)
+  	   js-console-prev-history))
+  (setq js-console-next-history '()))
+
+(defun js-console-push-prev-expression-history (exp)
+  "Add input-expression to history"
+  (setq js-console-prev-history (cons exp js-console-prev-history)))
+
+(defun js-console-push-next-expression-history (exp)
+  "Add previous expression to history for next expression"
+  (setq js-console-next-history (cons exp js-console-next-history)))
+
+(defun js-console-pop-prev-expression-history ()
+  "Pop previous expression from history"
+  (let ((prev-exp (car js-console-prev-history)))
+    (setq js-console-prev-history (cdr js-console-prev-history))
+    (js-console-push-next-expression-history prev-exp)
+    prev-exp))
+
+(defun js-console-pop-next-expression-history ()
+  "Pop next expression from next expression history"
+  (let ((prev-exp (car js-console-next-history))
+  (next-exp (cadr js-console-next-history)))
+    (setq js-console-next-history (cdr js-console-next-history))
+    (js-console-push-prev-expression-history prev-exp)
+    next-exp))
+
+(defun js-console-prev-evaluated-expression ()
+  "Insert prev expression into console."
+  (interactive)
+  (if (null (car js-console-prev-history))
+      (progn (kill-region js-console-input-start (point-max))
+           (message "history is empty."))
+    (progn (kill-region js-console-input-start (point-max))       
+       (insert (js-console-pop-prev-expression-history)))))
+
+(defun js-console-next-evaluated-expression ()
+  "Insert next expression into console."
+  (interactive)
+  (if (null (cadr js-console-next-history))
+      (progn (kill-region js-console-input-start (point-max))
+           (message "history is empty."))
+    (progn (kill-region js-console-input-start (point-max))
+       (insert (js-console-pop-next-expression-history)))))
+
 (defmacro with-js-runtime (&rest body)
   "Execute BODY with a new JavaScript runtime and execution context.
 BODY is a lisp expression - this function is not for evaluating
@@ -101,6 +153,9 @@
 
   (define-key js-console-mode-map [(control a)] 'js-console-bol)
 
+  (define-key js-console-mode-map [(meta p)] 'js-console-prev-evaluated-expression)
+  (define-key js-console-mode-map [(meta n)] 'js-console-next-evaluated-expression)
+
   (js-console-init-interpreter)
 
   (set (make-local-variable 'max-lisp-eval-depth)
@@ -157,6 +212,8 @@
               (js-console-exec-input input)
             (error
              (js-console-handle-error err)))
+	      (js-console-restore-history)
+	      (js-console-push-prev-expression-history input)
         (js-console-exec-input input))))
 
 (defun js-console-exec-input (input)
@@ -392,7 +449,6 @@
   ;; These two are for Narcissus.
   (js-define-builtin global "snarf" 'js-console-js-snarf 'no-wrap)
   (js-define-builtin global "evaluate" 'js-console-js-evaluate 'no-wrap))
-                     
 
 (provide 'js-console)