Shibuya.el#1でERTについて発表してきた

少し時間が経っておりますが、9/6にShibuya.el#1に参加&発表をしてきました。
関西Emacsでの顔見知りにも、一度会ってみたかった人にも会えてよかったです。
当日はど平日だったのと雨が降っていたのもあってか、キャンセルが結構出てしまっていたのが少し残念でしたが…
それではだらだらと思ったことなどを書いてゆきます。

ポジションペーパー

まずはポジションペーパーによる自己紹介。
一応私の自己紹介はこんな感じ。

ついでにkey-combo.elとflex-autopair.elの宣伝もしてました。
他の皆さんは自己紹介だけで笑いが取れていてすごいです...私もなにかネタを仕込みたい。

ライトニングトーク

id:shigemk2 さんの
LT #shibuya_el - by shigemk2がよくまとまっていると思います。関西Emacsの時も思いましたが、相変わらず早くて正確で素敵です。

[twitter:@ainame]さん:初めてのemacs lisp

hjkl-modeをmarmaladeに公開してみた話。
途中でmarmaladeの更新用コマンドを出してくれていたのでありがたかったです。
資料の公開が楽しみですね。
hjkl-modeの方針はEvilまでは目指してなくて、view-mode的なものなのかなという印象でした。

Emacs Lispのテスト

私の発表です。
テンパって内容を飛ばしたり、デモが想定外の動きをしたり、安定のグダグダっぷりです。
LTは初めてだったのでと言い訳しつつ…
スライドは

デモで使った資料は

です。
個人的にはorg-tree-slideを使えばモテると聞いていたのに、だれも触れてくれなかったのが悲しいです( ´・ω・` )

[twitter:@handlename]さん:emacsdvorak配列について

dvorakをさらにカスタマイズしましたというお話。
かな配列ですら覚えられない私のようなヘタレにはアナザーワールドですね。
すごいです、キーボード渡されても全く使える気がしないです。

[twitter:@hayamiz]さん:tokyo-emacs という歴史上の出来事

すごく発表がうまく、会場が盛り上がってました。
私は小指が短いので、選ばれしホモ・イーマックスではないようです。残念。
話の本筋は勉強会の運営のお話。やはり大変ですよね…shibuya.elは末長く続いていって欲しいなぁと思います。

[twitter:@m2ym]さん:最近作った拡張(popwin.elなど)について

最近github上でやり取りしてたので、お会いできるのを楽しみにしてました。
内容はpopwin,direx,emux,yascrollなどなどデモが盛りだくさん。
意外だったのが、見た目上はデフォルトから激しく変更している感じがしなかったことです(実際は色々変えていると思いますが)。
バッファの切り替えにもanything/helmではなくiswitchb(?)を使ってるみたいでした。
個人的には、gsocでのnative profilerの話も詳しく聞いてみたいところです。

[twitter:@at_aka]さん:clgrep.el について

まずはkinesisの話題。
他人が使えないキーボードを使うと、セキュリティ対策にもなるとの話で笑いました。
本題はgrep系のお話。メモを取ってる時には前後関係が分かるように行単位ではなくメモ単位でgrepできるといいでしょう(context grep)。
clgrep(というかバックエンドのblgrep)は汎用的な作りになっていて、clmemo以外にも色々(2ch,outline-modeなど)扱えるようになっている。org-modeで使えるとうれしいかもと思ったけど、元々の機能のorg-occurというので似たようなことができそうな感じ。

[twitter:@ShougoMatsu]さん:Vimmerから見たEmacs

VimEmacsとの比較。
完全に客観的な比較はできないので、偏りはまぁ想定範囲内といったところですかね。
Emacsのデフォルト設定が使いづらいのは同意です。とは言ってもどうしても無理なのは「C-h」のキーバインドぐらいですが。
デフォルトの設定をそのまま使うかカスタマイズして使うかは文化の違いだと思います。詰めVimの話にもその違いが表れていると思っていて、もしテキスト操作の機能がVimと同様だとしてもキーバインドが人によって違っていたら成り立たないよなぁとか思ってました。
お互いを理解するには、機能の点取り表では出てこない文化や思想の違いも重要な気がしますがいかがでしょうか?

その他

懇親会では[twitter:@arte_nut]さん,at_akaさん,[twitter:@masutaka]さんと「ブラウザはOperaがいい」「Macがいろいろ楽だ」という話になったり。
m2ymさん,ShougoMatsuさん,Hayamizさんと「拡張機能のメンテナンス(特にドキュメント、テストまわり)が大変」などなどの話をしてました。
m2ymさんからは(popwinのテストと同様に)auto-completeのテストもお願いされてしまいました…時間確保できるといいんですが…
他にもr_takaishiさんからorg-syncの話が聞けたり、楽しかったです。
主催のainameさん、会場の準備などしていただいたmixiの方々、発表者、参加者の皆様、本当にありがとうございました。

el-spec v0.2リリース:テスト結果からテスト定義へのジャンプ。個別のテスト実行をサポート

el-spec v0.2 をリリースします。変更点は以下です

  1. テスト結果からテスト定義へジャンプ可能に
  2. 個別のテスト実行をサポート

https://github.com/uk-ar/el-spec

変更詳細

1つ目はテスト結果からテスト定義へのジャンプです。テスト(example)が失敗した際にはテスト内容を詳しくみれると便利ですね。ertにもともと備わっている機能ですが、ertの関数をラップして使ってるのでv0.1では対応できてませんでした。今回、遅れて対応です。
使い方はertバッファ上の失敗/成功したテスト名の上で「RET」を押すことでテストを定義した場所へ飛ぶことができます。
ただし一つ制限があり、現時点ではshered-examples(include-examples)された定義には飛べません。実装を頑張らないとshared-examplesへの対応は難しそうです。

2つ目は個別のテスト実行です。テストが増えてくると全部を実行するには時間がかかるようになります。その場合、一部のテストだけを実行できると便利ですね。v0.1ではcontext単位のテスト実行をサポートしていましたが、これを拡張してexample単位で実行できるようにしました。M-x el-spec:eval-and-execute-examplesでカーソル付近のexample/contextだけを実行することができます。
挙動が分かりづらいので、スクリーンショットで説明します。

カーソル位置により実行範囲が変わり、

  • 灰色部分にあるときdescribe以下(3つのexample)が実行される
  • 青色部分にあるときit以下(各々1つのexample)が実行される
  • 黄色部分にあるときcontext以下(2つのexample)が実行される

ことになってます。shared-examplesやinclude-examplesの内部で実行しても上位のcontextなどを実行するだけなので、注意して下さい。
逆にM-x el-spec:eval-and-execute-allでdescribe以下のコンテキストを実行できます。M-C-x でevalした時の挙動も切り替えが可能で、M-x el-spec:toggle-selectionするたびにallとexamplesが切り替わります。

インストール方法

el-specはertに依存しています。
Emacs23以下の場合はert自体のインストールが必要です。
rspecのように前処理などを構造化できるEmacs Lisp用テストフレームワーク(el-spec)をリリース - むしゃくしゃしてやったを参考にしてください。
el-specのインストールは

(auto-install-from-url "https://raw.github.com/uk-ar/el-spec/52dc72aa71ddfa1dba87ba93703f1328b1496ed3/el-spec.el")

を評価するか、
marmaladeから
M-x package-install el-spec
でOKです。

感想

今回はv0.1をリリースした後すぐに対応しようと思っていた機能です。当初考えていたよりも時間がかかりました。
一つの理由はfind-functionの実装が正規表現で関数定義を探すようになっていたので、そのままでは対応が難しかったこと。もう一つはitのマクロ呼び出し時に(C言語の__LINE__的なもので)呼び出し行数を記録していけばタグファイル的なものを作れると思っていたけど、似た機能がなさそうだったことです。
結局、力技ですがdescribe以下のS式を自前でパースすることで対応しました。
その際readを使うことでメインのロジックと一部の処理を共通化することができました*1

扱いが難しいのがshared-examplesですね。サクッと対応したいところですが、泥臭い実装しか浮かびません。アイデアが欲しいところです。本家のrspecを見てみようか...

rspecのように前処理などを構造化できるEmacs Lisp用テストフレームワーク(el-spec)をリリース

[http://d.hatena.ne.jp/uk-ar/20120720/p1:title]にて機能追加をおこないました。そちらも参照ください。

Emacs Lisprspec*1のように前処理などをネストして定義できるテストフレームワーク(el-spec)が欲しくて作ってみました。
https://github.com/uk-ar/el-spec
実用になりそうなレベルになったのでリリースします。

きっかけ、が欲しいです。

Emacs Lispにもいくつかテストフレームワークがあり、今まではid:rubikitch さんのel-expectation*2を使ってました。使い勝手が良くてテスト数の少ないうちは満足していたのですが、テストが多くなると複数のテストで同じような前処理をしているのが目立ってきました。
このような場合マクロや関数で対応するのが常套手段です。しかし、前処理の内容も全く同じわけではなく少しずつ違っているので、マクロや関数が増えて管理が辛くなります。
そこでEmacs 24から添付されたert*3を利用して、テストフレームワーク(fixture?)を作ってみました。

特徴

テストを書くのに今回リリースするel-specを使用すると

  • 前処理や後処理を簡単に追加できる
  • テストが整理できる(同じようなテストが近い場所に固まる)
  • 個々のテストに対して、名前をつけなくてよい
  • 似たようなテストを増やしやすい(shared-contextやshared-examples)

というメリットがあります
逆にデメリットもありますが、最後に書きます。

準備

el-specはEmacs 24から標準添付されたertに依存しています。Emacs 23以下の場合は

(auto-install-from-url "https://raw.github.com/ohler/ert/c619b56c5bc6a866e33787489545b87d79973205/lisp/emacs-lisp/ert.el")

としておきましょう。

インストール方法

el-spec自体のインストールは

(auto-install-from-url "https://raw.github.com/uk-ar/el-spec/144c053a5303101378d6e3b981d10b715a6c8775/el-spec.el")

を評価するか、
marmaladeから
M-x package-install el-spec
でOKです。
インストール後に

(require 'el-spec)

とすると使えます。

お品書き

使い方をチュートリアル風に解説します。内容はこんな感じです。

  • 基本的な使い方
    • 前処理/後処理の設定
    • 前処理/後処理の階層化(コンテキストの使用)
  • 実用例
    • 変数の使用
    • コンテキストの共有
    • (異なる前提条件での)examplesの共有
    • テスト実行範囲の変更

基本的な構造(前処理/後処理の設定)

基本的にdescriptionブロックの中にテストケース(example)を書いていきます。descriptionの中でitブロックに囲われた部分が独立したexampleになります。
具体的には

(describe "description"
  (it "test 1"
    (should ...)
    )
  (it "test 2"
    (should ...)
    )
  ...
  )

のような形になります。
前処理/後処理を設定するにはbefore/afterマクロを使用します。

(require 'el-spec)

(describe "description"
  (before
    (message "before common")
    )
  (after
    (message "after common\n")
    )
  (it "test 1"
    (message "test 1")
    )
  (it "test 2"
    (message "test 2")
    )
  )

を実行すれば、

before common
test 1
after common

before common
test 2
after common

のように実行されます。
実行するには M-x el-spec:eval-buffer を使用するか、describeのS式を M-C-x とかでevalすればオッケーです(この方法はel-expectationを参考にしました)。実行するとertのテスト結果が出ますが、ここでは気にせずmessageバッファをみてみましょう。

前処理/後処理の階層化(コンテキストの使用)

さらに前処理/後処理はcontextマクロにより階層化することができて

(describe "description"
  (before
    (message "before common"))
  (after
    (message "after common\n"))
  (context "when 1"
    (before
      (message "before 1"))
    (after
      (message "after 1"))
    (it "test 1"
      (message "test 1")))
  (context "when 2"
    (before
      (message "before 2"))
    (after
      (message "after 2"))
    (it "test 2"
      (message "test 2")))
  )

のように書けば、

before common
before 1
test 1
after 1
after common

before common
before 2
test 2
after 2
after common

のように実行されます。
describe,context,itに与える(第一引数の)説明文字列は結合してテスト名となります(例えばtest 1は"description\nwhen 1\ntest 1"のようなテスト名になります)。ですので、説明文字列はコンテキスト内でユニークであることと、改行"\n"を使わないことを気をつけて下さい(重複した場合は、定義時に警告が出ます)。
before/afterの他にaroundマクロも使用することができるので、上のテストは

(describe "description"
  (around
    (message "before common")
    (funcall el-spec:example)
    (message "after common\n")
    )
  (context "when 1"
    (around
      (message "before 1")
      (funcall el-spec:example)
      (message "after 1")
      )
    (it "test 1"
      (message "test 1")))
  (context "when 2"
    (around
      (message "before 2")
      (funcall el-spec:example)
      (message "after 2")
      )
    (it "test 2"
      (message "test 2")))
  )

と書き直すことができます。
aroundを使えば、it節を囲うような使い方(with-temp-bufferやletなど)が可能です。

実用例

上で挙げた機能も含めて、実際の例で説明します。
題材として括弧の自動挿入を取り上げます。
ここでは

  • 開き括弧を入力すると括弧のペアを挿入
  • 閉じ括弧を入力すると閉じ括弧のみ挿入
  • 開き括弧かどうかは文脈やモードに依存

という仕様を確かめるため以下の3つのテストを書きます。

(with-temp-buffer
  (switch-to-buffer (current-buffer))
  (c-mode)
  (execute-kbd-macro "'")
  (should (string= (buffer-string) "''"))
  )

(with-temp-buffer
  (switch-to-buffer (current-buffer))
  (c-mode)
  (insert "'")
  (execute-kbd-macro "'")
  (should (string= (buffer-string) "''"))
  )

(with-temp-buffer
  (switch-to-buffer (current-buffer))
  (emacs-lisp-mode)
  (execute-kbd-macro "'")
  (should (string= (buffer-string) "'"))
  )

明らかにwith-temp-bufferとswitch-to-bufferの部分が冗長なのでaroundで共通化します。

(describe "auto-pair"
  (around
    (with-temp-buffer
      (switch-to-buffer (current-buffer))
      (funcall el-spec:example)))

  (it ()
    (c-mode)
    (execute-kbd-macro "'")
    (should (string= (buffer-string) "''")))
  (it ()
    (c-mode)
    (insert "'")
    (execute-kbd-macro "'")
    (should (string= (buffer-string) "''")))
  (it ()
    (emacs-lisp-mode)
    (execute-kbd-macro "'")
    (should (string= (buffer-string) "'")))
  )

itへの説明文字列はnilを渡すことで省略可能で、省略時にはブロック内容が説明文字列になります。
なおこれ以降は実行時にテストが失敗することがあると思います。その場合、ertバッファ上の失敗したテスト名の場所で「b」を入力すると実行時のバックトレースを表示することができます。また、本来ならテスト名の上で「RET」とすることでテストを定義した場所へ飛ぶことができるはずですが、el-specでは現状使えません。

変数の使用

次はわかりやすいようモードごとにテストをグループ化しましましょう。

(describe "auto-pair"
  (around
    (with-temp-buffer
      (switch-to-buffer (current-buffer))
      (funcall el-spec:example)))

  (context "in c-mode"
    (before
     (c-mode))

    (it ()
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "''")))
    (it ()
      (insert "'")
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "''"))))

  (context "in emacs-lisp-mode"
    (before
      (emacs-lisp-mode))

    (it ()
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "'"))))
  )

モード切り替え部分(before)が冗長ですね。モード名を変数にできたらスッキリします。

(describe ("auto-pair" :vars (mode))
  (around
    (with-temp-buffer
      (switch-to-buffer (current-buffer))
      (funcall mode)
      (funcall el-spec:example)))

  (context ("in c-mode" :vars ((mode 'c-mode)))
    (it ()
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "''")))
    (it ()
      (insert "'")
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "''"))))

  (context "in emacs-lisp-mode"
    (let ((mode 'emacs-lisp-mode))
      (it ()
        (execute-kbd-macro "'")
        (should (string= (buffer-string) "'")))))
  )

定義時の変数はそのままでは実行時に参照できません。なので、自前でlexical-letを使うか、describe,context,itに:varsというキーワード引数で変数宣言をしてあげるとうまいことしてくれます(:varsはletと同じ形式で変数を受け付けます)。
一度:varsで変数宣言をすれば、変数への代入はletやsetq、:varsで可能です。

コンテキストの共有(shared_context)

テスト(example)が増えてくると、beforeやafterをまとめたcontextを複数の場所で使いたいことがあります。
例えば、上記の2つ目のexampleのように文字を挿入するexampleを追加すると次のようになります

(describe ("auto-pair" :vars (mode))
  (around
    ...)

  (context ("in c-mode" :vars ((mode 'c-mode)))
    (it ()
      ...)

    (context ("buffer-string before" :vars (string-of-buffer))
      (before
        (insert string-of-buffer))
      (it (:vars ((string-of-buffer "'")))
        (execute-kbd-macro "'")
        (should (string= (buffer-string) "''")))))

  (context "in emacs-lisp-mode"
    (let ((mode 'emacs-lisp-mode))
      (it ()
        ...)

      (context ("buffer-string before" :vars (string-of-buffer))
        (before
          (insert string-of-buffer))
        (it (:vars ((string-of-buffer "\"")))
          (execute-kbd-macro "\"")
          (should (string= (buffer-string) "\"\"")))
        )))
  )

この例では(before (insert string-of-buffer))の部分を共有したいところですが、c-modeとemacs-lisp-modeでコンテキストを分けているので難しいです。
このような場合はshared-contextとinclude-contextを使います。

(describe ("auto-pair" :vars (mode))
  (around
    (with-temp-buffer
      (switch-to-buffer (current-buffer))
      (funcall mode)
      (funcall el-spec:example)))
  (shared-context ("insert string" :vars (string-of-buffer))
    (before
      (insert string-of-buffer)))

  (context ("in c-mode" :vars ((mode 'c-mode)))
    (it ()
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "''")))

    (context "buffer-string before"
      (include-context "insert string")
      (it (:vars ((string-of-buffer "'")))
        (execute-kbd-macro "'")
        (should (string= (buffer-string) "''")))))

  (context "in emacs-lisp-mode"
    (let ((mode 'emacs-lisp-mode))
      (it ()
        (execute-kbd-macro "'")
        (should (string= (buffer-string) "'")))

      (context "buffer-string before"
        (include-context "insert string")
        (it (:vars ((string-of-buffer "\"")))
          (execute-kbd-macro "\"")
          (should (string= (buffer-string) "\"\"")))
        )))
  )

shared-contextはcontextマクロと同じような使い方で定義します。定義したshared-contextをinclude-contextすれば、その場所で使えるようになります。

exampleの共有(shared_example)

テスト(example)が増えてくると、あるテストを違うコンテキスト(違う前提条件)で実行したいことがあります。
例えば、上記4つ目の「"」を入力するexampleはc-modeでもemacs-lisp-modeでも動作します。
このような場合はshared-examplesとinclude-examplesを使います。

(describe ("auto-pair" :vars (mode))
  (around
    (with-temp-buffer
      (switch-to-buffer (current-buffer))
      (funcall mode)
      (funcall el-spec:example)))
  (shared-context ("insert string" :vars (string-of-buffer))
    (before
      (insert string-of-buffer)))
  (shared-examples "examples for \""
    (it ()
      (execute-kbd-macro "\"")
      (should (string= (buffer-string) "\"\""))))

  (context ("in c-mode" :vars ((mode 'c-mode)))
    (it ()
      (execute-kbd-macro "'")
      (should (string= (buffer-string) "''")))
    (include-examples "examples for \"")
    (context "buffer-string before"
      (include-context "insert string")
      (it (:vars ((string-of-buffer "'")))
        (execute-kbd-macro "'")
        (should (string= (buffer-string) "''")))))

  (context "in emacs-lisp-mode"
    (let ((mode 'emacs-lisp-mode))
      (it ()
        (execute-kbd-macro "'")
        (should (string= (buffer-string) "'")))
      (include-examples "examples for \"")
      ))
  )

簡単にするため、上記の例とは少しテスト内容を変更しています。
最後にテストコードと実装コード一つにまとめるケースも考えて、dont-compileで囲むと

(dont-compile
  (when (fboundp 'describe)
    (describe ("auto-pair" :vars (mode))
      (around
        (with-temp-buffer
          (switch-to-buffer (current-buffer))
          (funcall mode)
          (funcall el-spec:example)))
      (shared-examples "examples for quote"
        (it ()
          (execute-kbd-macro quote)
          (should (string= (buffer-string) (concat quote quote))))
        (context ("buffer-string before" :vars (string-of-buffer))
          (before
            (insert string-of-buffer))
          (it (:vars ((string-of-buffer quote)))
            (execute-kbd-macro quote)
            (should (string= (buffer-string) (concat quote quote))))))

      (context ("in c-mode" :vars ((mode 'c-mode)))
        (context ("examples for \"" :vars ((quote "\"")))
          (include-examples ("examples for quote" )))
        (context ("examples for '" :vars ((quote "'")))
          (include-examples ("examples for quote"))))
      (context ("in emacs-lisp-mode" :vars ((mode 'emacs-lisp-mode)))
        (it ()
          (execute-kbd-macro "'")
          (should (string= (buffer-string) "'")))
        (context ("examples for \"" :vars ((quote "\"")))
          (include-examples ("examples for quote" :vars ((quote "\""))))))
      )))

といった感じで完成です。

テスト実行範囲の変更

テストが増えてくると、すべてのテストを実行した場合に時間がかかるようになります。普段は一部分のテストだけ実行して、区切りがついたら全部をテストできると便利ですね。
M-x el-spec:eval-and-execute-contextでカーソル付近のコンテキストだけを実行することができます。逆にM-x el-spec:eval-and-execute-allでdescribe以下のコンテキストを実行できます。M-C-x でevalした時の挙動も切り替えが可能で、M-x el-spec:toggle-selectionするたびにallとcontextが切り替わります。

課題と対応

現在判明してるデメリットとして、

  1. テストの失敗時にexampleに飛べない
  2. 個々のテスト実行ができない
  3. 変数定義が分かりづらい(:varsを忘れる)
  4. (無名関数を多用してるので)バックトレースが追いづらい

という4つは認識しています。
で、今後の対応予定/回避策は以下の通りです。
1. に関してはかなりだるい&対応策があるような気がするので次回対応します。
2. に関しても浅い階層のexampleを実行する際には欲しくなりますが、実装をがんばらないといけない感じです。とりあえずはcontextを作ることで回避可能なので、気力があれば対応します。
3. に関しては自分で使っていても:varsをつけ忘れてエラーがよく出ます。describe以下のletをlexical-letに置き換える技もありますが副作用が心配です。もう少し使い込んでから対応を決めます。
4. 設計的に対応が厳しいです。私は慣れてきました。アイデアがあれば下さい。

あとがき

最低限の動くものはすぐにできたのですが、使っているうちにいろいろと機能が欲しくなって時間がかかってしまいました。
マクロを使いまくったおかげでマクロに詳しくなった気がします。
実装のコンセプトは単純なので、クロージャが使える言語なら移植は可能です。最低限の実装は
https://github.com/uk-ar/el-spec/tree/a7b548271ae0cb996e5c201712f1e658c075c65c
にあるので興味があればのぞいてみて下さい。
もちろん要望やフィードバック、pull requestもウェルカムです!

flex-autopair v0.3:Emacs 24対応。指定したモードで無効化。モードごとの設定を強化など。

flex-autopair v0.3 をリリースします。変更点は以下です

  1. Emacs 24に対応しました
  2. モードを指定して無効にできるようにしました
  3. モードごとの設定を強化しました

変更詳細

1つめはEmacs 24への対応です。関西Emacsで[twitter:@masutaka]さんから報告を受けて気づきました。Emacs 24の新しいAPIを使おうとして、使い方が間違っていたため動かなくなっていたのを修正しました。

2つ目は無効にするモードの指定についてです。[twitter:@Fenril058]さんからの
提案を受けて、特定のモードで無効化することができるようにしました。
例えばlisp系のモードで無効にするには

(setq flex-autopair-disable-modes
        '(emacs-lisp-mode lisp-interaction-mode lisp-mode ilem-mode))

とします。これに関連して、v0.2までは
M-x flex-autopair-mode ですべてのバッファに対してトグルできましたが、
v0.3からは
M-x flex-autopair-mode で現在のバッファに対してトグル
M-x global-flex-autopair-mode で新規のバッファに対してトグル
となります。

3つめはモードごとの設定の強化です。v0.2までは条件設定(flex-autopair-conditions)をグローバルで持っていましたが、v0.3ではバッファごとに切り替えができるようになりました。これにより、

  • 設定が増えてもそれほど重くならない
  • 他のモードを気にせず書き換えられる

などのメリットがあります。
これに関連して各種モード用の設定を追加しました。

  • haskell-mode(')とcoffee-mode(`)の設定を追加しました。haskell-modeの方は(')がシンボル名としても使えるように、空白の後ろだけペアにするようになってます。[twitter:@regluu503]さんからのpull requestでした。
  • c系のモードで中括弧「{」を入れると自動で改行するようにしました。実際の動きは
{
    !! //←!!の場所にカーソルがインデントした状態で配置
}

みたいな感じです。こちらは第5回関西Emacs勉強会での id:lurdan さんからの要望を反映しました。

インストール方法

ソースがgithubにおいてあるので

(auto-install-from-url "https://raw.github.com/uk-ar/flex-autopair/0184a539b874e20938418e4dbeaec0665129bf46/flex-autopair.el")

を評価するか、
marmaladeから
M-x package-install flex-autopair
でインストール可能です。

その他

個人的に一番大きな変更点はモードごとの設定切り替えです。これで、設定が書きやすくなって実用的な使い方ができるかと思います。使ってみて設定の公開もしくはpull requestをしてみて下さい。

key-combo v1.5.1:文字列境界でのバグ修正。デフォルト設定の強化(特にrubyやc)。

key-combo v1.5.1をリリースします。変更点は以下です

  1. 文字列の境界の判定に関するバグを修正しました
  2. デフォルト設定を強化しました(特にrubyやc)
  3. 誤ってflex-autopairの関数を呼び出していたのを修正

変更詳細

1つめは文字列の境界の判定に関するバグ修正です。文字列やコメントのなかでkey-comboが動くとイラっとするので、判定して動かないようにしてるつもりでした。ですが、判定ロジックに穴があったので行末やら境界で誤判定してました。[twitter:@tomy_kaira]さんからのhttps://github.com/uk-ar/key-combo/issues/20:バグ報告があり、修正も@tomy_kairaさんがしてくれました。やっほい!
2つめはデフォルト設定に関してです。またまた@tomy_kairaさんのpull requestによる"!=="演算子を入れて、ruby演算子関連を
http://bojovs.github.com/2012/04/24/ruby-coding-style/
を参考に追加、後はc言語も。
3つめは[twitter:@ballforest]さんからのバグ報告に感する修正です。間違ってflex-autopairの関数を呼び出していたため、flex-autopairを入れてないと動かなくなる(これはひどい…)のを修正しました。

インストール方法

ソースがgithubにおいてあるので

(auto-install-from-url "https://raw.github.com/uk-ar/key-combo/36cc9bc85868cea9c32b18031850b1f5d9633e31/key-combo.el")

を評価するか、
marmaladeから
M-x package-install key-combo
でインストール可能です。

その他

大きな機能追加はなく、デフォルト設定の強化とバグ修正がメインでした。

Emacs Lispを書くときのyasnippetやauto-completeの設定など(もしくは関西Emacs勉強会の続き)

またまた時間が経ってしまいましたが、第5回関西Emacs勉強会の記事の続きです。
プレゼンでEmacs Lispを書くときにこんな風にyasnippet.elとauto-complete.elを使ってますとの紹介をしましたが、詳しい設定はブログでと言ったままでした。
ようやく公開です。

Emacs勉強会を見ていない人のために要点をまとめると

問題

yasnippet便利!!でも…
1. 何を登録したのか忘れる

  • 似たもの (defun defvar)
  • 環境 (会社 or 自宅)

2. どの省略形で発動するかを忘れる

  • 短いほうがうれしいけど
  • 展開後と大きく違うと忘れる
  • 括弧は不要?必要?
    • (不要なのにつけると)括弧が二重に展開
    • (必要なのにつけないと)括弧がない
解決策

auto-completeで対応

  • 省略形は元のシンボル名(忘れない)
  • 途中まで打てば候補が出る(思い出せる)
  • 括弧がないときだけ括弧を挿入(悩まない)

という方針です。

yasnippetとauto-completeの設定

.emacsあたりに以下の設定を追加します。

;; add syntaxes for lisp function
(add-to-list 'yas/key-syntaxes "(w_.)" t)

;; save position before yasnippet execution
(defun yas/my-save-marker ()
  (setq yas/my-pre-marker (point-marker))
  (setq yas/my-post-marker (set-marker (make-marker) (1+ (point)))))

(add-hook 'yas/before-expand-snippet-hook 'yas/my-save-marker)

;; swap ac-source-yasnippet and ac-source-functions
(defun ac-emacs-lisp-mode-setup ()
  (setq ac-sources (append '(ac-source-features ac-source-yasnippet ac-source-functions ac-source-variables ac-source-symbols) ac-sources)))

1つめの設定(yas/key-syntaxes)は(付きでも展開できるようにするための設定です。
2〜3つめの設定(yas/before-expand-snippet-hook) は挿入前後の位置に()があるかどうかを判断するためにマーカを仕込んでます。
4つめの設定(ac-emacs-lisp-mode-setup)はauto-completeでシンボル名とyasnippetの短縮形が重複するとシンボル名の補完が優先されてしまうので、優先順位を逆転させてます。ただ、これだと逆にシンボル名での補完ができなくなる問題があったりします。
(Eclipseみたいに)略語展開系とシンボルの補完は両方出てくれた方が嬉しい気がするので、[twitter:@m2ym]さんに要望をだしてみようかなと。

yasnippetスニペット

yasnippetスニペットはこんな感じです。最初の行と最後の行で空気を読んで括弧を入れたり入れなかったりします。

# -*- mode: snippet -*-
#name : function template
#contributor : Yuuki Arisawa
# --
`(if (eq ?\( (char-after (1- yas/my-pre-marker)))
     (delete-char 2) (delete-char 1))`
(defun $1 (${2:args})
  "Return
${2:$(mapconcat 'upcase (split-string (replace-regexp-in-string "&[a-z]+" "" yas/text)) " is\n")}"
  (interactive)
  $0
  )
`(if (eq ?\) (char-after (1- yas/my-post-marker)))
     (progn (forward-line -1) (delete-char -2)))`

id:kiwanamiさんの記事
yasnippet や emacs lisp の小ネタなど - 技術日記@kiwanami
を参考に引数の文字列からdocstringを生成するようにしてます。
後から引数の変更をした時にも追従したいけど、うまいやり方はあるのかな?知っている人はどなたか教えて下さい(なければeldocあたりを使って作ろうかな…)

第5回関西Emacs勉強会

かなり時間が経っておりますが、第5回関西Emacs勉強会に参加してきました。
勉強会に参加するのも初めてでしたが、調子に乗って発表までしてます。
詳しくはid:peccuさんのまとめ
= #kansai_emacs 5 終了 - ぺっくブログミラー@peccul
にリンクがまとまって載っているのでそちらを参考にするとよいかなと。
それではだらだらと思ったことなどを書いてゆきます。

ポジションペーパー

まずはポジションペーパーによる自己紹介。
私は人の名前を覚えるのが非常に苦手なので、後から見返すことができて大変助かりました。
普段ブログやtwitterでしか見たことのない人と実際に会うことができて、かなり感動でした。
気になったのはquickrun.elとopen-junk-file.elの連携(peccuさん)。
最近、仕事でquickrun.elを使い始めたので設定を追加してみようかなと。
一応私の自己紹介はこんな感じ。

Uk ar
View more presentations from uk-ar

発表

id:kiwanami さん

Emacsをアプリケーションのプラットフォームとしてとらえるとというお話。
私はEmacsをエディタやIDEとしてしか考えていなかったので大変興味深い内容でした。
特にバッファローカル変数がmodel、バッファがview、keymapやコマンドがcontrollerに対応するというのは意識してなかったけど言われてみればそうだなぁという感じ。
意識すれば自作のEmacs Lispの責務の切り分けがよりはっきりしそうだなと思ったり。
tableやtreeなどのUI部品がないとアプリケーションを書くのが辛いとの話も共感しました。
あとは、メインの話ではないのですがemacs-develでのorg-modeの変数名がひどくて話題になってるとの話も(最近emacs-develを購読し始めたので)、そんな話だったのかと楽しくかったです。

[twitter:@takaxp] さん

org-modeでのプレゼン!
2011 advent calendarでは気になっていましたが、実物を見ると結構自然にアニメーションしていてびっくりでした(見ていない人はustreamhttp://www.ustream.tv/recorded/22182983をぜひ)
人のEmacs操作を見るのは好きだし、デモ画面とプレゼンの切り替えもスムーズなのでイイですね〜
org-modeの更新が早くて、ドキュメントの翻訳が大変とのこと。でも、odt(open office?)へのエクスポートやmobile org(iphone)などの新機能は気になりますね。
具体的な翻訳の進め方はもう少し詳しく知りたいところです。手が空いたら少しwatchしてみようかな。

[twitter:@shohex] さん

普段quickrunと「気になった Emacs記事」 にはお世話になってます。
今回はquickrunの紹介ということで、基本的な使い方から開発の振り返りまで。outputterはうまく使えば便利そうですね。

自分

恥ずかしいですが、実際の動画は


Video streaming by Ustream
スライドは

にあります。
内容はflex-autopairの紹介とEmacs lispでのyasnippet.el & auto-complete.elの設定です。
色々意見をもらって宿題ももらいました。解決したものもあるので、覚えてる範囲であげてみます。

  • プレゼンでのyasnippet(とauto-complete)の設定はよ

じ、次回のエントリでお願いします…

  • 括弧を閉じた時対応する括弧が光る機能があるけど、一緒に動くの? by takaxp さん

聞かれてからちゃんと調べてみたのですが、カーソルが移動してから光るblink-matching-parenとただ光るだけのshow-paren-modeがあるようです。昔は端末ではblink-matching-parenしか使えなかったようですがEmacs23では端末でもshow-paren-modeが使えるのを確認しました。端末上でflex-autopairの動作確認もしましたがどちらとも動いてるようです。一瞬操作が固まるのがいやならshow-paren-modeが良いかなと思います。
開き括弧を入れた時でも(自動挿入した)閉じ括弧が光ると便利な気がするので、試してみたいなぁ。

  • emacs24で動かないよ by [twitter:@mastaka] さん

動作確認してないので、気づいてませんでした…環境作って修正したのでもう一度試してもらえるとうれしいです。

  • (C言語とかで?)中括弧「{」入れるときは改行入れて戻ってするから結局面倒では? by id:lurdan さん

c系のモードで中括弧「{」を入れると自動で改行するようにしました。

{
    !! //←!!の場所にカーソルがインデントした状態で配置
}

試してみて感想いただけるとうれしいです。

  • 端末でコピペしたとき余計な括弧がついたりするよね by id:tarao さん?

おっしゃる通りです。現状は文字の入力間隔で判定するのが妥当な線のようですね。ただ、キーボードマクロの実行時など考えることがありそうなので対応はちょっと先にしようと思います。

括弧で囲うぐらいならflex-autopairでもできたりします(宣伝)。それ以外の場合はyas/wrap-around-regionあたりを使うのが妥当でしょうか?
http://yasnippet-doc-jp.googlecode.com/svn/trunk/doc-jp/snippet-development.html#yas-wrap-around-region

id:kozo2 さん

elispのインストール手法について。
ポイントはツールとしての違い(package.elとel-get.el)とリポジトリで何を管理するか(el-get.elとMELPAはレシピと呼ばれるインストール手順、marmaladeはelisp自体)
ツールとしてpackage.elと比べたel-get.elの利点は手元でオレオレレシピを登録出来るところかな?メリットは理解できるけど、現時点で自分にはそこまで必要ないかもと思っています。
githubリポジトリから直接持ってくるのが主流になった場合、elispの開発者側からすると(masterには不安定な変更が入ったりするので)tagを認識してくれるとありがたいかもと感じました。
その他、emacs-starter-kitの話が出てたので、新しい言語を使うとき用に開発言語毎のメタパッケージみたいのがMELPAやel-getにあるといいなぁとふと思った。

その他

懇親会までは [twitter:@arte_nut] さんとNTEmacsやらターミナルやEmacs24の話など。Emacs24もそろそろ環境を作ろうと心に刻む。

懇親会でもいろいろな人とEmacsについてやきれいなコードの書き方やらdebianの話など話せて楽しかったです。意外だったのが結構key-combo使ってるよと言ってもらえたこと。ちゃんとメンテしていこうと思います。

初参加でしたが本当にアットホームな雰囲気が心地良く、帰るのが名残惜しかったです。
主催のpeccuさん、会場の手配などいただいた [twitter:@uwabami] さん始め、発表者、参加者の皆様、本当にありがとうございました。