Humanity

Edit the world by your favorite way

これであなたも節約上手!キーボード上のキーを最大限活用できる人のN個の習慣とは?

ばよえ〜ん(訳:Vim Advent Calendar 2012への18回目の投稿です)
この記事はVim Advent Calendar 2012の159日目の記事になります。
158日目は@ujihisaさんでVimConf#0でした。

という記事があったので、似たような記事になりますがマッピングについての私見を書いてみようと思います。

序文

キーボード上のキーは有限です。
新たにキーを割り当てようとしても、

  1. 使いやすいキーは他の機能にすでに割り当てられている
  2. Vimのデフォルトの機能にすでに割り当てられている

ということが起こり得ます。


そもそもVimはファンクションキーなどを除くほとんどのキーに何らかの機能がデフォルトで割り当てられています。
新しくマッピングを割り当てる場合は、Vimデフォルトの機能は使わないか、
他で代用可能なキーを見つけだすことで新たな機能に割り当てることができます。


本記事ではそういった代用可能なキーをいくつか紹介し、さらにそういったキーを作り出していくための節約術を紹介していきます。*2


sを節約する

sを何かのプラグインのprefixキーとして使っている人は割とよく見かける気がします。
代わりのキーとしては私はclを使っています。
vcでもいいでしょう。

Sを節約する

Sはccと等価です。
そのためSを潰してもいいですが、Shiftキーが必要なためprefixキーとしてはあまり適していないように思います。

x、Xを節約する

x→sと同じく、dlまたはvdで代用可能です。
X→dhで代用可能です。

;と,を節約する

;と,はデフォルトではf、F、t、T*3などによる前回の検索を繰り返すためのキーです。
これは/や?*4に対するnとNに似ています。
つまり[fFtT]コマンドは[/?]コマンドに対する、検索対象が1文字のみの場合のショートカットだと考えるのです。
[fFtT]コマンドでの検索の場合にも[nN]コマンドが使えるようにすると、うまく;キーと,キーを節約することができます。

設定

以下の設定を.vimrcに追加します。*5

" Like builtin getchar() but returns string always.
" and do inputsave()/inputrestore() before/after getchar().
function! s:getchar_safe(...)
  let c = s:input_helper('getchar', a:000)
  return type(c) == type("") ? c : nr2char(c)
endfunction

" Like builtin getchar() but
" do inputsave()/inputrestore() before/after input().
function! s:input_safe(...)
    return s:input_helper('input', a:000)
endfunction

" Do inputsave()/inputrestore() before/after calling a:funcname.
function! s:input_helper(funcname, args)
    let success = 0
    if inputsave() !=# success
        throw 'inputsave() failed'
    endif
    try
        return call(a:funcname, a:args)
    finally
        if inputrestore() !=# success
            throw 'inputrestore() failed'
        endif
    endtry
endfunction


nnoremap <expr><silent> f '/\V'.<SID>getchar_safe()."\<CR>"
nnoremap <expr><silent> F '?\V'.<SID>getchar_safe()."\<CR>"
nnoremap <expr><silent> t '/.\ze\V'.<SID>getchar_safe()."\<CR>"
nnoremap <expr><silent> T '?\V'.<SID>getchar_safe().'\v\zs.'."\<CR>"

fなどを押す度に(デフォルトの[fFtT]コマンドはハイライトされないのに)ハイライトされてうっとうしいと感じる方は、先ほどの設定の末尾4行を以下のように変更すると良いでしょう。

Map -expr -silent [n] f <SID>search_char('/\V%s'."\<CR>:nohlsearch\<CR>")
Map -expr -silent [n] F <SID>search_char('?\V%s'."\<CR>:nohlsearch\<CR>")
Map -expr -silent [n] t <SID>search_char('/.\ze\V%s'."\<CR>:nohlsearch\<CR>")
Map -expr -silent [n] T <SID>search_char('?\V%s\v\zs.'."\<CR>:nohlsearch\<CR>")

function! s:search_char(cmdfmt)
    let char = s:Vital.getchar_safe()
    return char ==# "\<Esc>" ? '' : printf(a:cmdfmt, char)
endfunction
思わぬメリット

上記の設定は思わぬメリットを引き起こします。
[fFtT]コマンドは同じ行にある文字しか検索しないのに対し、
上記の設定は異なる行にある文字にも移動できます。

f、F、t、Tを節約する

「;と,を節約する」の設定の代わりに「[fFtT]コマンドは[/?]コマンドのショートカットと考えればいいなら、[/?]コマンドで全て実現できるし[fFtT]コマンドいらなくね?」と考えるのが自然でしょう。


w、e、bを節約する

webではないです。
もちろん

  • w(前方の単語の最初に移動)
  • e(前方の単語の最後に移動)
  • b(後方の単語の最初に移動)

です。

ちなみに「後方の単語の最後に移動」するにはgeというマッピングがあります。
geだけ2文字なのを見ても分かる通り、何か中途半端です。
「wordなんでwに割り当てるか。eはendね。bはback。あれ?なんか機能足らなくね?」
みたいな印象をgeには受けます。
w、e、bの3つがいかにキー配置を意識していないかに気付いたあなたは、
いっそこれらのキーを分かりやすいキーに配置したくなるのではないでしょうか。

設定

以下の設定は「w→」「e→」「b→」「ge→」のようにマッピングします。
hjklの延長で覚えられるため、直観的&キーを節約できるので慣れれば良いのではないでしょうか。
「メタキーを使うのはいいの?」と聞かれそうですが、CtrlはAの左に配置していればさほど押しにくいキーではありません。

nnoremap <C-h> b
nnoremap <C-l> w
nnoremap <S-h> ge
nnoremap <S-l> e

#、*を節約する

w、e、bだと左右だけの動きですが、
マッピングしたことで「じゃあ上下移動も」となるのは自然な考えでしょう。
このように既存のマッピングの動作に似たマッピングにすることで
覚えやすいだけではなく既存のマッピングの動作などとの組み合わせから発想が得られます。
良いマッピングとはこのようにさらなる発展が期待できるようなマッピングです。
textobj + operatorなども良いマッピングの例と言えるでしょう。
キーワードは組み合わせです。


閑話休題
へのマッピング例としては、それぞれ#、*などをマッピングしてみるといいのではないでしょうか。
これによって、で現在のカーソル下の単語間をジャンプする事が可能になります。

nnoremap <C-j> *
nnoremap <C-k> #

#、*コマンドも[/?]コマンドに対するショートカットと考えるとこの考えは自然です。
なぜなら、[/?]コマンドは検索の為のキーではなく移動のためのキーだからです。
[/?]コマンドがあれば同じバッファ内であればどこへでも移動できます。


ブラウザのページ内検索などについても同じ事が言えます。
検索とは本質的に移動です。

2文字以上のマッピング

これまで代用できるキーボード上のキーを紹介してきました。
これらのキー1文字に割り当ててもいいですが、
2文字以上のマッピングのprefixキーとしても使用可能です。
そのため、1キー空く事でかなり多くのマッピングを作成可能になります。

まとめ

せっかくなので紹介したキーをまとめてみました。

キー 代わりのキー
s cl、vc
S cc
x dl、vd
X dh
f、F、t、T / + 設定
w、e、b 設定
#、* 設定

最後に

  1. 節約可能なマッピングを見つけだす
  2. 押しやすく覚えやすい良いマッピングを考える

のは別の能力だと思います。
1は最適化の能力、2は発想の能力です。

ちゃんと既存のキーの機能を意識して割り当てられれば良いですが、たまに「えっ、そのキー潰しちゃうの?」と思うような キーを潰しているのを見かけます(出典:vimrc読書会にて)。

vimでキーマッピングする際に考えたほうがいいこと

他人のvimrcを見ると段々とこの2つの視点が養われていくと思います。
ぜひvimrc読書会に参加してはいかがでしょうか(宣伝)。*6

*1:こっちは後で気付いた

*2:あえて胡散臭さを強調

*3:以下「[fFtT]コマンド」と表記

*4:以下「[/?]コマンド」と表記

*5:ちなみに「s:getchar_safe()」はvital.vimからコピペした関数です(コミットしたの自分ですが)。

*6:と言いながら最近自分はあまり参加していないですが...