clojure用emacsの設定
slimeとか良く分からないので、必要そうなものを自分で書いた。
あとでここに書いてある設定が不要になることもあるかもしれない。
run-clojure
schemeの設定と同様に"C-c S"でclojureのreplを立ち上げる。
既にreplが立ち上がっている場合には、other-windowにreplを表示
- "C-x C-e"でS式をreplに送る
- "C-c C-e"で{beginnig,end}-of-defunの範囲をreplに送る
- "C-c C-l"で現在のbufferの内容を全部replに送る
clojure-eval
渡されたS式をclojureのreplで実行する。その実行結果を文字列として返す。
これでreplと通信できるようになった。(片方向だけ)
これを使って以下のようなコマンドを作った。
code
(require 'clojure-mode) (eval-when-compile (require 'cl)) (defvar clojure-program-name "clojure") (defvar clojure-repl-name "*clojure*") (defun run-clojure (cmd) (interactive (list (if current-prefix-arg (read-string "Run Clojure: " clojure-program-name) clojure-program-name))) (let ((buf clojure-repl-name)) (unless (comint-check-proc buf) (let ((cmdlist (split-string-and-unquote cmd))) (make-comint "clojure" cmd) (setq clojure-program-name cmd) (setq clojure-buffer buf))) (display-buffer buf))) (defun clojure-kill-repl () (interactive) (let ((repl-buf (get-buffer clojure-repl-name))) (when repl-buf (kill-buffer repl-buf) (when (> (count-windows) 1) (delete-other-windows))))) (defun clojure-repl-alive-p (&optional forcep) (or (get-buffer clojure-repl-name) (and forcep (run-clojure clojure-program-name)))) (defun clojure-repl-wakeup () (clojure-repl-alive-p t)) ;;;connect with repl ;;internal variable (defvar clojure-output-storage nil) (defvar clojure-reading-p nil) (defvar clojure-output-filter-functions nil) (defvar clojure-reading-timelimit 5) (setq clojure-filtering-context-visible-p nil) (defmacro clojure-filter-function-maker (regexp) (let (( s (gensym))) `(lambda (,s) (cond ((string-match-p ,regexp ,s) (setq clojure-reading-p nil) (push (replace-regexp-in-string ,regexp "" ,s) clojure-output-storage)) (t (push ,s clojure-output-storage))) (if clojure-filtering-context-visible-p ,s "")))) (setq clojure-output-filter-functions (list (clojure-filter-function-maker "\n?.+=> $"))) (defun clojure-stop-reading () (interactive) (setq clojure-reading-p nil)) (defsubst clojure-cleanup-storage () (setq clojure-output-storage nil)) (defmacro %clojure-connect-repl (send-action) `(let ((clojure-reading-p t) (old-comint-filter comint-preoutput-filter-functions) (comint-preoutput-filter-functions clojure-output-filter-functions)) (run-at-time clojure-reading-timelimit nil 'clojure-stop-reading) ;;for safe(time limit) (unwind-protect (progn (clojure-cleanup-storage) ,send-action (while clojure-reading-p (sleep-for 0 100)) (apply 'concat (reverse clojure-output-storage))) (setq comint-preoutput-filter-functions old-comint-filter clojure-reading-p nil) ))) (defmacro clojure-connect-with-repl (send-func) `(progn (clojure-repl-wakeup) ;prepare (%clojure-connect-repl ,send-func))) (defun clojure-eval (sexp) (let ((content (with-output-to-string (prin1 sexp)))) (clojure-connect-with-repl (clojure-send-string content t)))) ;;;find-doc interface (defvar clojure-find-doc-history nil) (defsubst clojure-read-arg () (list (read-from-minibuffer "finddoc(M-n,M-p lookup history):" "" minibuffer-local-map nil 'clojure-find-doc-history))) (defun clojure-find-doc (rx) (interactive (clojure-read-arg)) (add-to-list 'clojure-find-doc-history rx) (with-output-to-temp-buffer "*clojure-find-doc*" (princ (clojure-eval `(find-doc ,rx)))) (clojure-search-forcus-other-window "*clojure-find-doc*" rx)) (defun clojure-find-doc-at-point () (interactive) (clojure-find-doc (current-word))) (defun clojure-search-forcus-other-window (buf rx) (let ((w (some-window (lambda (w) (string= buf (buffer-name (window-buffer w)))) 'no-minibuffer 'current-frame))) (and w (with-selected-window w (re-search-forward (format "^[^ ]+/%s" rx) nil t) (recenter 0) ;; (save-excursion ;; (let* ((beg (point)) ;; (end (progn (re-search-forward "^-+$" nil t) ;; (match-beginning 0))) ;; (ov (make-overlay beg end))) ;; (overlay-put ov 'face 'region))))))) )))) ;;eval via (C-x C-e .etc) (defun clojure-send-string (str &optional newline-p) (comint-send-string (get-process "clojure") (if newline-p (format "%s\n" str) str))) (defun clojure-eval-with-action (beg-ac end-ac &optional newline-p) (let* ((end (progn (funcall end-ac) (point))) (beg (progn (funcall beg-ac) (point))) (str (buffer-substring-no-properties beg end)) (newline-p* (or newline-p (= (point-max) end)))) (clojure-send-string str newline-p*))) (defun clojure-eval-last-sexp () (interactive) (clojure-eval-with-action 'beginning-of-sexp 'end-of-sexp t)) (defun clojure-eval-last-defun () (interactive) (save-excursion (clojure-eval-with-action 'beginning-of-defun 'end-of-defun))) (defun clojure-eval-buffer () (interactive) (save-excursion (clojure-eval-with-action 'beginning-of-buffer 'end-of-buffer t))) (defun clojure-output/arrow () (interactive) ;行頭にカーソルが合ったときに上手くいかない。 (let ((arrow " ; => ") (r (clojure-connect-with-repl (clojure-eval-last-defun)))) (goto-char (point-at-bol)) (when (re-search-forward arrow (point-at-eol) t) (delete-region (match-beginning 0) (point-at-eol))) (goto-char (point-at-eol)) (insert arrow (replace-regexp-in-string "\n" "" r)))) ;;; convenient function when insert closing(paren, brace...) (defun clojure-insert-closing (prefix default-close others) ;;others = ((open . close) ({ . }) ...) (insert default-close) (unless prefix (let ((open-pt (condition-case nil (scan-sexps (point) -1) (error (beep) nil)))) (when open-pt (let ((open-char (aref (buffer-substring-no-properties open-pt (1+ open-pt)) 0))) (and-let* ((other-close (assoc-default open-char others))) (delete-backward-char 1) (insert other-close))))))) (defun clojure-insert-closing-paren (&optional prefix) (interactive "P") (clojure-insert-closing prefix ?\) '((?\[ . ?\]) (?\{ . ?\})))) ;;;setting (setq my-clojure-key-bindings '(("\C-cS" . run-clojure) ("\C-c\C-i" . clojure-output/arrow) ("\C-c\C-k" . clojure-kill-repl) ("\C-x\C-e" . clojure-eval-last-sexp) ("\C-c\C-e" . clojure-eval-last-defun) ("\C-c\C-l" . clojure-eval-buffer) (")" . clojure-insert-closing-paren) ("\C-hd" . clojure-find-doc-at-point) ("\C-hf" . clojure-find-doc))) (add-hook 'clojure-mode-hook (lambda () (loop for (key . cmd) in my-clojure-key-bindings do (define-key clojure-mode-map key cmd))))
class-pathを追加するのが面倒。
jvm系の言語はclass-pathを追加する機会が頻繁にあるのかな?良く分かっていないけれど、~/.clojureに読み込む必要がありそうなファイルを列挙してそれを読み込む関数を書いた。(ファイルならそれ自身、ディレクトリなら直下の.jarファイル)
http://d.hatena.ne.jp/e-o-n/20100124/1264341065
(defun clojure-get-files-from-buffer (buf pattern) (with-current-buffer buf (loop initially (goto-char (point-min)) until (eobp) for file = (buffer-substring-no-properties (point-at-bol) (point-at-eol)) if (file-directory-p file) nconc (directory-files file t pattern t) else collect file do (forward-line 1)))) (defun clojure-class-path-list () (clojure-get-files-from-buffer (find-file-noselect "~/.clojure") "\\.jar$"))