Open3
Vimのビルトイン補完をぜんぶ動かして候補をまとめられるかと思ったけど無理だった

ここで補完をフォールバックできるようにしたので「各補完を表示させて、候補を集めて、一つの補完ウィンドウにまとめて出せばよいのでは?」と思ったけど無理だった
途中でfeedkeysを挟むため普通のcompletefuncの仕組みに乗せることができない
頑張ったのがこれ
let s:sid = expand("\<SID>")
let s:cmp_list = []
function! s:start() abort
let s:cmp_list = []
call feedkeys($"\<c-x>\<c-f>\<cmd>call {s:sid}f_cmp()\<cr>", 'ni')
endfunction
function! s:f_cmp() abort
let file_items = complete_info(['items']).items
call map(file_items, "{'word':v:val.word, 'menu':'file'}")
call extend(s:cmp_list, file_items)
if &filetype == 'vim'
call feedkeys($"\<c-x>\<c-z>\<c-x>\<c-v>\<cmd>call {s:sid}v_cmp()\<cr>", 'ni')
else
call feedkeys($"\<c-x>\<c-z>\<c-x>\<c-o>\<cmd>call {s:sid}o_cmp()\<cr>", 'ni')
endif
endfunction
function! s:v_cmp() abort
let vim_items = complete_info(['items']).items
call map(vim_items, "{'word':v:val.word, 'menu':'vim'}")
call extend(s:cmp_list, vim_items)
call feedkeys($"\<c-x>\<c-z>\<c-x>\<c-o>\<cmd>call {s:sid}o_cmp()\<cr>", 'ni')
endfunction
function! s:o_cmp() abort
let omni_items = complete_info(['items']).items
call map(omni_items, "{'word':v:val.word, 'menu':'omni'}")
call extend(s:cmp_list, omni_items)
call feedkeys($"\<c-x>\<c-z>\<c-x>\<c-k>\<cmd>call {s:sid}k_cmp()\<cr>", 'ni')
endfunction
function! s:k_cmp() abort
let dict_items = complete_info(['items']).items
call map(dict_items, "{'word':v:val.word, 'menu':'dict'}")
call extend(s:cmp_list, dict_items)
call feedkeys($"\<c-x>\<c-z>\<c-x>\<c-u>", 'ni')
endfunction
function! Completefunc(findstart, base) abort
if a:findstart
return mi#cmp#findstart()
endif
return s:cmp_list
endfunction
set completefunc=Completefunc
inoremap <c-x><c-u> <cmd>call <sid>start()<cr>

vimの関数補完が(
終わりで表示されるのでオートクローズできないかと思ったけど無理だった
function! s:auto_close_paren() abort
if slice(get(v:completed_item, 'word', ''), -1) ==# '('
call feedkeys(")\<left>", 'ni')
endif
endfunction
autocmd CompleteDone * call s:auto_close_paren()
なぜかというと一つ候補を選択した段階でCompleteDoneが発火してキーが入力され補完が終了してしまうため2番目以降の候補を選択することができない

辞書キーワード補完をomnifunc的にできるかと思ったけど無理だった
function! mi#cmp#dict_completefunc(findstart, base) abort
if a:findstart
return mi#cmp#findstart()
endif
let base = substitute(s:cmp_info.lastword, "'", "''", 'g')
let compl_list = mi#cmp#dict_keywords()
" if empty(base)
return compl_list
" endif
" Filter the list based on the first few characters the user entered
" return filter(deepcopy(compl_list), "v:val =~# '^" . escape(base, '\\/.*$^~[]') . ".*'")
endfunction
function! mi#cmp#dict_keywords() abort
if exists('s:dict_keywords_cache')
return s:dict_keywords_cache
endif
let dict_path_list = split(&dictionary, ',')
let keywords = []
for dict_path in dict_path_list
try
let lines = readfile(dict_path)
call extend(keywords, map(lines, "{'word':v:val, 'menu':'dk-" .. dict_path->substitute('.*/', '', '') .. "'}"))
catch
echomsg v:exception
endtry
endfor
call uniq(sort(keywords))
let s:dict_keywords_cache = keywords
return s:dict_keywords_cache
endfunction
表示はできたのだが絞り込みが遅くて使用に耐えなかった