VimperatorがFirefox 51で動かなくなり、自分で修正xpiをインストールしたりしてたのだけど、その時、 Vimperatorを代替できるアドオンとして VimFxなるものの存在を知り、それ以降はずっと VimFxを使っている。
追記: 2017-10-21
VimFxもFF57で使えなくなる。参考:
Turn VimFx into a WebExtension
追記ここまで
追記: 2017-10-23
Vimium-FFの使用感想はこちら
追記ここまで
VimFx メモ - クマーなひとときv2←こちらのブログを見て頂ければ、それで事足りる。私の設定も殆ど、ここからのパクりだ。以下、私の戯言を読みたい人はどうぞ。
修正されたvimperator 3.16.0であっても、マルチプロセスウィンドウが有効の状態では、TypeError: 'stopPropagation' called on an object that does not implement interface Event.
というエラーが出てまともに動かない。
マルチプロセスウィンドウ有効化の設定↓
user_pref("extensions.e10sBlocksEnabling", true);
user_pref("extensions.e10sBlockedByAddons", true);
しかし、 VimFxならマルチプロセスウィンドウが有効でも正常に動く。そう VimFxならね。
1 Vimperatorに取って代われるかも知れないアドオン
2 Vimiumとは違うぜ
初め、ノーマルモードに戻ろうとするのが escでなければ上手く動かず、「糞!」と思ったけど、<force><c-[>
とすれば、ちゃんと機能した。
tabduplicate
コマンドないから「タブ複製できねーじゃん」とか思ったら、yt
で可能。history
コマンドないから「履歴開けねーじゃん」とか思ったら、gH
で可能。
タブの選択もg0
、g$
といった形でタブ先頭、タブ最終を選べるし、4gt
とすれば右タブ4個移動なるのでタブ移動も問題なし。そもそもFirefoxの標準ショートカットキーでAlt+1
からAlt+9
(数字は右から数えて何番目のタブか)といった形でタブ移動が可能。
他にも Vimperatorでは標準ではできなかったであろう、 ブラウザ要素の選択(というのか、ブラウザのローケションバー、ツールバーを含む上部というか…)がeb
で可能。ヒントモードでの 重なったマーカー文字を前後を入れ替えがc-space
、s-space
で可能。 要素のコンテキストメニューを開くなんてこともできる(ec
)。
Vimperatorでは、キャレットモードにするために、一度適当な文字を検索して、それからc
を押していたが、 要素にキャレットを置く(v
)で近い要素を選択してのキャレットモードへの移行が一度で行える。
qmark a https://anime.dmkt-sp.jp/animestore/tp_pc
といったクイックマークも
'custom.mode.normal.open_danime_store': 'goa',
[
{
name: 'open_danime_store',
description: 'Dアニメストアを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://anime.dmkt-sp.jp/animestore/tp_pc')
}
],
といったような形(vimfx.addCommand
、vimfx.set
)で可能( 上記のコードをそのまま書いても動かないので注意)。
nnoremap ecc :emenu ツール.Default User Agent.Browsers - Windows.Chrome 45.0 (Windows 10 - 64 bit)<CR>
nnoremap ep :emenu ブックマーク.Pocket のリストを表示<CR>
nnoremap ef :emenu ツール.Web 開発.FireFTP<CR>
といったemenu
コマンドが使えないのは辛いが、ブラウザ要素の選択が可能なことから、
DOM Inspector をインストールし、chrome://browser/content/browser.xul
を開く。
ブラウザのUIを呼び出す
そして、 ツール -> Web開発 -> DOM Inspectorで DOM Inspectorを起動し、 Find a node to insepect by clicking on itでクリックした要素のIDを調べて、以下のように記述すれば、ツールバーをクリックすることも可能となる。他にもノーマルモードやキャレットモードで選択した文字列を使って特定のURLに向けて投げたりといったこともできる。
[
{
name: 'click_toolbar_pocket',
description: 'Click toolbar button [Pocket]'
}, ({vim}) => {
vim.window.document.getElementById('pocket-button').click();
}
],
ツール -> Web開発 -> DOM Inspector
クリックした要素のIDを調べる
私がFirefoxを使い続けている一つの理由は Vimperatorだった。しかし、Firefox 51から VimFxに乗り替えて2ヶ月ぐらいになるが、十分満足している。ヒントモードがある程度動けば、コマンドがそんな使えなくてもいいというライトユーザーなので、 VimFxでやりたいことはほぼやれている。
設定ファイルは VimFx/config-file.md at master · akhodakivskiy/VimFx · GitHubの通り、
user_pref("extensions.VimFx.config_file_directory", "~/.vimfx");
でディレクトリ指定して、そこにusr.js
、frame.js
を置いてそこに記述すればOK。
Share your config file · akhodakivskiy/VimFx Wiki · GitHubというconfig.jsの紹介一覧ページがある。
設定ファイルの読取はgC
。エラーが起きたとき、コンソールで確認できるとあるけど、何故かできなかった。
また、設定ファイルの読み取りに成功したのに、vimfx.set
でのショートカットが動かない場合は?
でショートカット一覧を表示して、設定したショートカットが正しく指定モードに記述されているか確認するといいかも知れない。
ss
といったショートカットを設定しても、s
のショートカットがある場合、途中のショートカットs
が動いてしまうため、ss
のキーの割り当てはできないみたいだ。
絶賛した VimFxだけども、不満もある。アドレスバーにフォーカスした時にWindows以外だとIMEの制御ができない(ヒントモードではIMEを特に意識しなくても動作する)。そして、qmark
、emenu
みたいに簡単にカスタマイズ設定を記述できないこと。設定ファイルの記述が多くなる。
なにより、ただでさえ Vimperatorでも情報が少ないのに、 VimFxとなると、もっと情報が減ってしまう。公式ドキュメントは、かなり充実してるのでちゃんと読み解いていけばかなりのことが解決するのかも知れないが、やはり英語なのと、Firefox、Javascriptの知識がないので辛い…今後もっとVimFxユーザーが増えることを期待したい。
最後に、私のコピペ設定を貼っておく(先に設定ファイルを残してくれた方々感謝)。
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
const gClipboardHelper = Cc['@mozilla.org/widget/clipboardhelper;1']
.getService(Ci.nsIClipboardHelper);
const {Preferences} = Cu.import('resource://gre/modules/Preferences.jsm', {});
const VIMFX_PREFS = {
'prevent_autofocus': true,
'blacklist': 'https://feedly.com/i/latest https://tweetdeck.twitter.com/',
'hints.chars': 'abcdefghijklmnopqrstuvwxyz',
'prev_patterns': 'prev previous back newer ^前(の)?ページ 前.* ← ^<$ ^(<<|≪)$ ^(<|≪) (<|≪)$ ^前(へ|の|ペ) ^戻る prev|previous ^(<<|«)$ ^(<|«) (<|«)$',
'next_patterns': 'next more older ^次(の)?ページ 次.* → ^>$ ^(>>|≫)$ ^(>|≫) (>|≫)$ ^次(へ|の|ペ) ^続き ^(>>|»)$ ^(>|») (>|»)$',
};
const MAPPINGS = {
'mode.normal.copy_current_url': 'yy',
'mode.normal.go_home': 'gh',
'mode.normal.history_back': 'H',
'mode.normal.history_forward': 'L',
'mode.normal.stop': '<c-escape>',
'mode.normal.stop_all': 'a<c-escape>',
'mode.normal.scroll_left': 'h',
'mode.normal.scroll_right': 'l',
'mode.normal.scroll_page_down': '<c-f>',
'mode.normal.scroll_page_up': '<c-b>',
'mode.normal.scroll_half_page_down': '<c-d>',
'mode.normal.scroll_half_page_up': '<c-u>',
'mode.normal.mark_scroll_position': 'mm',
'mode.normal.scroll_to_mark': 'gm',
'mode.normal.tab_new': 'T',
'mode.normal.tab_new_after_current': 't',
'mode.normal.tab_select_previous': 'gT',
'mode.normal.tab_select_next': 'gt',
'mode.normal.tab_select_first_non_pinned': 'g^',
'mode.normal.tab_select_last': 'g$',
'mode.normal.tab_close': 'x',
'mode.normal.tab_restore': 'u',
'mode.normal.tab_restore_list': ',uu',
'mode.normal.follow_previous': '[[',
'mode.normal.follow_next': ']]',
'mode.normal.enter_mode_ignore': 'I',
'mode.normal.quote': 'i',
'mode.normal.esc': '<force><escape> <force><c-[>',
'mode.caret.exit': '<escape> <c-[>',
'mode.hints.exit': '<escape> <c-[>',
'mode.find.exit': '<escape> <enter> <c-[>',
'mode.marks.exit': '<escape> <c-[>',
'custom.mode.normal.click_toolbar_pocket': 'cp',
'custom.mode.normal.copy_as_markdown': 'ym',
'custom.mode.normal.copy_title_and_url': 'yc',
'custom.mode.normal.view_source': 'gf',
'custom.mode.normal.open_danime_store': 'goa',
'custom.mode.normal.open_browser': 'gob',
'custom.mode.normal.open_aws_console': 'goc',
'custom.mode.normal.open_feedly': 'gof',
'custom.mode.normal.open_pocket': 'gop',
'custom.mode.normal.open_simplenote': 'gos',
'custom.mode.normal.start_tweetdeck': 'got',
'custom.mode.normal.open_addons': 'addon',
'custom.mode.normal.zoom_in': 'zi',
'custom.mode.normal.zoom_out': 'zo',
'custom.mode.normal.zoom_reset': 'z0',
'custom.mode.normal.search_selected_text': 'sst',
'custom.mode.normal.selected_google_translate': 'sgt',
'custom.mode.normal.selected_url_open': 'suo',
'custom.mode.caret.search_selected_text': 's',
'custom.mode.caret.selected_google_translate': 't',
'custom.mode.caret.selected_url_open': 'u',
'custom.mode.normal.tab_move_to_index': 'mi',
'custom.mode.normal.goto_tab': 'b',
};
const {commands} = vimfx.modes.normal;
const CUSTOM_COMMANDS = [
[
{
name: 'copy_as_markdown',
description: 'Copy title and url as Markdown',
category: 'location',
order: commands.copy_current_url.order + 2
}, ({vim}) => {
let url = vim.window.gBrowser.selectedBrowser.currentURI.spec;
let title = vim.window.gBrowser.selectedBrowser.contentTitle;
let s = `[${title}](${url})`;
gClipboardHelper.copyString(s);
vim.notify(`Copied to clipboard: ${s}`);
}
],
[
{
name: 'copy_title_and_url',
description: 'Copy title and url',
category: 'location',
order: commands.copy_current_url.order + 1
}, ({vim}) => {
let url = vim.window.gBrowser.selectedBrowser.currentURI.spec;
let title = vim.window.gBrowser.selectedBrowser.contentTitle;
let s = `${title}\n${url}`;
gClipboardHelper.copyString(s);
vim.notify(`Copied to clipboard: ${s}`);
}
],
[
{
name: 'click_toolbar_pocket',
description: 'Click toolbar button [Pocket]'
}, ({vim}) => {
vim.window.document.getElementById('pocket-button').click();
}
],
[
{
name: 'view_source',
description: 'ページのソースを表示',
}, ({vim}) => {
let url = vim.window.gBrowser.selectedBrowser.currentURI.spec;
if ( url.includes('view-source:') ) {
url = url.replace(/^view-source:/, '');
} else {
url = `view-source:${url}`
}
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI(`${url}`)
}
],
[
{
name: 'open_danime_store',
description: 'Dアニメストアを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://anime.dmkt-sp.jp/animestore/tp_pc')
}
],
[
{
name: 'open_aws_console',
description: 'AWSコンソールを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://console.aws.amazon.com/')
}
],
[
{
name: 'start_tweetdeck',
description: 'tweetdeckを始める',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://tweetdeck.twitter.com/')
}
],
[
{
name: 'open_simplenote',
description: 'simplenoteを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://app.simplenote.com/')
}
],
[
{
name: 'open_feedly',
description: 'Feedlyを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://feedly.com/i/latest')
}
],
[
{
name: 'open_pocket',
description: 'Pocketを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('https://getpocket.com/a/')
}
],
[
{
name: 'open_browser',
description: 'Chrome URI Browserを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('chrome://browser/content/browser.xul')
}
],
[
{
name: 'open_addons',
description: 'アドオンを開く',
}, ({vim}) => {
let location = new vim.window.URL(vim.browser.currentURI.spec)
vim.window.gBrowser.loadURI('about:addons')
}
],
[
{
name: 'zoom_reset',
description: 'ズームリセット',
}, ({vim}) => {
vim.window.FullZoom.reset();
}
],
[
{
name: 'zoom_out',
description: 'ズームアウト',
}, ({vim}) => {
vim.window.FullZoom.reduce();
}
],
[
{
name: 'zoom_in',
description: 'ズームイン',
}, ({vim}) => {
vim.window.FullZoom.enlarge();
}
],
[
{
name: 'search_selected_text',
description: 'Search for the selected text',
mode: 'normal',
}, ({vim}) => {
//let {messageManager} = vim.window.gBrowser.selectedBrowser
vimfx.send(vim, 'getSelection', null, selection => {
let inTab = true // Change to 'false' if you'd like to search in current tab.
vim.window.BrowserSearch.loadSearch(selection, inTab)
})
}
],
[
{
name: 'selected_google_translate',
description: '選択文字列をGoogle翻訳',
mode: 'normal',
}, ({vim}) => {
vimfx.send(vim, 'getSelection', null, selection => {
vim.window.switchToTabHavingURI('http://translate.google.co.jp/?source=osdd#auto|auto|'+selection, true)
})
}
],
[
{
name: 'selected_url_open',
description: '選択URLを開く',
mode: 'normal',
}, ({vim}) => {
vimfx.send(vim, 'getSelection', null, selection => {
let url = selection;
if ( !selection.includes('http') ) {
url = 'http://' + selection;
}
vim.window.switchToTabHavingURI(url, true)
})
}
],
[
{
name: 'search_selected_text',
description: 'Search for the selected text',
mode: 'caret',
}, ({vim}) => {
//let {messageManager} = vim.window.gBrowser.selectedBrowser
vimfx.send(vim, 'getSelection', null, selection => {
let inTab = true // Change to 'false' if you'd like to search in current tab.
vim.window.BrowserSearch.loadSearch(selection, inTab)
})
}
],
[
{
name: 'selected_google_translate',
description: '選択文字列をGoogle翻訳',
mode: 'caret',
}, ({vim}) => {
vimfx.send(vim, 'getSelection', null, selection => {
vim.window.switchToTabHavingURI('http://translate.google.co.jp/?source=osdd#auto|auto|'+selection, true)
})
}
],
[
{
name: 'selected_url_open',
description: '選択URLを開く',
mode: 'caret',
}, ({vim}) => {
vimfx.send(vim, 'getSelection', null, selection => {
let url = selection;
if ( !selection.includes('http') ) {
url = 'http://' + selection;
}
vim.window.switchToTabHavingURI(url, true)
})
}
],
[
{
name: 'tab_move_to_inex',
description: 'Move tab to index',
category: 'tabs',
order: commands.tab_move_forward.order + 1,
}, ({vim, count}) => {
if (count === undefined) {
vim.notify('Provide a count')
return
}
let {window} = vim
window.setTimeout(() => {
let {selectedTab} = window.gBrowser
if (selectedTab.pinned) {
vim.notify('Run from a non-pinned tab')
return
}
let newPosition = window.gBrowser._numPinnedTabs + count - 1
window.gBrowser.moveTabTo(selectedTab, newPosition)
}, 0)
}
],
[
{
name: 'goto_tab',
description: 'Goto tab',
category: 'tabs',
}, function(args) {
commands.focus_location_bar.run(args);
args.vim.window.gURLBar.value = '% ';
}
]
];
Object.entries(VIMFX_PREFS).forEach(([name, value]) => {
vimfx.set(name, value);
});
CUSTOM_COMMANDS.forEach(([options, fn]) => {
vimfx.addCommand(options, fn);
});
Object.entries(MAPPINGS).forEach(([cmd, key]) => {
if (!cmd.includes('.')) {
cmd = `mode.normal.${cmd}`;
}
vimfx.set(cmd, key);
});
//sendAsyncMessage('VimFx-config:tabCreated')
vimfx.listen('getSelection', (data, callback) => {
let selection = content.getSelection().toString()
callback(selection)
})
@namespace url(http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul);
#VimFxMarkersContainer .marker {
font-size: 14px !important;
}
/* Make location bar red while in ignore mode */
#main-window[vimfx-mode="ignore"] #urlbar {
background: red !important;
}
statuspanel label.statuspanel-label {
background: linear-gradient(45deg, rgba(22, 22, 22, 0.9), rgba(99, 99, 99, 0.9)) !important;
color: whitesmoke !important;
font-size: 14px !important;
}
#urlbar {
ime-mode: inactive;
}
#TabsToolbar {
counter-reset: tabs-counter;
}
.tab-close-button:not([pinned]) {
visibility: hidden;
display: block;
color: inherit;
counter-increment: tabs-counter;
&::before {
content: counter(tabs-counter);
visibility: visible;
font-weight: normal;
font-style: normal;
}
}
-
● まんが/瀬尾浩史
● デザイン/シオズミタロウ
● 初出 /株式会社アスキー・メディアワークス「 Ubuntu Magazine Japan vol.09 」
● ( http://ubuntu.asciimw.jp/)2012 年 9 月 6 日発行 ↩︎ -
● まんが/瀬尾浩史
● デザイン/シオズミタロウ
● 初出 /株式会社アスキー・メディアワークス「 Ubuntu Magazine Japan vol.09 」
● ( http://ubuntu.asciimw.jp/)2012 年 9 月 6 日発行 ↩︎