主にJavaScriptのコーディング用としてVimを使い始めたのですが、当初夢見ていたようには使いこなせておらず、挫折しそうになりました。使いこなせていない原因を考えてみると、基本的な操作は覚えたものの、それらを組み合わせてチャチャッと編集する方法が瞬時に思い浮かばず、その都度う~んと考え込んでしまっているからだと思います。
そこで、コーディング中によく出てくる場面をお題にして、自分用の練習ドリルを作ることにしました。これを納得のいくスピードでクリアできるようになるまで、ひたすら繰り返そうと思います。その前に、あわよくばベテランVimmerの方々からアドバイスを頂いてもっと改善できないか、と考えて投稿した次第です。
ドリルの解法例は、必ずしも効率的な操作にはなっていません。というより、むしろ全体的に非効率です。なぜなら、各解法は以下のような個人的な事情を踏まえて考えたものだからです。
- 検索コマンドやコマンドラインモードは使わない(自分がまだそのレベルにないから)
- 個人的に苦手な、キーボードの一番上の列にあるキー(
%
とか数字とか)は極力使わない(例えば%
は[{
や]}
で代用する、など) - Vim Golfではないので、手数の短さよりも、多少条件が変わっても使える汎用性を優先する
- 環境に左右されないように、素のVimでできる範囲で
上記の1つめや2つめ、特に2つめは、まずそこを練習しろという話ですが…。
個人的な矯正設定
まずは練習に入る前に、私の操作のクセを矯正する目的で、いくつかの便利なキーを使えないようにしています。
横移動は f
を徹底する
f
や t
で横移動する方法が効率よいと分かってはいるのですが、つい無意識に h
や l
を押しっぱなしにして移動してしまいます。
これを矯正するために、h
と l
での移動を禁止しました。
nnoremaph<nop>nnoremapl<nop>
ついでに、単語単位での移動も禁止しました。
nnoremapw<nop>nnoremapb<nop>nnoremape<nop>nnoremap ge <nop>vnoremapw<nop>vnoremapb<nop>vnoremape<nop>vnoremap ge <nop>
最初の頃は「横移動は全て単語移動でやってしまおう、それが一番シンプルで覚えやすい」と意気込んでいました。ですが、実際にやってみると、プログラムの一行の中には様々な記号が混じっているのでガッツガッツと引っかかってしまい、いっそのことしばらくは使わないことに決めました。
ちなみに大文字での移動はもともと使いこなせていなかったので、そのままです。英文ならともかく、プログラミングでは利用場面も少ないと思います。またカーソルキーは、そもそもカーソルキーを使うのが面倒だという理由でVimに乗り換えたため使っておらず、そのままです。
これでもう f
で移動するしかなくなりました。
(ただ、日本語で書かれた行だと身動きが取れなくなりますが…)
改行は <ESC>o
を徹底する
1回の挿入モードの中で何行もコードを書いてノーマルモードに戻り、最後の1行だけ元に戻そうとして u
したら全部消えた、という失敗を繰り返してしまったので、挿入モード中の <CR>
を禁止することにしました。
inoremap<CR><nop>
なお、挿入モード中にどうしても必要な場合は <C-j>
します。
エスケープは標準の方法だけにする
つい最近まで jj
をエスケープキーに割り当てていましたが、いつもと違う環境で操作した時や、コメント文を日本語で入力した後などにアッとなってしまうため、やめました。
練習ドリル
ここから本題です。
プログラミング中によくある場面をお題として挙げ、その解法を示し、最後に練習用のサンプルコード、という構造になっています。
なお、言い訳がましく繰り返しになりますが、解法はあくまで私個人の低レベルな事情を考慮したものであり、最適な解ではありません。
お題1|変数名を打ち直して変更する|cw{chars}<ESC>
変数名を変更したい、という場面です。
例えば、hoge
という変数がいくつかあって、名前が気に入らないのでそれら全てを piyo
に変えたいような場合です。hoge
の先頭にカーソル位置がある前提で、cwpiyo<ESC>
とします。
練習
開始カーソル位置は、1行目の先頭です。
私は、fh*Ncwpiyo<ESC>n.
で練習しています。
consthoge=1;console.log(hoge);
constpiyo=1;console.log(piyo);
お題2|変数名をプットして変更する|cw<C-r>0<ESC>
変数名を変更したい、という場面です。ただしお題1とは少し条件が異なり、あらかじめ変更後の変数名をヤンクしておいて、それをプットしたいという場合です。
例えば hoge
という変数がいくつかあり、それらをヤンク済みの piyo
に変えたい場合には、hoge
の先頭にカーソル位置がある前提で、cw<C-r>0<ESC>
とします。わざわざ <C-r>0
とするのが面倒ですが、.
で繰り返し可能にするには仕方ありません。
このパターン、私はよく、一般的なエディタのノリで viwp
して次の単語でまた viwp
してアッとなってしまいます。
練習
開始カーソル位置は、1行目の先頭です。
私は、fpyejFh*Ncw<C-r>0<ESC>n.
で練習しています。
constpiyo=1;hoge+=1;console.log(hoge);
constpiyo=1;piyo+=1;console.log(piyo);
お題3|スネークケースからキャメルケースに変更|f_qmxgUlq
スネークケースにしていた変数をキャメルケースに変更したい、という場面です。あまり遭遇しない場面ですが、つい最近あったので。
例えば、hoge_hoge
で先頭にカーソルがある前提で、f_qmxgUlq
とすれば hogeHoge
となります。マクロに登録しているのは、繰り返しを簡単にするためです。私はマクロやマークはなんでもかんでも m
に登録することにしています。
なお、gUl
は ~
としたほうが手っ取り早いですが、~
はめったに使わないキーなので敬遠しています。
練習
開始カーソル位置は、1行目の先頭です。
私は、fh*Nf_qmxgUlq;@myiwncw<C-r>0<ESC>n.
で練習しています。なお、途中から(cw
以降)はお題2と同じ操作です。
consthoge_piyo_fuga=1;hoge_piyo_fuga+=1;console.log(hoge_piyo_fuga);
consthogePiyoFuga=1;hogePiyoFuga+=1;console.log(hogePiyoFuga);
お題4|変数をなにかで囲う|cw{chars}<C-r><C-o>"{chars}<ESC>
変数を、記号や関数などで囲いたい、という場面です。これはよくあるかと思います。
例えば、hoge
を "hoge"
とするような場合、現在のカーソル位置が hoge の先頭にある前提で、cw"<C-r><C-o>""<ESC>
とします。
その後に、別の変数に対しても同じ編集を行いたい場合には .
で繰り返すことができるのですが、もし <C-r><C-o>"
を <C-r>"
と手抜きしていると失敗します。
練習
開始カーソル位置は、1行目の先頭です。
私は、fhcwparseInt(<C-r><C-o>", 10)<ESC>jFp.
で練習しています。
consta=hoge;constb=piyo;
consta=parseInt(hoge,10);constb=parseInt(piyo,10);
お題5|関数全体を選択する|vf{]}
以下のような関数をまるごと選択したい、という場面です。
functioncompare(a,b){returna-b;}
現在のカーソル位置が function の先頭にある前提で、vf{]}
で選択状態にできます。
なお、]}
は %
としたほうが手っ取り早いですが、%
キーは遠いので敬遠しています。
練習
開始カーソル位置は、1行目の先頭です。
私は、fc*Nffvf{]}dnviwpNdj
で練習しています。
constcompare=function(a,b){returna-b;};array1.sort(compare);
array1.sort(function(a,b){returna-b;});
お題6|離れた2つの単語を入れ替える|demmf{char}viwp`mP
「隣り合った」単語を入れ替える方法を私は早い段階で覚えたのですが、そもそもプログラミングでは隣り合った単語の順序が違っていたら文法エラーになるケースが多いのでその状況に出会う機会が少なく、せっかく覚えた方法を活用できていませんでした。
しかし、「離れた」単語同士を入れ替えたい場面ならあります。
例えば以下の変数 hoge と piyo を入れ替えるような場合です。
hoge-piyo;
現在のカーソル位置が hoge の先頭である前提で、demmfpviwp`mP
で入れ替えられます。
練習
開始カーソル位置は、行の先頭です。
私は、fhdemmfpviwp`mP
で練習しています。
consta=hoge-piyo;
consta=piyo-hoge;
お題7|複数行をブロックで囲う|V>O{chars}<ESC>`>o{chars}<ESC>
いくつかの行をif文などのブロックで囲いたい、という場面です。
方法はいくつも考えられますが、個人的には、まずはブロックで囲いたい部分をビジュアルモードで選択することから始めるのが視覚的に分かりやすかったので、それを生かす方法にしました。
練習
開始カーソル位置は、行の先頭です。
私は、jVj>Oif (fuga) {<ESC>`>o}<ESC>
で練習しています。
hoge=1;piyo=1;foo=1;bar=1;
hoge=1;if(fuga){piyo=1;foo=1;}bar=1;
お題8|ifブロックをコピーしたelseを追加する|f{vaBygPI else <ESC>
もともとif文があり、中身が似たようなelseを追加したい、という場面です。
練習
開始カーソル位置は、行の先頭です。
私は、f{vaBygPI else <ESC>j<C-a>
で練習しています。
if(fuga){hoge=0;}
if(fuga){hoge=0;}else{hoge=1;}
お題9|オブジェクトリテラルを縦書き?にする|f,a<C-j><ESC>viBs<C-j><C-r>"<C-j><ESC>
正しい用語が分からないのですが、JavaScriptのオブジェクトリテラルの書き方を、
a={hoge:1,piyo:2};
から
a={hoge:1,piyo:2};
にしたい、という場面です。
上記のような単純な形であれば、f,a<C-j><ESC>viBs<C-j><C-r>"<C-j><ESC>
とします。階層が深い場合でも、この操作の組み合わせでなんとかなります。
練習
開始カーソル位置は、行の先頭です。
私は、f,a<C-j><ESC>;.viBs<C-j><C-r>"<C-j><ESC>
で練習しています。
constuser={name:"hoge",age:20,address:"fuga"};
constuser={name:"hoge",age:20,address:"fuga"};
では、一ヶ月くらい毎日練習してみます。