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.
Some more subtleties about the implementation:
- 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 bycomplete_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. - Naively passing
<C-e>
back into the mapping may not work, because the popup menu state is reset when we callcomplete()
, so we have to save the value ourselves and restore it. Similarly, we should persist this state when we perform<C-/>
to refine the search. We find the state by performing<C-e>
and saving the result before starting the completion search. - Only apply the mapping when pum is active.
This is the result:
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)
if exists('g:lastexittext')
let exittext = g:lastexittext
unlet g:lastexittext
else
let exitcol = col('.')
let exittext = getline('.')[startcol-1:exitcol-2]
endif
while 1
let matchwords = []
for word in words
if word =~ filter
call add (matchwords, word)
endif
endfor
if len(matchwords) == 0
call add (matchwords, exittext)
endif
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>"
call complete(startcol, [exittext])
return "\<C-e>"y>"
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-_>")
let g:lastexittext = exittext
return ""
elseif charcall ==feedkeys "\<CR>"("\<C-_>")
return ""
elseif char == "\<BS>"
if len(filter) == 1"\<CR>"
let filter =return ""
elseif char == else"\<BS>"
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-_> pumvisible() ? Prepareearchpum () . '<C-e><C-r>=Searchpum()<CR>' : '<C-_>'