magic comment を自動的に付ける

Ruby1.9 で漢字を扱うには、magic comment で文字コードを指定しないといけない。

この面倒な作業を自動的にやってくれる Emacs の設定がある。

1 ヶ月以上 何不自由なく使っていたんだけど、文字コード以外の magic comment が使えないことに気がついた。
何か設定していても消されてしまう。

拡張子を付けずにスクリプトを書いて chmod して使うことが多いので、普段から -*- ruby -*- とか書いている。困った。


Emacs の info を斜め読みして、いろいろ試してみた結果、以下のことが分かった。

  • 名前だけを書くと major mode の設定になる。
# -*- ruby -*-
  • 明示的に major mode の設定であることを宣言できる。
# -*- mode: ruby; -*-
  • magic comment は複数行書けない。
    • 先勝ち
# -*- coding: utf-8; -*-
# -*- mode: ruby; -*-     # この行は無効
  • 複数の設定は 1 行にまとめて書く。
# -*- coding: utf-8; mode: ruby; -*-

最終的に、この形式にしたい。


Lisp は苦手だけど、やってみた。

(defun ruby-coding-from-buffer-file-coding-system ()
  (let ((coding-system (symbol-name buffer-file-coding-system)))
    (cond
     ((string-match "japanese-iso-8bit\\|euc-j" coding-system) "euc-jp")
     ((string-match "shift.jis\\|sjis\\|cp932"  coding-system) "shift_jis")
     ((string-match "utf-8" coding-system) "utf-8"))))

(defun ruby-normalize-major-mode ()
  (goto-char (point-at-bol))
  (or (re-search-forward ":" (point-at-eol) t)
      (if (re-search-forward "^#\\s *-\\*-\\s *\\(\\S +\\)\\s *-\\*-\\s *$"
			     (point-at-eol) t)
	  (replace-match
	   (format "# -*- mode: %s; -*-"
		   (buffer-substring (match-beginning 1) (match-end 1)))))))

(defun ruby-remove-coding ()
  (goto-char (point-at-bol))
  (if (re-search-forward
       "coding\\s *:\\s *[^ 	;]+\\s *;?\\s *" (point-at-eol) t)
      (delete-region (match-beginning 0) (match-end 0))))

(defun ruby-insert-coding ()
  (goto-char (point-at-bol))
  (if (re-search-forward "^#\\s *-\\*-\\s *" (point-at-eol) t)
      (replace-match (format "# -*- coding: %s; " ruby-coding-system))))

(defun ruby-modify-magic-comment ()
  (ruby-normalize-major-mode)
  (ruby-remove-coding)
  (ruby-insert-coding))

(defun ruby-set-coding-system-in-magic-comment ()
  (save-excursion
    (goto-char 1)
    (when (looking-at "^#!")
      (forward-line 1))
    (if (re-search-forward "^#.*-\\*-" (point-at-eol) t)
	(ruby-modify-magic-comment)
      (insert (format "# -*- coding: %s; -*-\n" ruby-coding-system)))))

(defun ruby-set-coding-system-in-magic-comment-if-needed ()
  (let ((ruby-coding-system (ruby-coding-from-buffer-file-coding-system)))
    (and ruby-coding-system
	 (eq major-mode 'ruby-mode)
	 (find-multibyte-characters (point-min) (point-max) 1)
	 (ruby-set-coding-system-in-magic-comment))))

(add-hook 'before-save-hook 'ruby-set-coding-system-in-magic-comment-if-needed)

空白の許される位置が、これで当ってるのか分からんけど、なんとか動いた。

ところで、ちゃんと Emacs の info を読むと、interpreter-mode-alist を設定すれば、#! 行に書いたプログラム名を基に major mode を設定できるって書いてあった。
magic comment は不要。_| ̄|○

こんな感じかな。

(let ((mode '(("ruby" . ruby-mode)
	      ("ruby19" . ruby-mode))))
  (while mode
    (setq interpreter-mode-alist (cons (car mode) interpreter-mode-alist))
    (setq mode (cdr mode))))

ま、将来 magic comment が増えたときのためだと思えば ……