爆速でvimを普段使いする

# はじめに
ターミナル操作中ちょっとファイル編集したいとき、どうしてます？メモ帳？VS Code？ターミナルから直接viで編集しちゃいましょう。

viは非常に強力なエディタで、いろいろな使い方ができます。が、それゆえ敷居が高かったりもします。

このエントリでは、viを普段使いするために必要な最低限の操作方法だけを紹介します。visualモードは使いません。`.vimrc`は無設定でOK。viスクリプト（プラグイン）も使いません。画像はWindowsなGitBashをベースにしていますがLinuxでもMacでも同じです。

:::note info
**基本的な操作の組み合わせでなんとかなる！**
無理してたくさんのコマンドを覚えようとしない！！

* ページ上端への移動(`H`)を覚えてなくても、上移動(`k`)をたくさん押せばいい
* 単語単位の移動(`w`/`b`)を覚えてなくても、左右移動(`h`/`l`)をたくさん押せばいい
* カーソル位置以降を削除してインサートモード(`C`)を覚えなくても、必要な文字を削除(`99x`)してインサートモードに遷移(`a`)すればいい
:::

エディタ論争には参戦してません。


# 基本操作

とはいえ、他の操作で代替できないコマンドは覚えるしかありません。これができないと死ぬので、死ぬ気で覚えましょう。

## コマンドモードとインサートモード

viでは、「コマンドモード」と「インサートモード」を切り替えて文書を編集します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334782/d3368b61-f4ba-5f3b-5346-6a5fc87e2d19.png)


### コマンドモード
文字を削除したりコピペしたりできるモードです。
Undo/Redoや保存/終了など、viコマンドは全てこのモードで行われます。

多くのコマンドは直前に数字（`n`）を入力してから入力するとコマンドをn回繰り返します。
例えば、`x`は「カーソル位置から1文字を削除」ですが、`3x`とすると「カーソル位置から3文字を削除」できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334782/7d43f22c-d16b-4e92-b409-164959f0874f.png)

**インサートモードからコマンドモードへの遷移方法**
|コマンド|説明|
|:---:|:---:|
|`ESC`|インサートモードからコマンドモードへ遷移|

`Ctrl-[`は`ESC`と同じなので、タイプしやすいほうでOKです。


### インサートモード
文字を入力する以外なにもできないモードです。
ウインドウの下の方に「挿入」や「insert」と表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334782/62291401-93f0-a7b6-e2be-0e2b3af93df6.png)

**コマンドモードからインサートモードへの遷移方法**
|コマンド|説明|
|:---:|:---:|
|`i`|カーソル位置に「挿入するように」入力開始|
|`a`|カーソル位置に「追加するように」入力開始|

後述する移動コマンドと組み合わせて「行頭から入力：`0i`」や「行末に追加：`$a`」ということもできます。
（それぞれ `I`,`A` と同義なので、余裕があればこれで覚えてもOKです）

|コマンド|説明|
|:---:|:---:|
|`o`|カーソル行の「後に」行を追加して入力開始|
|`O`|カーソル行の「前に」行を追加して入力開始|


## 保存と終了

|コマンド|説明|
|:---:|:---:|
|`:w`|上書き保存|
|`:q`|終了|
|`:q!`|保存しないで終了|

保存して終了は `ZZ` , `:wq` , `:x` などいくつものコマンドで可能ですが、最低限「`:w`して`:q`する」を覚えれば大丈夫です。
Officeアプリと同じように「上書き保存」して「終了」するだけです。

私は手癖で `:w` の頻度が半端ないです。（気持ちを落ち着かせる `ls` みたいなもんですね）


## カーソル移動

**行内の移動**
|コマンド|説明|
|:---:|:---:|
|`j`|下の行に移動|
|`k`|上の行に移動|
|`h`|左に1文字移動|
|`l`|右に1文字移動|

|コマンド|説明|
|:---:|:---:|
|`^`または`0`|カーソル行の先頭に移動|
|`$`|カーソル行の末尾に移動|


**行間の移動**
|コマンド|説明|
|:---:|:---:|
|`Ctrl-f`|下方向にページ移動|
|`Ctrl-b`|上方向にページ移動|

|コマンド|説明|
|:---:|:---:|
|`gg`|ファイルの先頭行に移動|
|`G`|ファイルの最終行に移動|


## コピペ

viではコピペ関連の用語として以下を使います。
* yank（=コピー）： レジスタ（クリップボードみたいなもの）に保管すること
* delete（=カット）： 削除して、削除したものをレジスタに保管すること
* put（=ペースト）： レジスタに保管されたものを貼り付けること

yankは「やんく」です。

**文字単位の操作**
|コマンド|説明|
|:---:|:---:|
|`yl`|その文字をyank（=コピー）|
|`x`|その文字をdelete（=カット）|
|`p`|カーソル位置の「後に」put（=ペースト）|
|`P`|カーソル位置の「前に」put（=ペースト）|

`yl`や`x`は最初に数字を入れることで「n文字yank」「n文字delete」とできます。なのでカーソル位置より後ろの文字を全て削除したいときは `9999x`で全部消せます。（数字は適当）

稼働環境が日本語化されていれば、全角文字は「１文字」としてカウントしてくれます。


**行単位の操作**
|コマンド|説明|
|:---:|:---:|
|`yy`|その行をyank（=コピー）|
|`dd`|その行をdelete（=カット）|
|`p`|カーソル行の「後の行に」put（=ペースト）|
|`P`|カーソル行の「前の行に」put（=ペースト）|

`yy`や`dd`は最初に数字を入れることで「n行yank」「n行delete」とできます。なのでカーソル行より後ろの行を全て削除したいときは `9999dd`で全部消せます。（数字は適当）

`p`,`P`のペーストは、直前に操作した結果のレジスタ内容がペーストされるので、直前に`x`で文字削除していればその１文字が、`yy`で1行ヤンクしていればその１行が、それぞれ貼り付けされます。


## undo / redo

|コマンド|説明|
|:---:|:---:|
|`u`|アンドゥ|
|`Crtl-r`|リドゥ|


## 検索
|コマンド|説明|
|:---:|:---:|
|`/正規表現`|正規表現パターンを検索|
|`*`|カーソル位置の「単語」を検索|
|`n`|次（後方）を検索|
|`N`|前（前方）を検索|

正規表現に`\c`を入れると大文字/小文字を区別しなくなります。（`\c`は最初でも最後でも、どこでも効くみたいですが、どういう仕組みなんだろう？）

`n`で検索してファイルの最終行に達したら、ファイルの先頭行から後方に繰り返して検索します。
`N`で検索してファイルの先頭行に達したら、ファイルの最終行から前方に繰り返して検索します。



# 応用操作

他の操作でも代替できないことはないですが、普段使いするうえで覚えておくべき操作だと思うものをリストアップしました。


## 行の連結

|コマンド|説明|
|:---:|:---:|
|`J`|カーソル位置の行に、次の行を連結|

コマンドモードであれば、カーソル位置は行内であればどこでも良いので、とても楽です。
また、処理系によっては「インサートモードにして次の行の先頭にカーソルを持って行ってバックスペース」で改行コードが削除できない場合もあるので、その場合はこれしか手段がないです。


## 置換

**文字単位の置換**
|コマンド|説明|
|:---:|:---:|
|`~`|カーソル位置の大文字と小文字を入れ替え|
|`r[任意の１文字]`|カーソル位置の文字を[任意の１文字]と入れ替え|

`r`の置換は文字だけの説明だとわかりにくいのですが、`abc`という文字があったとき、`b`の位置にカーソルを置いて`rx`とタイプすると、`axc`となります。`r`を打った直後の１文字と置換する。ということです。

**文字列の置換**
|コマンド|説明|
|:---:|:---:|
|`:%s/正規表現/置換文字列/g`|ファイル内の正規表現のマッチ部分の全てを、置換文字列で置換|
|`:%s/正規表現/置換文字列/gc`|ファイル内の正規表現のマッチ部分の全てを、確認しながら置換文字列で置換|

`g`はglobalで、ファイル内の複数個所をすべて対象にします。（gなしの場合は、最初にマッチしたものだけを対象にする）
`c`はcheckで、検索された箇所ごとに置換する/しないを選択しながら置換していきます。
`g`や`c`の部分に`i`を追加すると、ignore-caseとなり、検索する正規表現の大文字/小文字を無視します。


## マークを利用したコピペ

複数行のコピペ（カット）したい場合に、「ここから何行だろう？」と数えるのが面倒なのでマークを利用します。

|コマンド|説明|
|:---:|:---:|
|`ma`|現在のカーソル行にマークをつける|
|`y'a`|マークした行から現在のカーソル行までをyank|
|`d'a`|マークした行から現在のカーソル行までをdelete|

`a`はマークを記録する場所を示すものなので、`b`に置き換えてもOKです。この場合、`a`に記録したマークはそのままで`b`に新たなマークを記録できるようになります。
（マークは`:marks`で一覧を表示できますが、複数マークを使いこなすシーンが思いつかなったので、表には入れてません。）


## インクリメント/デクリメント

数値を１づつ増加したり減少させることができます。

|コマンド|説明|
|:---:|:---:|
|`Ctrl-a`|インクリメント|
|`Ctrl-x`|デクリメント|

ただし、進数を自動判別してしまうケースがあるので、注意が必要です。

```
# 通常の数値は10進数で判断される
10000
1000

# 0xで始まる数値は16進数で判断される
0x221
0xa9f

# 0bで始まる数値は2進数で判断される
0b1100
0b0101
```


## 繰り返し処理

**単一コマンドの繰り返し**
|コマンド|説明|
|:---:|:---:|
|`.`|直前のコマンドを再度繰り返す|

コマンドモードで直前に実施した「ひとつの操作」をもう一度繰り返します。`10.`とすると、直前に実施した操作を10回繰り返します。

**複数コマンドの繰り返し**
|コマンド|説明|
|:---:|:---:|
|`qa`|マクロ記録開始|
|`q`|マクロ記録終了|
|`@a`|マクロ実行|

複数の操作を「マクロ」として記録して、任意のタイミングで再実行できます。

`a`はマクロを記録する場所を示すものなので、`b`に置き換えてもOKです。この場合、`a`に記録したマクロはそのままで`b`に新たなマクロを記録できるようになります。

`@a`は`a`に記録されたマクロを１回実行するだけで、`10@a`とすると、10回繰り返しを実行します。

**例：ホスト名を連番で生成したい**
以下の１行があるとして、これを連番のホスト名として`server100.example.com`まで全部で100行のテキストを生成したいとします。
```:ホスト名を連番で生成したい
server01.example.com
```

ここにカーソル行をあわせて以下のコマンドを打ちます。
* `qa`：マクロの開始
* `yy`：カーソル行をコピー
* `p`：コピーした行を、カーソル行の次の行にペースト
* `Ctrl-a`：数字をインクリメント（カーソルが行頭にあってもOK）
* `q`：マクロの終了
* `98@a`：カーソル行を起点にしてマクロを98回実施

すると、以下のようになります。
```:マクロの実行結果
server01.example.com
server02.example.com
server03.example.com
（ホスト名がずらーっと）
server100.example.com
```


## 日本語入力について

ターミナルで直接viを利用している場合の話ですが、、、
日本語で文字を入力していて、コマンドモードに遷移するために`ESC`をタイプしても、日本語モードはそのままなので、手動で英語モードにする必要があります。

工夫次第でなんとかできる場合もあるようですが、リモート接続時などでの環境依存の場合もありますので、OS設定として日本語切り替えキーをカスタマイズしておくと良いです。

私は普段からWindowsを利用していますが `半角/全角`キーは遠いので `Ctrl-Space` でON/OFFをトグルできるようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334782/cb54d651-826f-f3c9-d1e6-ce0cf763a2b0.png)



# vimdiff

viでdiffしながら編集できるツール。viベースなので、操作方法はviと変わらない。

複数ウインドウ間は以下で移動できる
* 右に移動： `Ctrl-w l`
* 左に移動： `Ctrl-w h`


例として https://github.com/zabbix/zabbix-docker にあるdocker-composeのYAMLファイルを比較してみる。

```
$ git clone --depth=1 https://github.com/zabbix/zabbix-docker ~/tmp/zabbix-docker
$ vimdiff ~/tmp/zabbix-docker/docker-compose_v3_alpine_*_local.yaml
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334782/bfd6cd8e-0434-9727-7016-25f09ef5676b.png)


３つ以上のファイルも比較できる。
```
$ vimdiff ~/tmp/zabbix-docker/docker-compose_v3_alpine_*
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/334782/8f2460e4-1553-58e6-e360-dd83b573fde0.png)


# 参考（わたしの.vimrc）

わたしが実際に利用している`.vimrc`です。

```:.vimrc
" syntaxがあれば有効
if has("syntax")
  syntax on
endif

" インデントは無効
filetype indent off
set noautoindent
set nosmartindent
set paste

" マークダウンで_をエラー表示しない用の設定
augroup update_markdown_syntax
  autocmd!
  autocmd FileType markdown syntax match markdownError '\w\@<=\w\@='
augroup END

" ターミナルの背景（黒）で見やすい配色
set background=dark

" 画面ベルも不要なので全部無効
set belloff=all

" 検索ハイライトをON（目がチカチカするときは:nohlすればいい）
set hlsearch
```


# さいごに
とある勉強会の用途でこの資料を作成しましたが、普段の自分の操作や思考（嗜好？）を記載しただけの資料になってしまいました。

とはいえ、たったこれだけでも（vim全機能の10%も利用していないと思いますが）十分に強力なエディタとして使えるようになっているはずです。

「１ストロークでも減らしたい！」と思い始めたら、そこが沼への入り口ですので、お気をつけて。。。

---
私自身、`.vimrc`は上に書いてあるものですし、プラグインも未使用、普段の操作もここに記載しているものがほぼ全てです。
組み合わせれば何とかなるものは組み合わせればいいと思ってます。１ストロークにイライラするわけではないですし、特定パターンの文字削除を0.1秒短縮したいわけでもないので、あまりたくさんを覚える気にはならないです。

`:20`で20行目に移動しても、結局移動したいカーソル位置はそこではないので、お目当ての文字列を`/`で検索してカーソル移動しちゃいます。「何行目」かを目視確認するのが面倒ですし、行番号を意識すること自体少ないですし。（スペースがもったいないので行番号は普段から非表示です）

ファイル内を目grepするときでも、`Ctrl-u`と`Ctrl-d`でちょっとずつ移動するより、`Ctrl-f`と`Ctrl-b`で大幅に移動して行きすぎたら戻るような操作をよくしています。
このあたりは、完全に個々人の好みの問題だと思いますが。

とはいえ **押しつけはしない。けど、同じように使ってくれたら話が通じるので嬉しい！** とは思いますよね。


# 他の勉強会資料へのリンク

https://qiita.com/bashaway/items/62d38f377801fe3d631e

https://qiita.com/bashaway/items/4b0b3c874e153bc79989