なぜVimmerの僕はマルチカーソルを必要としないか
はじめに
VSCode などのテキストエディタには、マルチカーソルという機能があります。
これは、エディタ上に複数のカーソルを出現させ、一度に複数の場所に同じ操作を行うことができる機能です。
VSCode上でのマルチカーソル
自分はVSCodeをメインとしていた時にはこの機能を多用していたのですが、Neovimに移行してから一切使っていません。
一応Vim/Neovimにもマルチカーソルを実現するプラグインがいくつか存在します。
ですが、一度は入れてみるものの結局使わないままアンインストールしてしまいました。
ではなぜ、マルチカーソルが必要なくなったのか。
それはVim/Neovimの操作体系/機能が十分に強力であるので、マルチカーソルを使わなくても同じことができるからです。
この記事では、自分がVSCodeのマルチカーソルで行っていた操作をVim/Neovimの操作体系/機能でどのように行っているかを紹介します。
行の先頭に文字を追加する
例えば以下のようなテキストがあるとします。
foo
bar
baz
例えばこのテキストの行頭に1.
を追加したいとします。
1. foo
1. bar
1. baz
この時、VimではVisual Blockモードを使うことで簡単に行うことができます。
-
0
または^
で行頭に移動する -
Ctrl-v
でVisual Blockモードに入る -
j
やk
で追加したい行を選択する -
I
で挿入モードに入る - 追加したい文字列を入力する
-
Esc
で挿入モードを抜ける
Visual Blockモードを使って行の先頭に文字を追加する
行の末尾に文字を追加する
行の末尾に文字を追加する場合もVisual Blockモードを使うことで簡単に行うことができます。
-
Ctrl-v
でVisual Blockモードに入る -
j
やk
で追加したい行を選択する -
$
で行末に移動する -
A
で挿入モードに入る - 追加したい文字列を入力する
Visual Blockモードを使って行の末尾に文字を追加する
追記: Visual Blockモードを使って数字をインクリメントする
Visual Blockモードを使うと、数字をインクリメントすることも簡単に行うことができます。
例えば
1. foo
1. bar
1. baz
この時、Visual Blockモードで数字を選択し、 <Ctrl-a>
/<Ctrl-x>
を押すことで、数字を一気にインクリメント/デクリメントすることができます。
2. foo
2. bar
2. baz
もちろん一気に増やしたい時は、20<Ctrl-a>
のように数字を指定することもできます。
さらに、g<Ctrl-a>
/g<Ctrl-x>
を使うことで、連番でインクリメント/デクリメントすることもできます。
この場合、数字を選択してからg<Ctrl-a>
を押すことで、
1. foo
2. bar
3. baz
のように数字をインクリメントすることができます。
dial.nvimについて
さらに、Neovimのプラグインであるdial.nvim
を使うことで、色々な形式の数字をインクリメント/デクリメントすることができます。
2024-12-29 hello
2024-12-29 hello
2024-12-29 hello
2024-12-29 hello
2024-12-29 hello
この日付をVisual Blockモードで選択して、`g<Ctrl-a>`を押すと
2024-12-29 hello
2024-12-30 hello
2024-12-31 hello
2025-01-01 hello
2025-01-02 hello
こうなる
カーソル下の単語の編集
Vimでは*
を用いるとカーソル下の単語を前方検索することができます。
また、gn
を用いると前方検索&ビジュアル選択を行うことができます。
さらに、cgn
を用いると前方検索&ビジュアル選択&変更を行うことができます。
ところで、vimにはドットリピートという機能があり、直前の変更を繰り返すことができます[1]。
これを使うと、直前の変更を繰り返すことができるので、繰り返し置換が簡単に行えます。
これらを組み合わせると、以下の操作でカーソル下の単語を繰り返し置換することができます。
-
*
でカーソル下の単語を検索 -
''
で元の位置に戻る[2] -
cgn
で単語を編集 -
n
で次の検索結果へ移動 -
.
で単語の置換を繰り返す(いわゆるドットリピート)
カーソル下の単語を繰り返し置換
このような操作を毎回行ってもいいのですが、いい感じにkeymapを設定してしまうと便利です。
vim.keymap.set("n", "<leader>*", "*''cgn")
*の挙動についての補足
標準の*
や#
では、現在のカーソルの位置から前方検索を行って次の検索結果にジャンプします。
そのため本文中にある通り*
で検索を行った後に''
で元の位置に戻る必要があります。
もしカーソルを移動したくない場合はvim-asterisk
のようなプラグインを使うと便利です。
LSPを用いて変数を一括変更する
Neovimの組み込みのLanguage Server Protocol(LSP)クライアントを用いることで、変数名の一括変更を行うことができます。
:lua vim.lsp.buf.rename()
LSPを用いて変数を一括変更する
また inc-rename.nvim
を用いると、変更中にプレビューが表示されるようになります。
Buffer全体で単語を一括変換する
Vimには便利な置換コマンドが用意されています。
例えば、:s/foo/bar/g
でBuffer全体でfoo
をbar
に置換することができます。
これを利用して、自分は選択した範囲から置換コマンドを一発で呼び出せるkeymapを設定しています。
vim.keymap.set("x", "<leader>r", 'y:%s/<C-r><C-r>"//g<Left><Left>')
vim.keymap.set("n", "<leader>r", 'yiw:%s/<C-r><C-r>"//g<Left><Left>')
このコマンドのやっていることは以下の通りです。
-
y
で選択範囲をヤンク -
:%s//g
でBuffer全体に対して置換コマンドを開始 -
<C-r><C-r>"
でヤンクした内容をペースト[3] -
<Left><Left>
で最初の//
の間にカーソルを移動
選択範囲から置換コマンドを一発で呼び出す
より複雑な操作
ではもっと複雑な操作を考えてみましょう。
例えば、以下のようなテキストがあるとします。
foo
bar
baz
hoge
fuga
このテキストを以下のように変更したいとします。
[foo](https://example.com/foo)
[bar](https://example.com/bar)
[baz](https://example.com/baz)
[hoge](https://example.com/hoge)
[fuga](https://example.com/fuga)
このような操作はVisual Blockで一発で行うことは難しいです。
移動が伴うために、挿入モードで一発で行うことができず、ドットリピートも難しいです。
このような場合に使えるのがマクロです。
マクロは、複数の操作を記録して再生することができる機能です。
この場合だと以下のようなマクロを記録して再生することで簡単に変換することができます。
- 最初の行に移動
-
qq
でq
レジスタへのマクロの記録を開始 - 最初の行の編集を行う[4]
-
q
でマクロの記録を終了 -
4@q
でマクロを4回繰り返す(Neovimなら4Q
でもOK)
マクロを使って複雑な操作を行う
先の場合では、繰り返し回数を指定することで置換を行いましたが、他にもマクロの適用方法はあります。
例えば、g
コマンドを使うことで、特定のパターンにマッチする行に対してマクロを適用することができます。
:g/foo/normal @q
このコマンドは、foo
という文字列を含む行全てに対してマクロq
を適用します。
検索結果に対して個別にマクロを適用したい時もあるかもしれませんね。
その場合は、先に述べた*
&gn
を使って一個ずつ@q
でマクロを適用していくことができます。
(もちろん適用したい行までj
やk
で移動してもOK)
また行単位に限らず、Visualモードに入って
:'<,'>normal @q
を実行することで任意の選択範囲に対してマクロを適用することができます。
AIを使う
「Vimの操作覚えるのとかめんどくせえよ、そんなことちまちまやってられっかよ」という方にはAIを使う方法がおすすめです。
GitHub Copilotを使ってコードを生成する
まとめ
- VSCodeなどのマルチカーソル機能は便利だが、Vim/Neovimの操作体系/機能で代替できる
- Visual Blockモード/マクロ/LSP/置換コマンドなどを使うことでマルチカーソルで行っていた編集をカバーできる
- AIは正義
おまけ
Neovim向けにEmacsでお馴染みのdmacroが実装されつつあるらしい...?
-
この場合だと
0yiwI[<esc>A](https://example.com/<C-o>p)<esc>j
↩︎
Discussion
あくまで上の例についてなら、正規表現でおてがるにいけますよ。というつもりで示しておきます。
時間ができたのでもう少し丁寧に書いておきます。
(上記コマンドの\vは、very magicを指定しています。詳細はhelp参照のこと)
vimでも、traces.vimを使えば、プレビューできます。
ありがとうございます!
そうなんですよね、この指摘は他の方からもいただいておりました。
自分は正規表現を書くのをサボりがち(まだ成熟してない)のでマクロに頼りがちです。
もう少し複雑な例を示せばよかったかなとも思っております。
とても有用なので先頭にピン留めさせていただきますね
おお、参考になれば幸いです。
反対に、私はマクロもドットリピートも、まだうまく使えないですし、他にもcopilotも動作例があって、とても参考になりました。
調子に乗ってもう一つだけ書いておくと、置換で式が評価できる機能が非常に強力です。
下は先の例を置き換えただけなのであまり旨味はないですが、数値をどうこうしたい時には有用です。
\=以降が、式として評価されます
Vim script使えるのめちゃくちゃ強力ですよね〜
自分はNeovimから入った人なので、すぐにlua使っちゃうんですが、少しずつ慣れていきたいです!
:%s/awsome/awesome/
VimでCopilot呼び出せるんですか。すごい!知らなかったのでありがたいです!
ですね!公式にサポートされています。
Copilot Chatは公式のサポートは発表されていないのですが、OSSで有志による開発が進んでいますね。
一部の章が隠れてしまっていたので修正しました。