10:29 PM投稿記事の長さ:天声人語 × 29.1個 くらい

Bracketsの拡張機能を作成して痒い所は自分で掻いてみる

トップイメージ

皆様、いかがお過ごしであろうか? 最近仕事をしている夢ばかり見る野地である。悪夢か。

最近職場でPhpStormを使用するようになり、とりあえずCtrlを押しながら関数名をクリックすると定義元までジャンプする機能に驚愕した。

自分は今まで

  1. Dreamweaver
  2. Brackets
  3. Visual Studio

という作業環境の変遷をたどってきたのだが、なるほど、Twitter上を始め様々なところで一流な方々が褒めちぎる理由も分かるという感想だ。JetBrainsさん、ヤバいっす。

しかしいくら機能に優れているとしてもIDEとしての宿命か、やはり重い……というのも正直なところだ。

そんなわけで、ちょっとしたコードの修正や普通の文章を書く際は未だにBracketsを愛用している。

実質使っていた期間は一番長く、軽快さや拡張の気楽さで、浮気しつつもやっぱり好きなエディタだが、今回はそんなBracketsの拡張機能を作成してみることにした。

Bracketsは内部がWeb View、つまりHTML + CSS + JavascriptでできているアプリなのでWeb開発者が気楽にカスタマイズできるアプリとしての側面がある。

自分も実際に作ってみたが、多少Javascriptの心得があれば簡単に拡張機能が作れたので、是非挑戦してみてはいかがだろうか?

目次

  1. 今回作る拡張内容 現在編集しているペインの色を反転させる
  2. main.jsを用意する
  3. 開発者ツールを使ってBracketsの内部を調べる
  4. 初期化処理を用意する
  5. メインの処理を行う関数を作る
  6. CommandManagerで関数を登録する
  7. メニューに項目を追加する
  8. KeyBindingManagerでショートカットを登録する
  9. package.jsonを作成する
  10. そしてアップロード
  11. まとめ

今回作る拡張内容 現在編集しているペインの色を反転させる

実際プラグインが動作している画面

閲覧者の皆さんはライトUIとダークUI、どちらが好きだろうか?

自分は断然ダークUI使いで、明るい画面を見続けると目が疲れてしまう(このブログが文字を読ませるサイトのセオリーから外れて黒背景なのも、読むのに時間のかかる記事が多いからという理由があったりする)。

そんな筆者でも、作業が煮詰まってくると気分転換にライトUIに変更することもしばしばあり、Bracketsではテーマを変えていた。

しかし、このテーマを切り替える作業、デフォルトではショートカットキーが割り当てられておらず、あんまり数を試していると億劫になってくる。

そこで今回作るのがテーマはそのままに作業中画面を反転色にする拡張機能だ。

Bracketsは上のメニューバー部分以外HTMLで書かれているので、当然CSSが当たるしJavascriptで動的に変更することができる。

つまりユーザーがメニューから項目を選択する or ショートカットキーを押すというイベントを拾い、現在編集中のエディタ部分を括っているタグにstyleを当ててやれば拡張機能が作れるだろう。

CSSとしては、

  .hoge{
    -webkit-filter: invert(100%) hue-rotate(180deg);
  }
  

と書けば.hogeの色が反転する。

fiterにはプレフィックスが必要なので、普通のWebサイト相手にはautoprefixer等を使って様々なプレフィックスを付与する必要があるが、今回の相手はBracketsただ一つなので-webkit-だけでよい。

このCSSを仕込んだ<style><head>に仕込み、イベントが発火したらエディタ部分のタグに.hogeを付けたり外したりするのが今回の拡張機能になる。

main.jsを用意する

まずBracketsを立ち上げ、上部メニューから「ヘルプ」「拡張機能のフォルダーを開く」を選択する(Macの場合表記にブレがあるかもしれないが、似た名称のメニューを選択しよう)。

そうすると文字通り、Bracketsの拡張機能を保存しているフォルダが現れる。

そのフォルダ中、「user」というフォルダの中に、拡張機能名を付けたフォルダを作成しよう(今回は「reversePaneColor」とする)。

Bracketsは拡張機能を認識する際、まず拡張機能のフォルダにあるmain.jsというファイルを読みに行く。

主なBracketsとのやりとりはこの中に書いていくのだが、コードが短く済みそうならこの中にすべての処理を書いてしまっても構わないだろう。

今回も機能部分のコードはすべてmain.jsに書いてしまうことにした。

BabelTypeScriptを使いたい人は設定を済ませた上で、そうでない人は直接、先ほど作った拡張機能のフォルダにmain.jsを作ろう。

さて、これから実際にmain.jsへコードを書くわけだが、Bracketsの拡張機能はJavascriptのモジュールとして定義する。

具体的にはRequireJSが言うところのSimplified CommonJS Wrapper的な書き方をするのだが、ぶっちゃけ理解しなくても大丈夫だ。

まずは何も考えず、以下のコードをmain.jsに貼りつけよう。

  define(function (require, exports, module) {
    "use strict";
    
  });
  

厳格モードを使用する宣言である“use strict”;の下にコードを追加していく。

次にすべきは、Bracketsが用意しているモジュール(というかAPI)のインポートだ。

Bracketsは開発者がメニュバーの操作やショートカットキーのイベント発火に悩まないよう、専用のモジュールを用意してくれている。

今回は

  • 処理を行う関数を登録をするCommandManager
  • ショートカットキーを割り当てるKeyBindingManager
  • 初期化処理を管理するAppInit
  • 上部メニューバーを操作するMenus

の四つを使いたいので、以下のようにmain.jsへコードを追加する。

  define(function (require, exports, module) {
    "use strict";
    
    var CommandManager = brackets.getModule("command/CommandManager"),
    KeyBindingManager = brackets.getModule("command/KeyBindingManager"),
    AppInit = brackets.getModule("utils/AppInit"),
    Menus = brackets.getModule("command/Menus");
  });
  

開発者ツールを使ってBracketsの内部を調べる

必要なモノをインポートしたら今度は処理部分を書いていくわけだが、開発対象がBracketsなのでコードを書き始める前に開発者ツールを開いてBrackets自身の解析から始めてみよう。

開発者ツールは上部メニュー「デバック」「開発者ツールを表示」を選択しても開くが、多くのブラウザと同じくF12でも開く。

中身は英語だが、Chromeで使われている開発者ツールとほぼ同じものなので、そんなに迷うことなくデバックができるだろう。

「Elements」タブの左隣にある矢印をクリックして、Bracketsの任意の部分をクリックすれば「Elements」タブの中で選択した部分のHTMLが指定される。

これを用いて拡張機能で操作したいタグを見つけるのだ。

今回は編集中のエディタ画面(以下ペイン)が対象なので、先に画面分割機能をonにした後開発者ツールでペインをくくるタグを探す。

結果、それぞれid=”first-pane”id=”second-pane”という属性が指定された<div>達を見つけることができるだろう。

ご丁寧にidがふられているので、これを利用して処理を作成できるのが分かった。

初期化処理を用意する

大抵の拡張機能は、最初になにかしらの初期化処理をしたいことが殆どだろう。

今回の拡張機能では、<head>に先述した<style>を挿入するのが目的だ。

用意したモジュールのうち、AppInitは初期化処理のためのオブジェクトであり、appReadyメソッドの第一引数にfunctionを仕込んでやれば、その処理がBracketsが立ち上がったタイミングで実行される。

  var CLASS = 'reverseColor';
  
  AppInit.appReady(function(){
    var style = document.createElement('style');
    style.textContent = '.'+CLASS+'{-webkit-filter: invert(100%) hue-rotate(180deg)}';
    var head = document.getElementsByTagName('head')[0];
    head.appendChild(style);
  });
  

上記のコードをmain.jsに追記してやれば<head>に今回使用するCSSが含まれた<style>を挿入できるだろう。

メインの処理を行う関数を作る

初期化処理が作れたら、今度はイベントごとに実行されるメインの処理、今回の拡張機能で言えば「ペインの色を反転させる」関数を作成しよう。

  var CLASS = 'reverseColor';
  
  function handleReversePaneColor() {
    var pane = document.getElementById('first-pane');
    if(!pane.classList.contains('active-pane')){
      pane = document.getElementById('second-pane');
    }
    
    pane.classList.toggle(CLASS);
  }
  

CommandManagerへ関数を登録する時に関数名が必要になるので、今回はhandleReversePaneColorという名前を付けた。

開発者ツールを使い、現在アクティブなペインには.active-paneというクラスが付くことが判明したので、これを用いてpaneにアクティブなペインを格納。

そして、paneに対してclassListメソッドを用いてreverseColorクラスを着脱する。

これで先ほどhead内に挿入したstyleの中身に書かれてるCSSを実際のペインへあてることができた。

CommandManagerで関数を登録する

関数は用意するだけではなく使わねば意味がないので、Bracketsが用意しているCommandManagerへ関数を登録しよう。

先ほどの関数にはhandleReversePaneColorという名前を付けておいた。

この名前を使いまわしたいところだが、実際にこの関数を使いまわす際には別途IDを設定しなくてはならない。外側が即時関数で括られているように、この関数名はこのモジュール内でのみ有効なので、CommandManagerを通してこの関数を呼び出す時のIDが必要なのだ。

というわけで、変数を新たに作成し、文字列でIDを指定してやる。

  var MY_COMMAND_ID = "reversePaneColor.run";
  

今回はAdobe公式の例に従って「拡張機能名.アクション名」とした。

当然、このID名が被っていたら正常に動作しないので、複数の関数を登録する場合は違う名前を設定しよう。

この変数をmain.jsを用意するで用意したCommandManagerオブジェクトを通して登録する。

  CommandManager.register("ReversePaneColor", MY_COMMAND_ID, handleReversePaneColor);
  

このようにCommandManagerオブジェクトに用意されているregisterメソッドを使用するのだが、引数にはそれぞれ、

第一引数
登録されるアクションをUI上に表示する際の名称。今回は後で「表示」メニューにアクションを追加する際に表示される。
第二引数
先ほど用意したID。
第三引数
実際に登録する関数

を仕込んであげよう。

メニューに項目を追加する

関数の登録が済んだので、このIDを使いまわしてメニュー項目への追加とキーボードショートカットの登録を行おう。

メニューの場合もmain.jsを用意するで用意したMenusオブジェクトを利用して登録をする。

メニューにはデフォルトで

  • 「ファイル」
  • 「編集」
  • 「検索」
  • 「表示」
  • 「ナビゲート」
  • 「デバッグ」
  • 「ヘルプ」

の七つがあるが、「デバッグ」以外の6つには定数が割り当てられており、その定数で目当てのメニューを選択することになる。「デバッグ」は弄られたくない項目なのだろうか。内部のコードを覗いてみても、「デバッグ」のみ操作するあれやこれが用意されていないため、ここにメニューを挿入する時はゴリ押しで開発することになりそうである。

定数は以下の通り。

「ファイル」
FILE_MENU
「編集」
EDIT_MENU
「検索」
FIND_MENU
「表示」
VIEW_MENU
「ナビゲート」
NAVIGATE_MENU
「ヘルプ」
HELP_MENU

今回は「表示」メニューへ項目を挿入したいので、コードは以下のようになる。

  var menu = Menus.getMenu(Menus.AppMenuBar.VIEW_MENU);
  menu.addMenuItem(MY_COMMAND_ID);
  

MenusオブジェクトにあるgetMenuメソッドを用いてmenu変数に「表示」メニューそのものが格納されたオブジェクトをセットする。

そしてそのmenu変数に存在するaddMenuItemメソッドを用い、CommandManagerで関数を登録するで登録したIDを設定してやれば、「表示」メニュー項目にCommandManagerオブジェクトのregisterメソッド第一引数に登録した名前が表示され、選択すると実際に動作するはずだ。

KeyBindingManagerでショートカットを登録する

今度はKeyBindingManagerでキーボードショートカットを設定しよう。

既に使い倒しているとは思うがBracketsには既に膨大な量のショートカットが存在するので、それらショートカットと被らないショートカットを設定する必要がある。

デフォルトで設定されているショートカット一覧を調べつつ、実際に押しやすそうなショートカットを決定しよう。

ショートカットを実際に登録する際、同時押しはを間に挟んで記述する。

例を挙げると、Ctrl+Shift+Alt+Sというショートカットを登録する際は、

“Ctrl-Shift-Alt-S”

という文字列を用意しよう。

この文字列をKeyBindingManageraddBindingメソッド第二引数にセットし、第一引数にはCommandManagerへ登録したIDをセットしてやればショートカットが動作する。

  KeyBindingManager.addBinding(MY_COMMAND_ID, "Alt-3");
  

ただし、Bracketsが動いているOSでショートカットを変える場合にはもうひと手間かける必要がある。

先述のコードはプラットフォーム名を省略した形なので、全てのOSで同じショートカットが働く(余談だが、Ctrlを使用した場合、Macでは自動でCmdキーに置き換わる。Brackets優秀)。

特定のOSでショートカットを上書きする場合には、addBindingメソッドの第二引数を配列に変更しよう。

配列の中身はオブジェクトとし、keyキーにショートカット文字列、platformキーに“Win”“Mac”“Linux”のいずれかをセットすればOKだ(platformを省略した場合は明示的に上書きされていないOSのショートカットとして扱われる)。

  KeyBindingManager.addBinding(MY_COMMAND_ID, [
    {
      key: "Alt-3"
    },
    {
      key: "Cmd-3",
      platform: "Mac"
    }
  ]);
  

package.jsonを作成する

ここまでで拡張機能としての動作部分は完成した。

しかし、実際にBracketsの拡張機能として世界に公開する際には拡張機能自身の自己紹介文的なpackage.jsonが必要になる。

雛形をAdobeが用意してくれているので、このデータを書き換えて用意するのがベストだろう。

name
ズバリ拡張機能の名前だが、作成者名.拡張機能名として設定するのが一般的なようである。なお、nameには小文字しか使用できず、大文字を使用してしまうと後述するアップロードの際に怒られてエラーが発生してしまうので気を付けよう。
title
実際に表示される拡張機能の名前なので、機能が想像しやすくかつカッチョエエ名前を付けよう。
description
拡張機能の説明。頑張って英語で書こう。
homepage
拡張機能の説明ページURL。無ければ空白でもよいが、GitHubに上げてある拡張機能のリポジトリURLを載せるケースが多い。
version
バージョンの数字。特にこだわりがないなら“1.0.0”で良い。
author
作成者の情報。<>内にメールアドレス、()内に自身のホームページを入れてもよい。なお、homepageでGitHubへのリンクを指定すると、この文字列が勝手にGitHubのメンバーページにリンクするようになる。
license
拡張機能が該当するライセンスの文字列を指定する。文字列はSPDXのライセンスIDリスト中、「Identifier」の欄を参照。
engines
オブジェクトを指定し、中身の“brackets”キーに“>=実際に動作確認をしたBracketsのバージョン”を設定する。
i18n
このpackage.jsonの対応言語。対応言語を設定していない言語でも拡張機能は動作するが、指定した言語に翻訳されています、という意味となる。配列で、中身の文字列はISO 639 and ISO 639-2: The Code Lists「639-1」欄を参照。
package-i18n
先述したi18nで設定した国コードごとにdescriptionなどの翻訳を設定する。キーを国コードにし、値をpackage.json自身のように設定すればOK。

以上を踏まえて、今回作成したpackage.jsonが以下になる。

  {
    "name": "go-noji.reverse_pane_color",
    "title": "Reverse Pane Color",
    "description": "This extentsion can reverse the color of a pane choosing now.",
    "homepage": "https://github.com/Go-Noji/ReversePaneColor",
    "version": "1.0.0",
    "author": "Go Noji &lt;gisosyadfe@gmail.com&gt; (http://noji.wpblog.jp/)",
    "license": "MIT",
    "engines": {
      "brackets": ">=1.8.0"
    },
    "i18n": [ "en", "ja"],
    "package-i18n": {
      "ja": {
        "description": "この拡張機能で作業中ペインの色を反転させることができます。"
      }
    }
  }
  

そしてアップロード

ファイルが一通り揃ったらいよいよアップロードなのだが、アップロードにはGitHubのアカウントが必要なので、もし持っていなかったら作っておこう。

ついでにREADME.mdを作ってGitHubのリポジトリを作っておくといいかもしれない。
書き方で参考にさせて頂いたのがコチラの記事

ファイルのアップロードはZIP形式に圧縮してから行うが、既にGitHubにリポジトリを作成している場合はリポジトリのページにある「Clone or download」「Download ZIP」を利用するといいだろう。

リポジトリを作成していない場合でもWindows10環境であればフォルダーを右クリックして「圧縮」から「.zip」で圧縮しても構わない。

ただし、Mac環境を使用している場合はFinderから作成したZIPはエラーになる場合があるため、前述のGitHubを使用するかコマンドラインでの圧縮で回避しよう。

拡張機能のフォルダを圧縮できたらhttps://brackets-registry.aboutweb.com/へとアクセスし、右上の「Sign in via GitHub」でログインした後に出てくる「Upload an extension」をクリックするかZIPをドロップして拡張機能をアップロードする。

無事成功すれば、Bracketsの拡張機能マネージャーから自分のアップロードした拡張機能が確認できるはずだ。

まとめ

長丁場になってしまったが今回のBracketsの拡張機能作成記事、いかがだったろうか?

実際に今回作成した拡張機能のリポジトリはhttps://github.com/Go-Noji/ReversePaneColorで確認できるので、参考にして欲しい。

BracketsはいわゆるIDEと呼ばれるものとは違い、拡張機能で自分好みにカスタマイズしていくテキストエディターだが、肝心の拡張機能が少なければそのメリットも薄くなってしまう。

しかしご存じの通り、Bracketsには膨大な数の拡張機能が存在する。

理由の一つは間違いなく、Bracketsを扱うユーザーそのものが拡張機能を作るためのHTML・CSS・Javascriptを扱うスペシャリストだからだろう。

スペシャリストと書いたが、多少の知識を持ってさえすれば誰でもすぐに拡張機能の作成に取り掛かれる敷居の低さはBracketsの大きな魅力だ。

最近は他アプリの台頭も激しくなり様々な競合ソフトにシェアが分散しているが、いまだに無料かつ軽快な動きで人々を魅了して止まないBrackets。

是非、そんなBracketsの世界で多用されるような素晴らしい拡張機能を作ってみてはいかがだろうか。

なぜなら「こんな機能があったらいいのに」という願望を一番よく知っているのは、他でもないBracketsを使用しているあなたなのだから。

コメントを付ける

入力エリアすべてが必須項目です。

内容をよくご確認の上、送信してください