徒然技術日記

学生プログラマです.chaika の開発をしていました.

プログラムからFirefoxのScratchpadでファイルを開き、適切にシンタックスハイライトをするには

Firefox のアドオンにおいて、AtomSublime Text のように plain text な設定ファイルをメニューから開けるようにするにはどのような方法が考えられるでしょうか。

nsIFile#launch

XPCOM を使って任意のファイルを開くためにまず思いつくのは、nsIFile#launch を使う方法です。

developer.mozilla.org

しかし、これは(おそらく)OS に処理を丸投げしているだけなため、状況や環境によっては問題が生じます。例えば、JSONXML を開きたい場合、それらがテキストエディタに関連付けられていない環境では、ウェブブラウザで開かれてしまったり、最悪該当アプリケーションがないというエラーが出てしまったりします。

nsIFile#reveal

同じく nsIFile から、reveal メソッドを使うという方法も考えられます。これは、ファイルに対して用いるとそのファイルを内包しているフォルダを開くという動作になります(Mac における Reveal in Finder のような機能)。

launch を使うよりはまだマシですが、結局 JSONXML の取り扱いに関する知識をユーザーに求めるという点においてあまりよい方法とは言えないでしょう。

nsIProcess

nsIProcess を利用して、エディタを指定したうえでファイルを開くという方法もあります。

developer.mozilla.org

この方法では、Mac/Windows/Linuxでデフォルトのエディタを変えなければならず、設定が複雑になるという欠点があります。

しかし、設定画面を作ることでユーザーが使い慣れたエディタを指定できるようにするなどの機能を提供できるという利点もあるため、ファイルをエディタで開くという動作が多くあるようなアドオンでは検討に値するかもしれません。

Scratchpad

Firefox には Scratchpad という CodeMirror ベースのエディタが内蔵されています。

developer.mozilla.org

しかしながら、開発者がどのように Scratchpad を使ったらよいかについての解説は MDN に書かれているものの、プログラムからどのように操作できるのかという点についてはドキュメントが全く存在しません。せっかく内蔵しているのにもったいない…

というわけで、本体のコードを読み解くことで判明した利用方法は以下の通りです。

let fileToOpen = ...; // nsIFile
let { ScratchpadManager } = Cu.import('resource:///modules/devtools/scratchpad-manager.jsm', {});
let win = ScratchpadManager.openScratchpad();

win.addEventListener('load', () => {
    win.Scratchpad.addObserver({
        onReady: () => {
            win.Scratchpad.removeObserver(this);
            win.Scratchpad.importFromFile(fileToOpen, false, () => {
                win.Scratchpad.editor.setMode({ name: 'xml' });
            });
        }
    });
});

まず、resource:///modules/devtools/scratchpad-manager.jsm にて定義されている ScratchpadManager を読み込みます。

そして、openScratchpad メソッドにより Scratchpad を開きます。このメソッドaState という Optional な引数をとり、初期化時のパラメータを渡すことができるようです。おそらく、Firefox の起動時に前回 Scratchpad で開いていたファイルを復元するために使われているのだと思われます。しかし、ファイル名、ファイルの内容(テキスト)、実行コンテキスト(Content or Browser)を渡せるのみであり、今回の用途には適しませんでした。

ウィンドウの読み込みを待った後は、エディタの初期化を待つ必要があります。これには Scratchpad.addObserver を用います。初期化が完了すると onReady が呼ばれます。

最後に、ファイルを読み込むためには Scratchpad.importFromFile(aFile, aSilent, aCallback) を用います。aFile は読み込むファイルを、aSilent は読み込み失敗時にエラーを表示するかどうかを、aCallback は読み込みが完了した時に呼ばれるコールバックを指定します。

JavaScript を読み込む分には aCallback を無指定にして読みこめば完了ですが、(Scratchpad は JavaScript 専用エディタのようにできているにも関わらず)他のタイプのファイルを読み込むことも可能であり、その場合はシンタックスハイライトを適切に設定しなくてはいけません。具体的には上の win.Scratchpad.editor.setMode({ name: 'xml' }); の部分がそれです。

エディタの設定は Scratchpad.editor(実体は devtools/sourceeditor/editor.js)からいろいろできるっぽく(あまり調べていない)、いじってみるとなにかおもしろいことができそうな雰囲気がします。ちなみに、editor.js には xml というモードは定義されていませんが、指定してみたら動作しました :)

まとめ

  • テキストファイルを開くなら nsIProcess か Scratchpad
  • Scratchpad を外部からいろいろやるとおもしろそう