有馬総一郎のブログ

(彼氏の事情)

2017年04月05日 00:32:26 JST - 8 minute read - Mozilla

Vimperatorに取って代われるかも知れないアドオンVimFx

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で可能。

タブの選択もg0g$といった形でタブ先頭、タブ最終を選べるし、4gtとすれば右タブ4個移動なるのでタブ移動も問題なし。そもそもFirefoxの標準ショートカットキーでAlt+1からAlt+9(数字は右から数えて何番目のタブか)といった形でタブ移動が可能。

他にも Vimperatorでは標準ではできなかったであろう、 ブラウザ要素の選択(というのか、ブラウザのローケションバー、ツールバーを含む上部というか…)がebで可能。ヒントモードでの 重なったマーカー文字を前後を入れ替えc-spaces-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.addCommandvimfx.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を開く。

DOM Inspector
アドオン DOM Inspector

ブラウザUI

ブラウザのUIを呼び出す

そして、 ツール -> Web開発 -> DOM InspectorDOM 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();
  }
],

DOM Inspector

ツール -> Web開発 -> DOM Inspector

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.jsframe.jsを置いてそこに記述すればOK。 Share your config file · akhodakivskiy/VimFx Wiki · GitHubというconfig.jsの紹介一覧ページがある。

設定ファイルの読取はgC。エラーが起きたとき、コンソールで確認できるとあるけど、何故かできなかった。

また、設定ファイルの読み取りに成功したのに、vimfx.setでのショートカットが動かない場合は?でショートカット一覧を表示して、設定したショートカットが正しく指定モードに記述されているか確認するといいかも知れない。

ssといったショートカットを設定しても、sのショートカットがある場合、途中のショートカットsが動いてしまうため、ssのキーの割り当てはできないみたいだ。

絶賛した VimFxだけども、不満もある。アドレスバーにフォーカスした時にWindows以外だとIMEの制御ができない(ヒントモードではIMEを特に意識しなくても動作する)。そして、qmarkemenuみたいに簡単にカスタマイズ設定を記述できないこと。設定ファイルの記述が多くなる。

なにより、ただでさえ 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;
  }
}

  1. ● まんが/瀬尾浩史
    ● デザイン/シオズミタロウ
    ● 初出 /株式会社アスキー・メディアワークス「 Ubuntu Magazine Japan vol.09 」
    ● ( http://ubuntu.asciimw.jp/)2012 年 9 月 6 日発行 ↩︎

  2. ● まんが/瀬尾浩史
    ● デザイン/シオズミタロウ
    ● 初出 /株式会社アスキー・メディアワークス「 Ubuntu Magazine Japan vol.09 」
    ● ( http://ubuntu.asciimw.jp/)2012 年 9 月 6 日発行 ↩︎

Tags: Firefox

動画の表示領域を特定の範囲に切り抜く(Crop) UbuntuでQRコードを読み取る