hints-ext 更新しました

更新内容は

  • inline style 禁止 対策
  • 面積のないElementが 欠ける件、実質問題なさげなレベルまで改善
  • ラベル再配置(ラベルが重ならないようにする機能)
    • 高速化(10倍くらいになった)
    • ラベルの移動処理変更
  • 細々と微調整

です。

vimperator/hints-ext.js at master · caisui/vimperator · GitHub


inline style 禁止

基本 CSP による inline style禁止 対策です。
また、コンテンツのcssの影響を受けなくなったので、!importが多様されているページでも崩れてしまうことがなくなりました。


CSPによって、ヒントの配置に失敗の図


改善後


ちなみに、test で使った簡便なCSPを吐く http server はこんなです。

https://gist.github.com/caisui/7959322

面積のないElementが 欠ける

hints-ext について - caisuiの日記の件です。
力技で組込んでみましたが、10ms程度しか差異がでなかったので採用しました。



対策前 [パソコン], [PC周辺機器]等の部位が欠けています。


対策後


「解決」でなく「実質問題無い」としているのは、子孫ノードすら面積が無い(あるいは子孫が存在しない)ものは検知できてないからです。



本家では、'HR'と'HT' の 部位も表示されています。

しかし、見えていない Element なので、実質問題無いかなとしています。


従来の方式を望む場合は、

hints.fixRect0 = false;

とすれば、戻ります。


ラベル再配置

アルゴリズム変更しました。




と配置するようになりました。

hints-ext について

現在、特定条件を満たした node が ヒントとして表示できません。
欠落に気付くことは困難なため、使用しないほうが良いと思います。


上記、"Diary"画像のリンクが奇数が表示できていないと思います。
(Windows Xp Windows 8 で確認)


a タグだけ ごまかすなら

style -name=anchor0 * a::after{ display: block; position: absolute; width: 1px; height: 1px; content: "";}

でそれっぽく改善しますが、layoutが壊れるとこがあるかもしれません。

原因

nsIDOMWindowUtils.nodesFromRect の仕様変更により、面積0(height or width 0)の Node が無視されるようになったようです。(多分 24 から 少なくとも 17は 面積0でも 出ている)


hints-ext は、nsIDOMWindowUtils.nodesFromRect を利用することにより、速度を稼いでいます。どうしましょうかね…。

Smooth Scroll

vimperator/smooth-scroll.js at master · caisui/vimperator · GitHub


buffer の Smooth Scroll 化です。

hjkl,gg,G等ほぼ全てが Smooth Scroll になります。
ただし、は無理です。


デフォルトのスクロース時間は、300ms です。
変更したい場合は、

let smooth_scroll_duration=200

で変更できます。


以下変更にあたって、bufferの挙動変更点です。

  • window決め打ち系(g,GG等) の スクロール対象も hjklと同様 caret 位置から遡る
  • frame,iframeも遡り scrollbarを探します。
  • 画面外スクロールバーは無視


これ mozRequestAnimationFrame を使ってみたのですが、
私の環境では60fpsでています。




最後に、問題点です。


他のscroll と干渉(競合?)します。
アニメーション描画の都合上途中で、
他のスクロールが発生しても影響を受けずにそのまま続きを描写します。


どうしても、途中で操作したいなら、
buffer経由
あるいは

plugins.smoothScroll.scrollBy(node, x, y);

plugins.smoothScroll.scrollTo(node, x, y);

を 経由して干渉する必要があります。

hints-ext 更新しました。

hints-ext.js を 更新しました。


https://github.com/caisui/vimperator/blob/master/plugin/hints-ext.js


更新内容はざっくり 以下2点です。

  1. Hint Node が document から 破棄されても、そのまま動くように修正
  2. 高さを持つ ノードを子孫に持つ インラインノードの 表示方法の変更


1 は、document を 読み込み中に HaH を表示すると 動作しなくなることがある問題の改修です。


また、style sheet が 遅延して読み込みまれてがっつり位置がズレても、再配置できるように
redraw 関数を追加しました。(末尾 map の サンプルを参照)


2 は、

<a href="#"><img src="..."/></a>

のような Node 対策です。



となっていたものが



となります。


デフォルトで ON なので、以前のよう(=無効)にしたい場合は、


rc file に

let disable_adj_inline=1

で 無効になります。



最後に、hint の マップ設定例です。hints-ext.js を load後に指定して下さい。

" runtimepath 配下の pluginを全て読み込む
loadplugins

js<<END
(function() {
    // hint の 再描画
    hints.addSimpleMap("<C-l>", function() { this.redraw(); });
    // hint node 重なり除去
    hints.addSimpleMap("<C-S-l>", function() { this.relocation(); } ); 
    // 誤爆防止
    hints.addSimpleMap(["<C-n>", "<C-t>", "<C-f>", "<C-g>"], function() { });
    // インライン ノードの表示方法をトグル
    hints.addSimpleMap("<C-i>", function() { this.toggleInlineAdj(); });
})();
END

vimperator3.6 について

vimperator3.6で e4x を除去 し Template String で 代替するパッチがマージされました。
結果、pluginが影響を受けますので思い付くままに記載したいと思います。


vimperator 3.5で困ってないかたは、 バグの洗い出しや、pluginの 対応が終るまで移行しないほうが良いと思います。
(javascript.options.xml.chrome=true でも軽微ですが、副作用があります)


Template Stringの 正しい仕様は、http://wiki.ecmascript.org/doku.php?id=harmony:quasisを参照下さい。


現段階において、Firefox にTemplate Stringは実装されいません。
vimperator side で 現行javascriptで動くように変換して実行しています。


5行で書かれた Template String は 5行で変換してるので、エラー行番号は似た位置が表示されるはずですが、
記述したそのものが実行されるわけではありませんので plugin 開発においてやっかいな要因になると思います。


以下 私の理解した範囲で実装したものを説明します。

Template String とは

一言で言うと、heredoc っぽい形式で記述できる関数の糖衣構文です。

tag`Literal1${obj1}Literal2${obj2}Literal3`


と記載すると

tag({raw: ["Literal1", "Literal2", "Literal3"],cooked: ["Literal1", "Literal2", "Literal3"]}, [(obj1), (obj2)])


に相当します。
(cooked, raw は、Literal部位のエスケープシーケンスの解釈が変わります。詳しくは、http://wiki.ecmascript.org/doku.php?id=harmony:quasis を参照下さい。)


また、tagは省略することができ、その場合はraw tag 相当が適用されます。
(raw tag は、単純にLiteral と obj を 連結していく tagです)

var name = 'hoge';
var s = `こんにちは${hoge}さん
さようなら${hoge}さん`;

は、

var name = 'hoge';
var s = ("こんにちは" + (hoge) + "さん\n\
さようなら" + (hoge) + "さん");

に相当します。

tag の 自作

関数を作成すれば使えます。

function test(a, b) {
    var raw = a.raw;
    var i, j, s = "";
    for (i = 0, j = b.length; i < j; i++)
        s += raw[i] + "(" + String(b[i]) + ")";
    s += raw[i];
    return s;
}

で 埋め込み変数を括弧で囲うraw tag の 亜種が完成となります。


また、返り値が文字列である必要もありませんので、

function dom(a, b) {
    var raw = a.raw;
    var i, j, s = "";
    // サンプル用の 簡易実装のため文脈依存のエスケープ処理はしてません
    for (i = 0, j = b.length; i < j; i++)
        s += raw[i] + String(b[i]);
    s += raw[i];
    var ps = new DOMParser;
    var doc = ps.parseFromString(s, "text/html");
    var range = document.createRange();
    range.selectNodeContents(doc.body);
    return range.extractContents();
}

var node = dom`<table>
    <tr><td>1</td><td>2</td></tr>
    <tr><td>1</td><td>2</td></tr>
    <tr><td>1</td><td>2</td></tr>
    <tr><td>1</td><td>2</td></tr>
</table>`;

で DOMが生成できます。

plugin について

e4x を 使用しているものは全て動きません。
e4x 固有の文法を使用している場合は、代替案を模索することになります。

以下 変更 例です。

dom の生成
util.xmlToDom(<a href={url}>{text}</a>, document)

util.xmlToDom(xml`<a href=${url}>${text}</a>`, document)
plugin help
var INFO = <plugin name="hoge" version="0.1.0"
    href="xxxx"
    summary="yyy"
    xmlns="http://vimperator.org/namespaces/liberator">
    省略
</plugin>;

var INFO = xml`<plugin name="hoge" version="0.1.0"
    href="xxxx"
    summary="yyy"
    xmlns="http://vimperator.org/namespaces/liberator">
    省略
</plugin>`;
XUL生成
liberator.echo(<datepicker type="grid" xmlns={XUL}/>)

liberator.echo(xml`<datepicker type="grid" xmlns={XUL}/>`)

あるいは、

liberator.echo(xml`<xul:datepicker type="grid"/>`)

用は、xmlns:html と xmlns:xul と xmlns:nsをこっそり定義しちゃってます。

heredoc
node.style.cssText = <![CDATA[
color: red;
border: 1px solid blue;
]]>.toString();

node.style.cssText = `
color: red;
border: 1px solid blue;
`;
e4x の連結

結構差異がでます。(operator + や operator += は できません)

function hoge1(node1, node2) {
    return <pre>{node1 + node2}</pre>
}
function hoge2(array) {
    var x = new XML;
    for (var i = 0, j = array.length; i < j; i++) {
        x += array[i];
    }
    return x;
}

function hoge1(node1, node2) {
    return xml`<pre>${node1}${node2}</pre>;
}
function hoge2(array) {
    var x = xml``;
    for (var i = 0, j = array.length; i < j; i++) {
        x = xml`${x}${array[i]}`;
    }
    return x;
}
その他文法

@xx や .* 等は、DOMParserを使用してdomに変換すれば、似たことができます。

xml という 変数名

使うと xml tag が 使用できません!

変数のxml

template.maybeXML経由だと エスケープされません。

var html = "<a href='#sample'>xxx</a>";
liberator.echo(template.maybeXML(html))

muttatorで行こう!

muttator の 雑感 と独自の マップ一覧 と mode の 状態遷移をまとめました。

雑感

NORMAL mode
  • j/k :[選択 messege の移動]
  • +/- or / :[preview panel の ページスクロール]
  • n : Thunder Bird 標準ショートカットですが、未読ジャンプ * spam folder 無視
  • d : メール削除
  • s : メールの移動
  • ! : spam mark
  • r,f,c : メッセージ作成(返信、転送、新規)


あたりを押さえておけば使えると思います。


preview panel を もっと細かく制御したいなら、
で MESSAGE mode に入ると、vimperator の NORMAL mode に近い操作ができます。
ただし、で 遷移する先は、 MESSAGE mode でなく NORMAL mode のため 注意が必要です。
(HINTS, CARET, VISUAL, SEARCH を 抜けた後に戸惑うことになるかもしれません)

COMPOSE mode(=メッセージ作成)
  • map が少ないので全部覚えちゃいましょう!(一覧は後述)
  • COMPOSE mode で focus が 残ってしまい、未割当 keyで文字入力ができてしまう。
  • 本文でTEXT mode, VISUAL mode, INSERT mode の map が 動かない

focus の 問題は、以下のスクリプトでごまかしています。

js <<CODE
liberator.registerObserver("modeChange", function modeChange([oldMode], [newMode]) {
    if (newMode === modes.COMPOSE) {
        Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager)
            .clearFocus(window);
    }
});
CODE


本文の問題は、放置してますl:)

NORMAL モード に おける固有 map

a メッセージの差出人をアドレス帳に追加
mail adress のみ ("hoge@example.com" のような"xxxxx"の形式じゃない場合 上手くいかない)
,m message mode へ 移行
Thunder Bird に まる投げ
t スレッドを全て選択
d, 選択メッセージを削除
j, 次のメッセージを選択(閉じたスレッド内はスキップ)
k, 前のメッセージを選択(閉じたスレッド内はスキップ)
gj 次のメッセージを選択(閉じたスレッドは展開して選択だと思うけどエラー)
gk 前のメッセージを選択(閉じたスレッドは展開して選択だと思うけどエラー)
J, 次の未読を選択
* spam メールも対象になる
* が登録されているけど focus 移動が優先される
K 前の未読を選択
* spam メールも対象になる
* 同じ差出人の次のメッセージへ移動
# 同じ差出人の前のメッセージへ移動
c 新しメッセージを作成
C 選択メッセージの差出人に対して 新しいメッセージの作成
r 選択メッセージの差出人に返信
R 選択メッセージの差出人と Cc(?)に返信(menu の"全員に返信" に 相当)
f 選択メッセージを転送メッセージする
F 選択メッセージを転送メッセージする(インライン)
preview メッセージを line down
preview メッセージを line up
+ preview メッセージを page down
- preview メッセージを page up
u 元に戻す
やり直す
gm 全アカウントの新着メッセージを受信
gM 現在のアカウントの新着メッセージを受信
o フォルダを指定して移動
s 選択メッセージを 移動
S 選択メッセージを コピー
選択メッセージをアーカイブ
! 選択メッセージを迷惑メールマークをトグル
gi 受信トレイに移動
次のフォルダへ移動
前のフォルダへ移動
次の未読があるフォルダへ移動
前の未読があるフォルドへ移動
za 選択メッセージのスレッドを展開/折り畳む
zc 選択メッセージのスレッドを折り畳む
zo 選択メッセージのスレッドを展開
zr,zR すべてのスレッドを展開
zm,zM すべてのスレッドを折り畳む
menuの[移動(G)]-[進む(F)]に相当
menuの[移動(G)]-[戻る(B)]に相当
gg フォルダの先頭メッセージを選択
G フォルダの最終メッセージを選択
l 1-5: tag 付与 1-5
r: 既読
s: star
T カレントフォルダの未読を既読にする
全ての未読メッセージを既読にする
h ヘッダの表示形式をトグル変更
x html 表示を トグル変更
y y: author
s: subject
#: message id
t: 宛先
r: ccっぽい(未確認)
u: rss feed の url っぽいけど 動かない
p rss message を ブラウザで開く

COMPOSE mode

: 同じみコマンドライン
e editorで開く
y いますぐ送信
Y 後で送信
t To(宛先) field に focus となっているが実際は、field の 先頭 input box に focus
s Subject(件名) に focus
i 本文にfocus
q 閉じる。保存していないときは、確認
Q,ZQ 閉じる。保存の有無は無視

muttator mode 遷移


遷移図は2点注意書きがあります。

  • INSERT や VISUAL は、状態遷移の関係で複数に分けています。
  • COMPOSE mode の INSERT からの TEXT mode への遷移は 省略しています。



以上です!

vimperatorでもifがしたい!

Vimperator Advent Calendar 2012 9日目担当 caisui です。


ローカルパッチで 作成していた if コマンドを pluginに移植してみました。
改変部位が io.source のため 一工夫入れないと使えませんがご了承願います。


https://gist.github.com/4239753#file_cmd_if.js


特徴として

  • 評価するのは、javascript
  • heredoc サポート(最後の行を評価結果として判定)
  • elseif に 変数を引き継ぎする
  • 無いよりまし程度だけど、commandlineでも 動作
  • vim の if と違い 実行しないブロックのコマンドもパースする(heredocの関係で同等にすることは断念しました)


細かい仕様は、
https://gist.github.com/4239753#file_test.vimp
から読みとってください!


以下は、rc晒しです。参考になる部位があれば何よりです。
(ざっくりですがコメントも入れました)

" vim: set ft=vim:

" 以下2行を アンコメントで noplugin 相当でFirefox を 起動
"js liberator.commandLineOptions.noPlugins=true
":finish

" io.source を 上書き
so ~/vimperator/gist/4239753/cmd-if.js

" if が 有効な io.source で 実行
exstr <<RC

" runtimepath 初期化
set runtimepath=~/vimperator/github,~/vimperator/common


" profile name 固有 設定 plugin を 読み込む
execute "so ~/vimperator/" + liberator.profileName + ".vimp"
execute "set runtimepath+=~/vimperator/" + liberator.profileName

" migemo が 有効なら hint の 絞り込みを変更
if window.migemo
    set hintmatching=custom
endif

set hc=hjklasdfgyuiopqwertnmzxcvb
set nohlsearch

set nofocuscontent
set noautocomplete

nnoremap <silent> g# :b#<cr>

" 某plugin map 割り当て
nnoremap <silent> <S-b> :pi tab<CR>
nnoremap <silent> <c-s-j> :pi console<CR>
nnoremap <silent> <c-j> :pi download<CR>
nnoremap <silent> ,b :pi hatenta-bookmark<cr>
nnoremap <silent> ,s :pi noscript<cr>
nnoremap <silent> ,a :pi tab-history<CR>

cnoremap <silent> <C-f> :pi command-mru<cr>

nnoremap / :grep2<space>


" カレンダー表示
command cal :js liberator.echo(<datepicker type="grid" xmlns={XUL}/>)
" MOW で 右クリック有効化
js document.getElementById("liberator-multiline-output").setAttribute("contextmenu", "contentAreaContextMenu")

" 某プラグイン 諸設定
let use_hintchars_ex=2
let use_hints_ext_hinttags=1
let use_hints_ext_extendedhinttags=1
let use_hints_ext_caret="c"
let use_hints_ext_visual="v"
let typescript_path="~/vimperator/lib/typescript/"

" nomenu(menu非表示)状態から menuを出すと1行ずれるので overlay 化
style -name=menu chrome://browser/content/browser.xul <<CSS
#toolbar-menubar[autohide="true"] {
    background-color:transparent!important;
    position:fixed;
    z-index:999999;
    left:1ex;
    top:1ex;
}
#toolbar-menubar[autohide="true"] #main-menubar {
    background-color:rgba(0,0,0,.7)!important;
    padding: 1ex!important;
    border-radius:4px;
}
#toolbar-menubar[autohide="true"] #main-menubar>menu {
    color: white!important;
    border-radius: 2px;
    transition: .5s background;
}
#toolbar-menubar[autohide="true"] #main-menubar>menu[_moz-menuactive="true"] {
    -moz-appearance:none!important;
    background-color: rgba(255,255,255,.5)!important;
}
CSS

noremap <silent> <C-h> :tabh back<CR>
noremap <silent> <C-l> :tabh forward<CR>

" tree style tab 用 設定
if window.TreeStyleTabService
    style   -name=floatbox  chrome://browser/content/browser.xul .liberator-floatbox{z-index: 9999;}
    let mapleader=":tst<space>"

    nmap <silent> ,t <Leader>open<Space>
    nmap <silent> ,T <Leader>

    nmap <silent> zj <Leader>next<cr>
    nmap <silent> zk <Leader>prev<cr>

    nmap <silent> zh <Leader> goto parent<cr>
    nmap <silent> zl <Leader> goto cfirst<cr>
    nmap <silent> zL <Leader> goto clast<cr>
    nmap <silent> zH <Leader> goto root<cr>

    nmap <silent> zK <Leader>move -r<cr>
    nmap <silent> zJ <Leader>move<cr>

    nmap <silent> d  <Leader> bdelete<cr>
    nmap <silent> zd <Leader> delete!<cr>

    nmap <silent> >> <Leader> shift<cr>
    nmap <silent> << <Leader> unshift<cr>

    nmap <silent> gzz <Leader>scroll --pos=center<cr>
    nmap <silent> gzt <Leader>scroll --pos=top<cr>
    nmap <silent> gzb <Leader>scroll --pos=bottom<cr>

    nmap <silent> zn <Leader>goto pnext<cr>
    nmap <silent> zp <Leader>goto pprev<cr>
    nmap <silent> zN <Leader>goto rnext<cr>
    nmap <silent> zP <Leader>goto rprev<cr>

    nmap <silent> gp <Leader>paste<cr>
    unlet mapleader
endif

"ここでplugin 読み込み
loadplugins
" 以下plugin 依存諸設定

if plugins.hintsExt
    hi -append HintExt font-family: Consolas;
    hi HintExt::before opacity: .6;
endif

hi StatusLineSecure -append font-weight: bold;

coffee <<CODE
userContext.__defineGetter__ "doc", () -> content.document
userContext.__defineGetter__ "win", () -> content.window

s = ",t"
map = mappings.get modes.NORMAL, s
if map
    [modes.CARET, modes.VISUAL].forEach (m)->
        mappings.remove m, s
        mappings._user[m].push map
        map.modes.push m

if plugins.hintsExt
    hints.addSimpleMap "<C-l>", () -> this.relocation()
CODE
RC