neco-ghc が neocomplcache 無しでも利用できるようになりました

neco-ghc は neocomplcache 専用のプラグインでしたが,構造を変更して neocomplcache 無しでも Vim のオムニ補完の関数として動作できるようにしました. neocomplcache ユーザにとっては表面上の変更点はありません.neocomplcache の有無にかかわらず,補完能力そのものは同じです.

ujihisa/neco-ghc - GitHub

necoghc#omnifunc を 'omnifunc' に設定することで,非 neocomplcache ユーザでも neco-ghc による補完を Vim のオムニ補完として使えます. ~/.vim/ftplugin/haskell.vimsetlocal omnifunc=necoghc#omnifunc と設定すると Haskell のコードを開いたときに自動的に 'omnifunc' に設定できます. そしてインサートモードで C-x C-o とすれば補完できます.

以下は neocomplcache 専用のプラグインから 'omnifunc' としても使えるように書き換えたときにちょっとハマった部分の記録.Vim の補完関数を書いたり,それを neocomplcache からも使えるようにしたい人には参考になるかも.

補完のキャンセル

'omnifunc' の仕様は :help complete-functions にある. a:findstart が 1 のときは補完位置を返すわけだけど,

Return -1 if no completion can be done, the completion will be cancelled with an error message. Return -2 to cancel silently.

と書かれている. これを読んで俺は「-1 を返すと補完はキャンセルされるので a:findstart が 0 で再び呼ばれることはなく,さらに -2 を返すと『パターンは見つかりませんでした』のエラーメッセージも出ない」と解釈した. しかしどうやら,-1 を返したときに関しては a:findstart が 0 で再び呼ばれるのが仕様らしく, そのとき a:base は空文字列になっている. 一方 -2 を返したときは呼ばれない. なので,とりあえず neco-ghc では補完できないときは位置として -1 を返し,a:base が空のときは補完できないものとして空の補完候補を返している.

col('.') の値

a:findstart が 0 で 'omnifunc' が呼ばれたとき,col('.') は「補完位置」を返す. つまり,例えば

foo ba_

において _ の位置でオムニ補完をして,補完位置として ba の先頭を返した場合,補完候補を計算する段階では col('.') の値は 7 ではなく 5 となる.

一方 neocomplcache のプラグインとして動作するとき,s:source.get_complete_words() の内部では col('.') の値は 7 になる. そのかわり(?),s:source.get_complete_words() の第一引数で補完位置として 4 が渡される.