ホーム > タグ > Vim Hacks

Vim Hacks

Hack #130: :grepをより便利に利用する

問題

Hack #129: 複数のファイルから検索する では:grepについて紹介しました。 色々と便利なコマンドなのですが、頻繁に使っていると次のような不満が出てきます:

:grepの引数は基本的に

:grep {pattern} {file} ...

なのですが、同じファイル郡に対して異なるパターンで:grepする というケースは少なくありません。例えば

:grep rebase *.[ch]

で検索した後で

:grep rebase_cmd *.[ch]

と検索しなおすとしましょう。全てのコマンドを入力しなおすと面倒ですから、 通常は入力履歴を<Up>/<Down>で参照して以前入力した コマンドを修正する形になります。

ここで、入力履歴を手繰るまではよいのですが、 パターンを修正するところが問題です。 修正するためには<Left><Left><Left>...などとしてパターンの箇所まで カーソルを移動しなければならないのですが、これが面倒です。 特に{file} ...の指定が長くなると面倒さが倍増します。 どうにかして簡単に修正することはできないでしょうか。

解決方法

以下の内容をvimrcに追加します:

command! -complete=file -nargs=+ Grep  call s:grep([<f-args>])
function! s:grep(args)
  execute 'vimgrep' '/'.a:args[-1].'/' join(a:args[:-2])
endfunction

これにより:grepの代わりに 以下のような:Grepコマンドが使えるようになります:

:Grep {file} ... {pattern}

:grepでの煩雑さは最初の引数が{pattern}であることが 原因なので、その逆で最後の引数が{pattern}であるコマンドがあれば パターンの修正は<Up><C-w><C-w>...だけで済みます。

参考資料

kana

Hack #129: 複数のファイルから検索する

時折、大量にあるテキストファイルから特定のテキストを検索したい場合があります。grep などのプログラムが有名ですが、やはり検索結果から直接該当のファイルにジャンプできると便利です。

:grep コマンド

:grep コマンドは、'grepprg' オプションで指定された外部プログラムを実行してテキストの検索を行い、結果を 'errorformat' オプションに従って解釈したあと、quickfix ウィンドウへ出力します。quickfix 上で結果を選択して <Enter> を押すことで目的のファイルの目的の行にジャンプすることができます。 各オプションにはデフォルトで適切な値が設定されており、通常はいじる必要はありません。

:grepadd と言うコマンドもあり、これは quickfix ウィンドウの中身を置き代えずに追記します。複数の grep の結果を見るのに便利です。

:vimgrep コマンド

:grep コマンドは外部のプログラムに依存するため、その実装によって挙動が異なることがあります。例えば、Windows では標準では findstr が使用されます。これは grep とはまったく異なるものなので、指定する引数も使える正規表現も違います。

そういう経緯もあってかどうかは知りませんが、Vim には :vimgrep と言う自力で grep を行う組み込みコマンドがあります。基本的な使い方は :grep と同じです。

:vimgrep /pattern/ {file}...
:vimgrepadd /pattern/ {file}...

pattern には Vim の正規表現が使えます。単純な単語の場合、前後の / は省略可能です。

{file} の部分に検索対象のファイルを指定します。指定にはワイルドカードが使えます(:help wildcard)。これは ** による再帰的な探索に対応しています。

:vimgrep /\<variable\>/ **/*.c

パターンの後ろにフラグを付けることで挙動を変更できます。

フラグ意味
g1 つの行に複数のマッチが見つかった場合、同じ行を複数回列挙します。
j通常、検索対象が見つかった場合は最初の候補にジャンプしますが、このジャンプを抑制します。
:vimgrep /\<variable\>/j **/*.c

コマンドの前に数字を置くことで、検索件数の上限を指定できます。これにより検索時間を短縮できます。例えば、最初に見つかった 1 件だけを表示するには以下のようにします。

:1vimgrep /\<variable\>/ **/*.c

'grepprg' に "internal" と設定すると、:grep で :vimgrep が使われるようになります。

set grepprg=internal

:vimgrep は挙動が統一されているので、様々な環境で同じように grep を行いたい場合に非常に便利です。

thinca

Hack #128: neocomplcache Hacks(3) キーワード補完

三回目の今回はneocomplcacheのキーワード補完と拡張プラグインを解説します。

キーワード補完とは

neocomplcacheのキーワード補完は、<C-n><C-p>で呼び出すことのできるVim組み込みのキーワード補完を模倣して設計されています。 言語ごとにキーワードパターンを定義して、一括して候補を検索できるのが最大の特徴です。 使用するキーワードパターンはg:NeoComplCache_KeywordPatternsで定義されています。

pluginとcomplfuncの違い

neocomplcacheには補完候補を収集するために、pluginとcomplfuncという二つの拡張プラグインが存在します。 それぞれ、autoload/neocomplcache/pluginautoload/neocomplcache/complfuncに配置します。 pluginはkeyword_complete.vimから一括して呼びだされる拡張プラグインで、単純なため実装が比較的簡単です。 complfuncは補完開始位置を拡張プラグイン側で計算しないといけません。 その代わり、補完開始位置は自由に決定できるので、ファイル名補完のような複雑な補完を実装できます。 初期のneocomplcacheでは、complfuncは一つのプラグインしか呼べないという制限がありました。 現在のバージョンでは、complfuncの候補も統合できるように改良されているため、そのような制限はありません。

標準添付のplugin

buffer_complete.vim

開いているバッファから補完候補を収集するプラグインです。バッファ内の単語を解析することで、使用頻度の学習も行います。

dictionary_complete.vim

g:NeoComplCache_DictionaryFileTypeListsから補完候補を収集するプラグインです。

include_complete.vim

バッファで開いているインクルードファイルから補完候補を収集するプラグインです。Vimにもインクルード補完はありますが、より拡張されています。

snippets_complete.vim

スニペットを補完するプラグインです。補完するだけではなく、単体でスニペット展開プラグインとしても動作します。

syntax_complete.vim

シンタックスファイルからキーワードを補完するプラグインです。Vim標準添付のsyntaxcomplete.vimよりも高機能であり、結果をキャッシュするので高速です。複雑なTeXのシンタックスも解析できます。

tags_complete.vim

タグファイルからキーワードを補完するプラグインです。

標準添付のcomplfunc

completefunc_complete.vim

補完関数を登録することで、自動呼び出しするプラグインです。neocomplcacheはcompletefuncを上書きしてしまうので、その対策に使えます。 手動補完として補完関数を個別に呼び出しすることもできます。

filename_complete.vim

ファイル名を補完するプラグインです。詳しい解説は、[Hack #119: neocomplcache Hacks(2) オムニ補完]を参照してください。

keyword_complete.vim

pluginから候補を収集し、キーワードを補完するプラグインです。

omni_complete.vim

オムニ補完を呼び出し、候補を収集するプラグインです。詳しい解説は、[Hack #119: neocomplcache Hacks(2) オムニ補完]を参照してください。

vim_complete.vim

Vimのオムニ補完機能を提供するプラグインです。Vim標準の<C-x><C-v>よりも機能が拡張されていて、ローカル変数や引数の補完も可能です。

Shougo

Hack #127: !を含む外部コマンドを実行する

問題

UNIXのechoコマンドで"hello!"と表示させる必要があるとします。Vimの:!を使えば出来そうな気がします。試してみましょう。

:!echo helloecho 'echo echo 1'
[No write since last change]
helloecho echo echo 1

なにやら狂ったような返答がきました。これは一体…。

※ 実行結果は人によって異なります。

解決

次のようにして!をエスケープします。

:!echo hello\!
[No write since last change]
hello!

次のようにしても、エスケープされませんのでご注意ください。

:!’echo hello!’

解説

ヘルプ:h :!によると、:!の中での!は前回実行した:!の引数になるそうです。 迷惑でしかない恐ろしい仕様です。('cpoptions'に依存するようです)

プラギンを作るときにハマる仕様です。:!を使うときによくわからない挙動があれば、まずは!のエスケープ漏れがないか確認してみましょう。

ujihisa

Hack #126: クリップボードを利用する

問題1

ふつうのテキストエディタであれば クリップボードにあるテキストがペーストされ、 コピーしたテキストはクリップボードに保存されます。

しかしVimではコピー/ペーストされるテキストは レジスタと呼ばれるVim内部の領域から読み書きされるため、 そのようにはなっていません。

クリップボードに対してコピー/ペーストを行なうにはどうすればよいのでしょうか。

解決方法1

Vimには クリップボードを表す仮想的なレジスタ"*があるため、 コピー/ペーストする際にそのレジスタを使用するよう指定します。 例えばクリップボードへ1行分のテキストをコピーする場合は 以下のコマンドを入力します:

"*Y

クリップボードからテキストをペーストする場合は 以下のコマンドを入力します:

"*p

問題2

Vimからクリップボードにアクセスする方法は分かりました。 しかし毎回"*を指定しなければならず、入力が面倒です。 Vim内でテキストのコピー/ペーストを行なう場合に、 何も指定しなくても自動的にクリップボードを利用するようにできないでしょうか。

解決方法2

以下のコマンドを実行します:

:set clipboard=unnamed

この設定を行なうと、 特にレジスタを指定せずにコピー/ペーストを行なった場合は クリップボードを利用するようになります。

また、xtermなどのX Window System下のアプリケーションでは 選択されたテキストが自動的にクリップボードへコピーされます。 VimのVisual modeでも同様の挙動にしたい場合は以下のコマンドを実行します。

:set clipboard=autoselect

もし両方の挙動が必要な場合は以下のように値をコンマ(,)区切りで指定します:

:set clipboard=unnamed,autoselect

問題3

基本的にGUI版のVimでしかクリップボードに関する機能は利用できません。 つまり、諸事情で端末エミュレータ内でVimを利用している場合は クリップボード関連の機能は使えません。

幸いなことに、 一部の環境ではクリップボードにアクセスするためのコマンドがあります。 例えばMac OS Xの場合はpbcopy/pbpasteでクリップボードの読み書きができるため、 :read !pbpasteなどとすればクリップボードの内容を取り込むことが できます。

しかしこのようなコマンドは入力が煩雑ですし、 何よりd/p/yのような Vim本来の操作と比べると不便です。 どうにかして"*と同様の操作性を実現できないでしょうか。

解決方法3

fakeclipを使います。 このプラグインをインストールすると Cygwin、Mac OS X、X Window Systemの非GUI版Vimでも "*Y"*pなどの操作ができるようになります (バージョン0.2.6現在)。

解説

Q. なぜ"*Yのようなまどろっこしい操作になっているのですか?

A. 祖先のviが生まれた頃にクリップボードなんてものはなかったからです。

Q. なぜ:set clipboard=unnamedがデフォルトでないのですか?

A. Vimのデフォルト設定は可能な限りviライクだからです。 そして祖先のviが生まれた頃にクリップボードなんてものはなかったからです。

参考資料

kana

Hack #125: 矩形選択で自由に移動する

Vim は <C-v> で矩形選択を行うことができます。非常に便利な機能ですが、矩形の端の行の行末にテキストがない場合、選択が困難、もしくは不可能になってしまう場合があります。これは設定で回避できます。

設定

以下の設定を行います。

set virtualedit+=block

この設定を行うことで、矩形選択中は行末にテキストがなくてもカーソルを行末以降に移動させることができるようになります。

解説

'virtualedit' オプションは、仮想編集を有効にするモードを設定するオプションです。仮想編集中は実際に文字がないところへカーソルを移動させることができます。つまり、タブ文字の内部や行末より後ろへカーソルを移動させることができるようになります。ただしファイルの末尾以降の行へは移動できません。

また、'virtualedit' オプションはカンマ区切りの値を指定するオプションで、+= を使っていたのはそのためです。他にも insert(Insert mode で仮想編集を有効にする) や all(全てのモードで仮想編集を有効にする) が指定でき、他にはちょっと変わった値として onemore(行末の1文字先までカーソルを移動できるようにする) が指定できます。

thinca

Hack #124: Vimで非同期実行を行う

VimがEmacsと比較して一番劣っている機能として、コマンドの非同期実行があげられます。ここでは現状のコマンド実行の問題点とEmacsとの比較、その解決方法について議論を行います。

Vimのコマンド実行の欠点

Vimはsytem():readを用いて外部コマンドを実行し、その結果を得ることができます。これは非常に便利なのですが、Vimが起動した外部コマンドの終了を待ち合わせるため、Vimが停止してしまうという欠点があります。これは特にquickrunや:makeを実行しているときに問題となります。プログラムをバックグラウンドで実行すれば、終了を待ち合わせずにすみますが、それでは結果がとれません。さらにWindows上のGVimの場合、外部コマンドを実行するたびにDOS窓が開いて煩わしいです。 処理内容がシェルに依存するという欠点もあります。たとえば、Windowsでプログラムを起動するためには適切にエスケープしなければいけません。

Emacsとの比較

対するEmacsはどうでしょう。Emacsでは、start-process関数を用いてプロセスを生成することにより、コマンドを非同期的に実行できます。 comint-modeでもこれを用いてインタプリタを起動し、非同期で通信しています。 ただし、Emacsにはマルチスレッド関数がありません。全ての処理はシングルスレッドです。

外部インタフェースの欠点

RubyやPerl, Pythonといった外部インタフェースを用いるという解決策もあります。 しかし、外部インタフェースは通常のVimScriptとは文法が異なるため、連携が非常にややこしいです。時折フリーズしたり、エラーが起こったときにデバッグしづらいという欠点もあります。さらに、すべての環境で使えるわけでもありません。インストールしているRubyやPerl, Pythonのバージョンにも依存するなど、 非同期に通信するためだけに外部インタフェースを用いるのは大げさすぎます。 ただし外部インタフェース内ではマルチスレッドが使えるので、真の非同期通信をするためには、外部インタフェースを用いるほかありません。

vimprocについて

外部ライブラリであるvimprocを用いて外部コマンドと通信すれば、上記に挙げたほとんどの欠点が解消されます。 vimprocとはYukihiro Nakadairaさんが開発している、優れた非同期実行ライブラリです。私が改良したものをgithub上で開発しています。 http://github.com/Shougo/vimproc/tree/master ソースからコンパイルしてVimのautoloadディレクトリに、「proc.soまたはproc.dll」と「vimproc.vim」をコピーすれば準備完了です。

ここでは、vimprocで実装されているvimproc#system関数を紹介します。これは、標準のsystem関数を置き換えることができます。 ただしシェルを起動しないので、シェルの内部コマンドは動作しません。パイプやリダイレクトも動かないので注意してください。 パイプやリダイレクトについては、今後実装予定です。 let l:result = vimproc#system('ls') vimprocを駆使すれば非同期実行をエミュレーションできますが、スレッドの存在しないVimでは使い方がとてもややこしいので、ここでは説明を省略します。 興味がある場合にはvimprocのtestディレクトリにあるサンプルスクリプトを参照してください。 ネットワーク通信もできるので、Twit.vimみたいなプログラムも簡単に作成できます。 いずれはcomint-modeのように、インタプリタ実行機能を統合させたいと思っています。 ちなみにVimScriptでのシェル実装であるvimshellでも、vimprocを用いて非同期実行を行っています。

その他の非同期実行ライブラリ

その他のライブラリとして、vimprocやvimshellをもとにNico Raffatoさんが製作しているConqueというプラグインがあり、とても精力的に開発されています。Emacsのansi-term.elを目標にしているようで、動作が散漫なのが欠点ですが、多機能のようなので使用してみるといいかもしれません。

CursorHoldイベントの落とし穴

しかも、Vimには指定された時間が経過したら関数を呼び出すタイマー機能がありません。 CursorHoldICursorHoldイベントで代用するという手がありますが、これにはイベントごとに時間を設定できない、 ポップアップが表示されているときは呼ばれない、カーソルが動くまで呼ばれないといった欠点があります。 この欠点は本質的に回避不能です。Vim本体にタイマー実行機能が搭載されるのを待つしかありません。

clientserver機能を使う

Vimのclientserver機能を用いると、処理が完了した際に機能を呼び出すコールバック機能を実装することが可能です。 ただし、X環境が必要になります。Windows上でも使用できますが、あらかじめ別のVimを用意しておく必要があります。詳しくは:help clientserverを参照してください。

Shougo

Hack #123: VimでSKK日本語入力環境を実現する

Emacsでは様々な日本語入力のプラグインがあります。
Vimでもkeymapなどの設定をいじれば可能ですが、
あまりkeymapファイルをいじった人は少ないでしょう。
そこでVimプラグインで日本語入力を実現しようと開発された、skk.vimを紹介します。

概要

skk.vimはYagi Noriaki氏によって作られたVimで日本語入力を実現するプラグインで、以下のような特徴を持ちます。

  • SKKによる日本語入力を実現する
  • so skk.vimのみで動作する (Windows環境では辞書ファイルのパスが異なるため、別途設定を.vimrcに書く必要があります)
  • 軽い

SKKについて簡単に説明すると、形態素解析(文節の区切り)をSKK自身が行わず、
使用者自身が行うように矯正することで、実装が簡単であり、
かつ慣れれば高速で正確なかな漢字変換が行えるInput Methodです。

形態素解析を行わないことで軽い動作を実現し、 かつ自分の意図した変換結果を得られやすいとされています。
詳しくは各自Wikipediaを見るなりググったりしてください。

導入

SKKの辞書を用意する

ここからダウンロードするか、Linuxだったら

skkdic skkdic-extra

のようなパッケージが用意されていればそれをインストールしてください。

skk.vim インストール

普通のVimプラグインと違いはありません。
www.vim.orgから最新版をダウンロードして解凍してVimのディレクトリにインストールするだけです。

メリット

  • sshのリモートホスト先で日本語入力ができない時もVim上で日本語を入力できる
  • 軽い
  • 自分の意図にあった変換をしてくれる(かもしれない)

デメリット

  • 他のInput Methodには戻れない
  • SKK信者でVim信者Vimmerは母数的に考えて少ない(要出典)

・・・はネタとして

  • Vimでしか使えない

MacならAquaSKK、Windowsならskkime、Linuxならuim-skkやscim-skkなど、
それぞれのOSのIMを変えてしまえばどのアプリケーションでも使えます。 ただ上で言った「sshのリモートホスト先で日本語入力ができない時もVim上で日本語を入力できる」は大きなメリットだと思います。
緊急時に備えてインストールしておくのもアリかもしれません。

  • SKKは人によっては慣れが必要かもしれない

SKKでもmecab-skkservというものを使えば普通の日本語入力と似た環境を手に入れられますが、残念ながらskk.vimはskkserv非対応です。

これまでのあらすじ

しかしskk.vimには一つ問題があり、 それはもう作者によるメンテナンスが行われていないことです。
skk.vimにはファンも多く、インターネット上でも野良パッチが散見されますが、
それらは本家にマージされることはなく www.vim.orgでの最終更新日は2006年10月15日で止まっています。

そんな中私はこの状況をなんとかしたいといった気持ちはまったくなく、
ふとした思いつきからGithubにskk.vimの改良版を登録し、それが一部の人の反響を受けます。(要出典)
気を良くした私は、勢いで作者である八木氏とメールで連絡をし、
ライセンスの確認と、メンテナンスの意志があるか、
また今後作者に無断で登録してしまったリポジトリに継続してコミットしていいかを尋ねました。
八木氏はこれに快く了承して、ライセンスは好きにしていいと仰ってくれました。

無事八木氏の了解が得られ、どんどんskk.vimのパッチをマージしていこうと考えたものの、
skk.vimがVimスクリプトの貧弱な時代に書かれたものだったため、互換性を維持するためのコードが散らばっていました。

ドキュメントはなんとか書いたものの、Vim6時代のコードを知らないために、変更をすることに怖気づいていたところ、
リポジトリのメンバーからもコードを一から書き直す必要があるとの声が高まってきます。
私はそれを喜んで賛成し、skk.vimのメンテナンスを続けることを約束しながらも、結局新しいskk.vimを作ることにしました。

skk7.vim (仮称)

新しいskk.vimは、今現在skk7.vimという仮称のもと開発が行われています。
それは今私のマシン上、完璧クローズドな環境で行われていて、
未だベータ版ですが、近々Githubに登録するつもりです。

しかしskk7.vimというのはあくまで仮称であって、正式名称ではありません。
未だ続々と希望が寄せられているものの、そもそも多数決で決めるのか、独断で決めるのか、それすらも決めていませんでした。

というわけで

  • 新しい名前希望!
    • 決め方は決めるつもり
  • skk.vimかskk7.vimの開発に興味があるなら、私かリポジトリのメンバーになんらかのコンタクトをとってくれれば、すぐにメンバーに招待したい
    • GithubのIDを取得している必要があります
    • 参加表明して何もしなくてもOKな気楽っぷり
    • 気が向いた時に機能追加なり修正を新しいブランチにプッシュしてもらえれば適当に拾ってマージします
  • その他要望等あれば下の連絡先からどうぞ

連絡先

参考リンク

tyru

Hack #122: 行末までヤンクする

問題

カーソル位置から行末まで削除するにはDします。 カーソル位置から行末まで編集する(削除して挿入モードに入る)にはCします。

さて、カーソル位置から行末までヤンクするにはどうすればいいでしょうか。 答えはYではなく、y$です。

VimのYの挙動は、「カーソル行をヤンク」です。カーソル位置より左もまとめてヤンクされてしまいます。 オリジナルのviとの互換性のためか、このような仕様になっているみたいです。 オリジナルのviがなぜこのような仕様になっているかは不明です。

解決

~/.vimrcに以下を記述します。

nnoremap Y y$

これで、Yの挙動がDCと同様に、「カーソル位置から行末までヤンクする」になります。 一貫性がとれる上に、$というとても人間には入力不可能な記号の入力を省略することができ、開発効率の大幅な向上が期待されます。

解説

なお、ヘルプ :h Y を引くと以下のように説明されています。

["x]Y yank [count] lines [into register x] (synonym for yy, |linewise|). If you like "Y" to work from the cursor to the end of line (which is more logical, but not Vi-compatible) use ":map Y y$".

しかし、このやりかたは2点留意事項があります。

  • ヘルプに書いている方法ですと、y$の挙動が上書きされているときに、上書きされた側の挙動で今回定義したYが実行されます。多くの場合それは意図した挙動ではないため、本Hackではmapではなくnoremapとしました
  • ビジュアルモードなどでは今回のYは関係ないため、mapではなくnmapがより好ましいです

組み合わせて、本Hackではnnoremapとしました。

ujihisa

Hack #121: バッファ名をペーストする

問題

ときおりバッファ名をペーストしたいことがあります。 短い名前なら手動で入力しても構いませんし、 ファイルに対応するバッファならばファイル名補完で多少の手間を省けますが、 面倒臭いことには変わりありません。 直接バッファ名をペーストできないでしょうか。

解決方法

Normal modeの場合は以下のコマンドを実行します:

"%p

Insert modeやCommand-line modeの場合は以下のコマンドを入力します:

<C-r>%

上記のコマンドはカレントバッファ名をペーストします。 %の代わりに#を用いると 代替バッファ の名前をペーストします。

解説

上記のコマンドは実際には指定されたレジスタの内容をペーストするコマンドです。 Vimには特殊なレジスタとしてカレントバッファ名や代替バッファ名を表すものがあり、 この二つを組み合わせることで上記のようにバッファ名のペーストができます。

Vimには他にも様々な 特殊レジスタ があるので、一度確認しておくとより便利に使うことができるでしょう。

参考資料

kana

Home > Tags > Vim Hacks

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

    Return to page top