Vim の complete-functions でハマった

久しぶりに Vim の complete-functions を書いていたんですが、知らなかった仕様にはまったので覚書。
さて、Vim でオレオレコード補完処理を行いたい場合は 'completefunc' や 'omnifunc' などのオプションにコード補完関数を設定して実現します。

function! Complete(findstart, base)
    if a:findstart
        return 補完を開始する列の位置
    endif
    return 補完の候補
endfunction

set omnifunc=Complete


雑に書くと上のような感じです。
実際に でオムニ補完を呼び出すと Complete() が2回呼び出されます。
1回目は『補完を開始する列の位置』を返し、2回目の呼び出しで『補完の候補』を返します。
詳しくは :help complete-functions を参照してください。

[ハマりポイント]

結論からいうと1回目と2回目の呼び出しでは getline(".") の値が異なります。
具体的に言うと 1回目に返した列位置からカーソルの列位置間のテキストが 2回目に呼び出された時には削られます。
どういうことかというと、動作を確認するために次のような補完関数を設定しておきます。

function! Complete(findstart, base)
    echom printf("%d 回目" , a:findstart ? 1 : 2)
    echom "line : " . getline(".")
    if a:findstart
        return 1
    endif
    return []
endfunction
set omnifunc=Complete


この時に

homu " ここで <C-x><C-o>


すると

1 回目
line : homu
2 回目
line : h


というような結果が出力されます。
こんな感じで1回目と2回目の呼び出しでは getline(".") の値が異なっている事がわかると思います。
また、この影響のためか、補完を呼び出す前と呼び出したあとでは b:changedtick の値が変わっています。
ちなみに で補完を呼び出してユーザに操作が戻った時にはバッファの内容は元に戻っています。
以前、marching.vim でオムニ補完を書いていたときはこんな仕様にはまらなかったんだけど気づかなかっただけなのかなぁ…。