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

Can't repeat change made immediately after pressing . (repeat#run) #25

Open
glts opened this issue Jun 14, 2014 · 1 comment
Open

Can't repeat change made immediately after pressing . (repeat#run) #25

glts opened this issue Jun 14, 2014 · 1 comment

Comments

@glts
Copy link

glts commented Jun 14, 2014

I was trying the mapping from Vimcasts episode 61.

nnoremap <silent> <Plug>TransposeCharacters xp:call repeat#set("\<Plug>TransposeCharacters")<CR>
nmap cp <Plug>TransposeCharacters

After running the mapping cp, repeating it ., and making another change ofoo<Esc>, the . becomes ineffective. It doesn't repeat ofoo<Esc>.

If this is a fundamental limitation of repeat.vim (CursorMoved ...?) I believe it should be mentioned in the docs. Thanks!

@gmnimrod
Copy link

I think I have an explanation for what is happening.

This can happen for other plugins. The conditions are:

  1. A plugin calls repeat#set and doesn't move the cursor after it does.
  2. Without moving the cursor, some other operation that is repeatable moves the cursor. repeat#set must not be called by this operation.

Result: The next time . is invoked, vim will repeat the plugin on stage (1), instead of the operation on stage (2).

What is happening?
@glts is right. It does have something to do with CursorMoved.
In short, g:repeat_tick is updated after (2) instead of in (1), because that is when CursorMoved event first occurs. This prevents vim-repeat in repeat#run from knowing (2) ever took place.

Detailed example:

  1. User invokes the <Plug>TransposeCharacters.
  2. repeat#set sets up an autocmd to update g:repeat_tick on CursorMoved event (code). The example plugin doesn't move the cursor after repeat#set is called, so the autocommand is not executed yet.
  3. User invokes ofoo<Esc>.
  4. The cursor has moved, so g:repeat_tick is updated after ofoo<Esc>.
  5. The user invokes dot-repeat. In repeat#run vim-repeat sees that g:repeat_tick == b:changedtick (code), concludes that the last operation was the plugin, instead of the last insert, and repeats it.

Another example where this would happen is with tpope/vim-unimpaired, but only if the cursor is first placed on the first non-blank character. Then: ]<Space>ofoo<Esc>. will add a blank line instead of a foo line. (It may seem like an artificial edge case, but that is what got me into this bug hunt.)

Possible Solution
The easiest solution that I can think of is to separate repeat#set into two different functions, one that sets up the autocommand and one that doesn't. The latter should be used in cases where the plugin does not pend on a motion to complete.
The drawbacks are that it complicates vim-repeat's API, and doesn't provide an automatic fix where vim-repeat is already used.

Maybe there is a solution that doesn't require a new function, but I don't see it right now.

Any thoughts?

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