As I suspected, this is possible. We use complete_info()
to find the existing matches, ask the user to type his desired filter using getchar()
, and replace the completions with complete()
.
One more subtlety is that we need to get the "startcol", that is, the start of the text being replaced, so that selecting the completions replaces that text, rather than appending to the end. We find this by <C-n>
(to ensure some completion is selected), get the cursor position after the selection and finding the length of the selected completion by complete_info()
, and compensating the <C-n>
with a <C-p>
. The "startcol" is the cursor position after selection minus the length of the selected completion.
I mapped this to <C-/>
/<C-_>
. To use this, after getting a lot of completions (e.g. from <C-n>
or <C-x><C-i>
), invoke <C-/>
and start typing to search for the completion you need. You will see your search at the bottom of the screen (like the usual command prompt). You can see the completions being filtered as you type.
You can use all normal vim regex patterns here (like the magic
mode). Press backspace to delete the last character or <C-u>
to delete your whole search. Press <C-/>
to accept this filter and begin another search. Press Enter to leave the search and go back to the completion. The usual completion bindings <C-e>
, <C-y>
, <C-n>
, <C-p>
, <C-h>
, <C-l>
, <Up>
, <Down>
, <Esc>
also work, and automatically leaves the completion search.
function! Prepareearchpum ()
let g:compstartcol = col('.')
let g:compinf = complete_info()
if len(g:compinf.items) && (g:compinf.selected == -1)
return "\<C-n>\<C-r>=Prepareearchpum()\<CR>\<C-p>"
else
return ""
endif
endfunction
function! Searchpum ()
let words = []
for item in g:compinf.items
call add(words, item.word)
endfor
let filter = ""
if len(words)
let startcol = g:compstartcol - len(g:compinf.items[g:compinf.selected].word)
while 1
let matchwords = []
for word in words
if word =~ filter
call add (matchwords, word)
endif
endfor
call complete(startcol, matchwords)
echomsg filter
try
let char = getchar()
catch /^Vim:Interrupt$/
return "\<C-c>"
endtry
if type(char) == 0
let char = nr2char(char)
endif
if char == "\<C-e>"
return "\<C-e>"
elseif char == "\<C-y>"
return "\<C-y>"
elseif char == "\<C-n>"
return "\<C-n>"
elseif char == "\<C-p>"
return "\<C-p>"
elseif char == "\<C-l>"
return "\<C-l>"
elseif char == "\<C-h>"
return "\<C-h>"
elseif char == "\<Up>"
return "\<Up>"
elseif char == "\<Down>"
return "\<Down>"
elseif char == "\<Esc>"
return "\<Esc>"
elseif char == "\<C-_>"
call feedkeys ("\<C-_>")
return ""
elseif char == "\<CR>"
return ""
elseif char == "\<BS>"
if len(filter) == 1
let filter = ""
else
let filter = filter[:len(filter) - 2]
endif
elseif char == "\<C-u>"
let filter = ""
else
let filter = filter .. char
endif
endwhile
endif
endfunction
inoremap <expr> <C-_> Prepareearchpum () . '<C-r>=Searchpum()<CR>'