8

When we edit a file, we usually do many UNDO in a row, say, 20 times. In VIM, that is usually performed by pressing u 20 times, and that makes VIM go "up" in the history stack 20 positions. If you then make a certain change, all those last 20 history commands are lost, and replaced by change. I would like to make the change without loosing those 20 positions. So I guess I would like to tell VIM to stop recording history before I do change, and resume history afterwards (don't want change in history).

EDIT Trying to be more clear: I have a function FF that updates the last lines of a file when the buffer is written. So, if I perform 20 undos + write, the last write opens a new undo branch. I tried adding undojoin inside FF (trying to follow a suggestion by jlmg below), but a sequence write-undo-write gives an error: undojoint not allowed after undo. I could instead do some sed .... after leaving vim instead, but since I use this through SSH I prefer a vim-only solution (execute a command after unloading the buffer does not write to the file).

EDIT 2 Try to do this in VIM: open a blank file, and do:

i1<ESC>:wa2<ESC>:wa3<ESC>:wa4<ESC>uu:w

If now you do a <CTRL>R, VIM will write the '3' back, a further <CTRL>R you get the 4. This happens EVEN if you do a:w after each <CTRL>R. However, if each time you do a :w you execute a fuction via BufWritePre, the <CTRL>R will not write the 3 back. And this is what I want to do, that's why I wrote to 'suspend hisotry', but maybe what I am asking is not possible, besides working with the full undotree().

  • 1
    This question is on topic for this site, but since it pertains to a fairly esoteric potential feature of VIM, have you considered trying vim.stackexchange.com instead? I mean, I know UNIX and I know vi, but to me vi's undo command undoes once, and then when you press u again it undoes the undo ("redo"). That's real vi. So I and other users of this site might not know the answer to your question. – Celada Dec 3 '16 at 13:30
  • 2
    @Celada I believe it's vi.stackexchange.com, or Vi and Vim. – Satō Katsura Dec 3 '16 at 13:44
  • @SatoKatsura you're absolutely right! – Celada Dec 3 '16 at 13:47
  • 1
    @Celada: I didn't know about vim.se, I would have tried that. Anyway, it seems Sato knows how to help. – Luis A. Florit Dec 3 '16 at 15:02
  • The problem you give "all those last 20 history commands are lost" is fixed with the existence of the undo branches, so it's confusing that you then want to fix your problem by avoiding their creation. You say you can get the result you want with sed, but sed can't do your undos, so how's that going to work? If you were able to "suspend history", what kind behaviour would you expect of vim when you do an undo afterwards? The only behaviour I could visualize is to group those changes (along with the undos) in one undo block, hence my answer. But if that's not it, what is? – JoL Dec 3 '16 at 17:58
12

You don't need to suspend Vim history. The actual data forms a tree, and you can retrieve it with the undotree() function. There are, of course, a number of plugins that turn that into something more user-friendly, f.i. gundo, mundo, and undotree. If you also enable undo persistence (cf. :h undo-persistence) you can easily navigate through the entire change history of a file.

  • Interesting, I didn't know the undo thing was actually a tree. In my case, I only want to go back once, i.e., to the previous undo branch. Do you know how to do that? It seems undotree() should help? – Luis A. Florit Dec 3 '16 at 14:59
  • Actually, in this particular case that motivated my question, I want that change does not appear at all, anywhere, nor in history, nor in the undo tree. That's why my question was to "suspend history". – Luis A. Florit Dec 3 '16 at 15:11
  • @LuisA.Florit: The quick and easy way to do this is with :earlier, which accepts a time unit and will cross undo branches if necessary (e.g. :earlier 1h makes the buffer look like it did an hour ago). If you can't use a time unit (because the changes were too close together?) then you'll have to use g- to walk the undo tree a few steps at a time. – Kevin Dec 4 '16 at 0:25
0

If I understand you correctly what you want is that afterwards doing an undo should undo that change and the 20 undos.

When vim executes a function or command, all actions that it does are undone together. I'm not sure if those actions may include undos. Maybe this part of the documentation can help:

3. Undo blocks                      *undo-blocks*

One undo command normally undoes a typed command, no matter how many changes
that command makes.  This sequence of undo-able changes forms an undo block.
Thus if the typed key(s) call a function, all the commands in the function are
undone together.

If you want to write a function or script that doesn't create a new undoable
change but joins in with the previous change use this command:

                        *:undoj* *:undojoin* *E790*
:undoj[oin]     Join further changes with the previous undo block.
            Warning: Use with care, it may prevent the user from
            properly undoing changes.  Don't use this after undo
            or redo.
            {not in Vi}

This is most useful when you need to prompt the user halfway through a change.
For example in a function that calls |getchar()|.  Do make sure that there was
a related change before this that you must join with.

This doesn't work by itself, because the next key press will start a new
change again.  But you can do something like this: >

    :undojoin | delete

After this an "u" command will undo the delete command and the previous
change.

To do the opposite, break a change into two undo blocks, in Insert mode use
CTRL-G u.  This is useful if you want an insert command to be undoable in
parts.  E.g., for each sentence.  |i_CTRL-G_u|
Setting the value of 'undolevels' also breaks undo.  Even when the new value
is equal to the old value.

Alas, I tried using :undo 2 | undojoin | normal ohi, but I got the error message E790: undojoin is not allowed after undo.

However, if the actions are done in a function like this:

function F()
  undo 2
  normal ohi
endfunction

then calling it with :call F() does perform the undo and other action in one undo block. Note that because you're using undo you're creating a new undo branch. After it's done you can use the normal mode command g-, to undo F(); using u will be like having done :undo 3 at the beginning.

  • Not sure if this works... What I want is that, after you do some undo, everything you do in your function F() does not open a new undo branch. In my case, F() is executed every time I write the file, and if I do undo and then write the file, F() opens a new undo branch. I tried with undojoin at the beginning of F(), but a sequence write-undo-write gives an error: undojoint not allowed after undo. – Luis A. Florit Dec 3 '16 at 17:13
  • Updated my question trying to be more clear. – Luis A. Florit Dec 3 '16 at 17:27
  • @LuisA.Florit I wrote in the answer that :undojoin did not work for what you wanted, mentioning the same error, and I wasn't suggesting to use :undojoin at the beginning of F(). F() is meant to do what :undojoin couldn't, which is to join the undos and other commands in one undo block. – JoL Dec 3 '16 at 17:38
  • @LuisA.Florit You say you want to avoid creating an undo branch, but I can't find the reason why you want that. I thought the point of your question was to be able to undo the commands and the multiple undos in one go. Looking back at it, I wonder if your point was to not lose the changes you had undone. If that's the case the existence of the undo tree is precisely what you want. You can navigate it with g- and g+. – JoL Dec 3 '16 at 17:45
  • Yes, it seems the undo tree is what I need. – Luis A. Florit Dec 3 '16 at 17:55

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.