Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"." may fail to repeat a command after reloading a buffer #82

Open
lacygoill opened this issue May 12, 2020 · 1 comment
Open

"." may fail to repeat a command after reloading a buffer #82

lacygoill opened this issue May 12, 2020 · 1 comment

Comments

@lacygoill
Copy link

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 the a and b characters.
  • Run :w to save the buffer.
  • Press h to move the cursor (necessary to prevent the CursorMoved autocmd from fixing the bug by accident).
  • Run :e to reload the buffer.
  • Press . to repeat the custom C-b command.

Result: the buffer contains baac.

Expected behavior

The buffer contains abc.

Screenshots

gif

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:

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?

svermeulen added a commit to svermeulen/vim-repeat that referenced this issue May 23, 2020
@svermeulen
Copy link

I encountered this issue as well, and have been using your suggested fix here. So far it has addressed the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants