[2010-12-04 12:28:30]
>>>[emacs]
Emacs Advent Calendar jp: 2010 : ATNDも6日目です.
5日目はid:kiwanamiさんのEmacs上のATOKで快適日本語生活 / 2010 Emacs Advent Calendar - 技術日記@kiwanamiで,7日目はid:moozさんです.楽しみ!
特に書くネタを考えていたわけじゃないんですが,先日anything-mac-itunes.elが公開されたので,それにからめて書いてみることにします.
流れ
- applescriptを使ったelisp
- Carbon Emacsにおけるdo-applescript
- defadvice入門
- defadviceのやや細かい話
- 引数とか返り値をいじるための話
ようするにdefadvice入門記事です.
長くなっちゃったので,気になるところだけつまんだ方がいいかもしれませんorz
applescriptを使ったelisp
MacのAppleScriptを利用しているelispが幾つかあるので紹介します.
- my-search-at-dictionary-app([Emacs]Carbon EmacsからDictionary.app(辞書)で単語を調べる: 旧式ひとりぶろぐ)
ミニバッファで入れた単語を辞書.appで調べる
- anything-mac-itunes.el(anything-mac-itunes.elというのを書きました - yaotti's diary)
anythingインタフェースでiTunesのライブラリを絞り込み検索して,再生したり,前後の曲へ移動,再生停止の操作ができます
- en-mac-create-note(Cocoa Emacs => Evernote - Clipboard)
Mac用のEvernoteアプリケーションにノートを渡せるようにしたもの
こんな感じでEmacsからMacのアプリケーションにメッセージを送っていろんなことができるので,使い用によってはかなり便利です.
AppleScriptの実行方法も(do-applescript "AppleScriptのコード")とするだけでいいのでとても簡単です.
Carbon Emacsにおけるdo-applescript
とっても便利なAppleScriptですが,Carbon Emacs(Emacs22)のdo-applescriptは文字コードをShift-JISでやり取りしているみたいなんですね.
Cocoa Emacs(Emacs23)ならutf-8でやり取りしているのでそのまま使えるのですが,へたれな私はまだCarbon Emacsを使っているので,なんとかしたいなと.
そこでdefadviceの登場ですよ.
文字コードが違うなら文字コード変えてAppleScriptに渡したらいいんですよね.というわけで
(when (string-match "^22" emacs-version) (defadvice do-applescript (before do-applescript-before-advice (script)) (ad-set-arg 0 (encode-coding-string (ad-get-arg 0) 'shift_jis))) (defadvice do-applescript (after do-applescript-after-advice (script)) (setq ad-return-value (decode-coding-string ad-return-value 'shift_jis))) ;;(ad-deactivate 'do-applescript) (ad-activate 'do-applescript))
こんな感じにアドバイスしてあげるとdo-applescriptを実行する前にAppleScriptのコードをshift_jis
に変換して,do-applescriptの実行結果をshift_jisからutf8(そのとき使っている文字コード)に変換してくれるようになります.
ちなみにapplescript-string-literal.elで曲名が文字化けしていたのでこんな感じに書きました.
(when (eq system-type 'darwin) (require 'anything-mac-itunes) (when emacs22-p ; (string-match "^22" emacs-version) (defadvice do-applescript (before do-applescript-before-advice (script)) (ad-set-arg 0 (encode-coding-string (ad-get-arg 0) 'shift_jis))) (defadvice do-applescript (after do-applescript-after-advice (script)) (setq ad-return-value (decode-coding-string ad-return-value 'shift_jis))) ;;(ad-deactivate 'do-applescript) (ad-activate 'do-applescript)) (global-set-key (kbd "C-c s m") 'anything-mac-itunes) (global-set-key (kbd "C-c s b") 'anything-mac-itunes-back-track) (global-set-key (kbd "C-c s n") 'anything-mac-itunes-next-track) (global-set-key (kbd "C-c s p") 'anything-mac-itunes-playpause-track) (global-set-key (kbd "C-c s c") 'anything-mac-itunes-show-current-track-info) )
defadvice入門
さて,defadviceが出てきたので,どういうことかという話
defadviceっていうのは,すでに定義されている関数の動作を変更するマクロで,関数を書き直すよりもスマートなカスタマイズができます.(あとCで書かれた関数はこれじゃないといじられへんと思う)
さっきのコードで簡単に説明してみます
(when (string-match "^22" emacs-version) ;(1) (defadvice do-applescript ;(2) (before do-applescript-before-advice (script)) ;(3) (ad-set-arg 0 (encode-coding-string (ad-get-arg 0) 'shift_jis))) ;(4) (defadvice do-applescript (after do-applescript-after-advice (script)) ;(5) (setq ad-return-value (decode-coding-string ad-return-value 'shift_jis))) ;(6) ;;(ad-deactivate 'do-applescript) ;(7) (ad-activate 'do-applescript)) ;(8)
(1)はemacsのメジャーバージョンが22かどうかチェックしてます.23だったらこのアドバイスがいらないからね.
ここではdo-applescriptに二つのアドバイスをしています.
(2)はこのアドバイスがdo-applescriptという関数に対するアドバイスってことを示してます
(3)で,アドバイスの名前がdo-applescript-before-advice.
このアドバイスが実行されるのはdo-applescriptが実行される前(before)ということを示しています.
(4)の最初のafterでdo-applescriptの実行後にアドバイスdo-applescript-after-adviceが実行されるってことを意味してます.
(4)がなにしてるかっていうと,(ad-set-arg 0 hoge)で0番の引数(1つめ)にhogeを設定するって意味で,(ad-get-arg 0)は0番の引数を取り出すって意味なので,引数scriptの文字コードをshift_jisに変換して,引数scriptに戻してることになります.
(6)ではad-return-valueが関数の返り値を表すことを使って,do-applescriptの返り値をshift_jisから現在のエンコードに変換して,返り値を設定し直していることになります.
(7)(8)はそれぞれdo-applescriptにたいするアドバイスを無効,有効にする記述です
before,afterの代わりにaroundを指定するとdo-applescriptそのものを書き換えることができます.
(defadvice do-applescript (around do-applescript-around-advice (script)) (ad-set-arg 0 (encode-coding-string (ad-get-arg 0) 'shift_jis)) ad-do-it (setq ad-return-value (decode-coding-string ad-return-value 'shift_jis)))
ad-do-itがdo-applescriptそのものを呼び出します
細かい話
GNU Emacs Lispリファレンスマニュアル: 16. Emacs Lisp関数のアドバイスみると詳しいんだけども,かいつまんで書きます.
詳しく見るとこんな感じに書きます.*がついてるのは省略可能です.
(defadvice アドバイスしたい関数名 (どの部分にアドバイスするか アドバイスにつける名前 *何番目のアドバイスにするか *引数のリスト 使い方フラグ) *ドキュメント 本体)
- どの部分にアドバイスするかはbefore,around,after,activation,deactivationが指定できて,それぞれアドバイスしたい関数(some-funcとする)にたいして,some-funcの前に実行するのか,some-funcのあとに実行するのか,その両方で実行するかを示してます.activationとdeactivationはわかりませんでした.
- アドバイスにつける名前はM-x describe-function some-funcしたときに*Help*バッファに表示されたり,アドバイスを解除する時,有効/無効にする時に使う名前なんで,かぶらんかったら何でもいいです
- 何番目かってのは,同じ関数に何個もアドバイスした時に何番目のアドバイスにするか.っていうので,0番目から順番に実行されていきます.普段は使わない.競合してうまく動かん時に思い出せたらいいと思います
- 引数のリストは,some-funcの定義と同じ個数で引数を書いていればアドバイス内で引数を参照することができます
- 使い方フラグ.これはこの記事を書くまで知りませんでした笑.てか今まで書いたことなかったけど動いてたな...
フラグ | 説明 |
activate | アドバイスをすぐ有効にする |
protect | 保護する.別のアドバイスやsome-func自身でエラーが発生しても実行されるようにする |
compile | 結合定義っていうのをコンパイルするらしいです.わかりません |
disable | とりあえず有効にしない(ad-activateで有効化する必要がある) |
preactivate | アドバイスをコンパイルする時にsome-funcのアドバイスを有効にしておく |
- ドキュメントを書くとsome-funcのドキュメントに追加して*Help*に表示されるようになります
- 本体にアドバイスの内容を書きます
好きなことを書けます.引数をいじったり返り値を変えたりできます.
よくやるのは,特定の関数を呼ぶ時だけ設定を変えるってやつやね.
引数,返り値の参照
(ad-get-arg n) | n番目の引数を返す |
(ad-get-args n) | n番目以降の引数リストを返す |
(ad-set-arg n value) | n番目の引数の値をvalueに設定する |
(ad-set-args n values) | n番目以降の引数の値をvalues(リスト)に設定する |
ad-return-value | 返り値を表す変数.参照すれば直前のアドバイスとかの実行結果が入ってるし,値を設定すれば返り値の値を設定できる |
yasnippet
忘れるところだった.というかいつも忘れるんだけど,defadviceって覚えらんないからyasnippetでスニペットほりこんだら楽に書けるよねー
というわけでスニペットです.emacs-lisp-modeでdefadviceと打って,TABキー押すと展開されて,関数名,どの部分にアドバイスするかをTABで順番に入力していけます
# -*- mode: snippet -*- # name: define advice # key: defadvice # binding: "keybinding" # expand-env: # -- (defadvice $1 (${2:$$(yas/choose-value '("before" "around" "after" "activation" "deactivation"))} $1-$2-advice ($3))$> $0$> )$> (ad-activate '$1) ;; (ad-deactivate '$1)
長くなりすぎましたが.最後まで読んでくれてありがとうございます.Emacs Advent Calendarもまだまだ続きます!
素敵なEmacsライフを!