メインコンテンツまでスキップ

AdobeのBracketsで拡張を書く話

Sato Taichi

少し前に 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](Simple "Hello World" extension#unittestjs).
  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は非常に望ましいものです。

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