-
-
Notifications
You must be signed in to change notification settings - Fork 85
Description
Describe the bug
.
may fail to repeat a command after reloading a buffer.
To Reproduce
Run this shell command (you'll need to update the path to the plugin):
vim -Nu NONE -S <(cat <<'EOF'
set rtp^=/path/to/vim-repeat
nno <c-b> xp:call repeat#set("\<lt>c-b>")<cr>
au CursorMoved,TextChanged * "
%d
pu!='abc'
update
EOF
) /tmp/file
- Press
C-b
to exchange the position of thea
andb
characters. - Run
:w
to save the buffer. - Press
h
to move the cursor (necessary to prevent theCursorMoved
autocmd from fixing the bug by accident). - Run
:e
to reload the buffer. - Press
.
to repeat the customC-b
command.
Result: the buffer contains baac
.
Expected behavior
The buffer contains abc
.
Screenshots
Environment
- plugin version: c947ad2
- Vim version: 8.2 Included patches: 1-740
- OS: Ubuntu 16.04.6 LTS
- Terminal: XTerm(322)
Additional context
The issue is due to the fact that when we reload a buffer, BufEnter
is fired but not BufLeave
.
We need an event to hook into and save the state of the ticks synchronization, so that we can restore it if necessary on BufEnter
. IOW, we need a replacement for BufLeave
; I think BufUnload
is the only possible event which can play this role.
It could be added into the list of events in this line:
vim-repeat/autoload/repeat.vim
Line 161 in c947ad2
autocmd BufLeave,BufWritePre,BufReadPre * let g:repeat_tick = (g:repeat_tick == b:changedtick || g:repeat_tick == 0) ? 0 : -1 |
Which would give:
autocmd BufLeave,BufWritePre,BufReadPre,BufUnload * let g:repeat_tick = (g:repeat_tick == b:changedtick || g:repeat_tick == 0) ? 0 : -1
^^^^^^^^^^
During my limited tests, it seemed to fix the issue. I can send you a PR if you think it's the right fix.
Note that BufReadPre
can't help here, because b:changedtick
is incremented by 1 on BufReadPre
when reloading a buffer.
Btw, why does the autocmd listen to BufReadPre
?
AFAICT, its goal is to save the state of the ticks synchronization.
But it seems we can't do that on BufReadPre
; either b:changedtick
is incremented by 1 when reloading a buffer:
$ touch /tmp/file{1..2} && vim -Nu NONE -S <(cat <<'EOF'
let g:abuf = 'expand("<abuf>")'
eval getcompletion('buf*', 'event')
\->filter({_,v -> v !~# 'Cmd$'})
\->map({_,v -> execute(printf(
\ 'au %s * unsilent echom "%s in buf "..%s..": tick is "..getbufvar(%s, "changedtick")'
\ , v, v, g:abuf, g:abuf))})
call map(range(10), {_,v -> execute('pu='..v)})
undo
EOF
) /tmp/file1
:mess clear
:e
:mess
BufUnload in buf 1: tick is 34
"/tmp/file1" 0L, 0C
BufRead in buf 1: tick is 35
BufReadPost in buf 1: tick is 35
BufEnter in buf 1: tick is 36
BufWinEnter in buf 1: tick is 36
" notice how the tick was 34 on BufUnload, and then 35 on BufRead
or it has already been initialized to 1 when loading a new file (it happens a little earlier on BufNew
):
$ touch /tmp/file{1..2} && vim -Nu NONE -S <(cat <<'EOF'
let g:abuf = 'expand("<abuf>")'
eval getcompletion('buf*', 'event')
\->filter({_,v -> v !~# 'Cmd$'})
\->map({_,v -> execute(printf(
\ 'au %s * unsilent echom "%s in buf "..%s..": tick is "..getbufvar(%s, "changedtick")'
\ , v, v, g:abuf, g:abuf))})
call map(range(10), {_,v -> execute('pu='..v)})
undo
EOF
) /tmp/file1
:mess clear
:e /tmp/file2
:mess
BufNew in buf 2: tick is 1
BufAdd in buf 2: tick is 1
BufCreate in buf 2: tick is 1
BufLeave in buf 1: tick is 34
BufWinLeave in buf 1: tick is 34
BufUnload in buf 1: tick is 34
BufReadPre in buf 2: tick is 1
"/tmp/file2" 0L, 0C
BufRead in buf 2: tick is 1
BufReadPost in buf 2: tick is 1
BufEnter in buf 2: tick is 2
BufWinEnter in buf 2: tick is 2
" notice how the tick is initialized to 1 on BufNew, and unchanged on BufRead
In both cases, b:changedtick
has been altered on BufReadPre
, so there's no guarantee that the state of the ticks synchronization has been preserved.
Is there some case where BufReadPre
is useful?