AdobeのBracketsで拡張を書く話

少し前にGitHubのAtomがβテストを開始しましたが、Windowsユーザなのでアプリを利用できなくて哀しい思いをしましたが、僕は元気です。

WebKitベースのブラウザをGUIアプリケーションのシェルとして利用してUIの大部分をjavascriptやCoffeeScriptで記述するというアーキテクチャのアプリケーションとしては、
AdobeのBracketsもあります。

こちらはWindowsで動きますので、今回は現在のBracketsを使いつつどの程度のことが出来るのか確認する為に拡張を書いてリリースしてみました。

尚、Bracketsの拡張は公式レジストリが運用されており誰でも気軽に拡張をリリースできます。

brackets-jsbeautifierについて

僕が作った拡張は、ソースコードを保存時に自動フォーマットします。
様々なエディタで標準的に搭載されているものですね。

実は、同じような機能を持つ拡張は既に存在しています、それはbrackets-beautifyです。

僕の作った拡張では、以下の点がbrackets-beautifyと違います。

  • EditorConfigをサポート
  • JSON5の記法で書かれた.jsbeautifyrcを使える
  • Bracketsの標準機能を使った多言語対応
    • 現在は英語と日本語のリソースファイルを内部に抱えています。
  • ユニットテストをそれなりに書いた
    • テスタビリティを向上する為に各種の処理単位を細かくしています。
  • GRUNTでビルドスクリプトを書いた

コードベースとリポジトリレイアウトが元の拡張とは大きく変わってしまったので、PRとはせずに自分のリポジトリで公開しました。

Brackets拡張を書くには

基本的な事柄については、公式ドキュメントである

を上から読んでいけば分かる様になっています。

ここでは、公式ドキュメントには明示されていない拡張を書くにあたって知るべき事柄について書きます。

依存ライブラリの取扱い

Bracketsで既存のモジュールやライブラリに依存したコードを書く場合、複数のAPIを利用します。

フロントエンドの依存性

ブラウザ上で動作する処理における依存性を解決する為に利用するAPIは二つです。

brackets.getModule

bracketsは拡張内で利用できるグローバルオブジェクトです。ソースコードとしては、この辺にあるので、見ておくと良いです。

しかしながら、Brackets本体とBrackets拡張では、requireの使い方が違うので注意して下さい。

Brackets拡張から、Brackets本体に含まれているコードを参照する際には、brackets.getModuleを使います。

例えば、こんな風になります。

var CommandManager = brackets.getModule("command/CommandManager");
var _ = brackets.getModule("thirdparty/lodash");

名前空間としては、

を起点とした相対パスを記述します。Bracketsが依存しているライブラリをthirdparty以下にgit submoduleを使って集約しているのが特徴的ですね。

僕が自分で書いた拡張でも、このディレクトリレイアウトを踏襲しています。

require

拡張と同じかその配下にあるディレクトリに配置したコードを読みだす際に使います。

RequireJSを使っており、textプラグインとi18nプラグインが標準で組み込まれています。

例えば、こんな風になります。

var strings = require("i18n!nls/strings");
var beautify = require("beautify").beautify;

つまり、Bracketsでは国際化対応をRequireJSi18nプラグインベースで行っています。

より詳細にBracketsの国際化対応について知りたければ、

辺りを見るとよいでしょう。

バックエンドの依存性

Bracketsではローカルディレクトリに触るなどブラウザでは出来ない事を実現する為に、バックエンドにnodeのプロセスを起動してWebSocketで接続します。

接続の仕方について詳しく知りたいなら、Brackets Node Process: Overview for Developers を読みましょう。

バックエンドのnodeプロセスでは、domainManager を引数にとるinit関数をexportすること以外は普通のnodeなコードを記述できます。

DomainManagerなる良い感じのオブジェクトは、UIとは違ったリポジトリにあるので中身が見たいなら注意が必要です。

拡張のユニットテストを書く

拡張の書き方が分かったところで、次はユニットテストを書きましょう。動作確認する程度のレベルでも自動化されたテストコードがあると、心身を健康に保つことができます。

しかしながら、公式レジストリにデプロイされている拡張のほとんどはテストコードがありません。

公式ドキュメントとしては、

に、ある程度のtipsは書いてあるのですけども、これだけではテストを実行できません。

特に、

  1. Learn about the Jasmine unit-test framework.
  2. Add a unittests.js module in the root of your extension folder.
  3. Write Jasmine test cases inside the module, using describe() {} blocks. Here’s an example.
  4. Choose Debug > Run Tests
  5. Click the Extensions tab
  6. Click the name of your Jasmine block to run it

と、ありますが後述するビルド環境を構築しないと、このRun Testsメニューが有効になりません。

それでは、Bracketsの愉快な世界を堪能しましょう。

ビルド環境を構築する

いきなり何を言ってるのかワカラネーかもしれませんが、拡張のユニットテストを実行する為には、Bracketsを構成する二つのリポジトリをcloneしてビルドするのが一番の近道です。

幸い、ビルドスクリプトはそれなりに整っているのでそれ程難しくはありません。手順は以下のドキュメントにまとまっています。

Windowsユーザは、ビルドの途中で管理者権限が必要になるのでcmd.exeを管理者権限で実行するのを忘れると特に何のエラーもなく期待通りの結果になりません。

尚、Windows XPでなくてもjunction.exeを使ってジャンクションを作る様にビルドスクリプトが構成されています。

もし、bashやunzipコマンドが見つからない場合、GitのインストールディレクトリにあるbinディレクトリをPATH環境変数に追加して下さい。

ビルドが成功したら、toolsディレクトリにある、setup_for_hacking.batsetup_for_hacking.shを実行します。

setup_for_hackingスクリプトは、brackets-shellをビルドして得られた実行バイナリが格納されているディレクトリを引数として設定します。

setup_for_hacking.bat C:\path\to\Brackets\repo\brackets-shell\Release

ここでも、Windowsユーザはcmd.exeを管理者として起動した上で、setup_for_hackingスクリプトを実行します。

ここまで上手くいくと、こういう状態になります。

build

最後に、書いた拡張のディレクトリをgit cloneしたbracketsリポジトリの/src/extensions/devの中に配置します。

これによって、ユニットテストを実行できます。

やりましたね!

ユニットテストを分割する

Bracketsのテストコードを実行するツールでは、プロジェクトのルートディレクトリにあるunittests.jsが決め打ちで実行されます。cf. utils/ExtensionLoader.js#testAllExtensionsInNativeDirectory

ビルドスクリプトの書き易さとの兼ね合いもあるのですけども、流石にユニットテストのコードがファイル一つだけというのはあんまりなので、やり方を考えます。

Bracketsでは、testディレクトリの中にspecディレクトリがあるのですが、僕のコードでは負荷テストや、スモークテストは書かないので、specsディレクトリにユニットテストを書く事にしました。

specsディレクトリにユニットテストを書いて配置したら、それをunittests.jsからrequireします。

つまり、以下のようになります。

/*global define*/
define(function(require, exports, module) {
    "use strict";

    require("specs/mainSpec");
    require("specs/guardSpec");
});

Bracketsのユニットテストにおける不満点

GUIアプリケーションなのである程度仕方ないのですけども、PhantomJS か何かを使ってヘッドレスにテストできるようにして欲しいものですね。

Bracketsの拡張も世間に数あるCIサービスを使ってビルドしたいものです。

ビルド

公式レジストリにデプロイされている拡張の殆どにユニットテストが無いことは既に言及しましたけども、真っ当なビルドスクリプトのあるリポジトリもありません。

git archiveコマンドでzipファイル作れば良いと等と公式ドキュメントに書いてあれば、確かにビルドスクリプトなんて書かないかもしれませんね。

しかし、git submoduleしたり、バックエンドのnodeプロセスで依存ライブラリがあると、git archiveコマンドでは必要なライブラリが内包されません。

特に難しい話でもありませんので、ビルドスクリプトを書いてみました。参考にして下さい。

まとめ

僕としては、自分で使っている開発環境は自分で拡張するべきだと考えています。

つまり、javascriptが得意な人が使う開発環境はjavascript及びその周辺技術で実装されている方が望ましいと考えています。

そう考えると、javascriptやhtmlを上手く取り扱えるプログラマが利用する開発環境として、AtomBracketsは非常に望ましいものです。

それぞれ、まだベータテスト段階ですけどもうまく成長して素晴らしい開発環境に育って欲しいですね。