deferred.el であそんでみる
deferred:loop を接続したいと思っていろいろやってみた。
ためしみた
最初に書いたのがこれ。
(deferred:$ (deferred:loop (loop for x from 0 to 1000 collect x) (lambda () (message "%s" x))) (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda () (message "%s" x))) (deferred:nextc it (lambda () (message "end"))))
うまくいかない。最初のloopと次のloopが一緒に走ってる感じ。次に deferred:nextc で loop を囲んでみた。
(deferred:$ (deferred:loop (loop for x from 0 to 1000 collect x) (lambda (x) (message "%s" x))) (deferred:nextc it (lambda () (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda (x) (message "%s" x))))) (deferred:nextc it (lambda () (message "end"))))
これだとうまくいった。deferred:loop は前の deferred と接続できないから、deferred:nextc で開始を遅延させる。
しらべてみた
微妙に不思議なのが 2回目の loop が終わってから "end" ってメッセージがでること。2回目の loop がはじまったらそのまま deferred:nextc が終わってすぐに "end" って出るんじゃないかと思った。
そこで deferred:nextc の実装を見てみた。deferred:nextc は簡単。 callback を呼び出す deferred を作って 引数の d と繋ぐだけ。
(defun deferred:nextc (d callback) "Create a deferred object with OK callback and connect it to the given deferred object." (let ((nd (make-deferred :callback callback))) (deferred:set-next d nd)))
ここから呼ばれてる deferred:set-next で目的の処理をしてた。deferred:exec-task の返り値が deferred ならそれを返り値としてる。
(defun deferred:set-next (prev next) "[internal] Connect deferred objects." ... (let ((ret (deferred:exec-task next 'ok (deferred-value prev)))) (if (deferred-p ret) ret next)) ...)
RAEDME の「実行時に接続」とか「lambdaの返り値に気を使う」は多分これのこと。
やっとなっとくした。
もうちょっとあそんでみた
まだほとんど使ってないけど、deferred:$ を使うときって大体 deferred:nextc で接続するんじゃないかと思う。そう思って引数の関数を deferred として順次実行する deferred:sequential ってのを作ってみた。
(defun deferred:sequential (&rest args) (deferred:loop args 'funcall))
これでさっきの loop のやつはこんな風に書ける。少しみやすくなった。
(deferred:sequential (lambda () (deferred:loop (loop for x from 0 to 1000 collect x) (lambda (x) (message "%s" x)))) (lambda () (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda (x) (message "%s" x)))) (lambda () (message "end")))
lambda を書くのが面倒ならこんなのを定義するのもいいかもしれない。
(defmacro deferred:$$ (&rest elements) (lexical-let ((elements elements)) `(deferred:sequential ,@(loop for i in elements collect `(lambda () ,i)))))
これを使うと lambda を書かなくてよくなる。やりすぎっぽけど。
(deferred:$$ (deferred:loop (loop for x from 0 to 1000 collect x) (lambda (x) (message "%s" x))) (deferred:loop (loop for x from 1000 downto 0 collect x) (lambda (x) (message "%s" x))) (message "end"))
いろいろためしてみたけど
test-deferred.el を見るのが一番はやく理解できるんじゃないかと思った。というわけで deferred.el を使いたい人は test-deferred.el を見るのがいいと思います。
2011までカウントダウン
カーソル位置に秒数を出してみる。deferred.el を使ってみたかった。けど、無限ループを正しく扱えてるか自信がない。
- M-x countdown-start で開始
- M-x countdown-stop で停止
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")))
region を入れかえる trans-regions.el を作った
インストールは以下で。
M-x install-elisp-from-emacswiki trans-regions.el
使い方は簡単。M-x trans-regions ってするだけ。そのあとは以下のように操作する。
- すでに region を選んでるときは二つめの region を選択する
- 選んでないときは、最初の region を選択して、次に二つめの region を選択する。
どちらも region を選択したらモードラインに書かれてる通り decide すれば region を決定する。二つの region が決定したら入れかえてくれる。
ごちゃごちゃ書いたけど、使ってみた方が速いと思うんで、興味のある人は試してみて下さいです。
EmacsWiki に swap-region と anchored-transpose っていう region 入れかえ用の elisp が既にあるんだけど、多分その子たちよりは使いやすいと思う。
この手のコマンドを作りたい場合
入れ替えをやるための標準の関数には以下がある
- transpose-regions
- transpose-subr
おすすめは transpose-regions の方。こっちの方が断然使いやすい。Emacsの各種入れ替えコマンドは transpose-subr を使ってるけど、使いにくい上にドキュメントが無いからやめた方がいい。ドリューたんも #3249 - 23.0.93; doc of transpose-subr and transpose-regions - GNU bug report logs で嘆いてた。
追記
- Emacs22をサポートしてみた。
最新のauto-completeを使って思ったこと
twitterでつぶやこうかと思ったんだけど、ここにつらつらと書いてみる
- popup-menuが案外completing-read の代わりになる。今回のisearch対応でさらにいい感じ。
- auto-start する source としない source を設定できるといいかも
- omni 補完する奴は auto-start して ac-source-words-in-* とかは明示的に入れたときに出るとか。
- やっぱ omni 補完が選択されたときも他の候補が出て欲しい
- auto-start じゃないときだけでいいかも。ac-source-functions 使ってて思った。
- 自分の枝で一応は実装してるけど、auto-complete の理念に合うのかが微妙でパッチ送ってない。
- eclipse みたいに複数のメニュー切り替えられるといいかも。
- やっぱ action に選択した候補が渡ってくれた方がうれしい。
- init で開始位置覚えとけばいいんだけど、ソースごとにそれやるのはめんどい。
- auto-start が nil のときの挙動が微妙に不安定
- これは自分とこで直してみてる。まだ途中。多分 nil の人はあまりいないだろうし。
今後も期待。
descbinds-anything と vline を更新した。
新しいのを取得するには以下で。
- M-x install-elisp-from-emacswiki descbinds-anything.el
- M-x install-elisp-from-emacswiki vline.el
それぞれの変更点はこんな:
- descbinds-anything
- Major Mode とかが先にくるようにした。
- persistent-action 足した。
- vline
- vline-visual が有効でも速度出るようになった
追記
- 2010-01-05 デフォルトアクションが実行できないバグがあったので修正。
ac-companyでdocumentに対応した。
ものは以下からどうぞ。
M-x install-elisp http://github.com/buzztaiki/auto-complete/raw/master/ac-company.el
company backend が doc-buffer か meta をサポートしてればそれを表示する。
あと、ac-company-define-source に引数追加して symbol とか足せるようにしてみた。例えば以下のようにすると、"s"ってシンボルが付いて、候補が100個まで出るようになる。
(ac-company-define-source ac-source-company-elisp company-elisp (symbol . "s") (limit . 100))