2

Minimal (not-)working example:

fun! Test()
  let save=changenr()
  call append(0, "THIS MUST BE UNDONE")
  exe "undo ".save
endfun
command! -nargs=0 T :call Test()

Now open a file, insert some garbage (because "undo 0" won't work), then call the command :T three times in a row.

On my machine at least, the first two times it works as expected (i.e. does nothing). But the last time the text THIS MUST BE UNDONE is added to the buffer. On closer inspection, the value of save used on the third execution is 2, while the two previous ones were both 1.

What happened? (Probably something mysterious related to undotree)?

edit: while I'm at it, the fact that undo 0 does not work looks like a bug in itself. If I open a file and then do two modifications A and B, then undo 1 puts me after A, undo 2 puts me after A+B, but vim refuses to undo 0 to put me before A...

  • 1
    What are you trying to do? (XY Problem) – mMontu Feb 12 '16 at 17:37
  • 1
    @Circonflexe The problem comes from the function changenr() which sometimes returns a value that you (and I) don't understand. Most of the time, it returns what you (and I) expect, but sometimes (when you go back to the root), instead of returning the change number of the current leaf, it returns the change number of the deepest leaf in the nearest "inactive branch". – saginaw Feb 12 '16 at 19:05
  • 1
    @Circonflexe Because of this, sometimes your save variable contains a value which is too high, therefore your exe "undo " . save doesn't bring you back to the expected leaf and your text this must be undone is not removed. – saginaw Feb 12 '16 at 19:13
  • 1
    @mMontu: This is a MWE, strictly about undo. The full function would be a :w filter (make changes, save file, undo changes). @saginaw, also @mMontu's answer below: so far nothing new, I did write all those echo lines in my function, (again MWE...) – Circonflexe Feb 15 '16 at 10:23
  • 1
    @Circonflexe I don't know how to do it, but you need to find a way to get the id of the current leaf / node, and changenr() is not a reliable way to get this info, because when you undo, it doesn't return the current id, it returns this: After undo it is one less than the number of the undone change. Maybe this link will help you a little: reddit.com/r/vim/comments/dszuz/… What this person says is that you must look in the dictionary returned by undotree(). – saginaw Feb 16 '16 at 10:12
3

If you are going to learn how the undotree feature works, similar to this question, then you should use some plugin to have a graphic representation of the undotree while you try the commands.

It is also useful to increase the verbosity of your test function:

fun! Test()
  echom 'prev changenr() = '.changenr()
  let save=changenr()
  call append(0, "THIS MUST BE UNDONE")
  exe "undo ".save
  echom 'post changenr() = '.changenr()
endfun
command! -nargs=0 T :call Test()

For your test case, I noticed that each execution of the :T command inserts a node on the undotree. Before starting the test the undo tree plugin shows the following:

 *   >2<   (1 second ago)
 *    1    (2 seconds ago)
 *    0    (Original)

First execution of :T

prev changenr() = 2
1 line less; after #2  20 seconds ago
post changenr() = 2


 *   {3}   (1 second ago)
 *   >2<   (21 seconds ago)
 *    1    (22 seconds ago)
 *    0    (Original)

After the second :T:

prev changenr() = 2
1 line less; after #2  31 seconds ago
post changenr() = 3


 *   {4}   (1 second ago)
 | *    3    (12 seconds ago)
 |/
 *   >2<   (32 seconds ago)
 *    1    (33 seconds ago)
 *    0    (Original)

Note the line less messages -- the command :T changed the buffer, thus it changed the undotree.

It is possible that the creation of the branch when change {4} is introduced caused changenr() to return the different value.

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.