Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

delimitMate breaks the repeat command #138

Open
neitanod opened this Issue · 28 comments

8 participants

@neitanod

If in normal mode you type:

ifunction_name(argument<Esc>

You get:

function_name(argument)

inserted into the document. But if you then go down a line and press the "repeat" command (the dot) you just get:

argument

inserted into the document.

¿Shouldn't you get everything you typed inserted again?

@qstrahl

Ideally, yes, but vim lacks decent support for hooking custom edit commands into the repeat command. There is vim-repeat, if plugin maintainers decide to make use of it. I'm not sure if delimitMate uses it.

@Raimondi
Owner

@qstrahl is right about Vim being the issue here. vim-repeat works for ex and normal mode commands, I don't see how to use it inside an insert session.

Bram proposed a solution for this but it hasn't been implemented yet.

@Raimondi Raimondi closed this
@blueyed

@Raimondi can you please re-open the issue?
Although it appears to need a fix in Vim itself, it's still an issue with delimitMate.

Would it be an option to use <C-o> (used to issue normal commands without leaving INSERT mode) and make vim-repeat handle the work of repeating?

@Raimondi
Owner

I don't think that approach would work, you need to "join" the buffer change history items used in the insert session, and only those that should be joined (good luck with that!). vim-repeat can't do that.

I don't see any way to fix this issue with viml, but I'll leave it open for a while.

@Raimondi Raimondi reopened this
@blueyed

I see.

The help mentions the following (:h ins-special-special):

Using CTRL-O splits undo: the text typed before and after it is undone
separately.  If you want to avoid this (e.g., in a mapping) you might be able
to use CTRL-R = |i_CTRL-R|.  E.g., to call a function: >
    :imap <F2> <C-R>=MyFunc()<CR>

I guess this has been tried already, or is it not appropriate here?!

@Raimondi
Owner

The problem is not inserting the text, it's that we either need to move the cursor back after inserting the closing delimiter or insert it directly after the cursor.

@blueyed

Isn't it possible to also move the cursor within the function, via normal h etc?

@qstrahl

Could also use the cursor function.

@Raimondi
Owner

Yes to both of you, but I wasn't clear about the issue. The problem with moving the cursor is that vim will start a new entry in the change history, and the same problem with using setline() from <C-R>=. So, we need to move the cursor or insert text after the cursor while keeping vim from breaking the insert session. That's why Bram's suggestion would be the most appropriate way to solve it, Vim wouldn't split the insert session when the cursor moves inside the inserted text anymore.

@Raimondi Raimondi closed this
@Raimondi
Owner

No, as explained above.

@justinmk

I think I've found a way to "fix" undo/redo:

inoremap ( ()<esc>:undojoin<cr>i

Since <esc> moves the cursor to the left, the arrow keys are never touched.

Repeat . still doesn't work with this approach, but that could be solved easily with tpope's repeat.vim (I can send a PR if you like).

I've only tested on Vim 7.4.155 but this should work with previous versions.


edit: simplified map, no need for <C-r>='()'<cr>

@justinmk justinmk referenced this issue
Closed

atomic undo #171

@justinmk

Also, this achieves the "pass through" behavior when the closing paren is reached:

inoremap ) <C-\><C-o>:undojoin<cr><C-\><C-o>a

This is just a proof of concept, since of course it isn't checking to see if the next char is actually a paren.

@Raimondi
Owner

This looks really nice! a PR would be great, just keep in mind that the issue with the closing paren was fixed with #169. So, just the opening paren and repeat need fixing.

@Raimondi Raimondi reopened this
@majutsushi

This does indeed fix undo, but unfortunately it introduces a different problem with block-inserts. Let's say you select a block with Ctrl-V and want to change it with c or insert something at the beginning with I. Then when you type one of the delimiters this mapping will leave insert mode, causing the text entered so far to be inserted in all of the selected lines, but it won't continue doing so after the mapping finishes. This means that everything that is entered after the delimiter will only appear on the current line.

@justinmk

Good catch. I wonder if there is a way to detect visual-block insert-mode...

@majutsushi

I can't find a way to detect that specific mode, mode() just return i like with normal insert mode, and visualmode() doesn't get cleared after the replacement finishes. The only way I've found that may work is use an autocommand:

autocmd InsertLeave * call visualmode(1)

and then check for mode() == 'i' && visualmode() == '�^V'. But that autocommand may have unintended side-effects.

@blueyed

@majutsushi @justinmk
Any updates? Have you been using/testing the autocommand approach?

And the help for mode() states that it would return ^V (and does so for me).

Tested with au CursorMoved * echom "v:" visualmode() "m:" mode() "m(1):" mode(1).

(For some reason mode(1) does not return the full name like stated in the help, also for normal mode).

Can we have a pull request for this please?
This would make it easier to test and improve it.

@justinmk

@blueyed after @majutsushi 's edge case I never got around to implementing. I was hoping to find a reliable way to address the edge case (even posted on vim_use). If what you say is true then that solves the edge case I think. PR tonight.

@majutsushi

As far as I can see mode() only returns ^V while you're actually still in visual mode, not after you've pressed something like I or c to write the new/replacement text, and that's where you actually want to detect the mode to change the behaviour. So I don't think this is going to work, but I'd be happy to be proven wrong.

@blueyed

@majutsushi
I've just tested it with the autocommand posted above, and it appeared to work.

But then it's just that the autocommand gets not executed after I anymore.

It appears that visualmode() might be used to in this case from the InsertEnter autocommand.

For debugging/inspection:

au CursorMoved,InsertEnter * echom "v:" visualmode() "m:" mode() "m(1):" mode(1) "col:" col(".")
@majutsushi

You could use a CursorMovedI autocommand, which would show that mode() returns i after an I (as should be expected). And since the mapping are only useful in insert mode just checking mode() in them won't work.

But by using InsertEnter you could do something like this:

autocmd InsertEnter * let b:pairs_visualmode = visualmode()
autocmd InsertLeave * unlet! b:pairs_visualmode

This would work similarly to the autocommand I posted above but without the potential side effects. You could then put something like this into the pair handling functions:

    if get(b:, 'pairs_visualmode', '') == '�^V'
        return a:char
    endif

This would essentially disable the plugin in block-insert mode, but it would be better than breaking block-insert mode. I don't have time to properly implement and test this and send a pull request at the moment, but it sounds like it could work.

@blueyed

Thanks @majutsushi for clarifying things!

@justinmk
Will you do the PR soonish? Otherwise I might do it.

@justinmk

@blueyed Haven't had time, sorry. Bit of a debacle the last few days.

@moll

Any news on this?

@blueyed blueyed referenced this issue from a commit in blueyed/delimitMate
@blueyed blueyed Fix undo via `<Esc>:undojoin<cr>i` (applied via `s:get_left_motion`)
While this fixes the undo chaining in general, `<Left>` is still being
used in block-wise visual mode, where exiting insert mode would also
exit the block-wise mode.

Ref: Raimondi#138
0962a66
@blueyed blueyed referenced this issue from a commit in blueyed/delimitMate
@blueyed blueyed Fix undo via `<Esc>:undojoin<cr>i` (applied via `s:get_left_motion`)
While this fixes the undo chaining in general, `<Left>` is still being
used in block-wise visual mode, where exiting insert mode would also
exit the block-wise mode.

Ref: Raimondi#138
5d466b6
@blueyed

I have created a pull request finally.

But it is missing support for repeating.

It seems like vim-repeat does not work with expression mappings, like the one being used with delimitMate (first it would append "0" when chained, which could be fixed, but then it expects to be called after the text has been changed anyway).

exec 'inoremap <expr><silent> <Plug>delimitMate' . ld
                            \. ' <SID>TriggerAbb().delimitMate#ParenDelim("' . escape(rd, '|') . '")'

There might be another method of using it..

@justinmk
Do you have it working already? (since you've mentioned it earlier)

@blueyed

For reference, vim-repeat has this text about its integration (from https://github.com/tpope/vim-repeat):

Adding support to a plugin is generally as simple as the following command at the end of your map functions.
silent! call repeat#set("\<Plug>MyWonderfulMap", v:count)

I have not investigated further, but would appreciate any feedback on this issue (and the PR (#184)).

@justinmk justinmk referenced this issue in cohama/lexima.vim
Open

breaks undo #2

@blueyed blueyed referenced this issue from a commit in blueyed/delimitMate
@blueyed blueyed Fix undo via `<Esc>:undojoin<cr>i` (applied via `s:get_left_motion`)
While this fixes the undo chaining in general, `<Left>` is still being
used in block-wise visual mode, where exiting insert mode would also
exit the block-wise mode.

Ref: Raimondi#138
ef4fac2
@lencioni lencioni referenced this issue from a commit in lencioni/dotfiles
@lencioni lencioni Vim: remove delimitMate plugin
I almost really like this plugin, but it sometimes gets in the way and
also breaks the repeat command [1], which I really don't like. I'm going
to remove it for a while and see how I get along without it.

[1]: Raimondi/delimitMate#138
c8c6093
@AKurilin

Just wanted to express enthusiasm for this being addressed at some point in the future :+1:

@justinmk

There's a patch on vim_dev that allows arrow-key movements on a single line without breaking undo. I'm going to merge it into Neovim and be done with it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.