generic.elで俺々モードを作る
Emacs Advent Calendar jp: 2010 : ATND 19日目です。昨日は [twitter:@r_takaishi] さんの org-modeとAnythingが交差するとき,物語は始まる - うどん駆動開発 でした。
Web漁ったり会社で Emacs 使ってる人に聞いたりしてると、みんな generic.el の事を知らないらしいので、ちょっと紹介してみようかと思います。
generic.el はハイライト程度の簡単なメジャーモードをお手軽に作るためのライブラリです。Emacs20くらいの昔から標準添付です。「このファイル用のモードってEmacsに無いなぁ」なんて時に使ったりします。
たとえば
例えばこんなシンタックスのファイルがあるとします
- "#" ではじまるのはコメント
- "/*", "*/" でかこまれてるのはコメント
- hoge, fuga, piyo はキーワード
- 数字には色を付けたい
そんなときはこんなのを.emacsとかに書いとけばいいんです。
(define-generic-mode hoge-fuga-piyo-mode ;; コメントになる文字列の指定 '("#" ("/*" . "*/")) ;; キーワードの指定 '("hoge" "fuga" "piyo") ;; もうちょっと難しいキーワードの指定 '(("[0-9]+" . font-lock-constant-face)) nil nil "Major mode for hoge, fuga, piyo")
そうするとこんな感じでハイライトしてくれます。便利。
ここで作った hoge-fuga-piyo-mode はちゃんとしたメジャーモードなので auto-mode-alist に設定することもできるし、hoge-fuga-piyo-mode-hook もちゃんと存在します。
define-generic-mode について
サンプルで大体分かるとは思いますが、define-generic-mode マクロの簡単な説明をしときます。
define-generic-mode はこんな風に宣言されてます。
(define-generic-mode MODE COMMENT-LIST KEYWORD-LIST FONT-LOCK-LIST AUTO-MODE-LIST FUNCTION-LIST &optional DOCSTRING)
- MODE: モード名。説明不要ですね。
- COMMENT-LIST: コメントにしたい文字のリストを指定します。指定できるのは「文字」「2文字までの文字列」「コンスセル」。一行コメントの場合は文字にして、ブロックコメントを作りたい場合はコンスセルを使います。
- KEYWORD-LIST: ハイライトするキーワードです。
- FONT-LOCK-LIST: 直接 font-lock-keywords を書く場所。KEYWORD-LISTじゃ足りない場合にはこっちを使います。普通は "(REGEXP . FACE)" の羅列で済むと思います。
- AUTO-MODE-LIST: ここに正規表現を書いておくと勝手に auto-mode-alist に登録してくれます。
- FUNCTION-LIST: hook の実行前にここにある関数を実行してくれます。
- DOCSTRING: docstringです。書いときましょう。
詳細は define-generic-mode の docstring を見てください。
auto-complete と組み合わせる
せっかくキーワードを指定してるんだから auto-complete と組み合わせないともったいないです。こんなのを定義しておけば、define-generic-mode で作ったモードで、キーワードの auto-complete ができるようになります。
(defvar ac-generic-keywords nil "Generic keywords for auto complete") (defadvice generic-mode-internal (after generic-mode-with-ac (mode comment-list keyword-list font-lock-list function-list) activate) "Add generic keywords into `ac-generic-keywords'." (set (make-local-variable 'ac-generic-keywords) keyword-list) (setq ac-sources (cons 'ac-source-generic ac-sources))) (defun ac-generic-candidates () ac-generic-keywords) (ac-define-source generic '((candidates . ac-generic-candidates) (symbol . "d")))