1. [OK] The following test produces the expected result: substitution change is joined to the previous change in the buffer (addition of line 3). Upon undo, the cursor position is correctly restored to the second line in the buffer.
set undolevels=10
normal ggiline one is bull of aaaa
set undolevels=10 " used to break a change into separate undo blocks
normal Goline two is full of bbbb
set undolevels=10 " used to break a change into separate undo blocks
normal Goline three is full of cccc
set undolevels=10 " used to break a change into separate undo blocks
undojoin
keepjumps %s/aaaa/zzzz/
normal u
2. [ERR] The following test produces an unexpected result: substitution change is joined to the previous change in the buffer (addition of line 4). Upon undo, the cursor position is incorrectly restored to the first line in the buffer.
set undolevels=10
normal ggiline one is bull of aaaa
set undolevels=10 " used to break a change into separate undo blocks
normal Goline two is full of bbbb
set undolevels=10 " used to break a change into separate undo blocks
normal Goline three is full of cccc
set undolevels=10 " used to break a change into separate undo blocks
normal Goline four is full of aaaa's again
set undolevels=10 " used to break a change into separate undo blocks
undojoin
keepjumps %s/aaaa/zzzz/
normal u
It appears that the bug is originating around line 2711 in undo.c Sorry, can't be more specific, C is not my strong point, so I'm just summarising what @doliver found so far.
A full description of what we've found is on StackOverflow:
Thanks for getting back to me!
On Tuesday, August 4, 2015 at 6:35:55 PM UTC+1, Christian Brabandt wrote:
> I am nut sure, I understand fully. How do you suggest to change the
> cursor positioning?
I'm afraid I know neither C nor VIM internals well enough to propose a solution. What I can do is prepare the groundwork (describe the inconsistent behaviour, provide the tests) and hope that someone would be willing to pick it up and suggest a fix.
Form everything I've read about the expected behaviour for restoring the cursor positioning (eg: http://vim.wikia.com/wiki/Restore_the_cursor_position_after_undoing_text_change_made_by_a_script), the cursor should be restored "closest to where the first change is made".
However, the inconsistency I found is that two almost identical bits of vimscript make the editor behave in different ways.
# Test 1
Imagine I have a buffer with two lines. I do `:normal Go` to add a 3rd line into that buffer, then undo it. The closest place to where the first (and in this case, the only) change is made is line 2. Thus, the cursor should be restored to line 2.
Now, let's complicate things a little. I have the same buffer with two lines. I do `:normal Go` to add a 3rd line. But this time, instead of `undo`, I do `:undojoin` and then run a substitute command (in my test I use `:keepjumps %s/aaaa/zzzz/`). Only then do I do `:undo`.
As far as positioning of the cursor goes, the above two examples should be absolutely identical. This is because we've `:undojoined` our substitution change onto the back of an existing change, which was the addition of the 3rd line. Thus, the closest place to where the *first* change is made is still line 2, which is where the cursor should be positioned after the `:undo`.
This is exactly how my first test behaves, the one marked with [OK].
# Test 2
My second test, marked with [ERR], displays a different set of behaviour. It demonstrates that under certain conditions, "closest to where the first change is made" rule for the cursor positioning is disregarded and instead the cursor is restored nearest to the first substitution made by the `:s///` command. This appears to be inconsistent with how the cursor should be positioned.
Would anyone be willing to patch this?
Regards,
Dmytro