Home > Vim Hacks

Vim Hacks Archive

Hack #78: CSVの特定のカラムをハイライトする

Excel形式はともかく、CSV形式のファイルであればプレーンテキストですのでVimで編集する場合もあるでしょう。

CSVは大抵の場合、テキストエディタで開くと各カラムの長さがバラバラで視認性にかけます。Hack #77の方法で縦を揃えることもできますが、別の方法として、ある特定のカラムをハイライトする方法を紹介します。

function! CSVH(x)
    execute 'match Keyword /^\([^,]*,\)\{'.a:x.'}\zs[^,]*/'
    execute 'normal ^'.a:x.'f,'
endfunction
command! -nargs=1 Csvhl :call CSVH(<args>)

以上のコードを.vimrcに書けば準備OKです。このコードの詳細は割愛します。以上のコードで:Csvhl n(nは0〜の数字)でn番目のカラムをハイライトすることができます。注意してほしいのは、0番目、1番目とカラムをカウントする点です。

Hack #76: Insert mode中で単語単位/行単位の削除を行なう

問題

テキスト入力中に誤入力をしてしまうことはしばしばあります。 例えば

if (foo == bar|

とまで入力したところでbarはloungeの間違いだったことに気付いたとしましょう (「|」はカーソル位置です)。 これには

<Esc> b ciw lounge

のようにカーソル位置を調整して該当単語を修正することができます (読み易さのためにスペースを入れていますが実際にスペースは入力しません)。

しかしこの手順は面倒です。Insert mode中のまま簡単に削除を行なうことは できないでしょうか。

解決方法

これにはInsert mode中で<C-w>を入力します。 例えば最初に例示した状態で

if (foo == bar|

<C-w>を入力すると以下の状態になります。

if (foo == |

Insert mode中での<C-w>は カーソル位置より前にある単語を削除することができます。

関連項目

<C-u>というコマンドもあります。 これはカーソル行で入力された文字を全て削除します。 例えば以下のようにテキスト入力を行なったとしましょう (「^」はInsert modeの開始位置です)。

if (foo == lounge) retrun a|
                   ^

ここで<C-u>を入力すると以下の状態になります。

if (foo == lounge) |
                   ^

また、<C-w><C-u>を使う場合は ‘backspace’の値を以下のように調整しておくと より便利に使うことができるでしょう。

set backspace=indent,eol,start

参考資料

kana

Hack #75: 正規表現で先読み/後読みを使用する

Hack #70 で、正規表現においてマッチする範囲を制限する方法を紹介しましたが、他の正規表現エンジンで同じようなことをしたい場合、肯定先読みや否定先読みと言った機能が使われます。 そして当然、Vim にも肯定先読みや否定先読みの機能は備わっています。

肯定先読み/否定先読み/肯定後読み/否定後読み

先読み、後読みとはすなわち、特定のパターンが対象のパターンの後ろに来る(先読み)、もしくは前に来る(後読み)ことを条件付けることです。否定の場合は逆に来ないことを条件付けます。

肯定の場合は単に続けて正規表現を構成した場合でもマッチする個所は同じですが、マッチする対象が異なります。先読み、後読みの部分はあくまで条件なのでマッチ対象には含まれません。

Vim では以下のパターンを置くことで直前の文字、もしくはグループを先読み、後読みに指定します。

肯定先読み\@=
否定先読み\@!
肯定後読み\@<=
否定後読み\@<!

例えば、

\w\+\%(()\)\@=

とすると直後に "()" が来る単語にマッチします。また、

\%(function\s\+\)\@<!\w\+

とすると直前に "function" と空白文字が来ない単語にマッチします。

\zs \ze との関係

\zs や \ze は肯定先読み/肯定後読みの省略形と考える事ができます。すなわち、

foo\zsbar\ze\baz

\%(foo\)\@<=bar\%(baz\)\@=

は同義になります。ただし、読み易さや処理速度を考えると可能ならば前者を使う方が良いでしょう。

Perl との比較

Vim の先読み/後読みは他の正規表現エンジンとは少し違う書き方をします。 例えば Perl の正規表現エンジンでは () でグループを作り、グループに対して先読み、後読みの指定をします。 対して Vim では、直前の文字、もしくはグループに対して先読み、後読みを指示する指定子があります。 Perl の書き方に慣れた人にとっては最初はわかりづらいかもしれませんが、慣れれば大した違いはありません。

VimPerl
肯定先読み\%(pattern\)\@=(?=pattern)
否定先読み\%(pattern\)\@!(?!pattern)
肯定後読み\%(pattern\)\@<=(?<=pattern)
否定後読み\%(pattern\)\@<!(?<!pattern)

また、Vim の後読みは Perl と違い可変長パターンも扱えます。ただし後読みは少々重いので処理速度には注意が必要です。

thinca

Hack #74: 簡単にvimrcを編集する

Vimの戦闘力を効率的に上昇させるためには、素早く.vimrcや.gvimrcを編集できる環境が必要となります。ここではVimの設定を工夫することで、.vimrcの変更を即Vimに反映させる方法について解説します。

.vimrcや.gvimrcを編集するためのKey-mappingを定義する

まずはすぐに.vimrcや.gvimrcを編集できるように、.vimrcにKey-mappingを定義しましょう。

nnoremap <silent> <Space>ev  :<C-u>edit $MYVIMRC<CR>
nnoremap <silent> <Space>eg  :<C-u>edit $MYGVIMRC<CR>
$MYVIMRCというのは特殊な環境変数で、現在使用している.vimrcへのパスが含まれています。$MYGVIMRCも同様に、.gvimrcへのパスが含まれています。 環境によって.vimrcや.gvimrcへのパスは異なるため、これを利用することで確実に.vimrcや.gvimrcを編集することができます。

.vimrcや.gvimrcの変更を反映するためのKey-mappingを定義する

即座に.vimrcや.gvimrcを編集できるように設定しても、設定を反映させるためにはVimをいったん再起動しなければならず不便です。 sourceコマンドを用いれば.vimrcや.gvimrcを再度読み込むことができるので、.vimrcや.gvimrcに行った変更を現在のVimに反映できるように、 .vimrcにKey-mappingを定義しましょう。

" Load .gvimrc after .vimrc edited at GVim.
nnoremap <silent> <Space>rv :<C-u>source $MYVIMRC \| if has('gui_running') \| source $MYGVIMRC \| endif <CR>
nnoremap <silent> <Space>rg :<C-u>source $MYGVIMRC<CR>

.vimrcや.gvimrcを変更すると、自動的に変更が反映されるようにする

しかし変更を適用するたびに、いちいちキーを入力するのは面倒です。自動的に変更が反映されるように.vimrcに設定してしまいましょう。

" Set augroup.
augroup MyAutoCmd
    autocmd!
augroup END

if !has('gui_running') && !(has('win32') || has('win64'))
    " .vimrcの再読込時にも色が変化するようにする
    autocmd MyAutoCmd BufWritePost $MYVIMRC nested source $MYVIMRC
else
    " .vimrcの再読込時にも色が変化するようにする
    autocmd MyAutoCmd BufWritePost $MYVIMRC source $MYVIMRC | 
                \if has('gui_running') | source $MYGVIMRC  
    autocmd MyAutoCmd BufWritePost $MYGVIMRC if has('gui_running') | source $MYGVIMRC
endif

記述の注意

.vimrcや.gvimrcを何度も読み込む設定にしていると、記述の仕方によってはエラーが発生したり動作がおかしくなったりします。 ここでは、それらへの対策を解説します。

関数やコマンドの定義

普通関数やコマンドを定義するときには、commandfunctionを使うことがあるかも知れませんが、これらはすでにある関数やコマンドを上書きできないため、設定ファイルを何度も読み込ませるとエラーとなります。上書きできるように、command!function!を使用しましょう。

autocmdの定義

autocmdは常に追加されます。設定ファイルを何度も読み込むとautocmdが頻繁に追加され、Vimの動作がおかしくなる原因となります。 これを防ぐためには、.vimrcの先頭で一度autocmdをリセットします。

autocmd!

・・・・
autocmd FileType * set textwidth=0
autocmd FileType * set formatoptions-=ro
autocmdを全部削除するのに抵抗があるのならば、augroupを設定するとさらに便利になります。詳しくは:help augroupを参照してください。
augroup MyAutoCmd
    autocmd!
augroup END

・・・・
autocmd MyAutoCmd FileType * set textwidth=0
autocmd MyAutoCmd FileType * set formatoptions-=ro

オプションの設定

オプションの初期値に値を追加する場合、次のように設定することがあると思います。

set hoge_option+=piyo
しかし設定ファイルを何度も読み込ませる場合、前の値が追加されてしまい、動作がおかしくなります。.vimrcには次のように設定し、一度初期値をロードするようにしましょう。
set hoge_option&
set hoge_option+=piyo

ここに挙げたことは、「Vimの極め方」やWEB +DB PRESS Vol.52の特集「Vimの流儀」にも詳しく書いてあります。どちらもkana氏の書いた必読の資料なので、参考にすると良いでしょう。

Shougo

Hack #73: setのちょっとしたtips

vimrcの編集をしたことがある人であれば、ほとんどの人が’set’は使ったことがあるでしょう。今回はその’set’に関するちょっとしたtipsを紹介します。

VimにはいくつかOn/Offが対応した設定項目があります。例えば’number’と’nonumber’、’list’と’nolist’などです。これらにはちょっとした機能が備わっています。以下、’number’の例です。


" 現在の値を表示 number/nonumber           
:set number?
" デフォルト値に戻す
:set number&
" On/Offのトグル
:set number!

‘number!’などのトグル系はkey mappingを割り当てておくと意外と便利です。

オマケとして:setを実行すると、デフォルト値以外が設定されているもののリストを表示することができます。

ukstudio

Hack #72: 英語の誤入力を防ぐ

問題と解決

Vimで英文を入力するとき、他のメーラや多くのアプリケーション同様、辞書を用いた英単語スペルチェックを行なうことができます。

スペルチェック機能を使いたいときはsetl spellを、解除したいときはsetl nospellしましょう。

nancy

(unfortunatellyではなく、正しくはunfortunatelyですね)

vim

(vimではなく大文字のVimです)

字の文としてだけではなく、プログラミングを行なうときにも変数名などに自信がなければ、一時的にスペルチェック機能をオンにすることで、 スペルミスをしていない確信を持つことができます。

ただし固有名詞などで辞書にない用語を使うとき、画面が真っ赤になり目が痛くなってしまいます。

uji

この場合、その単語の上でzgを打鍵し、その単語を辞書に登録することで解決します。

uji2

逆に、その単語を誤った単語とみなしたいときはzwを実行しましょう。gとwはgoodとwrongと覚えておきましょう。

議論

お使いのVimが+syntax付きでコンパイルされていなければVim付属のスペルチェック機能は使えません。 +syntaxの有無は、シェルで$ vim --versionするか、Vim内で:versionすることで確認できます。

また、スペルミスの確認ではなく辞書を用いた正しい英単語候補から絞り込んで補完を行なう場合は、 Hack #4: Insert mode補完 導入編を参照ください。

参考

  • :h spell
ujihisa

Hack #71: 編集操作を繰り返す

問題

バッファ中のある単語FOOを別の単語BARに置換するとしましょう。 これには

:%s/FOO/BAR/g

というコマンドでできるのですが、これはバッファ中の全てのFOOをBARに置換します。 全てではなく一部のFOOのみを置換したい場合はどうすれば良いでしょうか。

単純な方法は、該当する単語を

ciwBAR<Esc>

のようにして置換することですが、置換したい箇所で毎回このコマンドを入力するのは 苦痛です。どうにかして簡単にできないでしょうか。

解決方法

Vimでは最後に行なわれた編集操作を繰り返すことができます。 繰り返しを行うにはNormal modeで

.

を入力します。例えば最初に挙げた

ciwBAR<Esc>

を行なった後は.を入力するだけで同じ操作を行うことができます。

応用例

.による繰り返しはcだけでなく、 Normal modeから実行できるコマンドであればほぼ全てを繰り返すことができます。

なお、最初に挙げた単語置換を行なう場合、 予め置換したい単語を/FOOなどで検索しておけば、

n.n.n.

などとすることで次々と単語置換を行うことができます。

:%s/FOO/BAR/gc

とすれば各箇所で置換を行うかどうかを対話的に指定できるのですが、 単語の出現箇所が多く置換する箇所がまばらな場合には n.n.n.の方が便利です。

また、置換したいテキストが「FOOBARのBARの部分」(/FOO\zsBAR) のように特定の単語ではない複雑なパターンの場合、 textobj-lastpat プラグインをインストールしておくと

ci/XXX<Esc>

のようにi/で検索にマッチしている箇所を選択することができ、 便利です。

参考資料

kana

Hack #70: 正規表現でマッチする範囲を制限する

Vim の正規表現には変わった機能として、マッチングの範囲を制限する機能があります。他の正規表現エンジンではあまり見かけない書き方ですが、使い方さえわかれば非常に便利な機能です。

\zs と \ze

\zs (start の s) と \ze (end の e) を使うことでマッチする範囲を限定することができます。例えば、

/function\s\+\zs\k\+

と検索すると、function に続く単語が検索されます。この際、function の部分はハイライトされません。なぜなら、\zs のある箇所からがマッチ対象になるためです。同様に \ze があった場合はそれ以降の文字列はマッチ対象から外れます。

応用例

実際のところ、普段の検索では特段便利な機能ではありません。ここではこれらの使いどころを紹介します。

置換

:s/function\s\+(\zs.*\ze)//g

この置換ではマッチした対象を削除しますが、実際にマッチする対象を制限しているのでその部分だけ削除されます。よってこの置換では、"function" に続く括弧内の文字列が削除されます。

matchstr()

matchstr() 関数は文字列中のマッチする部分を返します。つまり \zs \ze で範囲を制限すれば好きな部分を取得できるということです。 これを使わなかった場合、matchlist() と後方参照を用いれば同様のことは可能ですが非常に煩雑になります。matchlist() はマッチ対象と括弧でグルーピングされた後方参照をリストで返します。 両者の方法を比較すると、例えば、"function" に続く括弧内の文字列を取得する場合は以下のようにします。

let match = matchstr(targetstr, 'function\s\+(\zs.*\ze)')

let list = matchlist(targetstr, 'function\s+(\(.*\))')
let match = empty(list) ? '' : list[1]

split()

split() 関数は文字列を正規表現がマッチする箇所で分割してリストにして返します。後方参照などは使えないので \zs \ze が威力を発揮します。 また、通常は区切る対象(正規表現がマッチした部分)は分割する際に消えてしまいますが、マッチ対象を調整することでこれも回避できます。 例えば、全ての文字にマッチさせつつ対象が空になるようにすれば、文字単位で分割することもできます。

let chars = split('こんにちは!', '.\zs')  " chars == ['こ', 'ん', 'に', 'ち', 'は', '!']
thinca

Hack #69: 簡単にカレントディレクトリを変更する

Vimは独自のカレントディレクトリを持っていて、タグファイルを参照するときにはカレントディレクトリにあるタグファイルを利用します。

'autochdir'の問題点

Vimには'autochdir'というオプションがあり、バッファを移動したとき、自動的にカレントディレクトリを変更することができます。しかし、このオプションはお薦めできません。なぜならば、タグジャンプでバッファを移動したときにもカレントディレクトリが変更されてしまい、タグファイルを辿れなくなってしまうからです。カレントディレクトリの移動は手動で行うようにしましょう。

カレントディレクトリ移動のためのキーマッピングを定義する

とはいえ、いちいちcdコマンドを入力するのは大変です。カレントディレクトリ移動はほとんどの場合、Vimで開いているファイルと同じディレクトリに移動したいことが多いです。よって単純なコマンドを作り、それに専用のキーマッピングを割り当てると便利でしょう。私は次のように設定しています。
command! -nargs=? -complete=dir -bang CD  call s:ChangeCurrentDir('<args>', '<bang>') 
function! s:ChangeCurrentDir(directory, bang)
    if a:directory == ''
        lcd %:p:h
    else
        execute 'lcd' . a:directory
    endif

    if a:bang == ''
        pwd
    endif
endfunction

" Change current directory.
nnoremap <silent> <Space>cd :<C-u>CD<CR>
簡単に解説すると、CDというコマンドを定義し、<Space>cdに割り当てています。コマンドが引数なしで呼び出されたときは、現在開いているファイルと同じディレクトリに移動し、そうでない場合は引数のディレクトリに移動します。:CD!として呼び出された場合、移動先のカレントディレクトリを表示し、どこに移動したのかを分かり易くしています。

Shougo

Hack #68: Subversionを使う

VimからGitを扱う方法をHack #67で紹介されていますが、まだまだSubversionを使用している人も多いでしょう。今回はVimからSubversionを扱う方法を紹介します。

http://www.vim.org/scripts/script.php?script_id=90

vcscommand.vimをインストールするといくつかのコマンドが使えるようになります。その中から使用頻度が高いと思われるものを幾つか紹介します。

VCSCommit

現在のバッファを対象にコミットを行います。<Leader>ccを実行すると一旦コミットメッセージを書く状態に遷移し、もう一度<Leader>ccを実行するとコミットを行います。

VCSVimDiff

個人的にはこれが一番使用頻度が高いです。現在のバッファと前回コミット時の状態を差分を表示します。VimDiffを使って差分を表示しますので、普段からVimを使っている人にも比較的使い易いコマンドでしょう。

その他のコマンドのついては:h vcscommandで調べるとよいでしょう。

ukstudio

ホーム > Vim Hacks

Search
Feeds
Links
  • 公式
  • 勉強会
  • 情報
  • コミュニティ
  • プラグイン
  • vimrc
  • Meta
    Etc
    Creative Commons License
    This blog is licensed under a Creative Commons License.

    Return to page top