前回の記事から約二ヶ月が経過しました。ddc.vim の開発は順調に進んでおり、ようやく仕様が安定化してきています。
正式リリースも近いです。
今回は最近私が実装を行っているpum.vim という新プラグインとddc.vim との連携について解説します。
https://github.com/Shougo/ddc.vim
https://github.com/Shougo/pum.vim
これはもともとnvim-cmp が実現していたアイディアになります。
https://github.com/hrsh7th/nvim-cmp/pull/224
pum.vim はネイティブで用意されている補完機能を使用せずに、自前で Vim の popup window 機能や floating window 機能を用いて補完を行うプラグインです。
Emacs でいうとpopup.el に相当します。
https://github.com/auto-complete/popup-el
ddc.vim +pum.vim とnvim-cmp の独自補完ウインドウの大きな違いとしては、nvim-cmp は neovim 専用なのに対してddc.vim +pum.vim だと Vim でも動作するということです。
nvim-cmp の独自補完ウインドウは専用なので他から使うことはできません。pum.vim はddc.vim 専用の仕組みではなく独立しているため、他のプラグインで使用することが可能です。
自作補完ウインドウを実装すると何がよいのかメリットについて説明します。
これはnvim-cmp で顕著のようですが、プラグイン側で細かい制御をするのでプラグイン側でパフォーマンスの改善を行える余地があります。
自前でウインドウを描画するので、ネイティブ補完のようにチラつきを制御するために細かい制御をする必要がありません。
ネイティブの補完ウインドウはマッピングがソースコードに組込まれており、完全に決まっています。一応ユーザーがpumvisible() を用いて条件分岐すれば変更できるのですが、一部マッピングできないキーが存在します。
pum.vim ではマッピングはユーザーが定義するため、完全に自由なマッピングにすることが可能です。
ネイティブの補完ウインドウは本体が C 言語側で実装しているので制限が多いのですが、pum.vim はプラグインで制御するので機能追加がやりやすく拡張の余地があります。
プラグインとの連携も比較的簡単です。
ネイティブの補完ウインドウは色付けに対応していませんが、独自に補完ウインドウを描画するとこの問題はありません。
pum.vim にはデフォルトで abbr, kind, menu に色付けを行うことができます。プラグイン側で独自に色を付けることも可能です。
もちろん、この機能は補完ウインドウのパフォーマンスに影響があるので注意が必要です。
現在ddc-fuzzy がカスタムハイライトによるマッチング位置の強調表示に対応しています。
https://github.com/tani/ddc-fuzzy

ネイティブの補完ウインドウはもちろん挿入モードの補完にしか対応していません。
ただし Vim にはコマンドラインで自由な補完を行うパッチがあったり、neovim は補完をポップアップウインドウで表示する設定set wildoptions+=pum に対応しています。
wilder.nvim を用いるとポップアップウインドウでコマンドライン補完を行うことが可能です。wilder.nvim は外部の補完プラグインを呼んでいるのではなく補完 source を組込みで持っているようです。
https://github.com/gelguy/wilder.nvim
pum.vim はコマンドラインでの補完に対応しており、以下のようにddc.vim での補完をコマンドラインで行うことが可能です。

応用例としては、neco-vim を用いてコマンド引数を補完する、ddc-cmdline-history を用いてコマンドラインヒストリから補完する、バッファーの内容から補完するといったことがあります。
https://github.com/Shougo/neco-vim
https://github.com/Shougo/ddc-cmdline-history
Note:nvim-cmp もコマンドライン補完対応機能を開発中です。
https://github.com/hrsh7th/nvim-cmp/pull/362
pum.vim は挿入モードやコマンドラインだけでなく、端末モードからでさえ補完を行うことが可能です。夢が広がるのではないでしょうか。

手元だと一応動作はしていますが、Vim/neovim の terminal API の制限により挙動は安定していません。あくまで実験的機能となります。
デフォルトの補完メニューは縦方向に補完が出ますが、これでは気が散るという方のために水平方向のメニューを出すことができるようになりました。
最初は neovim 専用機能でしたが、Vim にも対応しました。

pum.vim による自作補完ウインドウも万能ではありません。以下のようなデメリットがあります。
pum.vim による補完ウインドウは Vim や neovim の実装と完全に独立しているので、全て自前で機能を実装する必要があります。実装の複雑化は避けられません。
pum.vim はddc.vim と独立しているので、当然別個でインストールしなければいけません。依存が増えるのを嫌う人もいるかと思います。
既存の設定やプラグインはネイティブの補完ウインドウを前提に作られているので、コンフリクトする可能性が高いです。
マクロや. によるリピートとも相性がよくありません。
nvim-cmp はfeedkeys() による黒魔術で無理矢理対応しているようです。
既存の補完ウインドウのマッピングは使えません。pum.vim 独自のマッピングを使用する必要があります。pumvisible() も使えないので、pum#pumvisible() を使う必要があります。
ddc.vim においてpum.vim をどのように有効化するかについて説明します。
ここではそれぞれのプラグインと依存関係はすでにインストール済み、ロード済みとして話を続けます。
pum.vim との連携はデフォルトで無効になっています。以下の設定を行うことで有効化することが可能です。
call ddc#custom#patch_global('completionMenu','pum.vim')pum.vim はデフォルトマッピングを定義しないので、以下のようにマッピングを定義する必要もあります。
inoremap<silent><expr><TAB> \ pum#visible()?'<Cmd>call pum#map#insert_relative(+1)<CR>': \(col('.')<=1<Bar><Bar>getline('.')[col('.')-2]=~#'\s')? \'<TAB>': ddc#map#manual_complete()inoremap<S-Tab><Cmd>call pum#map#insert_relative(-1)<CR>inoremap<C-n><Cmd>call pum#map#select_relative(+1)<CR>inoremap<C-p><Cmd>call pum#map#select_relative(-1)<CR>inoremap<C-y><Cmd>call pum#map#confirm()<CR>inoremap<C-e><Cmd>call pum#map#cancel()<CR>コマンドライン補完を有効化するには、以下のように設定する必要があります。: を入力したときにddc.vim の設定を切り替えるようにします。
call ddc#custom#patch_global(#{ \ ui:'pum', \ autoCompleteEvents:[ \'InsertEnter','TextChangedI','TextChangedP','CmdlineChanged', \], \ cmdlineSources:{ \':':['cmdline','cmdline-history','around'] \}, \})nnoremap:<Cmd>callCommandlinePre()<CR>:function!CommandlinePre() abort cnoremap<Tab><Cmd>call pum#map#insert_relative(+1)<CR> cnoremap<S-Tab><Cmd>call pum#map#insert_relative(-1)<CR> cnoremap<C-n><Cmd>call pum#map#insert_relative(+1)<CR> cnoremap<C-p><Cmd>call pum#map#insert_relative(-1)<CR> cnoremap<C-y><Cmd>call pum#map#confirm()<CR> cnoremap<C-e><Cmd>call pum#map#cancel()<CR>autocmd User DDCCmdlineLeave++oncecallCommandlinePost()" Enable command line completion for the buffercall ddc#enable_cmdline_completion()endfunctionfunction!CommandlinePost() abortsilent!cunmap<Tab>silent!cunmap<S-Tab>silent!cunmap<C-n>silent!cunmap<C-p>silent!cunmap<C-y>silent!cunmap<C-e>endfunctionddc#enable_cmdline_completion() はコマンドライン補完を有効化する設定で、コマンドライン補完を使用するには必ず呼び出す必要があります。これはコマンドライン補完の設定をコマンドライン外で常に有効化するとパフォーマンスに影響があるためです。
以下の source はコマンドライン補完で使うのに便利です。
https://github.com/Shougo/ddc-cmdline
https://github.com/Shougo/ddc-cmdline-history
https://github.com/Shougo/ddc-around
今回のddc.vim プラグインの開発とpum.vim プラグインの開発は GitHub sponsors の皆さんの支援によって行われました。
https://zenn.dev/shougo/articles/github-sponsors
GitHubのコミット数を見てもらえれば、GitHub sponsorsを開始した6月以降にコミット数が劇的に伸びているのを確認できるかと思います。
https://github.com/sponsors/Shougo/
最近、テキストエディタのプラグインを開発するため、開発用ノートパソコンとして Thinkpad T14 Gen2 を購入しました。
これは 6 年半ぶりのマシン更新となります。効率的な開発には効率的なノートパソコンが必要不可欠です。
これも github sponsors があったから決断できたことです。github sponsors の皆さんには本当に感謝しています。
バッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。
