今更Vim Cheat Sheetを作ってみた

2022年12月13日火曜日

Vim

t f B! P L

この記事は EEIC Advent Calendar 2022 11日目の記事として作成されました。

はじめに

こんにちは。ねこれぽです。突然ですが,12/11はVimの日です。ご存知でしたか?
12/11をアラビア数字で表すとXII XIとなりますね。これを上下で折り返すとV I IVI, すなわちVIMという文字が現れるのがお分かりいただけるでしょうか。ちなみに嘘です。さっき適当に考えました。

Vimの日に因んで,Vimの操作方法をまとめてみようと思います。しかし2022年の年末にVimの操作方法をまとめたところで誰も読まないでしょう。素晴らしき車輪の再発明ですね。

なぜVimを使うのか?

Vimの良いところは色々ありますが,一番のメリットは豊富な移動系だと考えています。VSCodeなどのエディタでは,矢印キーとマウスを用いて移動するのが一般的だと思います。Vimには Normal mode という移動専用のモードが用意されていて,上手に活用することでファイル内を劇的な速度で移動することができます。この Normal mode で使えるたくさんのコマンドをなるべく覚えておきたいな,というのがこのブログを書こうと思ったきっかけです。Vimには他にも Visual mode, バッファとウィンドウ,プラグインなど様々な素晴らしい機能がありますが,このブログでは割愛します。

カーソル移動

Vimは矢印キーを用いても移動できますが,h,j,k,lのキーを用いて移動するのが一般的です。他にも様々な移動方法があります。頑張って全部覚えたいですね。

h,j,k,l – 1文字移動

←, ↓, ↑, → に対応しています。一番基本となる移動操作だと思います。ホームポジションに指を置いたときに押しやすいキーです。

余談ですが,なぜh,j,k,lなのかには様々な説があります。Vimの作者が使用していたADM-3Aという端末にはh,j,k,lに移動キーが割り当てられていたこと(昔の端末には矢印キーがない), Ctrl+H, Ctrl+J がそれぞれ Back Space, Line Feed を表す事実とうまく対応していることなど。ホームポジションといえばh,j,k,lではなくj,k,l,;じゃないのかと思うかもしれませんが,AZERTY配列やQWERTZ配列などlの右隣に;がないキーボード配列でも使えるようにという意味もあります。Dvorak配列とかは知らない。

10h,10j,10k,10l – n文字移動(繰り返し)

10jなら10行下,という風にキーを連打しなくても一発で移動することができます。このようにVimの操作は数字をコマンドの前に打つことで繰り返し行ったり,行数を指定したりできるものが多いです。これをカウンタといいます。

gg,G – 先頭・末尾行

ggで先頭行,Gで末尾行に移動します。大文字のGは Shift+G を表します。Ctrl+Home, Ctrl+End と同じです。

10gg,10G – n行目

gg,Gの前に入力した数字の行に移動します。上の場合は10行目に移動します。これもカウンタの1つですね。

行数関係の操作は

:set number

とコマンドを打つと行数が表示されてやりやすくなります。

0,$,^ – 行頭・行末

0で行頭,$で行末に移動します。^は空白文字(インデント)を除いた行頭に移動します。Home, End と同じです。$はカウンタに対応しています。

gj,gk,g0,g$,g^ – 移動(画面上)

Vimには長い行を折り返して表示する機能があります。このときj,kでは次/前の行にカーソルが移動してしまいますが,gj,gkでは折り返している行内も移動できます。また,g0,g$,g^は折り返し単位で行頭/行末に移動できます。

H,M,L – 先頭・中央・末尾行(画面上)

画面内の先頭行,中央行,末尾行に移動します。High, Middle, Low と覚えています。本当は Home, Middle, Last の意味らしいですが。

w,b,e,ge – 単語移動

wは次の単語の先頭に、bは前の単語の先頭に、eは次の単語の末尾に、geは前の単語の末尾に移動します。Word, Beginning, End, Go back to end といった感じで適当に覚えています。b,eは Ctrl+←, Ctrl+→ と同じです。これらもカウンタに対応しています。

W,B,E,gE – 速い単語移動

上と同じですが、単語の区切り方が空白基準になります。つまりカンマやピリオド、括弧などのついた単語をまとめて1単語として数えてくれるようになります。こちらのほうが速いというふうに適当に覚えています。

fx,Fx,tx,Tx – 1文字検索(行内)

一文字検索コマンドです。fxで行内の文字xを検索し,カーソルの位置から最初に現れるxの位置まで移動します。Fxはその逆で,カーソルの位置までで最後に現れたxの位置に移動します。Find と覚えます。

txは最初に現れるxの位置ではなく,その1つ手前まで移動します。Txは最後に現れたxの次の文字に移動します。Till と覚えます。

これらのコマンドも,カウンタに対応しています。

;,, – 1文字検索(繰り返し)

上で述べた Find コマンド,Till コマンドを繰り返します。;は同じ向きに,,は逆向きに移動します。たとえばfx;;と打つと,行内のxを3回順番に辿って移動します。3fxと打つと3番目のxに直接飛ぶことができます。

;,,やカウンタなど,繰り返しのためのコマンドが豊富に用意されていることはVimの良いところだと思います。プログラミングの基本 “Don’t repeat yourself” を守る素晴らしい思想ですね。

% – 括弧移動

対応する括弧に飛びます。シンプルで便利。

(,),{,} – 文移動・段落移動

(は前の文の先頭に,)は次の文の先頭に移動します。{は前の段落の先頭に,}は次の段落の先頭に移動します。カウンタにも対応しています。

この辺りから,コマンドに小指を多用するようになるのであまり使っていません。というより覚えていません。

+,- – 行移動

カウンタに対応したコマンドで,空白文字を除いた行の先頭に移動します。+が次の行,-が前の行です。

|,_,g_ – 行内移動

|0と同じように行の先頭に,_^と同じように空白文字を除いた行の先頭に,g_$と同じように行の末尾に移動します。これらのコマンドはカウンタに対応しています。たとえば10|はその行の10文字目に移動します。10_はその行から数えて10行目の空白文字を除いた先頭に,10g_はその行から数えて10行目の末尾に移動します。恐らくカウンタが1のときは挙動が変わらないようにしているのだと思います。

gm,gM – 行の中央に移動

gmで画面上でのその行の中央に,gMでその行の中央に移動します。

g;,g, – 過去の変更箇所に移動

Vimではファイルの変更履歴を

:changes

とコマンドを打つことで見ることができます。この変更箇所を新しいものから順番に遡るのがg;コマンドであり,逆に遡った変更箇所を新しいものに向かって進むのがg,コマンドです。

使ったことがありませんが,便利そうなので使えるようにしたいです。カウンタにも対応しています。

10go – nバイト目に移動

goでファイルの先頭に,10goでファイルの10バイト目に移動します。どういう場面で使うんでしょうか? バイナリ?

ma,`a,'a – マーク

ファイル内での位置を記録して,ジャンプできるコマンドです。maでカーソルの位置をaとして記録します。`aでマークaの位置に,'aでマークaのある行の,空白文字を除いた先頭に移動します。マークの一覧を確認したいときは

:marks

とコマンドを打ちます。

いくつか特殊なマークがあるので紹介します。これらのマークは自動で設定されます。たとえば``と打つと,カーソルが移動したとき直前にいた場所へ移動します。

マーク 移動先
`,' ジャンプの直前にいた場所
" 前回の終了時にいた場所
^ 最後に Insert mode を抜けた場所
. 最後に変更した場所

<C-o>,<C-i> – ジャンプリストを移動

これらのコマンドで移動した履歴は保存されており,この履歴をジャンプリストと言います。ジャンプリストは

:jumps

とコマンドを打つことで見ることができます。

<C-o>でジャンプリストを遡り,過去に移動コマンドで移動した場所へカーソルを戻します。<C-i>で戻ったジャンプリストを先に進みます。

スクロール

ここまででカーソル移動系はだいたい網羅できたと思います。続いては画面のスクロールを伴う移動をまとめようと思います。ここから Ctrl を使うコマンドが登場します。Vimでは<C-x>で Ctrl+x を表すことに注意してください。

<C-e>,<C-y> – 1行スクロール

<C-e>で1行下にスクロールし,新しい行を表示します。<C-y>で1行上にスクロールし,古い行を表示します。ファイル上のカーソル位置は変わりません。画面上ではカーソルが行と一緒に移動します。

<C-e>Extra lines と覚えます。<C-y>は気合で覚えます。

<C-f>,<C-b> – 1画面スクロール

<C-f>で1画面下にスクロールし,<C-b>で1画面上にスクロールします。それぞれ Forward, Backward と覚えます。

<C-d>,<C-u> – 半画面スクロール

<C-d>で半画面下にスクロールし,<C-u>で半画面上にスクロールします。それぞれ Down, Up と覚えます。

zt,zz,zb – カーソル位置移動

ファイル上のカーソル位置はそのまま,カーソルのある行を移動します。ztでカーソル行が画面の最上行に,zzで画面の中央行に,zbで画面の最下行になるようにスクロールします。

zt,zbTop, Bottom と覚えます。上で述べた<C-e>,<C-y>などのスクロールコマンドと一緒に使うことが多いです。

z<CR>,z.,z- – カーソル位置移動

<CR>は Enter を意味します。それぞれzt,zz,zbと同じですが,スクロールしたあとカーソルが空白文字を除いた行頭に移動します。

編集

Vimはiを押すことで Insert mode に入り編集することができますが,他にも様々な編集機能が用意されています。

i,I,a,A,o,O – Insert mode

iはカーソルの左側,すなわちカーソルのある文字の手前から Insert mode を開始します。aはカーソルの右側,すなわちカーソルのある文字の直後から Insert mode を開始します。Insert, After などと覚えます。

Iはカーソルのある行の先頭から,Aはカーソルのある行の末尾から Insert mode を開始します。

oはカーソルのある行の下に新しい行を挿入し,その行で Insert mode を開始します。Oはカーソルのある行の上に新しい行を挿入し,その行で Insert mode を開始します。

Insert mode から Normal mode に戻るときには <ESC> を押します。

R – Replace mode

Insert キーを押したときのように上書きモードを開始します。

x,s,S,p,P – カット・ペースト

xでカーソルのある文字を1文字カットします。カットされた文字はレジスタという専用の領域に保存されます。レジスタの解説は複雑なので割愛しますが,とりあえずクリップボードのようなものだと考えれば大丈夫です。

sはカーソルのある文字を1文字カットしたあと,Insert mode に入り挿入を開始します。Substitute と覚えます。

Sはカーソルのある行を丸ごと削除し,挿入を開始します。

pでレジスタの内容をペースト (Paste) します。通常の場合,ペーストする場所はカーソルの直後です。P でペーストした場合はカーソルの手前です。

xpと打つことでカーソルの文字とその次の文字を入れ替えることができます。

r,~ – 1文字置換など

rはカーソルのある文字を1文字置換するコマンドです。rxと打つことでカーソルの文字がxに置換されます。Replace と覚えます。

~はカーソルのある文字の大文字/小文字を変換するコマンドです。

J,gJ – 行の連結

Jでカーソルのある行とその次の行を連結します。gJも同様ですが,空白を入れずに連結します。

== – インデント

カーソルのある行のインデントを自動で揃えることができます。

. – 繰り返し

.は直前の編集コマンドを繰り返すコマンドです。非常に便利です。Vimを使う理由の1つとしてこのコマンドの存在が挙げられます。

たとえば直前に挿入した単語をもう一度挿入する,などということができます。

u,<C-r> – Undo, Redo

uUndo, <C-r>Redo です。

g-,g+ – Undo, Redo (undo-branches)

Vimにはundo-branchesという Undo/Redo 履歴をツリー状に管理する機能があります。便利そうですね。活用していないので何も解説できませんが。

g-,g+undo-branchesを遡るコマンドです。g-が Undo, g+が Redo に対応しています。

高度な編集

Vimにはオペレータとオブジェクトという概念があります。オペレータの後にオブジェクトを指定することで,そのオブジェクトに対して編集を行うことができます。
たとえばdは削除を表すオペレータ,iwはカーソルの下の単語を表すオブジェクトです。このときdiwと打つことでカーソルの下の単語を削除することができます。

オペレータを使った編集を.コマンドによって繰り返すことで,面倒な繰り返し作業を簡単に行うことができます。

オペレータ

以下がオペレータの一覧です。

コマンド 操作
d Delete (削除)
c Change (置換)
y Yank (コピー)
g~ 大文字/小文字の変換
gu,gU 全て小文字/全て大文字
<,> 左/右インデント

dで削除を行った文字列はレジスタに保存されます。
cは削除を行ったあとに Insert mode に入り,編集することができます。
yはヤンクと言って,文字列を削除することなくレジスタに保存できます。

よく使う操作は簡単のため,より短いショートカットが割り当てられています。

dd,cc,yy,Y – 1行削除/1行置換/1行ヤンク

説明の通り,カーソルのある行を丸ごと削除したり,置換したり,コピーしたりするコマンドです。これらのコマンドでレジスタに保存された文字列をpコマンドでペーストする際には,カーソルの直後ではなく次の行に挿入されます。

ccSは同じ動作をします。

D,C – カーソルから行末まで削除/置換

これらのコマンドではカーソルより前は削除を行わず,カーソルの後の文字列のみ削除を行います。

g~~,guu,gUU – 大文字/小文字の1行変換

カーソルのある行をまとめて変換します。

<<,>> – 1行インデント

>>はカーソルのある行を1つ右にインデントします。<<はカーソルのある行を1つ左にインデントします。

同じオペレータを2度打つことで,カーソルのある行全体にオペレータを適用しています。分かりやすいですね。

オブジェクト

簡単のため,オペレータをdとして説明します。適宜他のオペレータに読み替えてください。

モーション

オペレータとカーソル移動を組み合わせることで,カーソル位置から移動先までのテキストに対してオペレータを適用できます。たとえばdjは,カーソルのある行とその次の行を削除します。dfxは,カーソルのある文字から次のxまでのテキストを削除します。

_はカーソルのある行を表します。すなわちd_はカーソルのある行を削除しますが,これはddと同じです。

モーションにカウンタをつけることも可能です。たとえばd3fxはカーソルのある文字から数えて3文字目のxまでを削除します。d3_はカーソル以下3行を削除しますが,これは3ddと同じです。

iw,aw,iW,aW – 単語

diwはカーソルの下の単語を削除します。dawはカーソルの下の単語を隣接する空白ごと削除します。diW,daWは単語の区切り方が空白基準になります。

diwDelete inner word, dawDelete a word と覚えています。awが時々分からなくなるので,inside/around のi/aという覚え方もしています。

is,as,ip,ap – 文章/段落

dis,dasはカーソルのある文章 (Sentence) を,dip,dapはカーソルのある段落 (Paragraph) を削除します。上と同じくiは空白/空行を削除しませんが,aは隣接する空白/空行を削除します。

i(,i),ib,a(,a),ab()で囲まれた部分

カーソルが()で囲まれた部分にある場合,dib()の内部を削除します。dab()ごと削除します。カーソルが()の外にある場合,カーソル位置から最初の()で囲まれた部分に対して操作を行います。Brackets と覚えます。

i{,i},iB,a{,a},aB{}で囲まれた部分

カーソルが{}で囲まれた部分にある場合,dib{}の内部を削除します。dab{}ごと削除します。カーソルが{}の外にある場合,カーソル位置から最初の{}で囲まれた部分に対して操作を行います。

i[,i],i",i`,a[,a],a",a`, etc… – 囲まれた部分

上の例と同様に,これらのコマンドも括弧や引用符に囲まれた部分に対して操作を行います。iは囲み文字を削除しませんが,aは囲み文字ごと削除します。

検索

Vimは/を押すことで検索ができます。専らこれしか使っていませんが,他にも検索機能があるので使えるようにしたいですね。

/,? – 文字列検索

/を打つと Command-line mode に入り,入力した文字列を検索します。たとえば/patternと打って Enter を押すと,/を打った位置より後で最初に登場するpatternにカーソルが移動します。逆に?patternと打って Enter を押すと,?を打った位置より前で最後に登場するpatternにカーソルが移動します。

検索を行うときには

:set hlsearch

とコマンドを打つと検索結果がハイライトされて便利です。

検索には正規表現も使えるのですが,色々と複雑なので割愛します。

n,N – 次/前を検索

/,?で検索した文字列を順番に辿ります。nで順方向,Nで逆方向です。たとえば/で検索を行ったあとにnを押すと,ファイルの末尾に向かって一致する箇所を辿っていきます。一番最後まで行くとファイル先頭から検索するので,循環的に移動できます。Nの場合はファイル先頭に向かって辿っていきます。

繰り返しを避ける素晴らしいコマンドです。もちろんカウンタも使用できます。

*,# – カーソルのある単語を検索

*を押すと,カーソルのある単語をファイルの末尾に向かって検索します。#を押すと,ファイルの先頭に向かって検索します。n,Nで辿ることもできます。

マクロ

まだ使いこなせていない機能です。Vimには.コマンドがあるため,ある程度の変更は繰り返すことができますが,それでも複雑な編集を繰り返すことは難しいです。マクロを使うことで,複雑な操作を簡単に繰り返すことができます。まさに “Don’t repeat yourself” ですね。

qa,q – マクロの記録を開始/終了

qと打ったあとに何か1文字を打つと,マクロの記録が開始されます。たとえばqaと打つと,aという名前のマクロの記録が開始されます。もう一度qを押すと,それまでに打ったキーがマクロに保存され,記録が終了します。

@a,@@ – マクロを再生

@と打ったあとに何か1文字を打つと,記録したマクロが再生されます。たとえば@aと打つと,マクロaが再生されます。@@と打つと,直前に再生したマクロが再生されます。

マクロの使い道

たとえば,次のようなテキストを考えます。

The quick brown fox jumps over the lazy dog.

このテキストに対して,単語をすべて()で囲みたいとします。

(The) (quick) (brown) (fox) (jumps) (over) (the) (lazy) (dog).

大変ですね。しかしマクロを使うことでこの操作は簡単に行うことができます。

まず,先頭のTにカーソルを合わせます。これは0FTなどで可能です。

|The quick brown fox jumps over the lazy dog.

次にqai(<ESC>ea)<ESC>wqと打ちます。これはTの直前に(を挿入し,単語の末尾であるeに飛んでその直後に)を挿入し,次の単語の先頭に飛ぶ,という操作をマクロaに記録するコマンドです。これにより次のような状態になります。

(The) |quick brown fox jumps over the lazy dog.

あとは同じ操作を8回繰り返すだけなので,8@aと打つことで全ての単語を()で囲むことができます。

(The) (quick) (brown) (fox) (jumps) (over) (the) (lazy) (dog).|

おわりに

ここまでお疲れさまでした。Vimのコマンドが思ったより多くて大変でした。このブログを読んでもVimで編集できないのはギャグだと思います。なぜなら保存と終了のコマンドが書いていないからです。:wで保存,:qで終了です。

余談 – HHKBとVim

最近Happy Hacking Keyboard, 通称HHKBというキーボードを購入しました。このキーボードは Ctrl キーが A キーの左隣,通常の CapsLock の位置にあります。僕は今まで CapsLock を特に何もいじることなく放置していたのですが,Vimのコマンドは大文字と小文字で全く違う動きをするので,しばしば CapsLock を押した状態で想定もしないコマンドを打ってしまい苦労していました。 HHKBでは CapsLock を押すことがないので,Vimでの操作ミスが減って非常に快適になりました。Vimとの相性が抜群の素晴らしいキーボードだと思います。

QooQ