10

At some point my Vim would undo an entire word when I hit u.

[i]typing some words[C-c][u]

Would result in

typing some

However, the undo granularity seems to have changed to deleting one letter at a time.

[i]typing some words[C-c][u]

Results in

typing some word

I remember reading something about customising undo granularity in Practical Vim, but I can't remember how to control it. Any help appreciated!

  • 2
    There is probably something in your vimrc that does this. I can't replicate it, and can't find anything in the :help files about it ... See: How do I debug my vimrc file?... Also, using <C-c> to exit insert mode may have side-effects (see this) and may be part of the problem ... I would suggest using Esc or <C-[> (see this) – Martin Tournoij Mar 5 '15 at 13:07
11

Manually breaking the undo sequence

You can manually break the undo sequence in Insert mode with <C-G>u. From the help:

CTRL-G u        break undo sequence, start new change

Example

iHello<C-G>u world!<Esc>u

This will leave you with the text

Hello

Breakdown of commands

i                           " Enter Insert mode
 Hello                      " Type 'Hello'
      <C-G>u                " Break the undo sequence
             world!         " Type ' world!'
                   <Esc>    " Return to Normal mode
                        u   " Undo

Automatic solution

Finer-grained undo-level control

Here's a small snippet of VimScript that uses <C-G>u to create undo breaks when you start deleting text, and also when you start entering text again after a deletion.

function! s:start_delete(key)
    let l:result = a:key
    if !s:deleting
        let l:result = "\<C-G>u".l:result
    endif
    let s:deleting = 1
    return l:result
endfunction

function! s:check_undo_break(char)
    if s:deleting
        let s:deleting = 0
        call feedkeys("\<BS>\<C-G>u".a:char, 'n')
    endif
endfunction

augroup smartundo
    autocmd!
    autocmd InsertEnter * let s:deleting = 0
    autocmd InsertCharPre * call s:check_undo_break(v:char)
augroup END

inoremap <expr> <BS> <SID>start_delete("\<BS>")
inoremap <expr> <C-W> <SID>start_delete("\<C-W>")
inoremap <expr> <C-U> <SID>start_delete("\<C-U>")

Notes

This will work for <BS>, <C-W> (delete a word), and <C-U> (delete to beginning of line). If there are more ways to delete in Insert mode, they can be supported by adding additional inoremap calls at the end.

  • 4
    Only problem is, you don't foresee that you are going to be undoing later (otherwise you won't be doing it in the first place). So you usually end up with a situation where you wish you had used <C-G>u, which is too late already. – Shahbaz Mar 5 '15 at 15:40
  • 4
    Yes. Actually something I have been missing myself is this. Sometimes I do this i followed by long sentence followed by backspace to remove part of what I have written (all in insert mode). Then I realize I shouldn't have backspaced and what I had written was fine. I undo, but then the whole inserted text gets undone. So either I am left with none of the long sentence, or the part that is not backspaced. It's impossible to get the backspaced part back. Is there a way to tell vim to break the undo sequence upon backspace? (Now that I say it, maping backspace is an option of course). – Shahbaz Mar 5 '15 at 15:46
  • 1
    For future visitors, I also added inoremap <CR> <C-G>u<CR> so that each undo is contained within a single line (no more undoing the whole paragraph that you were typing nonstop). – Shahbaz Mar 6 '15 at 11:06
  • 1
    @Shahbaz, absolutely :) – tommcdo Mar 9 '15 at 12:14
  • 1
    @Shahbaz "Am I permitted to copy?" See link at the bottom of the page: "user contributions licensed under cc by-sa 3.0 with attribution required" – user859 Feb 14 '17 at 17:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.