Evil や VSCodeVim の複数行編集

[Vim advent calendar 2024(Adventar)](https://adventar.org/calendars/10040) 5 日目の記事です。参加者募集中 & Vim に関係していれば何でも OK とのことでした。

# Vim じゃない Vim たち

Vim/NeoVim 以外の Vim のエミュレーションを使うと、生の Vim とはちょっと違う挙動が見れて面白いことがあります。一例を紹介します。

# お題: 複数行編集

以下のテキスト編集を実施してください。各行の行頭・行末を両方編集する必要があります。

```org:編集前
A
BB
CCC
DDDD
```

```org:編集後
* A :tag:
* BB :tag:
* CCC :tag:
* DDDD :tag:
```

以下では 5 通りの編集方法を紹介します。

> 僕が好みは方法 3. ドットリピートです。

## 方法 1: マクロ

マクロは、一連のキー操作を記録し、繰り返し実行できる機能です。この記事を見つけるような人は、マクロにも親しんでいると思いますから、結果のみ紹介します。

```org:編集前
A
BB
CCC
DDDD
```

```org:(1 行分の編集 + 次行へのカーソル移動) をマクロに記録する
* A :tag:
BB
CCC
DDDD
```

```org:マクロを 3 回呼び出し
* A :tag:
* BB :tag:
* CCC :tag:
* DDDD :tag:
```

とても良いですが、マクロの記録開始・終了と呼び出し操作がちょっと手間だと思います。

## 方法 2: Dmacro (Evil, Vim)

[dmacro](https://github.com/emacs-jp/dmacro) は直近の操作履歴をマクロのように利用できる機能です。レコードしなくて良いマクロ！　Evil から利用してみます。

```org:編集前
A
BB
CCC
DDDD
```

```org:1 行分の編集を行う
* A :tag:
BB
CCC
DDDD
```

```org:2 行目をちょっと編集する
* A :tag:
*BB
CCC
DDDD
```

```org:dmacro-exec を呼ぶ (1 行目の編集にマッチし、繰り返される)
* A :tag:
* BB :tag:
CCC
DDDD
```

```org:あと 2 回 dmacro-exec を呼ぶ
* A :tag:
* BB :tag:
* CCC :tag
* DDDD :tag:
```

魔術的ですね。ぜひ道具箱に追加してください。

Vimmer は [tani/dmacro.vim](https://github.com/tani/dmacro.vim) で `dmacro` が使えるそうです。 Vim でキー操作を監視するのは相当大変そうで、 tani さん凄過ぎでした。 Dmacro の存在自体、 tani さんから知りました。

https://zenn.dev/dog/articles/dmacrod_2024

## 方法 3: ドットリピート (Evil)

これはVim にはできないと思います。 **本家 Vim 詳しくないため不正確です** が、 Vim だと改行がドットリピートの対象になりません。ところが Evil ではドットリピートでカーソル移動まで繰り返しできます。

```org:編集前
A
BB
CCC
DDDD
```

```org:1 行分の編集を行い、次の行へカーソル移動する
* A :tag:
BB
CCC
DDDD
```

```org:ドットリピートを 3 回呼び出し
* A :tag:
* BB :tag:
* CCC :tag:
* DDDD :tag:
```

Evil や VSCode のドットリピートはマクロっぽいと言えます。僕はこちらの振る舞いが好きで気に入っています。 この動作は Vim でも可能ですか？　情報募集中です。

## 方法 4: 複数行 insert を 2 回行う

Visual-line モードからの insert を 2 回実施します。ちょっと面倒くさいと思います。

```org:編集前
A
BB
CCC
DDDD
```

```org:行頭を編集
* A
* BB
* CCC
* DDDD
```

```org:行末も編集
* A :tag:
* BB :tag:
* CCC :tag:
* DDDD :tag:
```

## 方法 5: 複数行 insert を 1 度のみ行う (Evil, VSCodeVim)

Evil では　`C-a` や `C-e` で normal モードへ戻らずにカーソルをジャンプさせることができます。これがリピートの対象となるため、複数行に対し行頭・行末の編集を一括で実施できます。

```org:編集前
A
BB
CCC
DDDD
```

```org:複数行選択し、 1 行分の編集を行う
* A :tag:
BB
CCC
DDDD
```

```org:ノーマルモードへ戻る
* A :tag:
* BB :tag:
* CCC :tag:
* DDDD :tag:
```

VSCodeVim では複数行編集がマルチカーソルになり、これも一括処理が上手く行きます。

```org:編集前
A
BB
CCC
DDDD
```

```org:複数行選択し、行頭を編集する
* A
* BB
* CCC
* DDDD
```

```org:行末へジャンプし、行末を編集する
* A :tag:
* BB :tag:
* CCC :tag:
* DDDD :tag:
```

## その他の方法

置換を使うなど、様々な方法がありそうです。やきもきしている方、よければコメントでご共有ください >_<

# おまけ: 複数行編集 (改行を含む)

次のテキスト編集を実施してください。

```org:編集前
A
BB
CCC
DDDD
```

```org:編集後
* A :tag:
xxxx

* BB :tag:
xxxx

* CCC :tag:
xxxx

* DDDD :tag:
xxxx

```

今度は行毎に改行も追加したいということです。

## 方法 1: マクロ

マクロならできますね。

## 方法 2: Dmacro

Dmacro でもできます。

## 方法 3: ドットリピート (Evil)

Evil ならドットリピートできます。

## 方法 4: 複数行 insert を 1 度のみ行う (VSCodeVim)

Evil なら……できません。繰り返し適用の位置が (line, column) 値で記録され、改行すると位置の対応がズレるためです。

```org:こうなります。しょんぼりしました。
* A :tag:
* xxxx :tag:
* xxxx :tag:
* xxxx :tag:
xxxx




BB
CCC
DDDD
```

中身をハックできる人なら、わりと簡単に改良できそうです。やっぱり Evil 良くないですか？

VSCodeVim なら、複数行編集がデフォルトでマルチカーソルですから、問題なく実施できます。

# まとめ

- Dmacro を導入してみましょう。
- Evil のドットリピートがマクロっぽくて好きです。
- VSCodeVim で複数行 insert がマルチカーソルなのは意外と良いです。マルチカーソルのプラグインを試してみるのもありかと思います。