kansai-emacs #x04 行ってきた

今回で連続3回参加しました。はてなさんのオフィスがすごくかっこ良かったです。

当日のまとめとかは以下が詳しいです。

今回も自分が考えるEmacs周辺のお話をしてきました。

  • 話題
    • kiwanami の Emacs 環境紹介
    • Fukuoka Emacs の報告
    • 非同期いろいろ
    • EPC and EDBC

epcがあんまり間に合ってなくて、コンセプトしかお見せできませんでしたが、Emacs内で最高のSQL実行環境を作ってやろうと、日々頑張っています。

ワールドカフェが Fukuoka Emacs に続いて、こちらでも実践されました。普通の「勉強会」はみんな前を向いて一方的に聞くスタイルなので、あんまり横のつながりができにくいのですが、少人数で入れ替わりながら共通の話題について議論することで参加者同士のつながりがうまくできたのではないかなと思いました。早速、Emacs合宿や、twitterを利用したインストール環境、設定済みEmacsやWebサイトの計画が実行に移されているようです。関西Emacsはフットワーク軽いですね。

合宿に日程合えば行ってみたいなあと思っています。熊本とか湯布院(大分)でやると温泉のレベルが上がっていいですよ!

fukuoka-emacs 0x03 やりました

9月23日に福岡 Ruby・コンテンツセンターにて、 Emacs の勉強会 fukuoka-emacs 0x03 を鬼軍曹の id:k1LoW さんと一緒にやりました。

前回の fukuoka-emacs 0x02 (テキストエディタ勉強会)から2年もたってしまいましたが、今回は Emacs だけで25人(最多時)もの人が集まりました。特に県外の方が 1/3 くらいを占めており、兵庫・岡山・山口・熊本など、はるばる Emacs のために集まっていただいたことに大変感動しました。以下、簡単ながらレポートです。

レポート

はじめに
  • kiwanami より、ごあいさつ
  • 勉強会の趣旨などの説明
    • 初心者から上級者まで、情報共有や教えあえるようなコミュニティの育成を目指す
  • 質問
    • Emacs の使用歴?
      • 1年未満、1年から5年、それ以上、でそれぞれ 1/3 ぐらい
    • 現状の設定に満足しているか、設定は長期間変わっていないか?
      • ほとんどの人が現状に満足していない
    • Anything 使っているか?
      • 半分くらいの人が使っている
    • 仕事での開発で Emacs を使っているか?
      • 使っている人は 1/3 程度、あんまりいない
    • 周りに Emacs 使いたい人がいるか、広められるか?
      • みんな使いたい人が見あたらない → 孤立している
    • Elisp でアプリ書きたい?
      • 1/3 の人が書きたいみたい
自己紹介
  • ポジションペーパーを使って簡単に自己紹介
    • GoogleDocs のプレゼンテーションを使って各自記入してもらった
      • id:k1LoW さんからPHP祭りがこうしているということを教えてもらった
  • 年齢が幅広い。16歳から49歳まで。
  • Emacs初心者から20年以上ユーザーまで
  • スシが食べたい!!!
ラングリッチ社での場合
sql-mode の紹介など
org-mode紹介、Mew・MHC昔話など
MewEmacs昔話、小ネタなど
  • @moepanda さん
    • 1994年からEmacs
    • Mew 10周年誕生会があった
  • いろいろやってきた
  • calfw 長いと中身が見えません
    • → すいません。今複数行表示対応中です。。。
EmacsでのWeb開発について
小指とキーバインド
  • @ToshiChan さん
    • 遊戯王が強い
    • 大学では学生とデュエルしている
  • 物理的には 106 key (JIS) 配列がキーが多いので有利
  • 論理(キーコード)的には US 配列がプログラマ的にしっくりくる
  • 小指が働きすぎ
    • 一番か弱いのに 10 個以上を担当している
    • 親指はスペースだけ、しかも左右の指で一つ!!!
    • 「Z_AXIS」 とか辛い
  • そこで親指で Ctrl, Shift を押すようにすればよい
    • Ctrl は「C」の下にある。「A」の横には「S」しかない!!!
  • キーマップ方法いろいろ
9lispとelisp
  • @valvallowさん
  • 現在 Scheme (Gauche) で仕事をしている
    • マクロで DSL を作っていろいろしている
  • 9LISP という lisp 勉強会をずっとやっている
  • ずっと lisp やっていたら、上司が Scheme で仕事をしだした!!!
  • みなさん熊本で lisp 勉強しませんか!!!
ワールドカフェ

ちょっと時間が足りなかったのですが、みんなでいろいろ話し合えるようにワールドカフェをやりました。

  • お題 : Emacsのスタートアップを楽にするには
    • 初心者が入門するにはどうしたらいいか
    • 初心者が最初につまづく初期化ファイル
    • Emacsがどうなればいいか
    • コミュニティとしてできることは?

以下、簡単ながら各テーブルで出たトピックです。覚えているだけ書いてますので、もっといろいろあったと思います。

  • 昔は Emacs マスターが研究室の Emacs 環境を整えてた
    • 今は自分でやらないとダメなので、最初のハードルが高い
    • 設定済みの Carbon Emacs はとても良かった
    • 設定済み Meadow
      • →メンテナンスが大変
    • Emacs は設定しないと使えない、設定ばかりさせられる
    • →みんな必要なパッケージ(anything, auto-install とか)を配布しておく
  • Gentoo !!!
  • OS や他のアプリと操作が違いすぎる
    • メタって何だ!
  • Emacsチュートリアルが良くない
    • コピペ、終了をまず覚えたい
  • Emacs のラングリッチ的なレッスン環境が欲しい
  • ネットでの情報が断片的
  • 本は初心者向けすぎる?
  • 初心者が成長する過程のブログがあるといい
    • みんなが躓くところは大体同じ
  • →欲しい情報がまとまっていない
  • コミュニティがない?
  • Emacs の良いところ、他との比較が分かりにくい
  • エディタとして他のエディタより良いところは何か?
  • Emacs にしか出来ないことは何か?
  • Emacs のイメージはオタク的、BSD
    • →何だかすごいんだけど、近寄りがたい感じ







各テーブルの写真

  • 懇親会
    • ワールドカフェの延長で短時間ながら盛況
    • 3次会まであったみたい



  • 次回
    • 来年春くらいにやりたい

感想

今回の勉強会の目標であった「初心者から上級者まで、情報共有や教えあえるようなコミュニティの育成を目指す」ということはある程度達成されたかなと思いました。

昔の黙っていてもみんな Emacs を使っていた時代は終わり、近年の Emacs ユーザーは孤立するようになってきたように思います。使いたい人が使い続ければいいのですが、フリーソフトがメンテナンスされ、維持されるためにはユーザーのコミュニティが重要です。

スピードやシンプルさや容易さよりも、それ以上にもっと大事なことがあります。それがコミュニティです。

なぜ私たちはRubyを愛するのか、それは私たちが自由やパワーを愛するハッカーであり、Rubyがそれらを与えてくれるからです。しかもそれだけでなく、Rubyには素晴らしいコミュニティがあること、これは非常に幸運なことです。

多くのオープンソースプロジェクトにとって生き残ることは容易ではありません。生き残るためには前に進まなければなりません、そしてそれこそ進化の源泉であり、私は変化を好んでいます。

今回、特に一通りの発表が終わってからのワールドカフェはとても良かったと思います。開始してすぐに各テーブルで活発な議論が始まり、終わったときには、ネタやセミナー形式では得られないような、参加者間の一体感が得られ、確かなコミュニティの風を感じることが出来ました。



ということで、今後も Emacs の布教活動を頑張っていこうと思います。

fukuoka-emacs #0x03 開催します

大変遅くなりましたが、福岡にてEmacsの勉強会(情報交換会)をします。
場所を変更しましてRubyセンターで行います。
場所の確保には株式会社Fusicさんにご協力頂いております。いつもありがとうございます。(2011/09/15 修正)

以下のような内容が出来るといいなと思っています。参加される方でこの辺が発表できそうというのがありましたら、是非教えてください。(皆さんの発表が少ないと kiwanami 独演会になるかもしれません!!)

  • 自己紹介タイム
  • emacs intro and startup
    • Emacs自体の紹介
    • 各環境(Windows, Mac, Linux)でのEmacsの始め方
    • 最小限のセットアップ, auto-install.el など
      • ここはみんなで出来るといいですね
  • orgmode/howm紹介
    • メモ、スケジュール、情報管理ツールの紹介、始め方
      • howmはkiwanamiが出来ますが、orgmodeはどなたかお願いしたいです
  • Emacsアプリ紹介LT
    • ぜひ皆さんどうぞ!
  • Web開発環境構築(Ruby PHP JS Perlなど)
    • 開発環境の紹介と基本的セットアップなど
    • 実際のコーディング実演など
      • k1LoWさん、tomoyatonさん、masutakaさん、dragon3さんとかお願いできると良いですね
  • elisp入門・実用elisp
    • Emacsのプログラミング環境の紹介、学習方法など
    • elisp ハンズオンなど
      • 参加者の層次第か、introとセッションを分けるか
  • Vimもあるよ
    • なぜかVim入門。書籍紹介。モダンVim環境セットアップなど。
      • だれか。。。
  • Windows対策
    • WindowsでのEmacsを使うノウハウなど
    • shell 環境も合わせるか、eshell使うか
  • 小指負傷対策
    • 皆さんの経験など
    • キーボード、リマップ、小指の鍛え方など
      • しもかわさんにお願いできそうです。
  • 次回 #0×04 の企画など
  • などなど

皆さんのご参加をお待ちしております!!!

Chrome用 Backlog Wiki and Autofilter 拡張 / あるいは Firefox GM から Chrome 拡張への書き換えメモ

Backlog というチーム向けのバグ・タスク管理 ASP サービスがあるのですが、このタスク一覧を Excel のようにリアルタイムに絞り込んだりソートしたり、まとめて処理をしたり、 Wiki を勝手に拡張する Chrome Extension を作りました。各 OS の Chrome で無料で使えます。

ちなみに、以前作った Firefox GreaseMonkey 拡張の移植版です。

見た目や動作はほぼ一緒です。

Chrome だとクリック一発で入って、またアップデートも確実に行えるのが良いです。

Firefox GreaseMonkey から Chrome Extension へ

個人的なブラウザ環境は、Ubuntu 上の Firefox がうまく動かない時期があったので、 1年以上前に Firefox から Chrome に乗り換えていました。特にヘビーに使う拡張もなかったので移行はすんなりいきました。

GreaseMonkey のような仕組みは Chrome 拡張で出来るということは知っていましたが、まだ絶賛開発中のような状況だったので本格導入はせずに、しばらく様子を見ていました。どうしても調整しないと困るサイトだけ、 GreaseMonkey 互換の範囲で書いてはいました。

そのうちに、何人かの Autofilter のユーザーから Chrome 対応の声がだんだん上がるようになってきました。 Chrome 拡張も安定してさらに Web Store も開店したので、このタイミングなら良いだろうと言うことで、本格導入することにしました。

まず取りかかりとしては、 os0x さんの 続・先取り! Google Chrome Extensions が大変参考になりました。後は、本家ドキュメント(英語) がとてもよくまとまっていますので、一通り読めば大抵のことが出来るようになります。

後は、 GreaseMonkey からの非互換部分を頑張って修正するという作業でした。

コード量の少ない Wiki 拡張(300行弱)を書き直してみたところ、 manifest.json を追加する程度でほとんど動いてしまいました。やっててよかった! Web 標準万歳です。

これにとても encourage されまして、 Autofilter (約3600行)も同じような気持ちで取り組みました。結果、全体の1割程度の修正で動いたのですが、大変苦労しました。ということで、以下はそのときのメモです。

E4X の書き直し

Firefox では、 E4X という XML 構築や操作をリテラルで書けるシンタックスをサポートしています。慣れるとこれが大変便利で、式展開も出来るためコードが分かりやすくなります。 Rhino でも使えるので業務でもよく利用しています。

Autofilter や Wiki 拡張では、 XMLRPC の XML 構築や解釈、あとはヒアドキュメントとして使っていました。これらを E4X を使わない形に書き換えることが修正の主な作業でした。

ヒアドキュメントの書き直し

以下のようなコードを

var str = <>
ABCD
EFGH
</>.toString();

以下のようなコードに書き換えます。

var str = "ABCD\nEFGH";

GreaseMonkey では、追加の CSS はヒアドキュメントで書いて GM_addStyle で入れていましたが、 Chrome では manifest.json に書けば CSS は別ファイルに出来るので楽になりました。

XML リテラルの書き直し

以下のようなコードを

var xml = <a><b>cde</b></a>;

以下のようなヘルパー関数を使ったコードに書き換えます。

var xml = TAG("a",TAG("b","cde"));

function TAG(tag,content) { // E4X like helper
	if (content instanceof Array) {
		content = content.join("");
	}
	return "<"+tag+">"+content+"</"+tag+">";
}

大変そうでしたが、 XML 構築は単純な書き換えだったので楽でした。

DOM 操作の書き直し

以下のような E4X のコードを

function(response) { // XML object
   var struct = response..struct;
   :
}

以下のような XPATH コードに書き直します。

function(response) { // XML object
   var struct = $X("/methodResponse/params/param/value/struct",response);
   :
}

$X は以下のような XPATH のヘルパーです。よくあるコードです。

function $X(xpath, node) {
	node = node || document;
    var res = node.evaluate(xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    var copy = [];
    for (var i=0, j=res.snapshotLength; i<j; i++) {
        copy.push(res.snapshotItem(i));
    }
    return copy;
}

隣接するノードは、childNodes などの DOM 関数で取得するようにしたのですが、そうすると意図しない位置に空白文字があると TextNode が入って来てしまうので、うまくスキップさせたり、逆に必要な TextNode を判定するのが面倒でした。

これまでは E4X がうまく空気を読んでくれていたのだと思います。やはり E4X は偉大です。

XMLRPC 書き直し

ここも E4X のコードをひたすら書き直すだけでした。

Chrome 用の XMLRPC の先行実装をいくつか見たのですが、いまいちだったのでやっぱり自前で XMLRPC を書きました。オブジェクトと XML の変換がちょっと書いてて楽しかったです。

ここの部分は切り出して使えば、汎用の Backlog API コードとして使えそうかもしれません。

JavaScript 仕様

Firefox には 「for each」というループ構文があったのですが、 Chrome には無かったので、単純な for 文に書き直しました。

GreaseMonkey 仕様・GM関数の書き直し

いくつかの GreaseMonkey 仕様を書き直しました。これも対策が本家も含めて多く出回っているので対応は楽でした。

unsafeWindow の削除

GreaseMonkey では問題の多い「unsafeWindow」を使っていました。 Autofilter の場合は、単純に表のページのカレンダーのライブラリを読んでいるだけだったので、カレンダーのライブラリを同梱することにして解決しました。

manifest.json によるパッケージングは便利です。

GM_setValue, getValue

ローカルストレージで書き直しました。

Autofilter ではドメイン間のデータのやりとりはないので、 background.html や メッセージ通信は使っていません。

GM_xmlHttpRequest

こちらも通信先ドメインが同じなので単純に XMLHttpRequest のインタフェースを合わせて自前実装しました。 GET, POST しています。

複数非同期の待ち合わせで JSDeferred を導入し用かどうか悩んだのですが、まずは自前待ち合わせコードで済みそうだったので、導入は見送りました。ただ、これ以上複雑なことをする場合は導入しようと思っています。

CSS, HTML

CSS, HTML はブラウザが変わるとそもそも挙動が変わってしまうので、試行錯誤しながら一つ一つ元の動きになるように直していきました。

セレクタ

Firefox ではいい加減なセレクタを書いていても動いていたのですが、 Chrome だとうまくいかないことが多かったです。

おそらく、隣接セレクタの挙動が何か違っているような気がしました。

tbody がスクロールできない

今回一番苦労した部分です。

Firefox だと、 table の thead, tfoot は固定にして、tbody だけスクロールするということが出来たのですが、 Webkit 系では出来ないようです。

検索して解決方法を探したり、そういうプラグインのコードを読んだりしてみたのですが、 Autofilter で必要な機能を実現する根本的な解決方法は見つかりませんでした。

ということで、もうテーブルによる自動レイアウトはやめて、 tbody, thead, tfoot の display:block にして JS で幅を制御することにしました。個人的にはまだまだ不満点が多いのですが、ユーザーが Firefox との使用感の違いをすぐには認識できないくらいには、うまく動いているようでした。

その他

パッケージングにより、 loading のくるくる回るアイコンが出せるようになりました。



Chrome 拡張はとても面白かったので、今後も Backlog に欲しい機能や、 UI の実験などで積極的に使っていこうと思っています。

calfw の設計と利用方法

はじめに

前回の記事は calfw が使えるようになるところまででした。今回の記事では calfw をもう少し詳しく説明してみたいと思います。

今回の記事の目標は2点あります。一つはカレンダーの情報源である「情報ソース(cfw:source)」の作り方、もう一つは calfw 自体を他のアプリケーションに組み込む方法です。前者の情報ソースを作れるようになれば、自分でカレンダーに表示する内容を作れるようになります。後者が分かれば、「ここにカレンダーがあるとすごく便利なのに」と思うような場面で calfw を組み込むことが出来るようになります。

calfw の全体のアーキテクチャMVC になっています。 Emacs のアプリケーションでも、ある程度大きなアプリケーションでは OOP のテクニックはよく使われています。 calfw では、それほど複雑なクラス構成は出てきていないため、構造体と名前のルールによるシンプルなモジュール化を使っています。

cfw:source について

cfw:source はカレンダーの情報源を定義するオブジェクトです。 cfw:open-calendar-buffer などのカレンダー構築用関数に :contents-sources の名前付き引数で渡します。以下にコンポーネント構築のコード例を示します。

(cfw:open-calendar-buffer
   :contents-sources
   (list 
    (cfw:org-create-source)  ; org 用のソース生成関数
    (cfw:howm-create-source) ; howm 用のソース生成関数
    (cfw:ical-create-source "ical" ; icalendar 用のソース生成関数
      "https://../basic.ics" "Red")))

ここでは org, howm, icalendar の情報ソースをつくってカレンダーを構築しています。このように、任意の情報源を組み合わせてカレンダーを構築することが出来ます。

この情報ソースオブジェクトを自分で用意できれば、同じようにカレンダーに表示させることが出来ます。
まず、 cfw:source の詳細について簡単に説明します。その後、具体的なソースの作り方を説明します。

cfw:source 詳細

cfw:source は defstruct で定義された構造体です。各スロットの詳細は以下のようです。

名前 説明
name [必須] 情報源の名前。ステータスバーに表示される。
data [必須] 情報源の内容を返す関数。詳細は次の「 cfw:source-data 仕様」で説明。
update [オプション] 情報源の内容を更新する必要があるときに calfw 本体から呼ばれる。
ここでキャッシュクリアなどを行う。
color [オプション] この情報源の色。文字列で指定。
M-x list-colors-display で出てくる色名や、"#abcdef"のような6桁の16進。
主に通常のスケジュール表示用の色。
period-fgcolor [オプション] 期間スケジュールの前景色。省略すると白か黒。
period-bgcolor [オプション] 期間スケジュールの背景色。省略すると color を使う。
opt-face [オプション] face の色以外に指定したい項目。
フォントをイタリックにするとか太字にするとか。
:opt-face '(:weight bold) のように指定。
opt-period-face [オプション] opt-face と同じく、期間スケジュールに適用する face 項目。

name と data が必須であり、 name, date, update 以外は見た目の設定です。

ちなみに、なぜ直接 face を指定するようになってないかというと、 color だけ指定すればあとはうまく calfw が表示してくれることを想定しているからです。まず、 color だけでやってみて、気に入らないところがあれば追加で見た目を細かく調整できるように考えています。

cfw:source-data 仕様とコードサンプル

次に、cfw:source-data の関数の定義や返すべき値について説明します。

cfw:source-data 関数は、 開始日・終了日を受け取って、(日付 . (内容のリスト)) の alist を返す関数です。具体例で書くと以下のようです。(簡単のために、引数の開始日・終了日は使っていません)

;; cfw:source-data の簡単な例
(defun sample-data1 (b e)
  '(
    ((1  1 2011) . ("内容1"))
    ((1 10 2011) . ("内容2" "内容2行目"))
    ))

(cfw:open-calendar-buffer
  :contents-sources
   (list 
     (make-cfw:source
      :name "test1" :data 'sample-data1)))

このコードを scratch バッファなどで実行して2011年1月を表示させると、以下のような画面になります。



単純なスケジュールの例

日付は (list 月 日 年 ) で指定します。この形式は、 calendar.el や orgmode で広く使われている形式です。 Emacs の TIME 型やその他の形式との相互変換については、日付変換まとめ を参照してください。

期間スケジュールは以下のコードのように periods の項目を作って、そこに (list 開始日 終了日 内容) のリストを入れます。*1

;; cfw:source-data 期間スケジュールの例
(defun sample-data2 (b e)
  '(
    ((1  8 2011) . ("内容1"))
     (periods
      ((1 8 2011) (1 9 2011) "期間1")
      ((1 11 2011) (1 12 2011) "次の期間"))
    ))

(cfw:open-calendar-buffer
  :contents-sources
   (list 
     (make-cfw:source
      :name "test2" :data 'sample-data2)))

このコードの結果は以下のようになります。



期間スケジュールの例

以下、もう少し細かい仕様です。

  • 関数の引数の開始日・終了日は表示したい期間(両端含む)
  • 関数の引数の開始日・終了日の範囲外のものが含まれてもかまわない
  • 描画が必要な度に呼ばれるので、計算に時間がかかる場合はデータをキャッシュしておくべき
  • リスト内は日付順に並んでなくてもよい。periods もリストのどこにあってもよい。重複している場合はまとめられる。
  • 日付内での並び順はデフォルトではアルファベット順

ここまでは固定の日付データを使いましたが、実際には何らかのプログラムで結果リストを出力することになると思います。 org, howm, icalendar の実際のコードが参考になると思います。

calfw コンポーネント

次は、 calfw を他のアプリケーションで使う方法について説明します。

calfw はカレンダー表示の方法として次の3つの形態を用意しています。

  • 独立したバッファとして表示
  • リージョン内に挿入
  • テキスト出力

それぞれ簡単に説明します。

バッファ

通常の Emacs のアプリと同じように、独立したバッファを作ってカレンダーを表示します。

関数 cfw:open-calendar-buffer を呼ぶと、カレンダー用のバッファを作成し、 switch-to-buffer でバッファを表示します。バッファは cfw:calendar-mode のメジャーモードがセットされ、キーバインドなどもバッファ全体で cfw:calendar-mode-map がセットされます。

ふつうのよくある使い方です。アプリケーションの境界はバッファ単位であり、ユーザーは通常のバッファ操作を行うことが出来ます。

リージョン

別のアプリケーションバッファに埋め込みたい場合に使います。具体例としては howm のメニューへの埋め込みがあります。

どういうことなのか分かりにくいかもしれないので、 scratch バッファで以下の式を評価してみてください。

;; scratch バッファで以下の式を評価してみる
(cfw:create-calendar-component-region :height 10)



scratch バッファで実行 → そのままカレンダーの操作ができる

アンドゥで元に戻ります。

カレンダーのリージョン以外には影響をほとんど与えず、キーバインドも自前で用意(テキストの keymap プロパティにセットされている)するので、手軽に他のアプリケーションに組み込むことが出来ます。

テキスト

カレンダーが描画された純粋なテキストが欲しい場合に使います。 cfw:get-calendar-text でテキストを返します。
まだ具体的に活用された例はないのですが、外部への export とか、ツールチップ用の文字列準備などで考えています。

「描画先」と「ビュー」

以上、3つの形態を説明しました。これらの見た目や構築方法は全然違いますが、calfw を使うプログラムからは統一的に操作することが出来ます。

今後、これらの形態の違いは「描画先」と呼ぶことにします。似た単語に「ビュー」が出てきますが、これは月や週などの描画方法を指します。分かりにくくてすみません。

オブジェクトの構成

まず、概要をつかむために calfw のオブジェクト達について説明して、その後、各詳細とどう使うのかを説明します。

オブジェクト概要

大きく分けて以下のオブジェクトがあります。

  • 全体の入れ物である「 calfw コンポーネント(cfw:component)
  • 全データを管理する「モデル」 (cfw:model)
  • 各カレンダーのデータを定義する「情報ソース」 (cfw:source)
  • 「描画先」の抽象化 (cfw:dest)

UMLのクラス図で描くと以下のようです。*2



オブジェクトの概要図

calfw コンポーネントMVC の Controller の役目を担い、モデル描画先をつなげて全体を制御します。また、外界とのインタフェースとして各種オブジェクトへのアクセスを提供します。

モデル情報ソースMVC の Model に当たります。情報の管理とロジックを担当します。

描画先MVC の V に当たり、描画先を抽象化して切り替えられるような仕組みになっています。

それぞれもう少し詳しく説明します。

全体、見た目の操作 cfw:component

calfw コンポーネントは、全体の統括と見た目の制御等を担当しています。

calfw コンポーネントは以下のデータを保持しています。

  • 描画先 (dest) とモデル (model) への参照
  • 現在選択されている日付 (selected)
  • 現在のビュー (view)
  • 各種フック
    • 描画時 (update-hooks)
    • 選択 (selection-change-hooks)
    • クリック (click-hooks)

操作については以下のような項目があります。

  • 描画先、モデル、所属バッファなどの取得
  • 選択日の取得、更新 (get-selected-date / set-selected-date)
  • ビューの取得、変更 (get-view / set-view)
    • 内容は month, two-weeks, week, day などのシンボル
  • 描画サイズの変更 (resize)
  • 再描画 (update)
  • フックの追加 (add-xxx-hook)

なお、コンポーネント構築後の描画先の変更などは動的に出来ません。

見た目の要となるビューは、今のところただの関数として定義されていて、このコンポーネントのなかで描画担当の関数が振り分けられています。 (cfw:cp-dispatch-view-impl)

calfw コンポーネントインスタンスは、描画先によって保持される場所が違います。

  • バッファ → バッファローカル変数 cfw:component
  • リージョン → 範囲内のテキストプロパティ cfw:component
  • テキスト → なし

cfw:cp-get-component 関数を使うと、現在のカーソール位置から適切なインスタンスを取得できます。イベントハンドラなど、ステートレスなロジックはこの方法でインスタンスを取得すると良いと思います。

ステートフルな操作のためには、コンポーネントを使用する側がインスタンスを保持したほうが良いと思います。

カレンダーの内容へのアクセス cfw:model

情報ソースのインタフェースで情報の定義を統一し、モデルが情報ソースを束ねて管理します。

主に以下のようなデータを管理します。

  • 内容の情報ソース (contents-sources)
  • 注釈の情報ソース (annotation-sources)
  • 内容のソート関数 (sorter)

モデルは、描画先やビューについての情報は一切持っていません。純粋に、日付や期間に対応する内容だけを管理します。祝日に関しては calendar.el と同様に、 calendar-holiday-list 関数から取得します。

大抵の場合、モデルの内容は構築時に決定されますが、後で動的に情報ソース自体を操作したりするような場合にモデルを経由してアクセスします。

現在の実装では描画処理の過程で、ビュー固有のデータ(いわゆる ViewModel)が付加されます。このあたりの実装は個人的にも微妙な感じがしています。。。

描画先の抽象化 cfw:dest

描画先の操作を抽象化して、ビュー関数が描画先に依存せずに描画できるようにする仕組みです。

描画先は以下のデータや操作を持っています。

  • 描画先バッファ (buffer)
  • 描画範囲取得 (min-func, max-func)
  • サイズ (width, height)
  • 削除関数 (clear-func)
  • 更新関数 (before-update-func, after-update-func)
  • オーバーレイ管理 (select-ol, today-ol)

上の calfw の利用形態のところで少し書いたように、現在の実装ではバッファ、リージョン、テキストがあります。リージョンがやりたくてこの仕組みを作ったと言っても過言ではありません。描画に必要な情報をすべて持っていますので、一つのバッファに複数のカレンダーコンポーネントを入れることも出来ます。 *3

組み込み方針

calfw 活用の方法としては、アプリケーションのUIとして使ったり (howmの形態)、日付の入力用として一時的に表示させるということが考えられます。

まず、描画先としてバッファを丸ごと使うか、既にあるバッファに組み込むかという選択があります。切り替えるのは簡単ですので、いろいろ試してみて使いやすい方を採用したらいいと思います。
データの表示は、単純に情報ソースを作れば表示できます。

次に、ユーザーからの入力ですが、日付の選択やクリックなどの単純なアクションは、コンポーネントのフックで簡単に拾うことが出来ます。また、選択された日付は、 cfw:cursor-to-nearest-date か cfw:cursor-to-date で取得できます。現在は、単一の日付しか取得できません。

上記以外のイベント(エンターキーや任意のボタンクリックなど)は、構築時の :custom-map 引数に適当なキーマップオブジェクトを渡すことで取得することが出来ます。さらに細かく調整したい場合は、情報ソースで付加された大抵のテキストプロパティはそのまま表示まで持って行くことが出来ますので、文字単位でキーマップや face を設定することで対応できると思います。

上記のアクションでデータを更新したら、コンポーネントの再描画関数を呼ぶことで表示を更新できます。

これで一通りの入力・表示の操作が出来るのではないかなと思います。以上をまとめると下のような絵になります。



アプリケーションから calfw を使うイメージ

具体的には、 howm 連携のコードが参考になると思います。

今後の予定

以下、 calfw の今後についてのメモです。 orgmode の ML が熱いので、そちらを watch すると良いかもしれません。

  • viewの追加、改善
    • 3,4日表示とか
    • ガントチャートっぽく横長表示とか
    • もっとコンパクトな表示や、オーバーレイでポップアップとか
    • もっとスペースを有効活用できるような表示方法
  • 期間選択
  • 時間の扱い
    • 週表示や日表示
    • 時間の持ち方について検討
  • orgとの連携の強化
  • 他の連携の追加
    • diary とか

*1: ちなみに「(A . (B C) )」は「(A B C)」と同じになります。念のため。

*2: このクラス図はいつもお世話になっている Astah で描いています。データは githubリポジトリに入っています。

*3:この仕組みを拡張して、カレンダー以外の汎用的な「一つのバッファに複数入れて組み合わせることが出来るコンポーネント」の仕組みが出来ないかなということも考えています。ちょうど、近年の Web 上の JavaScript がお行儀よくなって、一つの document 内でも複数の JavaScript コンポーネントやライブラリが同居出来るようになってきた感じにならないかなと、勝手に考えています。

[emacs][ui] Emacs用カレンダー calfw v1.2 リリース

もうちょっと早く紹介記事を書くつもりだったのですが、orgmode の本家 ML から召喚されて、2週間ほど ML での対応やパッチ取り込み、細かい機能追加などを行っておりました。ということで、 v1.0 から飛んで v1.2 になりました。

calfw ?

Emacs 上でふつうに使えることを目指したカレンダーアプリです。こんな感じのよくあるレイアウトで表示します。



画面イメージ

以前の記事もある程度参考になると思いますが、設定方法が変わっていますので気をつけてください。

以前(v0.2.1)からの主な変更点

  • API の整備
    • カレンダーのネタ提供をグローバル変数をやめて、構築時の引数で指定
    • cfw:source でスケジュールを構築、複数組み合わせ
  • UIの強化

画面と操作

画面概要

こんな感じで、上にナビゲーションやビューを変更するツールバー、下側にカレンダーのネタを提供するステータスバーが付いています。よくあるカレンダーアプリと同じような構成です。



画面概要:ツールバーとステータスバー

今のところ以下の4種類のビューがあります。



ビューの種類:月、2週、1週、1日

カレンダーの表示内容は以下のようです。(以前のものの再掲)



カレンダーの表示内容

SPC キーで Macクイックルックのように、その日の詳細バッファをさっと開いて確認することが出来ます。もう一度 SPC を押すと引っ込みます。



詳細バッファの表示

キーバインド

cfw:calendar-mode-map で以下のように定義しています。

操作
[left], b, h 前の日
[right], f, l 次の日
[up], p, k 前の週
[down], n, j 次の週
^ 週の頭
$ 週末
[home] 月の初め
[end] 月末
M-v, [PgUp], < 前の月
C-v, [PgDown], > 次の月
t 今日へ
g 日付指定移動 (YYYY/MM/DD)
ビュー
M 月表示
W 週表示
T 2週表示
D 日表示
操作
r 内容更新
SPC 日の詳細表示 ( Mac の Quicklook 的)
RET, [click] 定義に移動 (howm, orgmode)
q バッファ隠す

以前のユーザー向け

既に calfw を使われている方は以下の点を確認して必要があれば設定を修正してください。先日紹介いただいた Ubuntu Recipe の記事 の内容からも変更が有ります。

howm : 設定に変更無し
  • 以前の記事通りであれば変わらないはず
  • アップデートしたらそのまま使えるはず
  • 期間スケジュールの対応 (new!)
    • [2011/07/04]@4 と書くと 7/4 〜 7/7 までの期間スケジュール表示になる
ical : 設定に変更が必要
  • まるっきり変わってしまったので、次の節の「導入方法」の内容を参照
  • HTTP 取得用の外部コマンド対応
    • プロキシ環境下ではEmacsSSLで通信できないため
    • cfw:ical-url-to-buffer-get を cfw:ical-url-to-buffer-external にするとコマンド(デフォルトは wget)で取りに行く
    • 今後の課題
      • PC の稼働環境の移動によってプロキシ環境が変わる場合は、工夫が必要
      • gconftool -a /system/https_proxy
      • .gconf/system/http_proxy/%gconf.xml
  • カレンダーバッファで r を押すと、キャッシュのクリアと再ダウンロードを行う
org : 設定に修正が必要
  • cfw:install-org-schedules の廃止
    • cfw:install-org-schedules の行を消すだけで OK
  • org 用の設定をいくつか追加

導入方法

必要なものはEmacs本体(手元では23.2で試しています)だけです。

ダウンロード

カレンダー本体 calfw.el については auto-install.elで以下の式を評価して入れるか、ダウンロードしてload-pathの場所に置いてください。

;; auto-installを使う場合
(auto-install-from-url "https://github.com/kiwanami/emacs-calfw/raw/master/calfw.el")

ダウンロード

他の拡張も合わせて使うような人は git clone しておくと便利かもしれません。

後、GentooDebian にはパッケージがあるそうです。メンテナーの皆さんありがとうございます。

基本設定

以下のコードを評価すると今月のカレンダーが表示されます。

(require 'calfw) ; 初回一度だけ
(cfw:open-calendar-buffer) ; カレンダー表示

calfw 自体はカレンダーが必要なアプリケーションのためのコンポーネントという立場なので、それ自体ではカレンダーが表示されるだけで余りうれしくありません。

howm などのスケジュールを管理するアプリと一緒に使うことで威力を発揮します。

スケジュール管理との組み合わせ

howm

howmのスケジュールやTODOをカレンダー内に表示することが出来ます。まず以下の追加プログラムをインストールします。

最小限の連携コードは以下のようです。

(require 'calfw-howm)
(cfw:install-howm-schedules)
(define-key howm-mode-map (kbd "M-C") 'cfw:open-howm-calendar)

これで、 howm-mode 使用時(howmのメニューなど)で M-C で howm のカレンダーを表示することが出来ます。
また、予定のある日付で Enter キーを押すと、日付で howm の記事を検索します。ここから予定の編集などが出来ます。qでカレンダーバッファを終了(kill-buffer)します。

HowmとElscreenの連携を使っている人は、バインド先のコマンドはこちらの方が良いかもしれません。

(define-key howm-mode-map (kbd "M-C") 'cfw:elscreen-open-howm-calendar)

こちらは新しいスクリーンを作成してそちらでカレンダーを表示します。

さらに、howmのメニューファイルに以下の行を追加すると、そこにカレンダーを表示させます。

%here%(cfw:howm-schedule-inline)

この場合、以下のような画面になります。



Howmのメニューに表示!

さらに、このカレンダー内部では前述の日付移動のキーバインドが使えますので、メニュー内で日付や月の移動、またその日の予定一覧の表示が出来ます。

さらに、自分は予定は一つのメモ(「2010年予定」とか)に集約しているのですが、そうするとカレンダーから日付を選んで予定を入力するように出来ます。自分が使っているHowm連携のコードは以下のようです。

;; howm and calendar framework

(defvar my-howm-schedule-page "2011年予定") ; 予定を入れるメモのタイトル

(defun my-cfw-open-schedule-buffer ()
  (interactive)
  (let* 
      ((date (cfw:cursor-to-nearest-date))
       (howm-items 
        (howm-folder-grep
         howm-directory
         (regexp-quote my-howm-schedule-page))))
    (cond
     ((null howm-items) ; create
      (howm-create-file-with-title my-howm-schedule-page nil nil nil nil))
     (t
      (howm-view-open-item (car howm-items))))
    (goto-char (point-max))
    (unless (bolp) (insert "\n"))
    (insert
     (format "[%04d-%02d-%02d]@ "
             (calendar-extract-year date)
             (calendar-extract-month date)
             (calendar-extract-day date)))))

(eval-after-load "howm-menu"
  '(progn
     (require 'calfw-howm)
     (cfw:install-howm-schedules)
     (define-key howm-mode-map (kbd "M-C") 'cfw:elscreen-open-howm-calendar)
     (define-key cfw:howm-schedule-map (kbd "i") 'my-cfw-open-schedule-buffer)
     (define-key cfw:howm-schedule-inline-keymap (kbd "i") 'my-cfw-open-schedule-buffer)
     ))

(setq cfw:howm-schedule-summary-transformer 
  (lambda (line) (split-string (replace-regexp-in-string "^[^@!]+[@!] " "" line) " / ")))

これで普通のスケジュール管理(Google Calendar, iCal, Outlookなど)と同じように、「カレンダーから開いている日を探して予定を入れる」というユースケースが実現できるようになりました。

org

Emacs の万能情報管理ツール orgmode との連携です。現在、 orgmode の本家 ML で機能追加などが議論されています。

org-agenda をカレンダーで表示するには、 Emacs の設定ファイルの適当なところに以下のように1行加えます。

(require 'calfw-org)

後は、 M-x cfw:open-org-calendar とすると、カレンダーで表示されるはずです。グローバルキーバインドに割り当てておけば、一発で開くことが出来て多分便利です。



org-agenda のカレンダー表示

cfw:org-agenda-schedule-args の変数に :scheduled :sexp :closed :deadline :todo :timestamp などを入れることによって、表示する内容を多少絞ることが出来ます。

google calendar

Google CalendariCal などの iCalendar(ICS)形式を表示することができます。(現在まだ繰り返しスケジュールが表示できません)

Google Calendar との最小限の連携コードは以下のようです。

(require 'calfw-ical)

(defun open-my-ical ()
  (interactive)
  (cfw:open-ical-calendar "http://www.google.com/calendar/ical/.../basic.ics"))

この場合 M-x open-my-ical とするとカレンダーが開きます。
適当なキーにバインドすると便利だと思います。

Google Calendar の URL はカレンダーの設定の以下の場所から取得できます。



Google Calendar での URL の確認

ローカルの ICS ファイルを指定することも出来ます。
また、複数のカレンダーを同時に表示することも出来ます。次の「混ぜて表示」のコードを参照してください。

混ぜて表示

org と ical のスケジュールを混ぜて表示させることが出来ます。以下のような感じで cfw:open-calendar-buffer を使います。

(require 'calfw-org)
(require 'calfw-ical)

(defun my-open-calendar ()
  (interactive)
  (cfw:open-calendar-buffer
   :view 'month
   :contents-sources
   (list 
    (cfw:org-create-source "Seagreen4") ; color
    (cfw:ical-create-source "my calendar" "https://www.google.com/calendar/ical/../basic.ics" "Pink")
    (cfw:ical-create-source "共有スケジュール" "https://www.google.com/calendar/ical/../basic.ics" "#2952a3"))))
    ;; title, URL, color

M-x my-open-calendar とすると、org と ical を一緒に表示することが出来ます。

cfw:open-calendar-buffer やカスタマイズの詳細については次回の記事に書く予定です。

その他

MHC とか diary とか、他にもスケジュールを管理するアプリがあります。
diary については、現在取りかかりのコードをもらったので後で追加すると思います。

カスタマイズ

ここでは簡単なカスタマイズだけ書きます。詳細な拡張方法などについては次回の記事に書く予定です。

キーバインド

カレンダー全体のキーバインドを変更する場合は cfw:calendar-mode-map で設定します。

個別のカレンダーごと、あるいはカレンダーのソースごとに追加のキーバインドを設定することが出来ます。(次回予定)

祝日設定

日本用の祝日を表示させるために japanese-holiday.el を入れておくと便利です。

自分の所では以下のように書いています。

(add-hook 'calendar-load-hook
          (lambda ()
            (require 'japanese-holidays)
            (setq calendar-holidays
                  (append japanese-holidays local-holidays other-holidays))))
月、曜日の表記

月や曜日の表示は calendar.el の設定をそのまま使っています。以下のコードを修正すれば変更できると思います。

;; 月
(setq calendar-month-name-array
  ["January" "February" "March"     "April"   "May"      "June"
   "July"    "August"   "September" "October" "November" "December"])

;; 曜日
(setq calendar-day-name-array
      ["Sunday" "Monday" "Tuesday" "Wednesday" "Thursday" "Friday" "Saturday"])

;; 週の先頭の曜日
(setq calendar-week-start-day 0) ; 日曜日は0, 月曜日は1
Face (色とか)

calfw.el の先頭に face の設定があります。その設定を参考に調整すると良いと思います。
以下の方々の設定がとても参考になると思います。

次回予定

例によって時期は未定ですが、以下の項目を予定しています。

  • コンポーネント
    • 構築方法、オブジェクト構成
    • 使い方
  • cfw:source について
    • 解説
    • 独自ソースの作り方
  • 今後の予定とか

(2011/07/25 追記:複数icalendar表示のコード追加)

e2wm の magit パースペクティブ

Emacs のウインドウ管理ツール e2wm の magit パースペクティブを作ってみたので簡単に紹介します。

magit は、 Emacs の git インタフェースです。

導入方法は後ろの方にあります。

画面と概要

コード編集用の画面から、さっと magit 用のパースペクティブに切り替えます。



パースペクティブの切り替え

M-x e2wm:dp-magit とやるのは大変なので、普通は適当なキーバインドに割り当てます。自分の所では M-w に割り当てています。

magit パースペクティブの画面は以下のようです。



各ウインドウの役割

中央に現在編集中のプロジェクトの magit-status バッファを表示し、右側にブランチ一覧と履歴を表示します。右側のブランチ一覧と履歴は参考程度ですので、基本的には真ん中の magit-status バッファで作業をします。ということで、 magit を既に使っている人なら特に覚えることはありません。

magit については以下の本家マニュアルが詳しいです。一通り読むと大体使えると思います。

ブランチ、履歴のブラウズ

ブランチ一覧は、 magit-show-branches コマンドを実行したバッファになっていますので、ブランチの切り替え(git checkout とか)が出来ます。

また、履歴一覧は、 magit-display-log コマンドを実行したバッファになっています。このバッファから n や p などのキーバインドで、コミット履歴を次々と見ていくことが出来ます。コミットの詳細は真ん中のメインのウインドウに表示されます。(簡単な対応なのでちょっとカーソールの動きが若干おかしいですが、動作は問題ありません。多分。)



コミット履歴の参照

常に画面の端に履歴が表示されるというのはなかなか良いものです。各ブランチの状況、タグの位置や積み上がった履歴を見ると、モチベーションが上がります。

magit を使ったワークフローとe2wm

以下、簡単な操作の流れです。

  • コードを書いて、ある程度の区切りが付いたところで magit パースペクティブに切り替え。
  • magit-status から、編集した内容を確認しながら、ファイル単位や hunk 単位で staging 。
    • n, p で移動。TABでファイル内容表示。 s でステージにあげて、 u でステージから戻す。もう複雑なコマンドを覚えなくていい!!!
  • 良ければ c でコミットログ作成。 C-c C-c でコミット、もしくは C-c C-k でキャンセル。
  • コミット後、履歴で各ブランチの状況などを確認。
  • リモートリポジトリに対して P で push 、F で pull 。



magit を使ったワークフロー

これらの作業をスムーズに行うことが出来ます。また、 magit はメニューで分かりやすく機能を提供していますので、「あんなことしたいんだけど、コマンド何だったかな〜」というようなうろ覚えでも大丈夫です。

結局 e2wm はどうでも良いので、 magit 使うと良いと思います。

導入方法

インストールですが、 auto-install 経由で以下のコードを実行するなどしてください。

(auto-install-from-url "https://github.com/kiwanami/emacs-window-manager/raw/master/e2wm-vcs.el")

magit.el は現時点の最新の ver 1.0.0 が必要です。 window-layout.el や e2wm.el 本体も最新にしておいてください。

あとは、 e2wm の設定に以下のように追加します。

(require 'e2wm-vcs)

;; M-w に magit パースペクティブ変更を割り当て
(e2wm:add-keymap
 e2wm:pst-minor-mode-keymap
 '(("M-w" . e2wm:dp-magit))
 e2wm:prefix-key)

上の設定であれば M-w で magit の画面になります。

そのうち、package.el に対応して簡単にインストールできるようにしたいなと考えています。

いろいろ

e2wm を作ってから、個人的にコーディング環境は非常に改善されたのですが、 git や subversion の操作はシェルから行っていて、ずっと問題を感じていました。Eclipse でいう Subclipse が用意しているようなパースペクティブが必要なのかなと思ってはいたのですが、なかなか実装までたどりつきませんでした。

今年になってからしばらく magit を使ってきていたのですが、気がついたらこれ無しで git が操作できない体になっていました。そこで、壮大なプラグインを作るのはやめて、 e2wm のウインドウ管理とぶつからない程度に簡単に統合する程度の対応をしてみました。もうこれで十分便利です。

名前が e2wm-vcs.el となっているとおり、 magit だけでなくて、他の VCS ツールのサポートを増やしていこうと思っています。現在は dsvn.el (psvn.elも多分可) のパースペクティブも入っていますが、Subversion の履歴の一覧は重いので、デフォルトでは表示しないようになっています。Subversionリポジトリブラウザがあると便利かなと思っていますので、そのうち必要になったら作ると思います。それ以外の VCS についても自分が必要になったら増えていくと思いますが、どなたかパッチを提供していただければ追加されると思います。

後は、プロジェクトの VCS にあわせて適切なパースペクティブに切り替えるなどの機能もあると良いのかなとか思っています。

取りあえず、今回は紹介と言うことで gdgd な感じでした。

はてなグループ::Emacs

最近、はてなグループ::Emacs部 にも書き始めました。

純粋な Emacs, Emacs Lisp ネタ、あんまりまとまってない小ネタなどを書いていくのかなと思っています。

http://vim-users.jp/vim-hacks-project/ のような感じで、盛り上がっていけばいいのかなとか勝手に思っています。