変わり者の Vim 組み込みオペレーター達

本記事はVimアドベントカレンダー2022 その3の24日目の記事です。

オペレーターというのはVimの編集コマンドの一群で、よく自然言語における動詞に例えられる。つまり編集の種類を指し示すコマンドである。もう少し具体的に言うと、「消す」であったり「コピーする」であったりするわけだが、「何を」消したりコピーしたりするのかオペレーター自身は知らない。「何を」はもちろんテキストであって、これは編集中のテキストファイルの「どこを」と言い換えてもよい。これは範囲を指定する他のコマンド(モーションやテキストオブジェクト)を使ってVimに伝えることができる。つまり、オペレーターとモーションは組み合わせてつかうことが前提のコマンドなのである。いくつかのコマンドを覚えると、その組み合わせによって多彩な編集を短いキーストロークで実現することが可能になるVimの愛すべき機能である。 (:help operator)

この記事では変わり者のオペレーターコマンドを紹介しようとおもう。 とはいえ、珍しいオペレーターの紹介ではない。 慣れ親しんだ(かどうかはわからないが)組み込みのオペレーターコマンドの奇妙な一面を紹介するのが趣旨である。 もしかすると細かすぎてほとんどの人は気にならないと思うが、まあ、そんな見方もあるのだな、ぐらいに思ってもらえれば幸いである。

c

最初は c コマンドである。 多くが使うであろう、メジャーといっていいオペレーターだ。

c コマンドは指定された範囲の文字列を削除して挿入モードに入り、入力された文字列に置き換える。 さらに . コマンドを使うと削除+挿入をまとめて繰り返すことができる。 c の特殊性はまさにこの . コマンド時の挙動にある。 例えば、d コマンドと i コマンドを順に使っても同じ編集はできるが、その場合 . コマンドで繰り返そうとしても繰り返されるのは i コマンドによる文字列の挿入のみとなってしまう。

ドットコマンドの繰り返す編集単位

. コマンドは基本的に単発の編集操作を繰り返す。 そしてこの編集操作はノーマルモードから挿入モードに入るときに区切られてしまい、「文字列を消す」と「文字列を挿入する」という2つの編集をまとめて繰り返すことはできないのである。 c コマンドを除いては。

今の所、この挿入モード開始時に編集操作が区切られるというのはほぼ絶対のルールである。 現在のVim scriptの提供する機能ではユーザー定義オペレーターで c の挙動を真似することはまともな方法ではできない。 変わり者というよりも特別なオペレーターだと言ってよいだろう。

y

続いて y コマンドだが、こちらも超メジャーだ。 こちらはVimのオペレーターという仕組みを知らない人でも使うコマンドであろう。

y コマンドは指定された範囲の文字列をヤンクする。 より一般的な語彙を使うなら文字列のコピーを司るオペレーターである。 y コマンドの特殊な点は、やはり . コマンド時の挙動に表れる。 端的に言って y コマンドは唯一「. コマンドで繰り返さない」ことができるオペレーターなのである。 :help cpo-y するとこのようなことが書いてある。

                             *cpo-y*
y   コピーコマンドを "." でリドゥできる。本当にこれを使い
    たいのであれば、2度考えること。いくつかのプラグインを
    壊すかもしれない。ほとんどの人が、"." が変更を繰り返す
    ことを期待しているからである。

デフォルトでは cpo、つまり 'cpoptions'y は含まれない。 つまり、ほとんどの人は y. コマンドで繰り返さない状態で使っており、おそらくそれが自然な挙動だと考えている。 しかし、この挙動は異常だ。 (特にプラグイン作者にとっては。) なぜなら、この挙動はユーザー定義オペレーターでは真似できないのである。 挿入モードへ入るタイミングがそうであったように、オペレーターコマンドの発動は必ず編集操作を区切り、絶対に . コマンドで繰り返されることになる。

オペレーターの最も重要な性質は . コマンドによる繰り返し可能な点である、というのは多くの Vimmer が賛成するところだと思っている。 とするならば、この話は奇妙に聞こえるかもしれない。 しかし「繰り返さない」ことができないというのも大きな制限だと私は思う。 目下のところ、この一点において y コマンドの模倣もまたできないのである。


私としては現状 cy はシステム的に特別扱いされているオペレーターという認識だ。 少なくともVim scriptのレイヤーではこれらを模倣することはできない。

現在のVim script における . コマンドまわりのインターフェースはあまり簡単ではないし、このように十全とも言い難い。 将来的には . コマンド制御のための API がもっと提供されてほしいと思う。 それがどんなものになるかは分からないが、cy が特別でなくなると嬉しい。