Hatena::Groupkeysnail

きすねた(ん)

2010-10-08

開発はじめかた

| 00:22 | 開発はじめかた - きすねた(ん) を含むブックマーク はてなブックマーク - 開発はじめかた - きすねた(ん)

後で書く.

2010-03-16

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

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

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

以下 addUserCommand - Vimperator から, だましだましパクりつつ改変しつつの説明.

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

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

2010-02-20

食堂はじめました

| 12:37 | 食堂はじめました - きすねた(ん) を含むブックマーク はてなブックマーク - 食堂はじめました - きすねた(ん)

ということで id:hogelog さんがやってくれました。 keysnail グループです。

手始めに とりあえず「アレが欲しいこれが欲しいもっともっと欲しい!」リスト。 - hogelogの日記 - keysnailグループ へのお答えというか、口調は自然体で失礼しますが、そんな感じのエントリです。

1.

vimperatorの[ [, ] ]が欲しい。ページ内から適当に「次へ」とか書いてあるリンク探して叩いてくれる機能。誤爆も多いけど特定のサイトでは楽。

これは確かに、一度欲しいなーと思って適当に以下のようなモノを書いた。これだと rel 属性をご丁寧にも指定してくれているサイトでしか働かないので、ほとんど役に立たない。

function followRel(doc, pattern) {
    let link = doc.querySelector(util.format('a[rel~="%s"]', pattern));
    plugins.hok.followLink(link, plugins.hok.CURRENT_TAB);
}

ext.add("follow-next-link", function () { followRel(content.document, "next"); }, "follow next link");
ext.add("follow-prev-link", function () { followRel(content.document, "prev"); }, "follow previous link");

おそらく上記のコードに doc.querySelector で value が "next", "prev" や "次", "前" を含むモノを加えれば、まあまあ動くものが出来るのではないかなと思う。

いざとなったら liberator から拝借してくればよいし。

2.

vimperatorのC-a、C-xも。URL中に含まれる末尾の数字部分をインクリメントしたりデクリメントしたり。

これは以前書いたものが incre-decre-url.js ? GitHub にある。 Tips japanese ? mooz/keysnail Wiki ? GitHub に色々と雑多なものがまとまっている。

3.

vimperatorの quickmark。普段よく開くサイトは登録してあって「g o t」とか叩くだけでhttp://twitter.comが開かれたりしてたので、手をホームポジションに持っていって「A-d t w ↓ ↓ C-h C-h C-h C-h C-h」とかめんどくさい

quickmark は「スクロール位置まで保存してくれる簡易ブックマーク」、という認識であっているのでしょうか?

KeySnail では persist.preserve(aObj, aName) と persist.restore(aName) という関数を使ってオブジェクトをローカルファイル (profD/keysnail/persist/{aName}.js) へ保存しておき、後で復元する事が可能。なのでおそらく一日もあれば実装は可能かと思われる。

おそらく g o というキーへコマンドを割り当てておき t の部分は prompt.reader を使って受けとることになるかと思う。 prompt.reader の onChange を使えば、ユーザが Enter を押さなくても一文字入力した時点で強制的に callback を呼ぶことが可能。この辺りの使い方は HoK の拡張ヒントモードが参考になるかと思う。

prompt.reader(
    {
        message  : "Extended hint mode (Press TAB to see completions): ",
        onChange : function (arg) {
            if (arg.event.keyCode === KeyEvent.DOM_VK_SHIFT ||
                arg.event.keyCode === KeyEvent.DOM_VK_TAB)
                return;

            var current = arg.textbox.value;
            if (current)
                arg.finish();
        },
        collection          : formatActions(actions),
        header              : [M({ja: "キー", en: "Key"}), M({ja: "説明", en: "Description"})],
        style               : ["font-weight:bold;text-align:right;margin-right:2em;", "color:#5100ae;"],
        width               : [40, 60],
        supressRecoverFocus : true,
        callback            : function (aStr) {
            if (aStr !== null)
                doAction(aStr);
            recoverFocus();
        }
    }
);

4.

なんかこうQuitキーのQuit力が弱い。たぶんhookとかかませば良いのだろうけど。Suspendからの復帰にもQuitを使いたい、 prompt表示されてる時にcontentsクリックしてフォーカス持っていった時もQuitで閉じたいなど。

suspend 中は key.suspendKey しか監視をしていないので、 suspend からの復帰はちょっと難しいかもしれない。 KeySnail 内部にもう一つ hook を設ければ良いだけの話なので、実装しても良い。

prompt を閉じる動作は、以下のようなコードで良いと思う。 prompt.finish の第一引数に true を渡すと、キャンセルになる。

hook.addToHook('KeyBoardQuit', function (ev) {
    if (ev.target !== document.getElementById("keysnail-prompt-textbox"))
        prompt.finish(true);
});

5.

Flashに入力食われてるとき表示が無いからちょっと混乱するかも。

Vimperator さすが。これはどうやって実現しているのか気になるところなので、少し調査をしてみたいと思う。

6.

vimperatorプラグインのmarker_reader.js機能が欲しい。ページダウンとかは淡々と長い文章を書いてあるページだとどこまでスクロールしたのかわからなくなるから、ページにマークを付けてスクロールする機能。自分で書いたプラグインだしよく使っていたけど、なんかこう非常にダサいなーと思うアレだったので書き直したい。

Scrollet というプラグインがページのスクロール位置を一時的にマークへ保存してくれる。ただ marker_reader.js とは動作が異なる可能性もある。

marker_reader.js の方で liberator.* な関数をあまり使っていないのであれば、移植はすんなり行くかもしれない。

7.

vimperatorプラグインloginManager.js相当の能力が欲しい。tumblrtwitterなど各種ログイン必要サイトでログアウトしてログインページ開いて入力してアカウント切り替え、とかめんどくさくて死ぬ。

何やら便利そうな響き。複数アカウントがあったときに、それをコマンドラインから一発でやってくれるというものなのだろうか。欲しい!

これも liberator.* な関数をあまり使っていないのであれば移植はすんなりいくかもしれない。 liberator.echoerr とかは display.echoStatusBar 辺りでまかなうとして。

8.

skk.ks.js……

KeySnail を変態アドオンとして世に知らしめる為にも必要だと思いました!

終わりに

Vimperator 使いの方からこういった要望とかアドバイスを頂けると助かりますね。そこから情報などを引き出していけるので。

色々と質問があれば答えていけると思いますので、ご覧の皆さんもぜひ keysnail グループへ加入して日記に書いてみてください。

hogeloghogelog2010/02/21 03:20すばらしい!
割ときすねたんの中がまだあんましわからんのでひどくありがたいです。