Skip to main content
3 of 7
added 186 characters in body

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>'