CoffeeScript, Electron, Mithril でソースリーディングを助けるソフトウェアをつくってみました。よかったら使ってみてください。
ページ SourceR - ソースリーディングを助けるソフトウェア
ソース hakomo/SourceR · GitHub
概要
巨大なフレームワークなどをメソッド単位までツリーで表示し、読み終わったファイルやメソッドにタグをつけたりメモを書いたりして進捗をグラフィカルに確認することができます。
つくった理由
とあるフレームワークを読んでいたんですが、読めども読めども読み終わらなくてつらい。「よっしゃー今日も読むぞー!」って気分にさせてくれるものが欲しくなった。
あと、他人のソース読んだほうがいいよ、勉強になるよっていうけれど読みかたって体系化されてないし、支援ツール的なものもないから何かつくってみようと思った。
技術的なはなし
Exuberant Ctags
メソッドの列挙には Exuberant Ctags を使っています。 Exuberant Ctags はプロジェクト内のシンボル一覧とその位置をファイルに書きだしてくれるもので、テキストエディタでシンボルにジャンプするときに使われます。本来メソッドの列挙に使われるものではないと思いますが、多数の言語に対応していて高速かつ他に手頃なものがなかったので使うことにしました。
Electron
ネイティブアプリは私がリッチな GUI 組めないし、 Web アプリはアップロードとかソースの解析とかが重く向いていないので Electron にしました。
ほとんど Web と Node.js の知識で書けてしまうので Electron について覚えることはメニューとダイアログと環境構築くらいでした。
Mithril
興味があったのと、複雑になりそうなロジックをシンプルに書き下せそうだったので仮想 DOM を使っています。ただ、これが完成する前に大きなライブラリを読みたくなかったのでソースが短い Mithril にしました。
高速化
フレームワークによっては総メソッド数が数千とか数万とかいくので DOM 数も数万いきます。これを馬鹿正直に描画するとめっちゃ重いので高速化をしています。
生 DOM の生成を遅延
見えている生 DOM だけ生成して、折りたたまれるなどして隠れている生 DOM の生成は遅延(見えるようになったら初めて生成)します。
仮想 DOM の差分計算を局所化
一般的なサイトの DOM 数なら仮想 DOM の差分計算は一瞬で終わりますが、 DOM 数が数万となるとそうもいきません。何かするたびに数秒待たされたくはありませんのでツリーの変更された部分にだけ差分計算をします。
上記の高速化を施した view を簡略化したソースです。
function view(ctrl) {
var changedDescendant = false,
model = ctrl.model,
visibleChild = !model.filtered() && model.children.length && !model.folded(),
views = [];
if (visibleChild) {
for (var i = 0; i < model.children.length; ++i) {
var v = model.children[i].view();
changedDescendant = changedDescendant || v.subtree != 'retain';
views.push(v);
}
} else {
for (var i = 0; i < model.children.length; ++i)
views.push({ subtree: 'retain' });
}
if (model.changedVirtualDom() || changedDescendant) {
model.changedVirtualDom(false);
return virtualDom;
}
return { subtree: 'retain' };
view が { subtree: 'retain' } を返すとその子孫の仮想 DOM は変更なしとみなされ差分計算は省略されます。
それを利用して、見えない DOM は { subtree: 'retain' } を返すことで生 DOM がなければないまま、生成されていればあるままになり、生 DOM の生成を遅延させています。 DOM が見えるかどうかはモデルから生えている見えるかどうかに影響するプロパティを元に判断します。ここでは filterd(フィルター), folded(折りたたみ)を参照しています。
また、モデルに changedVirtualDom(仮想 DOM が変更されているか)プロパティを生やし、仮想 DOM が変更されるようなモデルの変更時に一緒に true にします。 view はこのプロパティが false な部分に { subtree: 'retain' } を返すことで差分計算を局所化しています。そして changedVirtualDom プロパティは他のプロパティの setter 時に自動で true になるよう差し込むため間違えたり忘れないようにしています。
やたら複雑になってしまいましたが view と setter さえ気をつけて実装すればあとはどう書いても壊れないので、 jQuery などで常に気を張りながら書くよりはかなり楽になりました。
まとめ
Electron, Mithril でリッチな GUI のデスクトップアプリを楽に書けたし、パフォーマンス的な意味でネイティブでやれって内容でも意外と何とかなった。