対応する括弧で閉じてくれる elisp

3種類の括弧の閉じ分けが面倒なので作ってみた。デバッグ不十分なので不具合あるかも。あと、ナローイングとか考慮されていません。

;;  開き括弧 { or ( or [ に対応した閉じ括弧 } or ) or ] を挿入する
(defun clj-close-sexp ()
  (interactive)
  (let ((close-pos (point)))
    (insert ")")
    (condition-case ERR
	(clj-close-sexp-impl close-pos)
      (error (clj-close-sexp-error close-pos)))))

(defun clj-close-sexp-impl (close-pos)
  (backward-list)
  (let ((open-pos (point)))
    (let ((ch (buffer-substring open-pos (1+ open-pos))))
      (let ((new-char (cond ((string= ch "[") "]")
  			    ((string= ch "{") "}"))))
  	(when new-char
  	  (goto-char close-pos)
  	  (delete-char 1)
  	  (insert new-char)))))
  (goto-char (1+ close-pos)))

(defun clj-close-sexp-error (close-pos)
  (goto-char close-pos)
  (delete-char 1))

;; ) 入力時に実行されるようキーバインド
(add-hook 'clojure-mode-hook
          (lambda ()
	    (local-set-key "\)" 'clj-close-sexp)))


;; チェックなしで任意に ) を入力出来るように。
(defun clj-close-sexp-no-check ()
  (interactive)
  (insert ")"))

;; C-) にキーバインド
(add-hook 'clojure-mode-hook
          (lambda ()
	    (local-set-key (kbd "C-)") 'clj-close-sexp-no-check)))

) を入力すると対応する括弧に合せて } or ] or ) が挿入されます。) を押し続けると対応する括弧が無くなったところで止ります。
ただそれだとプログラム構造と無関係に ) を入れることが出来なくなるので、一応それようのコードも入れてあります。
本当は、C-] じゃなく、C-) にバインドさせたかったんだけど、キー定義の書き方がわからなかった。どうやるんだ? (kbd "C-)") とすることでバインドできました。

他モードへの適用

clojure-mode での利用を前提にしています。
lisp-mode 試したところ、backword-sexp で [ や ( へは飛んでくれますが、{ は式の先頭と認識してくれないようです。このため { があると正常に動きませんでした。

コード修正

  • 2010/06/28 開き括弧の前に ' や # などの記号がある場合正常に動作しないバグ修正 (clj-close-sexp-impl)
  • 2010/06/28 開き括弧へジャンプする処理を backward-sexp から backward-list に変更、こちらなら ' などに関係なく正しく開き括弧へ飛んでくれるみたい(clj-close-sexp-impl)。【などの全角括弧でもちゃんと働いてくれます。
  • 2010/06/30 clj-close-sexp-no-check のキーバインドを C-] から C-) に変更。