読者です 読者をやめる 読者になる 読者になる

Vimで編集を効率的に行うための繰り返し操作について

vim

Vim

この記事はVim Advent Calendar 2012の232日目の記事です。
231日目はrbtnnさんによる`vimproc#system({expr})`の{expr}の展開のされ方でした。

本記事は、Vimで編集を効率的に行うための繰り返し操作について記載します。

カーソル位置の単語編集の繰り返し

カーソル位置の単語を編集して同じ単語の編集をファイル内で繰り返したい場合、 以下の順で実行します。

  1. *でカーソル位置の単語で前方検索する
  2. (必要に応じて)Nで最初の位置に戻る(私は**Nをマッピングしてます)
  3. ciwでカーソル位置の単語を削除し編集する
  4. n.で単語の編集を繰り返し
  5. 確認不要だったり量が多ければ%s//<C-r>./g

以下は上記操作のGIF画像です。 screenshot1

最初から:substituteコマンドを使う事も考えられますが、 検索で確認しながら、編集に移行するかどうかを判断できるので 上記のようにまずは.での繰り返しを考えます。

:substituteのオプションでcをつけることにより確認もできます :%s/foo/bar/gcのように実行します。

上記では*を使っていますが、g*で、カーソル行の単語を 他の単語の一部として検索して置換することもできます。

  1. g*でカーソル位置の単語で前方検索する
  2. (必要に応じて)Nで最初の位置に戻る
  3. vと移動(lなど)で単語を選択し、cで編集する
  4. n.で単語の編集を繰り返し
  5. 確認不要だったり量が多ければ%s//<C-r>./g

以下は上記操作のGIF画像です。 screenshot2

visual modeで選択したテキストで検索できる visualstar.vimというVim pluginも合わせて使うと更に便利です。

検索でマッチする(しない)行を削除

:globalコマンドと:deleteコマンドを組み合わせて実行します。

" fooにマッチする行をすべて削除する
:g/foo/d

逆に検索でマッチしない行を削除したい場合 :vglobalコマンドと:deleteコマンドを組み合わせて実行します。

" fooにマッチしない行をすべて削除する(:vglobalコマンド使用)
:v/foo/d

編集の繰り返しとは話が若干それますが、:globalコマンドと:yankコマンドを 組み合わせて検索にマッチする行をすべてレジスタにコピーすることができます。

" aレジスタに検索にマッチする行をすべてコピー

" aレジスタをクリア
:let @a = ''
" fooにマッチする行をすべてヤンクしてaレジスタに追加(大文字で追加になる)
:g/foo/y A

コマンド繰り返し

@:で最後に実行したコマンドを実行できます。
例えば:bnコマンドを実行したあと@:で繰り返せばバッファ移動を繰り返せます。

:global:substituteコマンドを繰り返すこともできます。

:substituteコマンドは、&g&:&&などで繰り返すこと もできます。それぞれの意味は:help &などで調べて見てください。

マクロで繰り返し

マクロは難しいと思われている方もいるかもしれませんが、慣れればとても便利なので使ってみましょう
マクロを使いはじめるには、まずは行単位の操作をマクロとして記録することをおすすめします。

例:検索にマッチする行の行頭と行末に特定の文字列を挿入する

検索にマッチする行の、行頭と行末に特定の文字列を挿入する操作をマクロとして記録したい場合、以下のようにタイプします。

/fooのように検索したうえで

qqI/*<Esc>A*/<Esc>nq

を実行します。

上記を分割すると
qq:マクロ記録開始
I/*<Esc>:行頭に/を挿入
A*/<Esc>:行末に
/を挿入
n:検索前方移動
q:マクロ記録終了
となります。

これでqレジスタにマクロが記録されたので@qで同じ操作を繰り返せます。
マクロはcountが使えるので9@qなどと指定することでマクロを複数回実行出来ます。

以下は上記操作を繰り返したGIF画像です。 screenshot3

最後にエラーが発生していますが、これは9@qでマクロをcount指定で 実行した際、nの検索で検索文字見つからなかったために表示されたものです。
countでの繰り返しの中でエラーが発生するとマクロ操作が途中で止まるため、 検索でマッチする数がわからなくても適当に999@qなど、countを適当に指定して繰り返し実行できます。

複数ファイル一括操作

複数ファイル一括操作には、argdobufdowindotabdoなどが使用出来ます。
それぞれの詳細は:help argdoなどで調べてください。

ここではargdoを例として説明します。

カレントディレクトリ内のすべてのファイルを一括編集するには argsargdoのコマンドを組み合わせて実行出来ます。

" カレントディレクトリ内の拡張子がvimのファイルをすべてargdoの対象にする
:args *.vim
" カレントディレクトリ内のファイルのすべてのfooをbarに一括置換
:argdo %s/foo/bar/g
" 上記編集結果をすべてのファイルで保存
:argdo update

:argdoする前に:set hiddenしておく必要があります。
カレントディレクトリは:pwdコマンドで確認できます。

カレントディレクトリ内のすべてのファイルにマクロ実行するには argsargdoのコマンドを組み合わせて実行出来ます。

" カレントディレクトリ内の拡張子がvimのファイルをすべてargdoの対象にする
:args *.vim
" カレントディレクトリ内のファイルにマクロ実行
:argdo normal! 999@q
" 上記編集結果をすべてのファイルで保存
:argdo update

ここでは:args *.vimなどとしましたが、vimfiler.vim というVim pluginでのファイル指定および、actionでexアクションからargsコマンドを使うことにより、 指定したファイルに対して一括操作ができます。

編集操作を繰り返す際の考え方

Vimでの編集操作の繰り返しは:help repeat.txtを見てもわかるように大きく3つあります。

  1. .での繰り返し
  2. :substituteglobalvglobalなどのコマンドでの一括編集
  3. マクロ(qx,@q)での繰り返し

私が編集操作を繰り返す際に考えるのは、上記の13の順に繰り返しで編集操作できないか考えます。
まずは.で繰り返せるものなら.で繰り返します。
.で対応できないような操作はコマンドかマクロで対応します。
コマンドとマクロのどちらを使うかは場合によります。単純なパターンを書けるのであれば :substituteなどのコマンドを使うことを先に考える場合もありますし、 ちょっとパターンを書くのが面倒そうであればマクロを先に考える場合もあります。

例えばマクロの例であげたの操作は、以下のように書いても実現出来ますが、パターンを考えなくていい分、マクロで考えた方が若干楽です。

:s/\s*\zs.*/\/*&*\//g

このバランスは使っていくうちにわかっていくと思います。

コマンド、マクロで実行した上で、更に他のファイルにも同じ操作を繰り返したい場合は @:でのコマンド繰り返しや、normal! 999@aでのマクロ操作繰り返しを使います。
更に複数ファイルある場合は、argdobufdowindotabdoなどを 使用して繰り返します。

おまけ

qfreplaceというVim pluginを使用することで、 grepした結果のquickfixウィンドウの該当する各行を別バッファで開き、 そのバッファ上で編集を行い保存することで、該当行を編集することができます。

以下はqfreplaceを使った操作のGIF画像です。

screenshot4

grepでNeoBundleを検索して、quickfixウィンドウを開き、 qfreplaceで開いた 別バッファ上で編集することで該当行を編集しています。
上記GIF画像は1ファイルだけで編集していますが、qfreplaceで 開かれる別バッファでは複数ファイルの該当行を一緒に編集することができるため かなり強力です。

まとめ

以上が、Vimで編集を効率的に行うための繰り返し操作についてでした。

編集の繰り返しを使いこなせれば、編集効率がかなり上がります。
繰り返し同じ事をしてるな、と思ったら本記事を参考にして色々と 試してみてください。
皆さんの編集効率が上がれば幸いです。

本題とはそれますが、Vimレベルが上がるのでvimrc読書会に参加することをおすすめします。毎週土曜日23時からやってます。

それではVim Advent Calendar 2012の232日目を終わります。

明日のVim Advent Calendar 2012はmanga_osyoさんです。