はじめに
私は普段のコーディングやテキスト編集にはもっぱらVimを使っています。新しい開発環境を入れる必要があるときも、まずVimバインディングを探します。
Vimはエディタとして非常に生産性が高いと思っていますが、できることが多すぎるので、利用頻度の高い操作は勝手に手が覚える、そうでない操作は忘れていって定着しない、という状態になっています。考えずにできることに限定されていくんですよね。
たまに棚卸しをしないときっと無駄な力技をしているよなあ、ということで、この機会に見直してみました。
手元の環境のVimバージョン
私が普段利用しているPCはMacbookで、現時点でのVimのバージョンは以下の通りです。
本記事は、この環境を前提にしています。
$ vim --version
VIM - Vi IMproved 9.0 (2022 Jun 28, compiled Aug 17 2022 21:20:29)
Vimが手放せない理由
Vimが手放せないと感じる理由は、何と言っても、テキストエディタ内における移動や編集にかかる時間が短いことです。これは、操作を行うために必要なキー入力の簡易さによるもので、カーソルの移動やコピペなどに特化したノーマルモードにより実現されている部分が大きいと思っています。
ノーマルモードでは、例えば以下のようなキー入力でカーソルを移動します。
キー入力 | モーション |
---|---|
l | 右に1文字移動 |
w | 単語の終わりまで移動 |
$ | 行末に移動 |
G | ファイル末尾に移動 |
'a | マーク「a」をつけた行まで移動 |
また、ノーマルモードにおけるテキスト編集は、以下の要素から構成されます。
- オペレータ:操作内容
- モーション:操作対象
- カウント:実行回数(未指定時は1)
これにより、例えば、削除のオペレータ「d」と上に挙げたモーションとを併せて、以下のキーストロークで削除操作を行うことができます。
キー入力 | 効果 |
---|---|
d5l | カーソル位置から右5文字目までを削除 |
3dw | カーソル位置から単語の終わりまで削除を3回実行(→「d3w」と同じ) |
d$ | カーソル位置から行末まで削除 |
dG | カーソル位置からファイル末尾まで削除 |
d'a | カーソル位置からマーク「a」を付けた行まで削除 |
削除のdelete「d」と同様に、コピーはyankの「y」、貼り付けはpasteの「p」で同じように指定して実行できます。(貼り付けにはモーション指定はありません)
このようにまとまった量の移動やコピペが瞬時にでき、モーションのキー入力のバリエーションを覚えるほど、操作範囲を指定するバリエーションも増えます。
この他に、コピペのバッファを複数持てる名前付きレジスタ、複雑な操作を繰り返すためのマクロ、置換をはじめとして強力な操作を実行できるコマンドモードもテキスト編集の生産性に大きく寄与していると感じますが、今回学んだ下記の操作の内容とは関係性が薄いため、触れずにおきます。
もっと早く知っておきたかった操作
今回、棚卸しの意味でいくつかのサイトを参照し、Vimの操作をおさらいしました。
以下、ノーマルモードを中心に、今まで使えていなかった操作を記載します。実用性が高そうなので今後活用していこうと思います。
オブジェクト
ノーマルモードにおける操作対象を指定する際に、モーションではなくオブジェクトを指定できることを知りました。これはめちゃめちゃ便利なやつ…!
キー入力 | 対象オブジェクト |
---|---|
iw | カーソル位置の単語 |
i" | カーソル位置もしくは次の"〜"で囲まれる内容 |
i{ | カーソル位置もしくは次の{〜}で囲まれる内容 |
it | カーソル位置、もしくは次のHTML/XMLタグに含まれる内容 |
例えば、以下のようなHTMLがあって、
<div ng-class="{warn: !loginUser}">
<div>こんにちは</div>
<div>{{loginUser.name}}</div>
</div>
カーソルが1行目の「class」の「a」の部分に乗っていたとした場合、次のようにコピーを行うことができます。
キー入力 | コピーされる内容 |
---|---|
yiw | class |
yi" | {warn: !loginUser} |
yi{ | warn: !loginUser |
yit | <div>こんにちは</div><div>{{loginUser.name}}</div> |
(最後の例はdivタグの間の改行と空白もコピーされます)
また、「i」ではなく「a」を指定した場合、外側の囲みもコピーされます。
「iw」は「inner word」、「aw」は「a word」を表すようです。「t」はタグですね。
HTMLタグやJSONオブジェクトのように、入れ子構造になっているテキストも扱えることに衝撃を受けました。対象オブジェクトの内容が複数行に渡っていても問題ありませんでした。
また、今までだと、ダブルクオートに囲まれる内容をコピーしようとした場合、操作対象の先頭位置までカーソルを移動して「yf"」(次のダブルクオートまでコピー)のようにモーションで指定していましたが、オブジェクト指定を使うと移動がなくなって楽になります。
モーション
段落
「{」で前の段落、「}」で次の段落に移動します。
段落とは次の空行を指すようで、スペースやタブだけが入っている行はスキップされます。
ソースコードでもドキュメントでも、意味のあるまとまりの単位を扱うのに使えそうです。
オペレータ
インデント
「>」でインデントレベルを増加、「<」で減少します。
今までは、インデント調整対象の行数が少ない場合は「dw」で行頭のタブを削除して入れ直し、多い場合はコマンドモードで範囲指定の一括置換をしていました…。
function func() {
const line = inputLine();
const words = line.split(" ");
words.forEach(word => console.log(word));
}
上記で「line」にカーソルがある場合、「<}」でインデントを下げて揃えることができます。
大文字/小文字変換
「gU」で大文字に変換、「gu」で小文字に変換します。
今までは、「~」を変換したい文字数分だけ押していました…。
<div id="outer-div">
<div id="inner-div">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</div>
<div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
<div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div>
</div>
「outer-div」のタグにカーソルがある場合、「gUit」で以下のように変換できます。
<div id="outer-div">
<DIV ID="INNER-DIV">LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT, SED DO EIUSMOD TEMPOR INCIDIDUNT UT LABORE ET DOLORE MAGNA ALIQUA.</DIV>
<DIV>UT ENIM AD MINIM VENIAM, QUIS NOSTRUD EXERCITATION ULLAMCO LABORIS NISI UT ALIQUIP EX EA COMMODO CONSEQUAT.</DIV>
<DIV>DUIS AUTE IRURE DOLOR IN REPREHENDERIT IN VOLUPTATE VELIT ESSE CILLUM DOLORE EU FUGIAT NULLA PARIATUR.</DIV>
</div>
「inner-div」のタグにカーソルがあった場合は、「gUit」で以下のように変換できます。
<div id="outer-div">
<div id="inner-div">LOREM IPSUM DOLOR SIT AMET, CONSECTETUR ADIPISCING ELIT, SED DO EIUSMOD TEMPOR INCIDIDUNT UT LABORE ET DOLORE MAGNA ALIQUA.</div>
<div>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</div>
<div>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</div>
</div>
その他
ターミナル+ノーマル
コマンドモードで「:term」を入力することにより、ウィンドウを分割してターミナル(シェル)を開くことができますが、ここで「Ctrl-w N」とすることで、ターミナルをノーマルモードにできます。つまり、ターミナルの出力上でカーソルを自由に移動することができるようになります。
シェル上のインタラクティブな入出力をそのままファイルに保存することができるため、運用保守のエビデンス取得に活用できそうです。
最後に
Vimおもしろいです。
まだ活用できていないこととして「Ctrl-R」があるので、もう少し調べてみようと思います。
参考サイト
- help - Vim日本語ドキュメント(vim-jp.org)
- Vimはいいぞ!ゴリラと学ぶVim講座(さくらのナレッジ)
- 脱初心者を目指すなら知っておきたい便利なVimコマンド25選(Qiita)
- ステータスラインの表示内容を設定する(まくまくVimノート)
- 【(Neo)Vim】terminal mode のマッピングについての闇とその解決法(Qiita)
- Vim:インデントを1段浅く/深くする方法(もためも)
- Vimでファイルを開き直してもUndoができるように正しく設定する(Qiita)