How to efficiently undo an autocomplete in vim
Asked Answered
S

3

6

Right now I'm playing with autocompletions in vim, and I've settled on supertab to handle my completions. While it's running very smoothly and (I think) exactly as it's designed, I'd like to know how to change something.

Firstly, I'm running 7.3.429 on Ubuntu 12.04LTS with

set completeopt=menuone,preview,longest

so that I have bash-type autocompletion with supertab, and default complete.

Let's suppose I have the following in my file:

aaabbbcccddd
aaabbccddeef
aaabbcddeeff

If I type aa and hit Tab, then vim realized that aaabb is the longest common string among the matches, so it inserts aaabb and displays a menu containing the three options. If I really did want one of those options, then all is good. But maybe I really wanted aaaaazzzzz, but didn't realize I hadn't typed it yet.

Is there a good way to say to vim, "Oh, I'm sorry! I didn't mean to tabcomplete after all! Please pretend I didn't."

Right now, the options apparent to me are to:

  1. Hit Tab or Shift+Tab enough times to get back to my initial. But if there are many similar words, especially up to different lengths, this is annoying.
  2. Hit backspace however many times necessary, or some other naive deletion. But these are surely unnecessary key strokes.
  3. Hit Esc+u to get an undo, but this undoes my whole word (or more if I typed quickly). This is entirely unacceptable. And afterwards, I need to re-enter insert mode and retype. Gross.
  4. Hit Ctrl+U to undo without leaving insert-mode. But this also has a tendency to remove way too much.
  5. Hit Ctrl+W to delete the last word. While I get to do this without exiting insert-mode, I still have to retype. This is the best I have found so far.

If I didn't have longest enabled, then I could use Ctrl+E, which quits the menu without inserting anything else. But since longest is on, it stops autocomplete but leaves the longest common match entered.

Surely there has to be a better way to do this.

Spadix answered 1/8, 2013 at 6:36 Comment(0)
A
2

There are 2 native ways to do this in vim. If you know right way that the item is not in the completion menu you can use <c-y>. <c-y> accepts the current match which if you didn't move though any completions will send you back to only the text you inserted. (Second way) However if you did move through the completion menu you can move though until you get back to the original text.

However I imagine it isn't too hard to simply accept the longest matching and edit the word. You could also use <c-g>u to break up the undo block by working it into your <tab> mapping. Although that may break the history up more than you want.

Acquaintance answered 1/8, 2013 at 15:57 Comment(2)
Thank you. I didn't try <c-y>. But I admit, I am confused. Reading the documentation, I do not interpret the 'current match' as being the originally inserted, pre-longest text at all. Especially when <c-e> seems to indicate that it should be the one that undoes the completion. Am I reading the documentation very wrong?Spadix
I understand why you are confused about <c-e> however you need to read just a bit further: End completion, go back to what was there before selecting a match (what was typed or longest common string).Acquaintance
S
2

This is difficult, but I had the same problem and have implemented something. The downside is that I had to overload any (built-in and custom) completion trigger to first invoke a custom function that sets a mark to the beginning of the completion. Then I map <Esc> in insert mode (with popup menu visible) to delete the text up to that mark.

function! s:SetUndo()
    call setpos("'\"", getpos('.'))
    return ''
endfunction
inoremap <expr> <SID>(CompleteStart) <SID>SetUndo()
function! s:UndoLongest()
        " After a completion, the line must be the same and the column must be
        " larger than before.
        if line("'\"") == line('.') && col("'\"") < col('.')
            return "\<C-\>\<C-o>dg`\""
        endif
    endif
    return ''
endfunction
imap <expr> <Esc>      pumvisible() ? <SID>UndoLongest() : '<Esc>'

inoremap <script> <C-x><C-n> <SID>(CompleteStart)<C-x><C-n>
inoremap <script> <C-x><C-p> <SID>(CompleteStart)<C-x><C-p>
...
Shocking answered 1/8, 2013 at 6:59 Comment(0)
A
2

There are 2 native ways to do this in vim. If you know right way that the item is not in the completion menu you can use <c-y>. <c-y> accepts the current match which if you didn't move though any completions will send you back to only the text you inserted. (Second way) However if you did move through the completion menu you can move though until you get back to the original text.

However I imagine it isn't too hard to simply accept the longest matching and edit the word. You could also use <c-g>u to break up the undo block by working it into your <tab> mapping. Although that may break the history up more than you want.

Acquaintance answered 1/8, 2013 at 15:57 Comment(2)
Thank you. I didn't try <c-y>. But I admit, I am confused. Reading the documentation, I do not interpret the 'current match' as being the originally inserted, pre-longest text at all. Especially when <c-e> seems to indicate that it should be the one that undoes the completion. Am I reading the documentation very wrong?Spadix
I understand why you are confused about <c-e> however you need to read just a bit further: End completion, go back to what was there before selecting a match (what was typed or longest common string).Acquaintance
D
0

Know it's been a while, but this is how I managed to get it done.

(<C-n> auto-completion is what I use. You might have to change it to <Tab> as the case may be.)

" When the auto-complete menu is not visible, make C-n start a new undo sequence
" See - https://vi.stackexchange.com/a/2377
inoremap <expr> <C-n> pumvisible() ? "<C-n>" : "<C-g>u<C-n>"

" Overload Esc to just do an undo when auto-complete menu is visible
inoremap <expr> <Esc> pumvisible() ? "<C-o>u" : "<Esc>"
Druse answered 26/4, 2018 at 0:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.