rcmdnk's blog
Last update

20171118_cvim_200_200

Firefoxの更新によりVimperator難民になってる人も多いと思いますが、 現状Firefoxで使える類似アドオンは単なるショートカットキーの変更程度にしか使えません。

ということで前にちょっと使ってみたGoogle ChromeのcVimをもう一度設定し直して 使ってみようと思っています。

Sponsored Links

cVim

cVim - Chrome ウェブストア

Google Chrome用のVimライクな操作を可能にする拡張機能。

前に使ったときの感想だと、

  • J/Kなどでの上下移動、Hit-a-Hintの機能などがある。
  • qmarksの機能がある。
  • ちゃんとしたコマンドラインモードがある。
  • .vimrcの様な感じで設定が書ける。
  • GitHub Gistを使って設定ファイルの共有が可能。

といった感じ。

最初のキー設定やHit-a-Hintの機能などはFirefoxやChromeにある他の 拡張機能にもあるものですが あとのものがきちんと使えるものは他にはありません。

コマンドラインモードもどきみたいのが付いてるのは結構ありますが、 単にopen/tabopenでURLを開いたり検索をするだけで それだけならアドレスバーにフォーカス当てれば出来ることです。 (tabopenも入力終了時にAlt-Enterとか押すか 事前にControl-Tすればまあ。)

cVimであればopen/tabopen以外にも タブを閉じるquitだったり 設定を変えるsetmapも使えます。 executeという入力をそのままcVimに渡して実行するコマンドや scriptというJavaScriptを実行するコマンドもあります。

このscriptコマンドによってブックマークレットを マップすることも可能です。

一つ足りないな、と思うのはrestartコマンドですが、 多分再起動の様な機能は拡張機能側からだとアクセス出来ないのかと思います。

また、JavaScriptを使った関数を定義することが出来、 それをcallコマンドで呼ぶことが出来ます。

これが出来るのでVimperatorでやっていたことの大概の事は原理的には出来る様になるはずです。

以前ちゃんと見たのが2014年でもう3年もまえなので大分進化していて 当時上手く動かなかった部分とかもちゃんと動いたりしてます。

設定ファイルはGistのURLを指定してSyncして設定を反映させられます。 が、Syncとは名ばかりで実際にはPullというか取ってきて現在の 設定を上書きするだけです。 ローカルからGist側に返す機能はありません。

なので設定を変える時はGistの方を直接編集する必要があります。

Gistをアップデートしてすぐに手動でSyncをしても反映されないことがあるので、 これはGist側の問題なのかもしれませんがアップデートしたりするときにはちょっと確認が必要です。

また、設定にconfigpathを設定するとそのパスにあるファイルの内容を 追加で設定することが出来ます。

ただ、Windows/Macでの共用を考えたりするとGistだけでやった方が便利かな、と思っています。

現在の設定

設定はGist: .cvimrc にありますが 現状は以下の様なものになっています。

名前は.cvimrcになってますが別にどんな名前でも大丈夫です。

.cvimrc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
"Settings
" Smooth scroll at navigation
set smoothscroll
" Check Gist automatically and update settings
set autoupdategist
" Do not focus an input field when a page is loaded
set noautofocus

" Key of <Leader> (Default: "\")
let mapleader = ","
" Characters for Hit-a-Hint (Default: "asdfgqwertzxcvb")
let hintcharacters = "hjklasdfgyuiopqwertnmzxcvb"

" qmarks
let qmark m = ["https://mail.google.com"]
let qmark t = ["https://mail.google.com/tasks/canvas"]
let qmark c = ["https://www.google.com/calendar"]
let qmark l = ["http://www.google.com/language_tools"]
let qmark f = ["http://cloud.feedly.com/#latest"]
let qmark p = ["http://getpocket.com/a/queue/"]
let qmark g = ["http://github.com/rcmdnk"]

" Move
map <C-h> scrollLeft
map <C-j> scrollDown
map <C-k> scrollUp
map <C-l> scrollRight
map <C-u> scrollPageUp
map <C-d> scrollPageDown
map <C-b> scrollFullPageUp
map <C-f> scrollFullPageDown

" Navigation
map d closeTab
map u lastClosedTab
map <C-p> previousTab
" <C-n> is originally "Open new window" in Windows and it cannot be mapped.
" Use <A-n> and swap <C-n> <-> <A-n> by such AutoHotkey.aa
map <C-n> nextTab
map <A-n> nextTab
map <C-i> goForward
map <C-o> goBack
map R reloadTabUncached

" Yank current url
map y yankDocumentUrl

" Yank current selected word and search it
map <C-g> :execute vP<CR>

" Open command line with ;
map ; :

" Disable all mappings in Text boxes
iunmapAll

" pocket.
" Need to get own bookmarklet from https://getpocket.com/add/?ep=1
" If bookmarklet is encoded, need to decode it with something like http://home.kendomo.net/board/decode/decode2.php
map <Leader>p :script<Space>javascript:(function(){var e=function(t,n,r,i,s){var o=[5578755,3226513,3196261,1447352,5754625,1900724,9534081,1439849,9043574,4416713];var i=i||0,u=0,n=n||[],r=r||0,s=s||0;var a={'a':97,'b':98,'c':99,'d':100,'e':101,'f':102,'g':103,'h':104,'i':105,'j':106,'k':107,'l':108,'m':109,'n':110,'o':111,'p':112,'q':113,'r':114,'s':115,'t':116,'u':117,'v':118,'w':119,'x':120,'y':121,'z':122,'A':65,'B':66,'C':67,'D':68,'E':69,'F':70,'G':71,'H':72,'I':73,'J':74,'K':75,'L':76,'M':77,'N':78,'O':79,'P':80,'Q':81,'R':82,'S':83,'T':84,'U':85,'V':86,'W':87,'X':88,'Y':89,'Z':90,'0':48,'1':49,'2':50,'3':51,'4':52,'5':53,'6':54,'7':55,'8':56,'9':57,'\/':47,':':58,'?':63,'=':61,'-':45,'_':95,'&':38,'$':36,'!':33,'.':46};if(!s||s==0){t=o[0]+t}for(var f=0;f<t.length;f++){var l=function(e,t){return a[e[t]]?a[e[t]]:e.charCodeAt(t)}(t,f);if(!l*1)l=3;var c=l*(o[i]+l*o[u%o.length]);n[r]=(n[r]?n[r]+c:c)+s+u;var p=c%(50*1);if(n[p]){var d=n[r];n[r]=n[p];n[p]=d}u+=c;r=r==50?0:r+1;i=i==o.length-1?0:i+1}if(s==237){var v='';for(var f=0;f<n.length;f++){v+=String.fromCharCode(n[f]%(25*1)+97)}o=function(){};return v+'9954c60b5a'}else{return e(u+'',n,r,i,s+1)}};var t=document,n=t.location.href,r=t.title;var i=e(n);var s=t.createElement('script');s.type='text/javascript';s.src='https://getpocket.com/b/r4.js?h='+i+'&u='+encodeURIComponent(n)+'&t='+encodeURIComponent(r);e=i=function(){};var o=t.getElementsByTagName('head')[0]||t.documentElement;o.appendChild(s)})()<CR>

" google translator en/ja
map <Leader>e :script<Space>javascript:var t=((window.getSelection&&window.getSelection())||(document.getSelection&&document.getSelection())||(document.selection&&document.selection.createRange&&document.selection.createRange().text));var e=(document.charset||document.characterSet);if(t!=''){location.href='http://translate.google.com/translate_t?text='+t+'&hl=ja&langpair=auto|en&tbb=1&ie='+e;}else{location.href='http://translate.google.com/translate?u='+escape(location.href)+'&hl=ja&langpair=auto|en&tbb=1&ie='+e;}<CR>
map <Leader>j :script<Space>javascript:var t=((window.getSelection&&window.getSelection())||(document.getSelection&&document.getSelection())||(document.selection&&document.selection.createRange&&document.selection.createRange().text));var e=(document.charset||document.characterSet);if(t!=''){location.href='http://translate.google.com/?text='+t+'&hl=ja&langpair=auto|ja&tbb=1&ie='+e;}else{location.href='http://translate.google.com/translate?u='+encodeURIComponent(location.href)+'&hl=ja&langpair=auto|ja&tbb=1&ie='+e;}<CR>

" copy current url and title in MarkDown style
copyTitleUrl -> {{
  const t = document.title;
  const l = location.href;
  let ret = '['+t+']('+l+')';
  if(l.includes("rcmdnk.com") || l.includes("octopress.dev") || l.includes("en.dev")){
    ret =  '{{
  const t = document.title;
  const l = location.href;
  let ret = '['+t+']('+l+')';
  if(l.includes("rcmdnk.com") || l.includes("octopress.dev") || l.includes("en.dev")){
    ret =  '{% post_card ' + l.replace(/https?:\/\/(rcmdnk.com|octopress.dev|en.dev)/,'') + ' ' + t + ' %}';
  }
  Clipboard.copy(ret);
  Status.setMessage(ret, 2);
}}
map <Leader>i :call copyTitleUrl<CR>

こんな感じ。

設定の説明

以下の2つのページを参考にしながら設定します。

1995eaton/chromium-vim: Vim bindings for Google Chrome.

Custom script examples · 1995eaton/chromium-vim Wiki

set/let

cVimではBooleanな設定項目に関してはVim同様に ONにしたい時はset smoothscroll、 OFFにしたい時はnoを付けてset nosmoothscrollとします。

一方、数字とか文字列を設定する際にはletを使います。

Vimでは環境設定はsetletは変数を代入する時に使うものなので ちょっと感覚的に注意。

特にREADMEにはこれらBooleanのものもそうでないものもごちゃまぜになってるので 書く時にちゃんと=を付けるものがletでそれ以外がsetであることを確かめてください。 (分けて書いたほうが良い。)

取り敢えずはデフォルトのままでも良いと思いますが上の様な設定をしています。

autoupdategistはGist中心に管理するならやっておいた方が良いです。 ただ、どのタイミングで見に行ってるか分からないですが即座に反映される感じではないです。

noautofocusは好みだと思いますが、 Googleの検索ページを開くときならまだしも、 どこのページにも付いてる様な検索欄とかにフォーカスをいきなり当てられると 結構だるいのでnoにしておいた方が個人的には好みです。

入力欄にはGIでフォーカスできます。 (もしくはHit-a-Hintで入力欄を指定。)

hintcharactersはデフォルトでは左手左側から優先的に使われてますが、 まあキーボードの中側から使ってく方が使いやすいかな、と。 Vimperatorの時にこんな感じで使ってた、というのが一番の理由ですが。

もう一つ、barpositionというコマンド入力欄をtop(こっちがデフォルト)にするかbottomにするか という設定がありますが、 最初、Vimperatorっぽくbottomにしてましたが、 左下にはロード中の情報表示やマウスの下のリンクのURLが表示されたりして 場合によってはコマンド入力が見えない状態になったりしてしまうので topにしておいたほうが良いです。

qmarks

ここでMに設定されてるGmailページは

  • GOM: 今のタブで開く。
  • GNM: 新しいタブで開く。
  • GWM: 新しいウィンドウで開く。

という事が出来る様になります。

値は配列で、複数のURLを指定したときには GOMなら最初のURLを今のタブで、 GNMGWM なら全てのURLを新しタブやウィンドウで表示します。

Vimperatorでは一つのURLの指定で、 また設定ではlet無しでqmark m = ...だったのでちょっと注意。 さらにVimperatorではjavascript:...とブックマークレットをそのまま与えると 現在のページでブックマークレットを使う事が出来ましたが cVimでやるとjavascript:...という文字列をGoogleで検索した結果を表示しようとします。

httpで始まるURL以外の場合には検索にかけた結果を返す仕様です。

map

mapの使い方はVim的ですが、 noremapがないので、キーを直接マップしようとするときには そのキーの変更後の動作にマップすることに必ずなるのでちょっと注意。

通常はREADMEにあるMapping nameを直接マップした方が良いです。

上ではいくつかページを動かすものやタブ操作などをマップしています。

一つ素直に上手くいかなかったのが WindowsでControl-Nをマップできないことでした。

追記: 2017/11/19

下に追記していますが、これは拡張機能のページから キーボードショートカットに行き設定することで 可能になります。

追記ここまで

これはFirefoxでも同様の事が起こっていて、 一部の元々設定されているショートカットキーについては 拡張機能などで上書きできない様になっているようです。

上の設定の中ではNだけで、プリントするPや検索のFなんかは 大丈夫でした。

これに関してはChrome側でどうしようも無いっぽかったので Alt-Nにも設定して AutoHotkeyを使って

1
2
3
4
5
6
GroupAdd, Browser, ahk_exe chrome.exe
GroupAdd, Browser, ahk_exe firefox.exe
GroupAdd, Browser, ahk_exe ApplicationFrameHost.exe ; Edge
#IfWinActive, ahk_group Browser
^n::Send, !n
!n::Send, ^n

みたいにしてブラウザ上でAlt-NControl-Nを入れ替えて Control-Nを押すことで次のタブへ、を実現しています。

Macの方は元々Command-Nが新しいウィンドウを開く、なので <C-n>の設定も残しておけば共有で使えます。

ちょっとこれどうしても出来なくてAutoHotkeyとか使うと踏ん切るまで面倒でした。

もともとFirefoxのアドオンだとどれも出来なくて Chromeならこれ解決できるかも、と思ったんですがChromeにも同じような問題があるようです。

:execute

<C-g>のマップの所で:executeを使っていますが、 これでControl-Gを押した時に

  • V Shift-P

したのと同じことになります。 Shift-P はビジュアルモードでは選択した文字列を新しいタブで検索、という機能なので 文字列を選択した状態でControl-G を押すとビジュアルモードに入りそこから 選択した文字れうとぉ新しいタブで検索してくれます。

iunmapAll

入力欄とかでのマッピングを全て無効にする設定です。 ちょっと中途半端なVim動作の設定とかがしてあるので。

imapについては通常のMac的な操作(Emacsライクな)ものが 消されてしまってるようにも見えるので ちょっと自分で改めて設定したほうが良いかもしれません。

:script

map <Leader>pに割り当ててるのは開いているページをPocketに保存するブックマークレットです。

Vimperatorではqmarkにブックマークレットを割り当てる事ができましたが cVimではできずにどうしようか、と思う点でしたがこの方法で使うことが出来ます。

マップ先に:script<Space><BookMarklet>を指定すれば良いだけ。

ただこの際にブックマークレットはエンコードされたものだと上手く動きません。 上のPocketのブックマークレットはPocketにログインすると自分用のコードがとれますが Firefoxとかでブックマークレットを登録すると自動でエンコードされます。 なのでそこから値を取得してscriptに与えると上手くいきません。 その場合は上の設定中に書いてあるような URL エンコード/デコードフォーム 2 とかで適当にデコードします。 ちなみにChromeだとブックマークレットを登録してもエンコードされてない状態で見ることができました。

同様にしてGoogle翻訳を使って選択中の文字列、もしくはページ全体を英語、日本語に翻訳するようなブックマークレットも登録しています。

JavaScriptで関数を作る

Custom script examples の方に例がありますが、

1
2
3
FunctionName -> {{
  ...
}}

という書き方でFunctionNameとう関数をJavaScriptを使って書くことが出来ます。 cVimにどのような組み込み関数があるのかは余り記述がありませんが、少なくとも

  • Status.setMessage(var, time) : time秒間varを表示。
  • Clipboard.copy(var): varをクリップボードへコピー。

という関数があるようです。

これらを使って現在のページのタイトルとURLを取得して MarkDown的な形でクリップボードに入れる関数を作り、 <Leader>iにマップしています。

Vimperatorで出来てcVimで出来ないこと

Vimperatorでは外部プラグインが色々と開発されて、 例えば特定の検索結果を表示中の画面の上にオーバーレイして表示するものなどがありました。

この様な事は簡単には出来ないですが、Vimperatorのプラグインも基本はJavaScriptで書かれたものなので 作ろうと思えば作れると思います。

<C-n>がマップできない問題がありますが(Vimperatorでは出来た)、 今のところ使っているキーではこれだけで 取り敢えずなんとかなったのでOK.

ただこのタブ切り替えを<C-n>/<C-p>で素早くするとたまに プリントフォーマットが立ち上がったりする時があります。 ちょっと様子見ですが、余り気になるようなら<C-p><A-p>とスワップして 間違って押してもプリントフォーマットが立ち上がらないようにするかもしれません。 その場合はMacでもこの2つをスワップさせたほうが良いかもしれません。

後は拡張機能のページや一部のページで効かないこと。 Vimperatorも設定ページなどいくつかのページで動作しなくなることはありましたが、 cVimでもちょっと多くのページで効かない感じです。

特に新しいページで効かないのはちょっと困るところなんですが、 そこはcVimはtabopenで開く新規ページはcVim独自のページを開くようにして その上では操作できる様になっています。

コマンドでrestartがない、というのもありますが、 cVimの設定自体は設定をSaveするとで全てリセットして再度設定を読み込み直し、 みたいな感じになってるみたいなのでcVim自体には再起動の必要なし。 その他もChromeはFirefoxに比べて再起動する必要性が低い感じなので 無くてもそれ程困らないかな、と。

ということで出来ないことはあまり無いかな、という感じです。

キーボードショートカット設定

追記: 2017/11/19

メニューのその他のツール拡張機能に行くと 拡張機能一覧が見れますが、 その一番下にキーボードショートカットというリンクがあります。

これを開くと、

20171118_chromekeyboard.jpg

こんな感じのキーボードショートカット設定が出てきます。

名前的にcVimが一番上に出てきましたが、ショートカットの割当に 対応している拡張機能がいくつか下の方にありました。

他の拡張機能では大概その拡張機能を有効にする、無効にする、といったショートカットだけでしたが、 cVimでは見ての通り色々な設定が用意されています。

ここで、Go to the next tabControl-Nを入れてみると、 cVimの設定項目で設定したのと違い新しいウィンドウを開く を上書きしてタブ移動にすることが出来ました。

さらに、この設定は設定ページや新しいページなど、通常拡張機能では 操作できないページ上でも有効です。

なのでWindowsだけではなく、Macでもこちらで設定したほうが良いです。 また、Control-Nとか以外に設定する場合でも こちらで設定しておけば通常拡張機能が及ばないページでもタブ移動が指定のショートカットキーで出来るので いずれにしろここで設定しておいた方が良いです。

初期値では

  • Close the current tab*: Alt+W
  • Go to the next tab: Alt+K
  • Go to the previous tab: Alt+J

くらいが入ってましたが上の様にタブ移動と、 Control-Wを入力欄での1ワード削除に割り当てることをしました。

WindowsではControl-Wは通常タブを閉じる、ですが 普段のエディタ/コマンドライン操作から 文字を入力中に Control-Wでタブを消してしまうことがあるので それを避けるためにも。

Macでは Command-Wがタブを閉じるなので 間違えて閉じることは無いですが 入力欄でControl-W で1ワード削除が使えるようになるので使う人は設定しておくと良いです。

この設定は通常の入力欄やcVimのコマンドラインモードで有効になりますが、 アドレスバーでの入力では削除は出来ませんでした。 (ただ何もしてないとタブを閉じるのでそれは避けられる。)

この設定で大分便利になると思います。 AutoHotkeyとかで余計な設定をしなくて済む、というのもありますが、 設定ページとかからでもControl-Nで移動できる、というのが かなり大きいです。

他の設定もここに載せてくれれば、と思うのもありますが多分技術的に難しい? (なんでタブ移動は可能なのか、タブを閉じるとかは駄目なのか?)

また、ここで設定できるのはControlなど修飾キーが付いたものだけで、 A単体とかには設定できません。

ここで他の操作系も設定できると設定ページなどでも操作できて便利なのですが その辺にはなにか制限があるのか、上の様な一部の設定項目だけがここにあります。 いずれにしろ単独キーは設定できない様なので ページを閉じるをDにしたり、といったことは出来ないわけですが。

この設定では一番右のドロップダウンメニューでChrome専用グローバルという 値が選べる様になっていますが、 グローバルとすると他のアプリが最上位になった場合でも そのショートカットキーがChromeに送られ操作が行われます。 つまり、Control-N の設定をグローバルにしてしまうと ターミナル状などでControl-Nが使えなくなり 押すとChromeのタブ移動が行われる様な状態になります。 この機能は例えば音楽サイトの再生等を操作できる拡張機能があって その再生、停止をショートカットキーで設定できる様な場合には便利ですが cVimの様なブラウザ自体の操作に関しては他のアプリを邪魔しない様にChrome専用にしておくべきです。

このChromeの中からグローバルなキー設定が出来る、というのはちょっとどうやってるのかなと興味はありますが。

追記ここまで

気になる点

追記: 2017/11/20

どうもMacでopenコマンドから日本語で検索しようとすると、 日本語変換の決定時に押すEnterで即時にGoogleに飛んでしまいます。

IMEとアプリの動作の優先性によるんだと思いますが、 Google日本語入力でもMacの日本語やMicrosoft IMEでも同じでした。

なので日本語で複数単語を含む検索をしようとすると一旦最初の単語だけでGoogleに飛んでから そこで再度入力して検索、みたいな事になってしまいます。

ちょっとつらい。

WindowsだとMicrosoft IMEでもGoogle日本語入力でも変換決定のEnterは効いて、 決定後にもう一度Enterを押せばGoする様に出来ています。

追記ここまで

まとめ

Vimperator亡き後どうするか問題に長く悩みそうでしたが、 cVimが思ってた以上に良くなっていて出来ることが多くなってました。

qmark:scriptコマンドの存在も大きいですが、 特にJavaScriptで色々書けると言うのがホントにすごくて これで実質的になんでも出来る感が強いです。

もう数年続いてる拡張機能なので、他人の設定ファイルなんかも沢山あるでしょうし 参考になるものを見つけられると思います。

ということで、Firefoxは諦めてChromeに乗り換えるかな。。。

Sponsored Links
Sponsored Links

« GNU screen/tmux内でNeovimを使う時に必須な設定 Google Chromeの右上に出るユーザー名を消す方法 »