菅原の資料
Click here to get the file
Size
8.0 kB
-
File type
text/plain
File contents
-*-org-*-
anything のソースを書けるようになってみる + anything.el を読んでみる。
* なんとなく
anything 便利なんだけど、案外自分でソース書いてる人いない。
巷にあふれてるソースだけで十分っちゃ十分なんだけど、やっぱり自分で書けると楽しい。
というわけで、anything のソース書き講座。
# 実は本人はそこまで anything 信者じゃなかったりもする。
# 便利で好きだけどね。年寄りなもんで。
一応ソースのさんぷるとかと anything.el を行ったり来たりしながら話すつもり。
もしかしたら、あちこちしすぎてわかりづらいかもしれない。
で、anything の便利な使い方は他の人の方が知ってると思うからやんない。
あとで教えてくださいませ。
* 対象
elispがなんとなく読める人
- 色々わかってる人にはものたりないかも
- 全然知らない人には難しいかも
わからなかったらさくっと質問してください。
変なこと言ってたらさくっとつっこんでください。
* anything-sources
言わずとしれた anything の情報源。
ここにソースをごりごり書いてくと M-x anything で使えるようになる。
たとえば
(setq anything-sources
'(anything-c-source-buffers+
anything-c-source-file-name-history))
とか。
ちょっと anything の中身のはなし。
M-x anything すると
- いろいろ hook 実行したりしてから
- anything-initialize を実行
- そこで
(anything-funcall-foreach 'init)
ってのをやって
- minibuffer-local-map を anything-map にしてあげて
- anything-update を実行。最初のバッファ更新。
- ほいで、anything-preselect で最初の候補選んで
- あとは
(read-string (or any-prompt "pattern: ")
(if any-resume anything-pattern any-input))
ってして anything-map にまかせたげる。
anything-update はそーすまわして anything-process-source をぐりぐりよぶ。
入力があるごとにこいつがよばれる。
anything-process-source は一個のソースが相手。
anything-compute-matches でマッチした子を表示してくれる。
anything-compute-matches は anything の中核。
candidate 属性とか使って絞り込みをしてくれる。
* ソース書いてみる
name, action, candidates で大体はなんとかなる。
(setq anything-c-source-sample
'((name . "hoge")
(candidates . ("fuga" "mage"))
(action . (("Insert" . insert)
("Search" . (lambda (x) (search-forward x nil t)))))
))
(anything
'(anything-c-source-sample))
- candidates に書いたやつを anything が出してくれる
- action に書いたやつを選んだときに anything が実行してくれる
- 複数書いたら RET したら最初のが選ばれる
- TAB すると全部
candidates には関数も書ける。
(setq anything-c-source-sample2
'((name . "buffers")
(candidates . (lambda () (mapcar 'buffer-name (buffer-list))))
(action . (("Select" . switch-to-buffer)
("Kill" . kill-buffer)))
))
(anything
'(anything-c-source-sample2))
candidates は (DISPLAY . REAL) っていうペアのリストも返せる。
一覧に表示させたいものと、action で処理したいものが違うときなんかに有用。
(setq anything-c-source-sample3
'((name . "buffers2")
(candidates . (lambda ()
(mapcar
(lambda (x)
(cons
(if (buffer-file-name x)
(concat (buffer-name x)
"[" (buffer-file-name x) "]")
(buffer-name x))
x
))
(buffer-list))))
(action . (("Select" . switch-to-buffer)
("Kill" . kill-buffer)))
))
(anything
'(anything-c-source-sample3))
ひとつの候補が複数行ある場合は multiline 属性を使う
(setq anything-c-source-sample4
'((name . "multi")
(candidates . ("fuga\nhoge" "mage\nhoge"))
(action . (("Insert" . insert)
("Search" . (lambda (x) (search-forward x nil t)))))
(multiline . t)
))
(anything
'(anything-c-source-sample4))
ふつーは candidates はキャッシュしてくれる。volatile 属性つけるとキャッシュしない
ついでに init も。こいつは anything を実行するときに最初の一回だけ実行してくれる。
volatile 使わないときは candidates でがんばっちゃってもおけ。
(setq anything-c-source-sample5
'((name . "hoge")
(init . (lambda () (setq hoge 0)))
(candidates . (lambda ()
(setq hoge (1+ hoge))
(list "hoge" (format "hoge%d" hoge))))
(action . (("Insert" . insert)
("Search" . (lambda (x) (search-forward x nil t)))))
(volatile . t)
))
(anything
'(anything-c-source-sample5))
candidates-in-buffer つかうとバッファの中身を候補にしてくれる。
どっちかっていうとパフォーマンスを気にするときむけ。
candidates-in-buffer を使うには init で anything-candidate-buffer を使っ
てバッファとソースを紐付けしておく。anything-candidate-buffer の引数は
- バッファ
- そのバッファをマーク
- global ってシンボル
- ワークのバッファを作ってそれを登録。with-current-buffer と一緒に。
- それ以外のシンボル
- ソースだけじゃなくって、起動したバッファとも紐付ける。resume向けかな?
ふつーはglobal使うことが多いんじゃないかな。と。
あと、anything-current-buffer って変数に anything を起動したときのバッファがはいってる。
べんり。
(setq anything-c-source-sample6
'((name . "hoge")
(init . (lambda () (with-current-buffer (anything-candidate-buffer 'global)
(insert-buffer-substring anything-current-buffer))))
(candidates-in-buffer)
(action . (("Insert" . insert)
("Search" . (lambda (x) (search-forward x nil t)))))
(volatile . t)
))
(anything
'(anything-c-source-sample6))
** anything-compute-matches をちょいと見てみる。
match とって candidates をキャッシュからとってぐりぐり。
かんたん。
match は候補の一致のさせかたを変えるんだけど、 anything-match-plugin とのあいしょう悪し。
anything-default-match-functions が必ず match 関数に入っちゃう。
* anything お他のアプリとくみあわせる
anything 関数はソースのリストを引数に取れる。で、このソースのリストの中
身って別にシンボルじゃなきゃいけないって訳じゃない。
ってことは関数の中でリストをぐりぐり作ってそれを anything で表示するっ
てこともできるわけだ。
ソースの数が固定で中身をぐりぐりするだけじゃなくって、ソースの数を可変にするのもあり。
あんま見ないけど、descbinds-anything なんかはそんなことやってる。
* プラグインを書く
プラグインを書くには anything-compile-source-functions に関数を追加する。
関数はソースを引数に取ってソースを変えす。
ようするにプラグインってのはソースを変換するためのしかけってこと。
たとえば anything-match-plugin は match に機能を追加するためのプラグインだし、
candidates-file-plugin は candidates-file って属性を追加するためのプラグイン。
ためしに簡単なプラグインをひとつだけ。
候補にかならず草をつけるプラグイン
(defun anything-compile-source--grass (source)
(lexical-let
((trans (assoc-default 'candidate-transformer source))
(grass (assoc-default 'grass source)))
(setq grass (or grass "ww"))
`((candidate-transformer
. ,(cons
(lambda (cands)
(mapcar (lambda (c)
(let ((d (if (consp c) (car c) c))
(r (if (consp c) (cdr c) c)))
(cons (concat d grass) r)))
cands))
(if (listp trans) trans (list trans))))
,@source)))
(add-to-list 'anything-compile-source-functions 'anything-compile-source--grass)
(anything-document-attribute 'grass "grass plugin"
"wwww.")