Hatena::Groupkeysnail

きすねた(ん)

2010-03-16

たべぞう・はまぞう検索をコマンドシステムで

| 01:46 | たべぞう・はまぞう検索をコマンドシステムで - きすねた(ん) を含むブックマーク はてなブックマーク - たべぞう・はまぞう検索をコマンドシステムで - きすねた(ん)

こういう小物プラグインを呼び出すターミナルみたいなプラグインが欲しいなと思い始めた。

http://keysnail.g.hatena.ne.jp/basyura/20100316/1268748044

ちょっと違うかもしれないけれど, shell.input というコマンドシステムがある. VimVimperator の : を想像されたし.

key.setViewKey(':', function () {
    shell.input();
}, 'Command System');

このシステムでは, 自分でコマンドを追加することが可能. 今回の{たべ, はま}ぞう検索なら, 次のようなところか.

let user = plugins.options["zou_search.user"];

function defineFooZouCommand(names, description, dir) {
    shell.add(names, description,
              function (args, extra) {
                  let words = encodeURIComponent(extra.left);
                  let url = "http://d.hatena.ne.jp/" + user + "/" + dir + "/search?mode=&ie=utf-8&word=" + words;
                  gBrowser.loadOneTab(url, null, null, null, extra.bang);
              },
              {
                  bang      : true,
                  literal   : 0,
                  completer : function (args, extra) {
                      let engines = [util.suggest.ss.getEngineByName("Google")];
                      return completer.fetch.suggest(engines, true)(extra.left || "", extra.whole || "");
                  }
              });
}

defineFooZouCommand(["hama[zou]"], M({ja: "はまぞう検索", en: "hamazou search"}), "hamazou");
defineFooZouCommand(["tabe[zou]"], M({ja: "たべぞう検索", en: "tabezou search"}), "tabezou");

: としてコマンド入力画面を表示させてから, {hama, tabe} と打ち込んでスペースを一つ開け, 検索ワードを入力する.

f:id:mooz:20100317014454p:image

このとき何らかの文字を入力してから TAB を押せば Google サジェストを使って引っ張ってきた候補が表示される.

f:id:mooz:20100317014455p:image

入力が終わったら Enter. また, hamazou! や hama! のように ! を付けると, タブが背面に開かれる. Vim な方々にはお馴染みの bang システム.

いちいち : hamazou TAB などとするのが面倒だ, と言う場合はキーを一つ消費して次のようなことも可能. 初期値を与えて shell.input を呼んでいる.

key.setViewKey('h', function (aEvent, aArg) {
    shell.input("hamazou ");
}, 'hamazou search', true);

また, 組み込みコマンドだけれど僕はこんなことをしている. さっとググれて便利.

key.setViewKey('s', function (ev, arg) {
    shell.input("tabopen google ");
}, 'Google word');

shell.add を使いコマンドを追加するために

| 01:54 | shell.add を使いコマンドを追加するために - きすねた(ん) を含むブックマーク はてなブックマーク - shell.add を使いコマンドを追加するために - きすねた(ん)

コマンドの追加には shell.add を使う. このメソッドは Vimperator の addUserCommand にインタフェースを似せて作っているので, 移民の方々は比較的違和感無く使い始めることが可能かと思う. お世辞にもとっつきやすいとは言えないインタフェースなので, そうでない方々には苦労を強いることとなり少々心苦しい.

以下 no title から, だましだましパクりつつ改変しつつの説明.

Usage

shell.add(names, description, callback, additional, replace);
  1. names(Array) : コマンド名となる文字列の配列
  2. description(String) : コマンドの簡易説明
  3. callback(Function) : 実行される function オブジェクト
  4. additional(Object) : 補完機能やコマンド引数などを格納するオブジェクト (省略可能)
  5. replace(Boolean) : 既に存在するコマンドの場合、置き換えるか否か (省略可能)

以下, 各引数の詳細.

names

以下の様なコマンドを登録すると helloworld, hello, hw の 3 つのコマンド名で実行可能になる.

shell.add(["hello[world]","hw"], "print hello world",
          function () {
              window.alert("Hello World");
          }
         );

コマンド名となる文字列の後部を[]で囲むと省略可能とされて helloworld, hello の 2 つに展開される. ["[hello]world"] や ["hello[worl]d"] のようなものは無効.

description

概要を説明する文. コマンド補完時に使用される. プラグインから使う場合は M({ja: "説明", en: "Description"}) のようにすると良い.

callback

ユーザがコマンドを入力し Enter を押したときに実行される関数. この関数には以下に示す二つの引数が渡される.

  1. args(Array)
  2. extra(Object)

以下に, それぞれの詳細を記す.

args

args には, ユーザの入力したコマンド引数が入る. このとき, ユーザの入力した文字列がスペース区切りでパースされ, args に配列形式で格納されることに注意. 単なる文字列として受け取りたい場合は後述する extra.left を用いる.

extra

extra オブジェクトには, 以下のような値が入る.

  • left
    • ユーザが Enter を押した時点での, カーソル左にある文字列 (コマンド名は消去済み)
  • whole
    • ユーザが Enter を押した時点での, プロンプト上の文字列 (コマンド名は消去済み)
  • bang
    • コマンドに ! が付いていたかどうかの真偽値
  • count

additional で literal : 0 と指定していた場合は, extra.left を用いると良い.

additional

additional には, 以下のようなプロパティを持たせることができる. 細かなコマンドの挙動はここで指定するといって良い.

  • completer
    • タブ補完時に実行され, 補完値を返す function.
  • options
  • argCount
    • コマンド引数の数を定義する文字列
  • bang
    • command! といった ! を許可する場合は true を指定する
  • literal
    • 特にコマンドをパースして欲しくない場合, 単なる文字列を受け取りたい場合, 自分で補完システムを全実装したい場合などは, ここに 0 を指定する

以下に, 各プロパティの詳細を記す.

completer

この completer を使い, コマンドの引数入力時に用いられる補完候補を生成する. 一から書くのは大変なので, 元々用意された completer の力を借りるのが良いだろう.

以下にいくつか組み込み completer の使用例を示す. なお, collection は補完候補が格納された配列 (["hoge", "huga", "hehe"] のようなもの) とする. また, ユーザが現在入力している文字列をクエリと表記する.

なお, literal : 0 とした場合は extra.query の部分を extra.left へ置き換える必要があることに注意されたい. (この場合 extra.query には空文字列が入る)

completer.matcher.header

この completer は, クエリと collection 内の各要素を先頭から比較し, マッチしたものを返す.

function (args, extra) {
    return completer.matcher.header(collection)(extra.query || "");
}

completer.matcher.substring

この completer は, collection からクエリを部分文字列として含む要素を取り出し, 返却する.

function (args, extra) {
    return completer.matcher.substring(collection)(extra.query || "");
}

completer.matcher.migemo

XUL/Migemoインストールされている場合, クエリを用いて Migemo マッチングを collection 内各要素に対して行い, マッチしたものを返す.

function (args, extra) {
    return completer.matcher.migemo(collection)(extra.query || "");
}

XUL/Migemoインストールされていない場合は completer.matcher.substring が代わりに用いられる.

completer.fetch.suggest

現在のクエリ検索エンジンサジェスト機能へ投げ, 返ってきた結果を補完候補として用いる.

function (args, extra) {
    let engines = [util.suggest.ss.getEngineByName("Google")];
    return completer.fetch.suggest(engines, true)(extra.query || "");
}

options

以下のような配列を指定する. 各行が一つのオプションに対応する.

[
 [[オプション名], オプションタイプ, バリデータ or null, オプションの補完候補],
 [[オプション名], オプションタイプ, バリデータ or null, オプションの補完候補]
]

オプション名には, オプションの名前が格納された配列を指定する. ex) ["-foo", "-f"]

オプションタイプには, 以下のような値が使用可能.

shell.option.ANY 何でも OK. オプション値に文字列があればオプションの値になるし, 無ければ null となる
shell.option.NOARG オプション値が指定されないことを期待するタイプ/ 解析結果のメンバーに入るが値は null
shell.option.BOOL オプション値に true / false を期待し, 値は真偽値となる
shell.option.STRING オプション値に任意の文字列を期待する
shell.option.INT オプション値が数値であることを期待する (小数点を入れても無視される)
shell.option.FLOAT オプション値が数値であることを期待する (OPTION_INT と違い, 小数点 OK)
shell.option.LIST オプション値が "," 区切りのリスト (", " とスペースをいれてはいけない) であることを期待し, 値は配列となる

バリデータは現在入力中のオプション値を受け取り, その値が valid かどうかを真偽値で返す関数. 通常は null を設定しておけば問題ないだろう.

オプションの補完候補には, ["value1", "value2", "value3"] といった配列を指定する.

以上を踏まえ, オプション例を以下に示す.

[
    [["-fullscreen", "-f"], shell.option.BOOL, null, ["true", "false"]],
    [["-language"], shell.option.STRING, null, ["perl", "ruby"]],
    [["-speed"], shell.option.INT],
    [["-acceleration"], shell.option.FLOAT],
    [["-accessories"], shell.option.LIST, null, ["foo", "bar"]],
    [["-other"], shell.option.ANY]
];

argCount

そのコマンドがいくつ引数を受けとることができるかを指定する. なお, オプション引数に含まれない.

"0" arguments 無し
"1" arguments の数がきっちりと 1 つ
"2" arguments の数がきっちりと 2 つ
"n" arguments の数がきっちりと n 個
"+" arguments の数が 1 個以上
"*" arguments の数が 0 個以上
"?" arguments の数が 0 個か 1 個

replace

既にそのコマンドが存在しているときに上書きするか否かの真偽値. 通常は特に必要ない.