Vimで連番を入力するためのVim pluginを作った

この記事はVim Advent Calendar 2012の198日目の記事です。
197日目はyonchuさんによるVim Advent Calendar 2012 に’はてブ数’を表示させるグリモンとブックマークレットを作ったよ!でした。

Vimで連番を入力するためのVim pluginについての記事です。

6/16 18:30 追記(operatorを本体に統合したので記載を修正)

はじめに

文章で説明するより使用している様子を見たほうがわかりやすいと思うので、 まずは以下のGIFアニメをご覧ください。 これは、Vim scriptの配列の値として、 0〜99までを設定するために 今回ご紹介するVim pluginを使っている様子です。

使用感

0を100個入力するところまでは、本pluginは関係ありません。 その後のRengBangコマンドを実行して、連番に置き換えているところが 本pluginの機能です。

本pluginは、私がLingrでこんなplugin欲しいなぁとつぶやいたところ、 manga_osyoさんが原型のscriptをものの数分で作ってくださったという経緯があります。
それを元に色々機能追加してplugin化したのが本pluginです。 この場を借りてmanga_osyoさんにお礼を言いたいと思います。ありがとうございます。manga_osyo++

あと、rengbang(連番)という名前をつけていただいたのはujihisaさんです。 短くてわかりやすくて、とてもいいネーミングで気に入ってます。 この場を借りてujihisaさんにお礼を言いたいと思います。ありがとうございます。ujihisa++

インストール方法

プラグイン名はvim-rengbangという名前で、githubにあげてます。

NeoBundleを入れていれば.vimrcに以下を書いておけばインストール出来ます。

6/16 18:30 追記(operatorを本体に統合したので記載を修正)

operator-user用のplugin(vim-operator-rengbang)も用意したので、必要であれば合わせて入れてください。 vim-operator-rengbangは単体では動かず、使用するためにはvim-rengbangと kanaさんが作られたvim-operator-userが必要です。 vim-rengbang用operatorをvim-rengbang内に統合しました。 vim-operator-rengbangは不要です。vim-operator-userが インストールされていればoperatorは使えますが、されていなければ使えません。

NeoBundle 'deris/vim-rengbang'

もちろん、.vim(Winであればvimfiles)配下に直接入れても構いません。

使い方

本pluginのインターフェースは3つあります。

  • コマンド
  • 関数
  • operator

順に使い方を説明します。

コマンド

2つのコマンドを用意しています。

  • RengBang
  • RengBangUsePrev
" [range]RengBang [{pattern} {start} {step} {use_first}]
"   [range]で指定された範囲の中から、{pattern}にマッチする部分
"   を順に連番と置き換える。
"   引数はいずれもオプションで、指定されていなければ
"   デフォルト値を使う
"   {pattern}は連番を挿入したい箇所の正規表現を指定する。デフォルト値は\(\d\+\)
"   {start}は連番の開始時の値を指定する。デフォルト値は0
"   {step}は連番入力時の階数を指定する。デフォルト値は1
"   {use_first}は連番挿入時、一番最初にマッチした部分の数字を
"   起点として連番を挿入するかどうかを指定する。
"   0は起点としない。1は起点とする。デフォルト値は0
"   ※ただし、{start}も指定されている場合
"     マッチした部分の数字 + {start}が起点となる

" 例:{pattern}
" visual modeで選択下範囲の、配列内の添字を連番で置き換え
:'<,'>RengBang \[\zs\(\d\+\)\ze\]
" \(...\)で指定している場合、自動で\zs,\zeを挿入するので上記は以下と同様
:'<,'>RengBang \[\(\d\+\)\]

" 行頭に連番を挿入
:'<,'>RengBang ^

" 行末に連番を挿入
:'<,'>RengBang $

" hogeの後に連番を挿入
:'<,'>RengBang hoge\zs

" パターン内にspaceを使いたい場合は\でエスケープする必要があるので注意
:'<,'>RengBang \ \(\d\+\)\ 

" 例:{start}
" 5から連番を始める(5 6 7 8 9...)
:'<,'>RengBang \(\d\+\) 5

" 例:{step}
" 5階数で連番を入力(1 6 11 16 21...)
:'<,'>RengBang \(\d\+\) 1 5

" 例:{use_first}
" 最初にマッチした数字から連番入力
" (例えば最初にマッチした数字が10の場合、10 11 12 13 14...)
:'<,'>RengBang \(\d\+\) 0 1 1

" [range]RengBangUsePrev {start} {step} {use_first}
"   一つ前に実行したRengBangもしくはRengBangUsePrevもしくは
"   次章で記載する関数連番入力系関数で指定した
"   引数を使用してRengBangを実行する
"   引数が指定されていればそれを使用し、指定されていなければ
"   一つ前に実行したときの引数を使う
"   一度も実行されていなければデフォルト値を使う

" 下記の順にコマンドを実行すると、RenbBangUsePrevは上のRengBangと同等になる。
:'<,'>RengBang \(\d\+\) 0 1 1
:'<,'>RengBangUsePrev

関数

3つの関数を用意しています。

  • rengbang#rengbang()
  • rengbang#rengbang_use_prev()
  • rengbang#config()
" rengbang#rengbang([{pattern}, {start}, {step}, {use_first}]) [range]
"   RengBangと同等。
" rengbang#rengbang_use_prev([{start}, {step}, {use_first}]) [range]
"   RengBangUsePrevと同等。

" patternの文字列を''で囲むところ以外、コマンドとあまり変わらない
:'<,'>call rengbang#rengbang('\(\d\+\)')
:'<,'>call rengbang#rengbang_use_prev()

" rengbang#config([{pattern}, {start}, {step}, {use_first}])
"   デフォルト値を変更する。
"   vimrcなどに書いておくことでデフォルト値を変更できる。

" 以下でデフォルト値を変更できる
:call rengbang#config('\[\zs\(\d\+\)\ze\]', 1, 5, 1)

operator

2つのoperatorを用意しています。

  • (operator-rengbang)
  • (operator-rengbang-useprev)

6/16 18:30 追記(operatorを本体に統合したので記載を修正)

operatorを使用するには、上でも記載したとおり、operatorを使用するには 別途vim-operator-rengbangもインストールしておく必要があります。

" <Plug>(operator-rengbang)
"   operatorで指定した範囲を[range]としてRengBangを実行します。
"   引数は全てデフォルト値を使用します。
" <Plug>(operator-rengbang-useprev)
"   operatorで指定した範囲を[range]としてRengBangUsePrevを実行します。
"   引数は一つ前にRengBang系コマンドor関数を実行したときの引数を使用します。

" operatorを使用するには以下のようにvimrcにkey mappingを設定しておきます。
map <Leader>sr <Plug>(operator-rengbang)
map <Leader>sp <Plug>(operator-rengbang-useprev)

使用例

正規表現で指定できる箇所に連番を挿入(or置き換え)できるので、 大抵のことはできますが、3つほど使用例をあげます。

先頭に連番を挿入する

使用例1

fooの後に連番を挿入する

使用例2

配列の添字がずれてしまったのをなおす

使用例3

マクロと比較して

マクロを使って連番を入力している方も多いかと思います。 マクロ使えばできるからpluginなんていらないじゃんという方もいるかと思いますが、 まぁそれはその通りで、何事もそうですが要は使いやすいと思った方を選べばいいのです。

自分も連番入力にはマクロを使うことが多いですが、場合によってはマクロで やるのが結構面倒な場合があったので本pluginを作りました。

ちなみに、マクロを使った連番、およびちょっとした便利scriptについては以下の記事が詳しいです。 (私もここで紹介されているscriptとマクロを併用していました)

Vimで番号を順番につける方法

本pluginとマクロどちらを使えばいいか、個人的な感覚ですが、ケースによって分けてみました。

本pluginを使う

  • 本pluginのデフォルト値で事足りる(コマンドor関数を引数なしで呼び出す。できればoperator使ったほうが楽)。
  • 連番を入力したいパターンが決まりきっている(vimrcにデフォルト値を設定して使用)。
  • 連番を挿入したいパターンがぱっと思い浮かぶ(その場でコマンドor関数を実行)。

マクロを使う

  • 連番入力と合わせて何かする必要がある。
  • 単純な連番ではなく、この範囲の数字は1で統一、この範囲の数字は2で統一といった複雑なことをする必要がある。
  • マクロが大好きで使うのが苦にならない(考える前に手が勝手に動く)

まとめ

以上が、Vimで連番を入力するためのVim pluginについての記事でした。

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

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

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

Vimでできる脱出系パズルゲーム

この記事はVim Advent Calendar 2012の184日目の記事です。
183日目はLinda_ppさんによる一時ウィンドウを手軽に出し入れできる tmpwin.vim を作ったでした。

Vimでできる脱出系パズルゲームのvim-duzzleを作ったのでご紹介します。 タイトルのゲームジャンル名はよくわからないので適当です(笑)。

はじめに

先日ポータル2というゲームをプレイしたのですが、あまりにも面白くて 自分も何かゲーム作りたいと思い、 ノリと勢いで作ったのが本ゲームです。 ポータル2は本題ではないので、紹介は記事の最後にしますが、ほんとうに面白いので 未プレイの方は是非プレイしてください。

なぜVimでゲームを作ったかと聞かれたらそこにVimがあったからとしか答えようがありません。。。

本ゲームはポータルにインスパイアされて作ったものですが、ゲーム内容はポータルとは何も関連がありません。

なお、このポータルというゲーム上の、ポータルという仕組みを某thincaさんがVimに持ち込もうとしています!!!
おそらく、そのうちVim Advent Calendarで記事を書かれると思うので、ポータルファンは楽しみ&必見ですね!

インストール方法

ただのVim pluginなので、他のVim pluginと同じようにインストールしてください。 NeoBundleをインストール済みであれば以下の様な感じで行けます。

NeoBundle 'deris/vim-duzzle'

もちろん、.vim(vimfiles)配下にコピペでも構いません。

開始方法

インストールしていれば、以下で開始出来ます。

:DuzzleStart

注意点として、ゲームの操作性のために独自キーマッピング (というかいろんなキーをつぶしたり設定しなおしたり)しています。

:(コマンドラインモードに移行)も使えないようにしているため、ゲーム終了には:bd!を 割り当てたQを使うしかなかったりします。

また、本ゲームは今日やっと動いた程度のできたてホヤホヤのαバージョンです。 極力作業用Vimとは別で、ゲーム用にVimを立ち上げることをおすすめします。

ゲーム実行中に問題があって、編集中のファイルが飛んでしまっても、 一切責任を取れませんのでご注意ください。

(まぁ、Vimならswapファイル作るようにしておけば万が一のことがあっても 回復出来ますけどね:help swap)

何か問題があれば本記事にコメントいただけると助かります。
(特に色々キーマップを設定されている方は弊害がある可能性が高いです。)

ゲーム内容

簡単に説明すると、与えられたVimコマンドを使って閉じ込められた部屋から出口(g)まで たどり着くゲームです。

やっていただくのが一番手っ取り早いと思います。

イメージは以下のGIFアニメーションをご覧ください。

画像

本ゲームの目的は、各部屋ごとに与えられたVimコマンドを 使って閉じ込められた部屋から脱出する(gまでたどり着く)ことです。 各部屋で与えられたVimコマンド以外は使えません。 一応キーマッピングで無効化して使えないようにしていますが、簡単な無効化しかしていないので 頑張れば、他のコマンドを使うことが出来るかもしれません。 ただ、ゲーム性が皆無になるのでおすすめしません。
(そういうハックを楽しむことが目的のゲームではありませんw)

キーを無効化する都合上、:も使えないようになっています。 なので、ゲームを終了したい場合は、Qを押下してください。

現状はVim標準コマンドだけですが、モチベーションが続けば、 そのうちゲームの拡張でVimとは関係ないコマンドも用意するつもりです。

ゲームカスタマイズ

本ゲームは自分でパズルを追加するようカスタマイズ出来るようになっています。 ゲーム内で使えるVimコマンドは、現在用意されているものしか使えませんが 同じようなパズルなら作れます。

autoload/duzzle以下にあるファイルを見ればなんとなく ゲームの追加方法がわかると思います。

追加用の関数は以下のように引数2つで呼び出します。

call duzzle#add_puzzle(
  \ '実験名(拡張ごとにパズルセットに名前を付けられるようにしています。)',
  \ {'パズルの情報を辞書形式で(詳細は実際のファイル参照)'}
  \ )

興味がある方は是非作ってみてください。 pull requestもお待ちしております。

別プラグインを作って、そのautoload/duzzle配下に同じようにファイルを配置することでも可能にしていますが、まだ動かないかもしれません。

おまけ

Vimでできる他のゲームの紹介

Lingrでmanga_osyoさんに教えていただくまで知らなかったのですが、実は Vimで出来るゲームは結構あります。
紹介記事は以下の様な物があります。

Vimで動くゲーム
3D in Vim

私はテトリスくらいしか知りませんでした。

正直どれもレベルが高いです。 興味があればプレイしてみると息抜きになっていいかもしれません。

ポータル2について

冒頭でもお話しましたが、少しだけポータル2の話をします。

ポータル2は、ポータルというゲームの続編でWikipediaによると アクションパズルゲームとあります。 初期のポータルはもともとオレンジパックという5本セットのゲームの1つだったんですが、 人気のためか?後日単品で発売されたという経緯があります。 そのゲームの続編がポータル2です。

Wikipedia:ポータル
Wikipedia:ポータル2

ゲームの肝はポータルという穴をうまく使ってゲームをクリアしていくことです。 説明が難しいのでWikipediaから抜粋します。

プレイヤーは「壁に向かって撃つと穴(ポータル)ができ、もう一つの穴の方へ移動できるポータル銃」を使い、様々な仕掛けをポータル銃で突破しながら出口へ向かう。ポータルを作れるのは金属製ではない床と壁と天井。ポータルを使って自分が移動するほか、スイッチを押すのに必要なキューブや、その他のものをポータルに入れて別の場所へ移動させる事も出来る。

なかなか、このゲームの面白さを説明するのは難しいのですが、 難しいステージ(ゲーム内ではチェンバー)をポータルをうまく使ってクリアした ときの感覚が爽快なのです。

ゲームをプレイした時の感想(イメージ)

  • 「おい、このチェンバーまじかよ。。。こんなんクリアできんのかよ」
  • 「あ!そういうことか!スゲー!マジスゲー!このゲーム考えた奴どういう頭してんだよ!」
  • 「ヒ、ヒエェ。。。(ゲームをやった事ある人ならどの場面か多分分かるはず)」

ポータルもポータル2も面白いので是非どちらともやって見ることをおすすめします。
(ちなみに個人的にはポータル2のほうが好きです。)

私はXBOX360版でやりましたが、PS3でもWindows,Macでもできます。
Windows,Mac版はセットのやつがお得っぽいです。

ポータル2 (価格改定版)

ポータル2 (価格改定版)

EA BEST HITS ポータル2

EA BEST HITS ポータル2

[価格改定]ポータル1&2パック【日本語版】

[価格改定]ポータル1&2パック【日本語版】

まとめ

以上がVimでできる脱出系パズルゲームについての記事でした。

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

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

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

特定の列まで指定した文字を連続入力するVim pluginを作った

この記事はVim Advent Calendar 2012の172日目の記事です。
171日目はmanga_osyoさんによるunite-source を作成する流れをまとめてみたでした。

整形のために、スペースをたくさん入力するのが億劫だったので題記のプラグインを作りました。 本記事はこのプラグインについての紹介記事です。

目的

コーディング規約にもよると思いますが、上の行とxxの文字と揃えるために insert modeでスペースをたくさん入力するようなケースがたまにあります。

例えば、以下の様なケース

  // ケース1
  int hogehogehoge = 0;
  int hoge         = 0;
  //      ^^^^^^^^^
  //      ここの部分のスペース

  // ケース2
  callfuncion("hoge"
             ,"hige");
//^^^^^^^^^^^
//ここの部分のスペース

ケース1は、aligntaというVim pluginで整形可能で、 ケース2は、言語によってはset indentexprが設定されていて自動で入力されたりします。

でもたまに、言語が対応してなかったり、思うようにスペース入れてくれなくて <Space><Space><Space>...とか、<Tab><Tab><Tab>...とかしてて悲しくなることがあります。 Vim使ってて、繰り返し同じキー打ってる時ほど悲しい気分になることはないので 改善することにしました。

使い方

目的の例のケース2について使い方を載せます。 なお、本プラグインはinsert modeで使うことを意図したプラグインです。

以下のアニメーションGIFをご覧ください。

f:id:deris:20130522003802g:plain

ちょっとわかりづらいかもしれませんが、insert modeで、callfunctionの下の行に カーソルがある状態で、<C-k>(デフォルトキーマッピングの場合)をタイプすると Insert above column:というメッセージが表示されるので、(とタイプし、 Insert insert column:というメッセージが表示されるので、スペースをタイプすると、 callfunctionの行の(の列までスペースが入力されます。
((が複数ある場合は、直近の(まで入力されます。)

Insert above column:で入力する文字、Insert insert column:で入力する文字は あらかじめキーマッピングで指定することができるので、例えば必ずスペースを 入力するのであれば、Insert insert column:を出さないことも可能です。

また、上の行ではなく、下の行を見たい場合は<C-j>(デフォルトキーマッピング) を使えますし、入力する位置の終端が気に食わなければオプションで調整出来ます。

インストール方法

プラグイン名はfit-columnという名前で、githubにあげてます。
(helpは、間に合わなかったので明日くらいには上げる予定です。)

なので、NeoBundleを入れていれば.vimrcに以下を書いておけばインストール出来ます。

NeoBundle 'deris/vim-fitcolumn'

もちろん、.vim配下に突っ込んでも構いません。

設定方法

デフォルトキーマッピングが以下のように割り当てているため、これで良ければそのまま使えます。

" デフォルトキーマッピング
imap <C-k>  <Plug>(fitcolumn-abovecolumn)
imap <C-j>  <Plug>(fitcolumn-belowcolumn)

デフォルトキーマッピングが気に入らなければ以下のように.vimrcに書けば変更できます。

" デフォルトキーマッピングを変更
let g:fitcolumn_no_default_key_mappings = 1
imap <C-h>  <Plug>(fitcolumn-abovecolumn)
imap <C-l>  <Plug>(fitcolumn-belowcolumn)

さらにデフォルト動作が気に入らなければ、関数にマッピングすることで設定出来ます。 例えば、入力する文字を手動入力でなく、固定でスペースに割り当ててしまいたい場合、 以下のように設定することで割り当てられます。

" 入力文字をスペースにする
let g:fitcolumn_no_default_key_mappings = 1
inoremap <expr> <C-k>  fitcolumn#fitabovecolumn({
  \ 'insertchar': ' ',
  \ })
inoremap <expr> <C-j>  fitcolumn#fitbelowcolumn({
  \ 'insertchar': ' ',
  \ })

他にも、searchcharrightwardというオプションがあります。
searchcharは、使い方の章で紹介したInsert above char:で 入力する文字を指定することができます。
rightwardは、スペースを入力する終端を数値で微調整できます。 例えば、1を指定すると1文字多めに文字を入力します。 マイナスも指定出来ますが、入力する文字がなくなった場合は 何も入力されません。

例えば、以下のように設定出来ます。

" 上(下)の行の、'('と同じ位置まで、スペースを入力する
let g:fitcolumn_no_default_key_mappings = 1
inoremap <expr> <C-k>  fitcolumn#fitabovecolumn({
  \ 'insertchar': ' ',
  \ 'searchchar': '(',
  \ 'rightward': 1,
  \ })
inoremap <expr> <C-j>  fitcolumn#fitbelowcolumn({
  \ 'insertchar': ' ',
  \ 'insertchar': '(',
  \ 'rightward': 1,
  \ })

おまけ(考察)

使い方のケース2で挙げたケースの場合は、set indentexpr用の関数書いて設定するのが正解な 気もするんですが(lingrvim部屋でご指摘頂きました。)、 別解を思いついて作ってしまったのでVACに書いて公開することにしました。

作ってみたものの、使い道は限られるかもしれません。

まとめ

以上が、自作Vim pluginのご紹介でした。

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

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

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

追記(2013/5/22)

記載を忘れていたので追記します。

  • 今のところタブは対応していません。要望があれば対応するかも

Vimでパターン検索するなら知っておいたほうがいいこと

この記事はVim Advent Calendar 2012の166日目の記事です。
165日目はaueweさんによる.vimrcに書くべきでないVimの設定項目でした。

Vimに限りませんが、テキストエディタを使う際にテキスト検索はよく使うと思います。 ある程度慣れた人だと正規表現を用いたパターン検索を使うようになります。 もちろん、Vimにも正規表現を用いたパターン検索を行う機能は備わっています。 しかし、お世辞にもVimのデフォルト設定ではパターン検索は書きやすいとは言えません。 特に、他の正規表現方言に慣れている人は、Vim正規表現の書き方の違いに戸惑うと思います。 本記事では、Vimでパターン検索するなら知っておいたほうがいいこと+ Vimのパターン検索に使えるメタ文字をご紹介します。

パターンって何?という方は:help usr_27.txtを一読することをおすすめします。 一般的な正規表現について詳しく知りたい方は、ウェブや書籍などで一度しっかり勉強することをおすすめします。 ちょっと難しいですが、おすすめの書籍はサイドバーに載せてますのでご参考までに。

Vimでのパターン検索

Vim正規表現を用いたパターンは、検索コマンド/での検索、:subsutituteコマンド のパターン部分、:global:vglobalコマンドのパターン部分などで使うことができます。

他にもVim scriptの正規表現マッチ(=~,!~)や、いくつかの関数で使用出来ます。

ただ、オプションによってパターンで扱うメタ文字(正規表現として扱う特殊な文字、文字列)の指定の仕方に違いがあります。 オプション名はmagicで、現在のオプション値を確認するためには以下のようにコマンドを実行します。

:set magic?

デフォルトはmagicなので、おそらくmagicと表示されるはずです。

試しに検索コマンドでパターン検索をしてみましょう

以下は、0文字以上の任意の文字列にマッチするパターンです。

/.*

普段は.*だけでパターンを書くことはないと思いますが、一般的な正規表現ですね。

次は、fooかbarにマッチするパターンを入力してみましょう。

/\(foo\|bar\)

検索は出来ましたが、何か\でたくさん前置してますね。 そうなんです。Vimのデフォルトのmagicオプションでは、一部の特殊文字は、 \で前置しないとメタ文字として扱われません。

厄介なのが、メタ文字として扱うために前置する必要がある文字と、 前置が不要な文字が混在しているのです。

例えば、magicの場合、.,*,^,$\で前置しなくてもメタ文字として扱われますが、 (,),|,+,?\で前置しないとメタ文字として扱われません。

正直慣れていても、誤爆する事が多々あります。

要は慣れてないってことですねorz
特に+は鬼門。。。

very magicを使う

上記問題は、very magicを使うことで解消出来ます。 vim Hacksでも紹介があったのでご存じの方も多いと思います。

Hack #55: 正規表現のメタ文字の扱いを制御する

この機能を使うと、正規表現に使うすべての特殊文字\で前置せずに使えるようになります。 ただ残念ながらこの機能、set verymagicのようにオプションとして設定出来ません。 very magicを有効にするには、パターンを入力するところで\vと入力します。 すると\v以降がvery magicとして扱われます。 例えば検索コマンドでは以下の様に入力します。

/\v

very magicを有効にした状態でfooかbarにマッチする文字列を検索してみましょう。

/\v(foo|bar)

これで、パターン検索する際に(|)\で前置する必要はなくなりました。

他にも、+,?,{など、よく使う正規表現のメタ文字が\での前置なしで使えるようになります。

私はパターン検索を常用しており、毎回\vと入力するのが嫌なので、以下のようにキーマップを設定しています。

nnoremap /  /\v

これで大分Vimでのパターン検索がしやすくなりました。

Vimのパターン検索に使えるメタ文字

very magicで、パターン検索でのメタ文字の入力が楽になりました。 ただ、Vimのパターンはちょっとした方言があるので、よく使うメタ文字をまとめておきたいと思います。

わかりやすさのため使っている人が多いと思われるPerl正規表現方言との比較を載せます。 簡単な比較は:help perl-patternsに載っています。

ここでまとめるVimのメタ文字はすべてvery magicの指定を前提としており\で前置していませんのでご注意ください。 また、全てを網羅しているわけではないので、詳細が知りたい方は:help pattern-overviewで調べるといいです。 ただし、helpにはmagicnomagicしか載ってません。といってもvery magicは基本的にはmagicで 前置されている\をとるだけなのでhelpを見て困ることはないと思います。

Perlのメタ文字 Vimのメタ文字 説明 備考
. 同じ 任意の1文字
* 同じ 直前のアトムの繰り返し(0回以上) 最長マッチ
+ 同じ 直前のアトムの繰り返し(1回以上) 最長マッチ
? 同じ(=も同じ) 直前のアトム(0回、または1回) 最長マッチ
{n,m} 同じ 直前のアトムの繰り返し(n回以上m回以下) 最長マッチ
^ 同じ 先頭にマッチ
$ 同じ 末尾にマッチ
(...) 同じ グループ化してアトムにする
| 同じ 選択の区切り
[...] 同じ [...]内の任意の1文字にマッチ
\w 同じ 単語を構成する文字([0-9A-Za-z_])
\W 同じ 単語を構成する文字以外([\^0-9A-Za-z_])
\d 同じ 数字([0-9])
\D 同じ 数字以外([\^0-9])
\s 同じ 空白文字(微妙に違う。後述)
\S 同じ 空白文字以外(微妙に違う。後述)
*? {-} 直前のアトムの繰り返し(0回以上) 最短マッチ
+? {-1,} 直前のアトムの繰り返し(1回以上) 最短マッチ
?? {-0,1} 直前のアトム(0回、または1回) 最短マッチ
{n,m}? {-n,m} 直前のアトムの繰り返し(n回以上m回以下) 最短マッチ
\b < or > 単語の境界にマッチ(<は単語先頭、>は単語末尾)
(?=atom) atom@= 幅ゼロの肯定先読み
(?!atom) atom@! 幅ゼロの否定先読み
(?<=atom) atom@<= 幅ゼロの肯定後読み
(?<!atom) atom@<! 幅ゼロの否定後読み
(?>atom) atom@> 幅ゼロの否定後読み
(?:...) %(...) グループ化して部分正規表現としてカウントしない

その他Vim特有の便利なメタ文字

Vim特有で便利なメタ文字を紹介します。

メタ文字 説明 備考
\zs どこにでもマッチしてマッチの開始地点を設定する foo\zsbarとすればfoobarのbarの部分にマッチします
\ze どこにでもマッチしてマッチの終了地点を設定する foo\zebarとすればfoobarのfooの部分にマッチします
%[...] 任意にマッチするアトム列 r%[ead]とすれば'r','re','rea','read'にマッチします

PerlVim正規表現の意味が異なる主なメタ文字

PerlVimで意味が異なる主なメタ文字を紹介します。 他にもあるので、使用する際は、:help \xなどで調べてから 使用することをおすすめします。

メタ文字 Perl Vim
\s スペース、タブ(\t)、改行文字(\n)、復帰文字(\r)、フォームフィード(\f) スペース、タブ(\t)
\S 上記以外 上記以外
\b 単語境界 <BS>にマッチ
\B 単語境界以外 なし
\A 文字列の先頭 英字以外
\Z 文字列の最後 Unicodeの合成文字は無視

very nomagicを使う

very magicでメタ文字を入力しやすくなりましたが、\で前置が必要な文字がいっぱいあって、 メタ文字じゃなくてその文字自体を検索したい場合に困るという方は、 very nomagicを使うと便利です。

very nomagicはパターンを指定するところで、\Vと入力するとそれ以降の文字で すべての特殊文字\で前置が必須になります。 つまり\を検索したい場合だけ\\とすればよく、他の文字は\で前置せずに 検索することが出来るようになります。

例えば、very magicで入力していて、ここからは普通にテキスト検索したいと思ったら途中で \Vvery nomagicに切り替えることができます。 以下の様に入力します。

/\v(foo|bar)\V(foo|bar)

これは、fooかbarにマッチする文字列の後に(foo|bar)という文字列を検索という意味になります。

ちなみにデフォルトのmagic\m、他にもnomagicの指定は\Mと指定することで切り替えられます。 \v,\V,\m,\Mはやろうと思えば、パターン内で何度も切り替えられるので便利です。

エスケープが必要な文字

very magicだとすべての特殊文字が前置不要になるためその特殊文字自体に マッチさせたい場合は\でエスケープが必要になります。 また、very magicmagicnomagicvery nomagicと、それぞれエスケープが 必要な文字が異なります。

ここでは、very magicmagicnomagicvery nomagic のそれぞれでエスケープが必要な特殊文字(つまりその文字単体でメタ文字として扱われる文字)をまとめます。

very magicの場合

  • \
  • .
  • *
  • +
  • ?
  • =
  • {
  • ^
  • $
  • (
  • )
  • |
  • [
  • &
  • @
  • ~
  • /(検索コマンド/で検索する場合、/でパターンを囲う場合)

特に、=,&,@,~は、Perl正規表現方言ではメタ文字ではないのでハマりポイントです。

magicの場合

  • \
  • .
  • *
  • ^
  • $
  • [
  • ~
  • /(検索コマンド/で検索する場合、/でパターンを囲う場合)

nomagicの場合

  • \
  • ^
  • $
  • /(検索コマンド/で検索する場合、/でパターンを囲う場合)

very nomagicの場合

  • \
  • /(検索コマンド/で検索する場合、/でパターンを囲う場合)

おまけ

関連オプション

パターン検索に関連するオプションを紹介します。

" 大文字と小文字を無視する
set ignorecase
" 大文字と小文字を無視しない
set noignorecase

" 検索パターンが大文字を含んでいたら'ignorecase'を上書きする。('ignorecase'オンのときのみ使われる)
set smartcase
" 'ignorecase'を上書きしない
set nosmartcase

" メタ文字として扱う文字を`magic`で指定(`.`,`*`,`$`,`^`,`[`などを`\`で前置なしでメタ文字として扱う)
set magic
" メタ文字として扱う文字を`nomagic`で指定(`$`,`^`などを`\ `で前置なしでメタ文字として扱う)
set nomagic

Perlのplugin使う

Perl/Ruby方言の正規表現で検索、置換などができる eregex.vimというplugin があります。

ただ、eregex.vimインクリメンタルサーチができません。 何より、突然素のVimを使う必要に迫られた際や、 急に思い立ってVim scriptを書きたいと思った時にも、 Vim正規表現が使えなくて困るかもしれません。

なので、本記事ではインクリメントサーチが不要な方で、 どうしてもPerl/Ruby方言の正規表現じゃなきゃやだ! という方以外にはおすすめしません。

ちなみに私はPerl使い(というほどたいしたものでもありませんが。。。)なので、 しばらく、eregex.vimを使っていた時期がありました。 ただ、very magicを知ってからは、Vim正規表現もそんなに不便なく使えることがわかったので、 今はVimのパターン検索を使っています。

Vimのパターン検索で参考になるhelp

Vimのパターン検索は以下のhelpを参照するといいです。 本記事で書いたことはほとんどhelpに書かれていることです。

:help usr_27.txt
:help pattern.txt

まとめ

以上が、Vimでパターン検索する際に知っておくと便利なことでした。 正直ほとんどhelpに載っている話だったり、他の方が書かれた記事と変わらなかったりしますが、自分の整理のために書いてみました。
ちょっととっつきにくいだけでVim正規表現は普通に使えるのでちょこちょこ使ってVim正規表現に慣れていくことをおすすめします。

もしパターン検索について、こんなことも載せて欲しいとか、これも載せたほうがいいとかあれば、コメントやTwitterなどでご指摘いただけるとうれしいです。

あと、Vimのパターン検索に限りませんが、Vimレベルが上がるのでvimrc読書会に参加することをおすすめします。毎週土曜日23時からやってます。 次回(5/18)は大御所kanaさんのvimrcを読む予定です。すごいことになりそう。。。

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

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

Vimでgitのログをきれいに表示する

この記事はVim Advent Calendar 2012の161日目の記事です。
160日目はrbtnnさんによるEffective NeoBundle -- autoload関数を理解しNeoBundleを使いこなすための8の方法 --でした。

Inspired by ujihisa's VAC

本記事は、Vim Advent Calendar 2012 143日目にujihisaさんが書いた記事 撮った動画にインスパイアされて書いた記事です。ujihisa++

ujihisaさんの動画はこちら

ujihisaさんの動画を見て、GitLogViewer便利と思って早速設定してみました。 たしかに便利なんですが、コミットメッセージが表示されないので いちいち折畳を開かないと何のコミットなのかわからなくてちょっとめんどうだと思いました。 tigのメイン画面みたいに表示できたらもっと便利なのになーと思って ちょこちょこいじってたらそれっぽいものができました。 なので、本来今日上げようと思っていた記事を放り投げてこのネタで VACを書くことにしました。 本来今日あげようと思っていた記事は別の機会に載せます。

ちなみにtigって何?という方のために一応説明しておくと、 CUI上でGitのログとかdiffとかを見れるGitブラウザってやつですね。 紹介記事はWebに色々上がっているので詳しく知りたい方はググっていただきたいですが、 例えば以下の記事を見たらなんとなくイメージが掴めるかと思います。
CUI な Git ブラウザ tig を入れてみた

設定を載せる前に、実際の表示される画面の画像を載せておきます。

f:id:deris:20130510001655p:plain

画面の上部ウィンドウがgitのログを折りたたんで表示したものです。 流石にマージのグラフ表示とか色付けとかできてませんが、tigのメイン画面そっくりですね。 Date,Author,Commit Titleの順で並んでます。

折畳なのでzoなどで開けば以下の画像のように各コミットの中身が見れます。diffが見れて便利。 折畳を閉じる時はzcですね。

f:id:deris:20130510001703p:plain

以下の設定をすることで表示できます。元ネタのujihisaさんの設定もそうですが、動作にはvimprocが必要です。

" Inspired by ujihisa's vimrc
function! s:GitLogViewer()
  " vnewだとコミットメッセージが切れてしまうのでnew
  new
  VimProcRead git log -u 'ORIG_HEAD..HEAD'
  set filetype=git-log.git-diff
  setlocal foldmethod=expr
  setlocal foldexpr=getline(v:lnum)=~'^commit'?'>1':getline(v:lnum+1)=~'^commit'?'<1':'='
  setlocal foldtext=FoldTextOfGitLog()
endfunction
command! GitLogViewer call s:GitLogViewer()

" git log表示時の折りたたみ用
function! FoldTextOfGitLog()
  let month_map = {
    \ 'Jan' : '01',
    \ 'Feb' : '02',
    \ 'Mar' : '03',
    \ 'Apr' : '04',
    \ 'May' : '05',
    \ 'Jun' : '06',
    \ 'Jul' : '07',
    \ 'Aug' : '08',
    \ 'Sep' : '09',
    \ 'Oct' : '10',
    \ 'Nov' : '11',
    \ 'Dec' : '12',
    \ }

  if getline(v:foldstart) !~ '^commit'
    return getline(v:foldstart)
  endif

  if getline(v:foldstart + 1) =~ '^Author:'
    let author_lnum = v:foldstart + 1
  elseif getline(v:foldstart + 2) =~ '^Author:'
    " commitの次の行がMerge:の場合があるので
    let author_lnum = v:foldstart + 2
  else
    " commitの下2行がどちらもAuthor:で始まらなければ諦めて終了
    return getline(v:foldstart)
  endif

  let date_lnum = author_lnum + 1
  let message_lnum = date_lnum + 2

  let author = matchstr(getline(author_lnum), '^Author: \zs.*\ze <.\{-}>')
  let date = matchlist(getline(date_lnum), ' \(\a\{3}\) \(\d\{1,2}\) \(\d\{2}:\d\{2}:\d\{2}\) \(\d\{4}\)')
  let message = getline(message_lnum)

  let month = date[1]
  let day = printf('%02s', date[2])
  let time = date[3]
  let year = date[4]

  let datestr = join([year, month_map[month], day], '-')

  return join([datestr, time, author, message], ' ')
endfunction

一応gistにもあげておきました。

この設定を書いて、おもむろにGitLogViewerコマンドを実行すると上の画像のように 別ウィンドウでgitのログが表示されます。
ちなみにvimのカレントディレクトリ(:pwdで表示されるディレクトリ)でgit logコマンドを 実行するので、当たり前ですが、カレントディレクトリはgitで管理している ルートディレクトリ(.gitがあるディレクトリ)以下である必要があります。 必要に応じて:lcd,:cdなどで移動しておきましょう。
自動でプロジェクトルートディレクトリに移動してくれるvim-rooterなんて pluginもあるので必要に応じて入れておくといいと思います。

あと、ujihisaさんの動画でご紹介の通り、motemenさんのgit-vimという pluginを入れておくとGitLogViewerで表示されたウィンドウでの差分などの表示が 良い感じに色付けして表示されるのでこちらも必要に応じて入れておきましょう。 (というか元々は、git-vimで設定している FileTypegit-logをGitLogViewerで設定しているだけなんですけどね)

私は上記設定の他にautocmd FileType git-logでmotemenさんのgit-vimGitLogコマンドの出力も折畳で表示するようにしていますが、オプションによっては うまく表示されない可能性があるので、その設定は載せてません。

他にやりたいこと

大分きれいに表示されて自分的には満足なんですが、出来れば以下も設定したいなぁ。

  • tigみたいにブランチの位置も表示
  • 色付け(折畳表示なのでたぶん無理)

やるかどうかは未定。

おわり

折畳は普段あまり便利だと思ったことはないんですが、今回のように表示専用で 使う分にはスッキリ表示できて便利ですね。

折畳の設定などの普段使わない設定や、今回紹介したpluginなど新たな発見が あるのでvimrc読書会に参加することをおすすめします。
毎週土曜日23時からやってます。
ちなみに、5/11(土)は私のvimrc(後半)です。gkbr

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

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

追記(2013/5/10)

  • 今回載せた設定でGitLogViewerを実行した場合、本文中の画像と異なり、 ログが表示されるウィンドウの先頭行に空行が出ると思います。 本文中に載せた画像は自分が別で設定したmotemenさんのGitLogコマンドで 出力したもの折畳表示したものだったため微妙な差異が出てしまいました。すいません。
  • 本文中に補足するのを忘れていましたが、記事に載せた設定はgit log -u 'ORIG_HEAD..HEAD'としてます。 つまり、ORIG_HEADとHEADの間に何もなければ何も表示されません。必要に応じて書きかえてください。