あどけない話

インターネットに関する技術的な話など

Electric な Ruby モードの end

Ruby に付いてくる ruby-electric.el では、たとえば class と打った後に空白を入れると end が挿入されます。僕が欲しいエレクトリックな機能は、これじゃありません。

end と押すと対応する単語に飛んでほしいのです。というわけで作ってみました。別ファイルにすると設定方法を説明するのが面倒なので、とりあえず以下を .emacs に入れて下さい。

(defvar ruby-elct-regex "def\\|if\\|class\\|module\\|unless\\|case\\|while\\|do\\|until\\|for\\|begin\\|end")

(defun ruby-elect-end ()
  (interactive)
  (insert "d")
  (when (and (char-equal (char-before (1- (point))) ?n)
	     (char-equal (char-before (- (point) 2)) ?e))
    (ruby-indent-command)
    (let ((orig (point)) open)
      (forward-char -3)
      (when (looking-at "\\bend\\b")
	(setq open (ruby-elect-begin))
	(when open
	  (goto-char open)
	  (sit-for 0.3)))
      (goto-char orig))))

(defun ruby-elect-begin ()
  (let ((level 0))
    (catch 'loop
      (while (re-search-backward ruby-elct-regex nil t)
	(cond
	 ((string= (match-string 0) "end")
	  (setq level (1+ level)))
	 (t
	  (if (= level 0) (throw 'loop (match-beginning 0)))
	  (setq level (1- level))))))))

(add-hook 'ruby-mode-hook
	  (lambda ()
	    (define-key ruby-mode-map "d" 'ruby-elect-end)))

あんまりテストしていません。飛んで一瞬留まる方法も正しいのか調べていません。

こういう構想は、Pascal を使っていた学生時代に持っていたんですが、そのときの実力では実現できませんでした。

今日、書いてみたら 10 分ぐらいでできたので、成長したんだなぁと思います。。。(Pascal を使わなくなったとはいえ、構想の実現に 15 年以上もかかるとは。。。)