Raimondi / delimitMate

Vim plugin, provides insert mode auto-completion for quotes, parens, brackets, etc.
http://www.vim.org/scripts/script.php?script_id=2754
1.98k stars 117 forks source link

delimitMate breaks the repeat command #138

Closed neitanod closed 9 years ago

neitanod commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

@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.

blueyed commented 11 years ago

@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 commented 11 years ago

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.

blueyed commented 11 years ago

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 commented 11 years ago

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 commented 11 years ago

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

qstrahl commented 11 years ago

Could also use the cursor function.

Raimondi commented 11 years ago

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 commented 10 years ago

No, as explained above.

justinmk commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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.

majutsushi commented 10 years ago

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 commented 10 years ago

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

majutsushi commented 10 years ago

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 commented 10 years ago

@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 commented 10 years ago

@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 commented 10 years ago

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 commented 10 years ago

@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 commented 10 years ago

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 commented 10 years ago

Thanks @majutsushi for clarifying things!

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

justinmk commented 10 years ago

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

moll commented 10 years ago

Any news on this?

blueyed commented 10 years ago

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 commented 10 years ago

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)).

akurilin commented 9 years ago

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

justinmk commented 9 years ago

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.

blueyed commented 9 years ago

@justinmk What's the status of this patch? Is it merged into Neovim? Can you provide a reference otherwise?

justinmk commented 9 years ago

@blueyed Here's a reference: https://github.com/neovim/neovim/issues/1025#issuecomment-95357329

The patch affects only files we have not changed much, so shouldn't be difficult to port.

Konfekt commented 9 years ago

How about endorsing the prompt merge of the existent patch into Vim at

https://groups.google.com/forum/m/#!topic/vim_dev/gBumYDSEJoo ?

chrisbra commented 9 years ago

As of 7.4.859 the fix has been included.

Konfekt commented 9 years ago

Thanks! Does the patch need further adjustments in the plugin to make the dot and undo operator work?

chrisbra commented 9 years ago

Am 2015-09-02 16:45, schrieb Konfekt:

Thanks! Does the patch need further adjustments in the plugin to make the dot and undo operator work?

Yes. You need to use U to move without breaking undo/redo. See the help section in the patch for details.

Best, Christian

AndrewRayCode commented 8 years ago

Repeat still does not work with the latest delimitMate. Please re-open this issue.

blueyed commented 8 years ago

@DelvarWorld Are you using Vim with the required patch?

justinmk commented 8 years ago

FYI, Vim patch 7.4.859 is in Neovim now. https://github.com/neovim/neovim/pull/3306