Bracketsの拡張機能は、JavaScriptとHTML/CSSを理解していれば気軽に作成することができます。また、jQueryやMustacheなどのライブラリを標準で使えるため、JavaScript開発の学習としても役立つかもしれません。本稿ではBracketsのモジュールの使い方をベースに、拡張機能の開発手法を解説していきます。
Bracketsは2013年9月20日にリリースのSprint31 Mac版をベースに解説をします。下記リンクよりBracketsをダウンロードしてインストールしてください。
まずはデバッグコンソールにhello worldを表示
拡張機能開発においてのコンソールの使い方を学ぶために、helloworldをコンソールに表示する拡張機能を作成します。「ヘルプ>拡張機能のフォルダーを開く」を選択するとextensionsフォルダが開かれます。このフォルダ内のuserフォルダに拡張機能のファイル一式のフォルダを入れることで拡張機能が有効になります。自作の拡張機能を作成したら、Edge Codeでも同じ手順でフォルダに入れることでEdge Codeでも使用できるようになります。
まずはこのuserフォルダにhelloworldフォルダを作成し、main.jsとして下記のコードを書きます。上部のコメントはJSLint向けの設定です。既存の配布されているプラグインの多くは、JSLintのチェックをクリアしています。拡張機能を開発する際はお作法として記述しておきます。
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ /*global define, brackets, window, $, Mustache */ define(function (require, exports, module) { "use strict"; console.log('hello world'); });
以上のコードをhelloworldフォルダに保存できたら、「デバッグ>開発者ツールを表示」を選択します。この段階でGoogle Chromeを見ると、専用のコンソールが立ち上がり、hello worldと表示されていることが確認できます。以後この手順をベースに拡張機能ごとにフォルダを作り、main.jsを用意して作っていきます。
コードを修正してもコンソールの内容が変わらない場合は、キャッシュの可能性がありますので、コンソールパネル右下の設定アイコンからSettingsパネルを表示し、Disable cache(while DevTools is open)にチェックを入れます。
カスタムメニューからコマンドを実行できるようにする
先ほどのコンソールにログを表示するサンプルは、Brackets/Edge Codeの起動時に実行されるのみですので、今度は任意のタイミングでメニューから実行してアラートパネルを表示してみましょう。ここでの工程が拡張機能開発の基本構成となりますので、しっかり抑えておきます。このセクションではhelloworldと同様に今度は拡張機能フォルダにcustom_menuというフォルダを作成して進めていきます。
手順は下記の5つで、シンプルです。
- 利用するBracketsのモジュールを読み込む
- 拡張機能固有のIDを定義する
- 実行するコマンドの関数を定義する
- 関数をコマンドに登録する
- カスタムメニューを追加する
1. 利用するBracketsのモジュールを読み込む
コマンドの管理とメニューへの追加を行うために、CommandManagerとMenusというモジュールを読み込みます。
var CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus");
後ほどモーダルダイアログを使ったサンプルも作成しますが、この手順で必要なモジュールを追加して開発していきます。
2. 拡張機能固有のIDを定義する
自作の拡張機能をメニューに追加するために固有のIDを付与します。IDが重複してしまうと他の拡張メニューとバッティングしてしまう可能性があるので、配布をする前提であればドメインなどで名前空間を定義して、他の拡張機能と重複しないようにIDを定義します。
var COMMAND_ID = "jp.codezine.commands.hello_world"; var MENU_ID = "jp.codezine.custom_menu";
3. 実行するコマンドの関数を定義する
コマンドとして登録するための関数を定義します。通常の関数定義です。ここでは実行したらアラートを出すための関数を定義します。
function helloworld() { alert('hello world'); }
4. 関数をコマンドに登録する
3で定義した関数をCommandManagerモジュールに登録します。
CommandManager.register("Hello world", COMMAND_ID, helloworld);
5. カスタムメニューを追加する
Menuモジュールを利用して、自作拡張機能向けのメニューを追加します。addMenuで上部メニューが追加され、addMenuItemで4で登録したコマンドをメニューに追加ですることができます。
var menu = Menus.addMenu("CustomMenu", MENU_ID); menu.addMenuItem(COMMAND_ID);
addMenuで追加するメニューの位置は第3,4引数で指定することができます。例えばヘルプメニューの手前に追加したい場合は下記のように記述します。
var menu = Menus.addMenu("CustomMenu", MENU_ID, Menus.BEFORE, Menus.AppMenuBar.HELP_MENU);
また、追加したメニューにショートカットを割り当てるには、addMenuItemの第二引数に下記のように文字列を渡します。Ctrlは、Macではcommandに該当します。
menu.addMenuItem(COMMAND_ID, "Ctrl-Alt-Shift-H");
Bracketsを再起動すると、下図のようにメニューが追加されたことを確認できます。
メニューを選択するとアラートが表示されました。
以下は、このセクションのコードの全体像です。
define(function (require, exports, module) { "use strict"; /* * 利用するBracketsのコンポーネントを読み込む */ var CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus"); /* * @constant {string} * 各拡張機能固有のIDを付与する */ var COMMAND_ID = "jp.codezine.commands.hello_world"; var MENU_ID = "jp.codezine.custom_menu"; /* * メニューから実行するコマンドを定義 */ function helloworld() { alert('hello world'); } /* * Commandを登録 */ CommandManager.register("Hello world", COMMAND_ID, helloworld); /* * Custom menu */ //var menu = Menus.addMenu("CustomMenu", MENU_ID); var menu = Menus.addMenu("CustomMenu", MENU_ID, Menus.BEFORE, Menus.AppMenuBar.HELP_MENU); menu.addMenuItem(COMMAND_ID, "Ctrl-Alt-Shift-H"); });
モーダルダイアログを表示する
BracketsとEdge Codeの特徴の一つとしてダイアログパネルがOSのパネルではなく、アプリ内でHTMLでできたモーダルダイアログで一元化されている点が挙げられます。環境設定や保存先の指定など、ダイアログ形式の機能を作るためのモーダルダイアログを生成するコードについて見ていきます。下図のようなダイアログを表示する機能を作成します。このセクションではsimple_dialogというフォルダに機能を作成していきます。
モーダルダイアログは、JavaScriptテンプレートエンジンMustacheを利用した外部HTMLテンプレートファイルで管理し、ダイアログを表示するコマンドを実行する際に変数を渡して利用します。
- Mustacheの詳細は公式サイトをご覧ください。
前セクションのカスタムメニューサンプルの工程に加え、ダイアログの表示までに必要な手順は下記の3つです。COMMAND_IDは適宜変更してください。
- モーダルダイアログテンプレートファイルを用意する
- ダイアログに必要なモジュールとテンプレートファイルを読み込む
- ダイアログを表示するコマンドを定義する
1. モーダルダイアログテンプレートファイルを用意する
Mustacheを利用したHTMLテンプレートを用意します。モーダルダイアログのデザインはスタイルシートで定まっているので、下記のようなスタイルを適用しておきます。変数化しておきたい箇所は{{変数名}}としておき、表示する際に変数を受け取ります。Stringsモジュールは各言語に対応した文字列の定数を提供してくれます。Strings.CLOSEとしておくと、日本語環境下で「閉じる」と表示されます。
<div class="modal"> <div class="modal-header"> <h1 class="dialog-title">{{DIALOG_INFORMATION.TITLE}}</h1> </div> <div class="modal-body"> <p>{{DIALOG_INFORMATION.MESSAGE}}</p> </div> <div class="modal-footer"> <button class="dialog-button btn primary" data-button-id="close">{{Strings.CLOSE}}</button> </div> </div>
HTMLテンプレートはhtmlContentというフォルダを作成して格納するルールとします。上記のコードをhtmlContent/simple_dialog.htmlとして保存します。
2. ダイアログに必要なモジュールとテンプレートファイルを読み込む
Dialogsモジュールと文字列定数モジュールを読み込み、1で定義したファイルをmain.jsで読み込みます。
var CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus"), Dialogs = brackets.getModule("widgets/Dialogs"), Strings = brackets.getModule("strings"), SimpleDialogTemplate = require("text!htmlContent/simple_dialog.html");
3. ダイアログを表示するコマンドを定義する
まずはMustacheに渡す変数をまとめたオブジェクトを定義します。後ほどこれをMustache.renderメソッドに渡します。
var DIALOG_INFORMATION = { TITLE: 'はじめてのダイアログ', MESSAGE: 'わーい' };
実際にダイアログを表示するためのコマンドを定義します。Dialogs.showModalDialogUsingTemplateに2で読み込んだテンプレートを渡すことで表示されます。
function showDialog() { // Mustacheに渡すオブジェクト var context = { Strings: Strings, DIALOG_INFORMATION: DIALOG_INFORMATION }; // ダイアログを表示 var dialog = Dialogs.showModalDialogUsingTemplate(Mustache.render(SimpleDialogTemplate, context)); }
前セクションで追加したCustomMenuにSimple dialogのメニューを追加したいと思いますが、同じMENU_IDで登録されている場合バッティングしてしまいます。今回はCustomMenuがすでに存在している想定で、Menus.getMenuメソッドを使って取得できなかった場合のみaddMenuをコールするように記述します。
var menu = Menus.getMenu(MENU_ID); if (!menu) { menu = Menus.addMenu('CustomMenu', MENU_ID, Menus.BEFORE, Menus.AppMenuBar.HELP_MENU); }
ここまでの工程で、カスタムメニューからダイアログを表示することができました。
サイドのツールバーにボタンを追加する
前セクションまでの工程で、コマンドをメニューに追加できるようになりました。次は下図のように、サイドのツールバーにコマンドのショートカットアイコンを追加する方法を見ていきます。よく使うコマンドは、このツールバーに登録しておくと便利です。
- このセクションのサンプルはこちらです。
サイドバーにボタンを追加する工程は下記の5つです。
- ボタン用のアイコンを用意する
- ボタン用のHTMLテンプレートとCSSを用意する
- テンプレートと外部CSSを読み込むためのモジュールを読み込む
- 読み込んだHTMLをjQueryオブジェクトにする
- ボタンを追加する
1. ボタン用のアイコンを用意する
ここではiconmonstrからハートのアイコンを使用することにします。24x24のサイズでimg/toolbar-icon.svgとして保存します。アイコンのカラーは#bbbbbbにしておくと他のアイコンと同じ色になります。
2. ボタン用のHTMLテンプレートとCSSを用意する
ボタンを表示するためのaタグを1つ用意します。ボタンの画像はCSSの背景画像で指定します。titleはボタンにマウスをホバーした際に表示されるツールチップのために指定します。下記のコードをhtmlContent/custom_toolbar.htmlとして保存します。
<a href="#" id="custom-toolbar" title="Show dialog">
上記のHTMLに対してCSSを適用してボタン化します。下記のコードをstyle/style.cssとして保存します。1で用意したアイコンファイルを背景画像で指定します。
#custom-toolbar { background: no-repeat url("../img/toolbar-icon.svg"); display: inline-block; width: 24px; height: 24px; }
3. テンプレートと外部CSSを読み込むためのモジュールを読み込む
外部CSSを読み込むためにExtensionUtilsというモジュールを読み込みます。また、ダイアログのテンプレートと同様に1のテンプレートをrequireメソッドで読み込みます。今回の読み込みのコードは全体で下記のようになります。
var CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus"), ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), Dialogs = brackets.getModule("widgets/Dialogs"), Strings = brackets.getModule("strings"), CustomToolbarHtml = require("text!htmlContent/custom_toolbar.html"), SimpleDialogTemplate = require("text!htmlContent/simple_dialog.html");
4. 読み込んだHTMLをjQueryオブジェクトにする
Bracketsの開発ではjQueryが利用できます。2で読み込んだテンプレートをjQueryオブジェクト化します。Bracketsの拡張機能開発のコード規約として、jQueryオブジェクトを入れる変数には$をつけることが推奨されていますのでそのようにします。
var $toolbarIcon = $(CustomToolbarHtml);
5. ボタンを追加する
サイドのツールバーは#main-toolbar .buttonsというノードになっているので、$toolbarIconをappendToで追加します。
$toolbarIcon.appendTo("#main-toolbar .buttons"); $toolbarIcon.on('click', showDialog);
最後にExtensionUtils.loadStyleSheetを使用して、2で用意したCSSを読み込みます。
ExtensionUtils.loadStyleSheet(module, "style/style.css");
ここまでの工程でツールバーにボタンが追加され、ボタンからダイアログを表示することができました。
コンテキストメニューから選択テキストを操作する
少し具体的な機能として、選択したテキストを変換するサンプルを作ってみたいと思います。選択したテキストがHEXカラーだった場合に、コンテキストメニューからコマンドを呼び出してCSS書式のRGBに変換する機能を作ってみましょう。
- このセクションのサンプルはこちらです。
現在のエディタからテキストを取得するため、EditorManagerというモジュールを読み込みます。今回読み込むモジュールは下記の3つです。
var CommandManager = brackets.getModule("command/CommandManager"), Menus = brackets.getModule("command/Menus"), EditorManager = brackets.getModule("editor/EditorManager");
次に、HEX値をCSSのRGB書式に変換するコマンドの関数を定義します。
function hexToRGB() { // 選択したテキストを取得 var selection = EditorManager.getFocusedEditor().getSelectedText(); // #を取りのぞいた文字列を取得 var hex = selection.charAt(0) === "#" ? selection.substring(1, 7) : selection; // RGB値に変換 var R = parseInt(hex.substring(0, 2), 16); var G = parseInt(hex.substring(2, 4), 16); var B = parseInt(hex.substring(4, 6), 16); // 正しいRGB値に変換されていたら選択テキストを置き換える if (!isNaN(R) && !isNaN(G) && !isNaN(B)) { EditorManager.getFocusedEditor()._codeMirror.replaceSelection('rgb(' + R + ', ' + G + ', ' + B + ')'); } }
最後に、コマンドをコンテキストメニューに追加します。通常のメニューに追加する書式とほぼ変わりません。下記のように記述すると、コンテキストメニューにHex to RGBというメニューが追加されます。
CommandManager.register('Hex to RGB', COMMAND_ID, hexToRGB); var contextMenu = Menus.getContextMenu(Menus.ContextMenuIds.EDITOR_MENU); contextMenu.addMenuItem(COMMAND_ID);
早速使って変換してみましょう。
変換されましたね。簡単なサンプルですが、JavaScriptで好きな機能を作れる可能性を感じることができました。Edge Codeでも試してみてみましょう。
拡張機能マネージャーに表示される情報を用意する
拡張機能作成の仕上げとして、拡張機能マネージャーの一覧に表示される情報の定義をpackage.jsonとしてファイルを用意します。構成は下記のような形になります。nameとversionは必須です。nameは固有のものになるようにします。
{ "name": "製作者の名前.拡張機能の名前", "title": "拡張機能の名前を入れます。", "description": "拡張機能の説明を入れます。", "homepage": "基本的にはgithubのレポジトリのURLを入れます。", "version": "拡張機能のバージョンを入れます。", "author": "製作者の連絡先をいれます。", "license": "ライセンス形態を入れます。MITなど。", "engines": { "brackets": "利用可能なBracketsのSprintバージョンを入れます。>=0.30.0など。" } }
拡張機能フォルダに上記のpackage.jsonを入れると、下図のようにパネルに指定した情報が表示されるようになります。
Bracketsの拡張機能向けpackage.jsonの詳細は、下記のリンクで確認できます。
JavaScriptで開発できるメリット
いかがでしたでしょうか。このほかにも多言語対応や、ローカルファイルの読み取り書き込みなど、さまざまな機能が使えますが、本稿では拡張機能の基本的な機能の作成方法を紹介しました。
Bracketsのモジュールなどに関するドキュメントはまだあまり多くありませんが、これまで触れてきたとおりBracketsとEdge CodeはHTMLベースで作られているため、エディタ自体のコードを見ながら開発手法を学ぶことができます。
前回紹介した拡張機能のソースコードや、エディタ自体のソースコードを読み解くことで、実現したい機能の作り方のヒントにたどりつけると思います。JavaScriptの開発スタイルを学ぶための環境としても適していますね。本稿がJavaScript学習や拡張機能開発のきっかけになれば幸いです。