VimでPHPを書く私のneocomplcache設定を公開してみる

2012年1月14日

Vim

私のneocomplcacheの設定を公開します。たまにIDEに浮気しますが、基本的にはVimでPHPを書いています。

ある程度ノウハウが溜まってきたのでこのあたりで私の設定を公開してみます。neocomplcacheはその機能の豊富さゆえに使いこなすのがとても難しいと感じています。

この設定にたどり着くまでにずいぶん試行錯誤を繰り返しました。マニュアルもたくさん読みました。補完が重い状況が改善されず、1日中設定を調節していたこともあります。「ここはこうしてみてはどうか」「その設定は機能していないよ」といったご意見がございましたらコメントいただければ幸いです。

動作確認環境

  • Vim7.3
  • neocomplcache6.1
  • vimproc5.3

目次

  1. neocomplcacheのインストール
  2. vimprocのインストール
  3. vimrc
  4. シンタックス補完を無効に
  5. ディクショナリ補完の利用
  6. インクルード補完の設定方法がよくわからない
  7. タグ補完の利用
  8. オムニ補完はPHPでは使わない
  9. スニペット補完は超便利
  10. キーマップはヘルプを参考に
  11. 補完が重いと感じる場合の対処例
  12. PHPフレームワークへの対応
  13. Shougo さん、thinca からのコメント

neocomplcacheのインストール

下記よりバージョン6.1をダウンロードします。neocomplcacheは開発が盛んです。バージョン6.1はだいぶ古いのかもしれませんが、動作は安定していると思います。Windows, Mac, Linux で動作確認が取れています。

https://github.com/Shougo/neocomplcache.vim/tags

私はプラグイン管理にpathogenをを使用しているので、ダウンロードして解凍したフォルダを $HOME/.vim/bundle/ に配置しました。

windowsの場合、bundleディレクトリは通常vimfiles以下に置きますが、.vimrcで下記のように設定して、windowsでもlinuxのように.vimディレクトリを使用できるようにしています。

"windowsのvimfilesを.vimに変更する
if has('win32')
  set runtimepath^=$HOME/.vim
  set runtimepath+=$HOME/.vim/after
endif

vimprocのインストール

neocomplcacheはキャッシュの作成にvimprocを使用しています。

vimprocをインストールするとキャッシュの作成を非同期で行うようになり、キャッシュ作成時の待ち時間が減るようですゼロになるようです。

下記よりバージョン5.3をダウンロードします。

https://github.com/Shougo/vimproc.vim/tags

vimrc

vimrcです。
neocomplcacheに関係すると思われる部分を抜粋しました。

"windowsのvimfilesを.vimに変更する
if has('win32') + has('win64')
  set runtimepath^=$HOME/.vim
  set runtimepath+=$HOME/.vim/after
endif
"C-n補完の対象(カレントバッファ、タグ、辞書) :help 'complete'
"neocomplcacheには影響しない?
"neocomplcacheには影響しないそうなのでコメントアウト
"set complete=.,w,b,u,k,t
"補完ウィンドウの設定 :help completeopt
set completeopt=menuone
"起動時に有効
let g:neocomplcache_enable_at_startup = 1
"ポップアップメニューで表示される候補の数。初期値は100
let g:neocomplcache_max_list = 20
"自動補完を行う入力数を設定。初期値は2
let g:neocomplcache_auto_completion_start_length = 2
"手動補完時に補完を行う入力数を制御。値を小さくすると文字の削除時に重くなる
let g:neocomplcache_manual_completion_start_length = 3
"バッファや辞書ファイル中で、補完の対象となるキーワードの最小長さ。初期値は4。
let g:neocomplcache_min_keyword_length = 4
"シンタックスファイル中で、補完の対象となるキーワードの最小長さ。初期値は4。
let g:neocomplcache_min_syntax_length = 4
"1:補完候補検索時に大文字・小文字を無視する
let g:neocomplcache_enable_ignore_case = 1
"入力に大文字が入力されている場合、大文字小文字の区別をする
let g:neocomplcache_enable_smart_case = 1
"ポップアップメニューが出ているときに-を入力すると英数字で候補選択する。
"削除されていたようなのでコメントアウト
"let g:neocomplcache_enable_quick_match = 0
";で英数字候補選択できるようにしたかったけど動かない
"動かないのは仕様のようです。コメントアウト
"let g:neocomplcache_quick_match_patterns = {
"	\ 'default' : ';'
"	\ }
"大文字小文字を区切りとしたあいまい検索を行うかどうか。
"DTと入力するとD*T*と解釈され、DateTime等にマッチする。
let g:neocomplcache_enable_camel_case_completion = 0
"アンダーバーを区切りとしたあいまい検索を行うかどうか。
"m_sと入力するとm*_sと解釈され、mb_substr等にマッチする。
let g:neocomplcache_enable_underbar_completion = 0
"neocomplcacheを自動的にロックするバッファ名のパターンを指定。
"ku.vimやfuzzyfinderなど、neocomplcacheと相性が悪いプラグインを使用する場合に設定。
"let g:neocomplcache_lock_buffer_name_pattern = '\*ku\*'
"キャッシュディレクトリの場所
"RamDiskをキャッシュディレクトリに設定
if has('win32')
	let g:neocomplcache_temporary_dir = 'E:/.neocon'
elseif has('macunix')
	let g:neocomplcache_temporary_dir = '/Volumes/RamDisk/.neocon'
else
	let g:neocomplcache_temporary_dir = '/tmp/.neocon'
endif
"シンタックス補完を無効に
let g:neocomplcache_plugin_disable = {
	\ 'syntax_complete' : 1,
	\ }
"補完するためのキーワードパターンを指定
if !exists('g:neocomplcache_keyword_patterns')
	let g:neocomplcache_keyword_patterns = {}
endif
"日本語を補完候補として取得しないようにする
let g:neocomplcache_keyword_patterns['default'] = '\h\w*'
"twigはhtmlと同じに
let g:neocomplcache_keyword_patterns['twig'] = '</\?\%([[:alnum:]_:-]\+\s*\)\?\%(/\?>\)\?\|&amp;\h\%(\w*;\)\?\|\h[[:alnum:]_-]*="\%([^"]*"\?\)\?\|\h[[:alnum:]_:-]*'
"関数を補完するための区切り文字パターン
if !exists('g:neocomplcache_delimiter_patterns')
	let g:neocomplcache_delimiter_patterns = {}
endif
let g:neocomplcache_delimiter_patterns['php'] = ['->', '::', '\']
"カーソルより後のキーワードパターンを認識。
"h|geとなっている状態(|はカーソル)で、hogeを補完したときに後ろのキーワードを認識してho|geと補完する機能。
"修正するときにかなり便利。
g:neocomplcache_next_keyword_patternsは分からないときはいじらないほうが良いです。
if !exists('g:neocomplcache_next_keyword_patterns')
	let g:neocomplcache_next_keyword_patterns = {}
endif
"よく分からない場合は未設定のほうがよいとのことなので、ひとまずコメントアウト
"let g:neocomplcache_next_keyword_patterns['php'] = '\h\w*>'
"twigはhtmlと同じに
let g:neocomplcache_next_keyword_patterns['twig'] = '[[:alnum:]_:-]*>\|[^"]*"'
"ファイルタイプの関連付け
if !exists('g:neocomplcache_same_filetype_lists')
	let g:neocomplcache_same_filetype_lists = {}
endif
"let g:neocomplcache_same_filetype_lists['ctp'] = 'php'
"let g:neocomplcache_same_filetype_lists['twig'] = 'html'
"ディクショナリ補完
"ファイルタイプごとの辞書ファイルの場所
let g:neocomplcache_dictionary_filetype_lists = {
	\ 'default' : '',
	\ 'php' : $HOME . '/.vim/dict/php.dict',
	\ 'ctp' : $HOME . '/.vim/dict/php.dict',
	\ 'twig' : $HOME . '/.vim/dict/twig.dict',
	\ 'vimshell' : $HOME . '/.vimshell/command-history',
	\ }
"タグ補完
"タグファイルの場所
augroup SetTagsFile
	autocmd!
	autocmd FileType php set tags=$HOME/.vim/tags/php.tags
augroup END
"タグ補完の呼び出しパターン
if !exists('g:neocomplcache_member_prefix_patterns')
	let g:neocomplcache_member_prefix_patterns = {}
endif
let g:neocomplcache_member_prefix_patterns['php'] = '->\|::'
"スニペット補完
"標準で用意されているスニペットを無効にする。初期化前に設定する
let g:neocomplcache_snippets_disable_runtime_snippets = 1
"スニペットファイルの置き場所
let g:neocomplcache_snippets_dir = $HOME.'/.vim/snippets'
"zencoding連携
let g:use_zen_complete_tag = 1
"オムニ補完
augroup SetOmniCompletionSetting
	autocmd!
	autocmd FileType css setlocal omnifunc=csscomplete#CompleteCSS
	autocmd FileType html setlocal omnifunc=htmlcomplete#CompleteTags
	autocmd FileType javascript setlocal omnifunc=javascriptcomplete#CompleteJS
	autocmd FileType ctp setlocal omnifunc=htmlcomplete#CompleteTags
	autocmd FileType twig setlocal omnifunc=htmlcomplete#CompleteTags
"	autocmd FileType php setlocal omnifunc=phpcomplete#CompletePHP
augroup END
"オムニ補完のパターンを設定
if !exists('g:neocomplcache_omni_patterns')
	let g:neocomplcache_omni_patterns = {}
endif
let g:neocomplcache_omni_patterns['twig']= '<[^>]*'
"let g:neocomplcache_omni_patterns['php'] = '[^. \t]->\h\w*\|\h\w*::'
"keymap
"表示行単位で移動(snippet展開対策済み)
nnoremap j gj
onoremap j gj
xnoremap j gj
nnoremap k gk
onoremap k gk
xnoremap k gk
"挿入モードのキーバインドをemacs風に
inoremap <C-a> <Home>
"inoremap <C-e> <End>
inoremap <C-b> <Left>
inoremap <C-f> <Right>
"inoremap <C-n> <Down>
"inoremap <C-p> <Up>
inoremap <C-h> <BS>
inoremap <C-d> <Del>
"inoremap <C-k> <C-o>D
"改行で補完ウィンドウを閉じる
inoremap <expr><CR> neocomplcache#smart_close_popup() . "\<CR>"
"tabで補完候補の選択を行う
inoremap <expr><TAB> pumvisible() ? "\<Down>" : "\<TAB>"
inoremap <expr><S-TAB> pumvisible() ? "\<Up>" : "\<S-TAB>"
"C-h, BSで補完ウィンドウを確実に閉じる
inoremap <expr><C-h> neocomplcache#smart_close_popup()."\<C-h>"
inoremap <expr><BS> neocomplcache#smart_close_popup()."\<BS>"
"C-yで補完候補の確定
inoremap <expr><C-y> neocomplcache#close_popup()
"C-eで補完のキャンセルし、ウィンドウを閉じる。ポップアップが開いていないときはEndキー
inoremap <expr><C-e> pumvisible() ? neocomplcache#cancel_popup() : "\<End>"
"C-gで補完を元に戻す
inoremap <expr><C-g> neocomplcache#undo_completion()
"vim標準のキーワード補完を置き換える
inoremap <expr><C-n> neocomplcache#manual_keyword_complete()
"C-pで上キー
inoremap <C-p> <Up>
"補完候補の共通文字列を補完する(シェル補完のような動作)
inoremap <expr><C-l> neocomplcache#complete_common_string()
"スニペットを展開する。スニペットが関係しないところでは行末まで削除
imap <expr><C-k> neocomplcache#sources#snippets_complete#expandable() ? "\<Plug>(neocomplcache_snippets_expand)" : "\<C-o>D"
smap <expr><C-k> neocomplcache#sources#snippets_complete#expandable() ? "\<Plug>(neocomplcache_snippets_expand)" : "\<C-o>D"
"オムニ補完の手動呼び出し
inoremap <expr><C-Space> neocomplcache#manual_omni_complete()
"スニペットファイルを編集する
nnoremap <Space>nes :NeoComplCacheEditSnippets
"インクルード補完。よくわからない。初期化のみに留める
"通常は設定する必要はないらしい。
"Vim標準のインクルード補完を模倣しているそうなので、そちらを勉強する
if !exists('g:neocomplcache_include_paths')
    let g:neocomplcache_include_paths = {}
endif
if !exists('g:neocomplcache_include_patterns')
    let g:neocomplcache_include_patterns = {}
endif
if !exists('g:neocomplcache_ctags_arguments_list')
    let g:neocomplcache_ctags_arguments_list = {}
endif
"ctagsの引数
"let g:neocomplcache_ctags_arguments_list = {
"	\ 'php' : '-R --languages=PHP --langmap=PHP:.php.inc --php-types=c+f+d'
"	\ }

シンタックス補完を無効に

シンタックス補完とは、Vimのシンタックスキーワードから補完候補を取得する機能です。PHPの場合は、組み込み定数・関数・クラスなどを補完候補として取得できます。

シンタックス補完は、特に何も用意せずに補完ができて大変便利ですが、私は無効にしています。理由は、phpファイルを編集をしているときに、vbscript,css,javascriptの補完が候補として出てきてしまって編集効率が悪くなるためです。シンタックス補完を無効にした分は、この後説明するディクショナリ補完で補っています。

ディクショナリ補完の利用

ディクショナリ補完とは、辞書ファイルから補完候補を自動で取得する機能です。私はシンタックス補完を無効にしていますので、その分をディクショナリ補完で補っています。PHP用の辞書ファイルは自作のスクリプトで作成しています。PHPの組み込み定数・関数・クラス、その他補完したいキーワードを出力しています。作成にあたり、下記のサイトを参考にさせていただきました。

Vim で PHP 関数の辞書を作成する方法についてのメモ

インクルード補完の設定方法がよくわからない

この補完だけ使い方が良くわかっていません。requireやinclude構文からファイルを読み込み、tagsファイルを作成してくれるようなのですが。最近のPHPフレームワークはAutoLoad化やClassLoader化が進んでおり、メインのアプリケーション側ではrecuire構文を使用しない傾向にあります。ですので、「インクルード補完は使えなくても、今はまあいいか」と妥協して、tagsファイルをせっせと作成しています。とはいえ、便利な機能が使えないのは悲しいので、インクルード補完についての調査は継続します。

タグ補完の利用

タグ補完とは、ctagsで作成したタグファイルから補完候補を自動で取得する機能です。neocomplcache作者のShougoさんは、タグ補完よりインクルード補完を使用するべきとおっしゃっていますが、前述の通り、インクルード補完を使いこなせていないのでタグ補完を使用しています。タグファイルが巨大にならないよう、必要最小限のタグファイルを作成するように心がけています。

タグファイルの作成については以下の記事をご確認ください。

Vim
VimによるPHP開発でCtagsを使ってタグジャンプする

VimによるPHP開発で、タグファイルを使って関数の定義にジャンプする方法です。各 OS における ctags のインス ...

続きを見る

オムニ補完はPHPでは使わない

オムニ補完は文脈を解釈する補完です。PHPのオムニ補完は重い上にそれほど賢くないので、有効にしていません。neocomplcacheではPHPのオムニ補完はデフォルトで無効になっています。

スニペット補完は超便利

スニペット補完とは、コードの一部分を定型文として登録しておき、任意のタイミングで定型文を呼び出すことができる機能です。よく使うコードの部品を登録しておくと作業効率が上がります。if, foreach, function, try, catch のスニペットを使うだけでも作業効率は上がるでしょう。

私はneocomplcacheに標準添付されているスニペットは無効にしており、自分で作成したスニペットを別途用意しています。スニペットはバージョン管理して自分専用に育てていくといいです。

スニペットの書き方

スニペットの書き方は、標準添付のスニペットを参考にするのが手っ取り早いです。

標準添付のスニペットは下記にあります。

Shougo-neocomplcache/autoload/neocomplcache/sources/snippets_complete/php.snip

web上には数多くのスニペットが公開されていますので、そちらも参考にすると良いでしょう。フレームワークのスニペットを公開されている方もいらっしゃいます。

スニペット補完のやりかた

入力時に補完ウィンドウに現れたスニペット補完のキーワード(Snipと表示されます)を<c-n>で選択して<c-k>を押します。するとスニペットが展開されます。スニペット展開後は、再度 <c-k> でプレースホルダの移動ができます。プレースホルダ上にカーソルがあるときに文字入力をすると、プレースホルダの文字を任意に置き換えることができます。

プレースホルダの展開後の入力がうまくいかないケースも

キーマップによっては、プレースホルダの移動後の入力がうまくできない場合があります。例えば、下記のキーマップを設定されている状態で、プレースホルダの展開後の文字入力で「k」を入力するとカーソルが上に移動してしまい、スムーズに入力できません。

"表示行単位で移動
noremap j gj
noremap k gk

上記のキーマップは便利だったのですが、利用できなくて残念です。

1月16日追記
Shougoさん、thincaさんのご助力により、下記マップを定義することで解決しました。

"表示行単位で移動(snippet展開対策済み)
nnoremap j gj
onoremap j gj
xnoremap j gj
nnoremap k gk
onoremap k gk
xnoremap k gk

キーマップはヘルプを参考に

キーマップはヘルプに書かれているキーマップをベースにしています。各補完の詳細は本体のヘルプをご確認ください。

<c-y>で補完の確定

初めのうちはエンターキーで補完を確定させるようにしていましたが、VimShell上でうまく動かなかったことと、<c-y>で補完したほうが楽なことに気づいたので、今は<c-y>で補完を確定させています。

挿入モードにおけるemacs風カーソル移動

挿入モード時のカーソル移動にemacs風のキーマップを割り当てています。emacs風のカーソル移動キーマップはneocomplcacheのヘルプに載っているキーマップといくつか競合しますので、pumvisible()を使用してポップアップ時の動作とそれ以外の動作を振り分けています。詳細は <c-e>, <c-k> 等のマッピングをご確認ください。

補完が重いと感じる場合の対処例

下記を試してみてください。

  • :help php で出てくる設定をすべて無効にする
  • g:neocomplcache_max_listの数字を小さくする
  • g:neocomplcache_auto_completion_start_lengthの数字を大きくする
  • tagsファイルが巨大になっていないか確認する
  • オムニ補完を無効にする

php_foldingの設定を無効にしたことが、私の環境では最も効果がありました。tagsファイルのサイズを小さくすることも効果があります。

PHPフレームワークへの対応

フレームワークはCakePHPとsymfonyを使用しています。フレームワークへの対応としては、vimが「ctp」や「twig」のfiletypeを認識できるように設定して、それ専用のディクショナリやスニペットを用意しています。新しいfiletypeを追加する方法は :help new-filetype に書いてあります。

フレームワークのtagsファイルをトップディレクトリからドカンと作ると巨大になるので、本当に必要なものだけをファイル単位で作成するスクリプトを書いています。

フレームワークごとにtagsファイルは別々にしています。必要なときに必要なtagsファイルをスクリプトで都度作成しています。下記がスクリプトの例です。

#!/bin/sh
ctags -f ~/.vim/tags/php.tags ~/Sites/Symfony/vendor/symfony/src/Symfony/Component/DependencyInjection/ContainerInterface.php
ctags -a -f ~/.vim/tags/php.tags ~/Sites/Symfony/vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
ctags -a -f ~/.vim/tags/php.tags ~/Sites/Symfony/vendor/symfony/src/Symfony/Component/DependencyInjection/ContainerAware.php
...

Shougo さん、thinca からのコメント

コメント機能を停止したため、Shougo さんと thinca さんからコメントでいただいた内容を転記しておきます。

Shougo さんより。

neocomplcacheの作者です。こんにちは。
> neocomplcacheはその機能の豊富さゆえに使いこなすのがとても難しいと感じています。ですが、ある程度ノウハウが溜まってきたのでこのあたりで私の設定を公開してみます。
そうですね。neocomplcacheは私が初めて本格的に作ったVimプラグインですし、unite.vimと比較すると完成度はまだまだです。さらに、Vimの補完機能の仕様にも縛られているので……。私自身はオプションが嫌いなので、オプションはかなり整理しましたが、それでも多いですね。

 

> neocomplcacheは開発が盛んです。バージョン6.1はだいぶ古いのかもしれませんが、動作は安定していると思います。Windows, Mac, Linux で動作確認が取れています。
最新版であるneocomplcache Ver.6.2も安定してきています。そろそろリリースする予定です。

> vimprocをインストールするとキャッシュの作成を非同期で行うようになり、キャッシュ作成時の待ち時間が減るようです。
減るというより、ほぼ0になります。

> 理由は、phpファイルを編集をしているときに、vbscript,css,javascriptの補完が候補として出てきてしまって編集効率が悪くなるためです。
これはシンタックスファイルの問題ですね。難しいところです。

> インクルード補完の設定方法がよくわからない
http://d.hatena.ne.jp/thinca/20091026/1256569191
を読みましょう。普通は設定しなくてもよいです。

> 最近のPHPフレームワークはAutoLoad化やClassLoader化が進んでおり、メインのアプリケーション側ではrecuire構文を使用しない傾向にあります。ですので、「インクルード補完は使えなくても、今はまあいいか」と妥協して、tagsファイルをせっせと作成しています。
実はautoloadを自動で解析する仕組みはneocomplcache内で実装されています。
現在、Vim scriptとRubyに対応。PHPはujihisaさんが作ってくれる予定なのですが、まだできていないようです。

> autocmd FileType php set omnifunc=phpcomplete#CompletePHP
let g:neocomplcache_omni_patterns[‘php’]
set ではなく、setlocalを使いましょう。

> キーマップによっては、プレースホルダの移動後の入力がうまくできない場合があります。例えば、下記のキーマップを設定されている状態で、プレースホルダの展開後の文字入力で「k」を入力するとカーソルが上に移動してしまい、スムーズに入力できません。
noremapではなく、nnoremap, xnoremapを使いましょう。

> “C-n補完の対象(カレントバッファ、タグ、辞書) :help ‘complete’
“neocomplcacheには影響しない?
影響しません。それはVimの補完にのみ有効。Vimテクニックバイブルを買うとそのあたりもよく分かると思います。Webは情報が分散しているので……。

> “ポップアップメニューが出ているときに-を入力すると英数字で候補選択する。
> let g:neocomplcache_enable_quick_match = 0
この機能は削除されています。動かないのは仕様です。クイックマッチはunite.vimへと受けつがれました。

> “カーソルより後のキーワードパターンを認識。よく分からない設定
これは分かりにくいですが、h|geとなっている状態(|はカーソル)で、hogeを補完したときに後ろのキーワードを認識してho|geと補完する機能です。修正するときにかなり便利です。
g:neocomplcache_next_keyword_patternsは分からないときはいじらないほうが良いです。

> “phpのinclude補完の設定 よく分からない
neocomplcacheのインクルード補完はVim標準のインクルード補完を改良して実装されています。まずはVim標準のインクルード補完に関する設定を勉強しましょう。

thinka さんより

> プレースホルダの展開後の入力がうまくいかないケースも
http://d.hatena.ne.jp/thinca/20090526/1243267812

-技術ブログ
-,