1

When you want to leave insert mode, it's possible to avoid to trigger the InsertLeave event by pressing Ctrl-C instead of Esc.

But when I try to use this method inside an insert mapping, it still triggers the event.

For example, in a new buffer, let's run:

:au InsertLeave <buffer> call append('$', 'InsertLeave was triggered')
:imap <buffer> <cr> <c-c>

Then if you press the (mapped) return key in order to exit insert mode, despite the use of <c-c> in the mapping definition, you can see that the InsertLeave event is triggered.

How can I do to avoid the trigger of this event? The use of :noautocmd does not help as well, since we need to leave insert mode first to use this command.


EDIT (explanations)

To explain the aim of my question: I want to write a tiny plugin which would allow to easily edit several lines at the same start column, by hitting the return key. This 'table mode' should be togglable by hitting a quick shortkey in insert mode. For example, this kind of situation is quite usual:

" The following strings need to be filled with something:
var1 = ""
var2 = ""
var3 = ""

The plugin looks pretty easy to write... But :

When my 'table mode' is active, I want that when leaving insert mode (by pressing Esc, or any other way, like the plugin vim-easyescape - so remapping <esc> is not an answer), then my 'table mode' just simply cancels and unmaps the return key. So, I added an autocommand for the InsertLeave event, which cancels all the stuff.

But my mapping for the <cr> key (aka the main feature) does trigger this event as well (because it needs to go to normal mode, in order to move to the right position), then it cancels my 'table mode' after the first press of the return key. I don't want this at all.

To sum it up, I need to:

  • Cancel my 'table mode' each time insert mode is leaving
  • Make an exception for the <cr> mapping, which needs to go to normal mode to execute the [count]| command, without cancelling anything.

Here is the code I started to write, with some comments about the issue:

let s:is_active = v:false


function! tablemode#activate()
    if s:is_active
        return
    endif
    let s:is_active = v:true

    let s:start_col = virtcol('.')

    " Not needed for this question, running ":set ve=all" is enough:
    " call _#set_temp_option('virtualedit', 'all')

    exe printf("inoremap <buffer> <cr> <c-c>j%s<bar>i", s:start_col)
    " !!! Even when using <c-c> instead of <esc>, the InsertLeave event
    " is triggered, then cancels everything, which is not wanted at all

    augroup tablemode
        au!

        au InsertLeave <buffer> call tablemode#desactivate()
        " !!! This line is needed, but the <cr> mapping will
        " not work as expected (unless you comment this :au command)
    augroup END
endf


function! tablemode#desactivate()
    if !s:is_active
        return
    endif
    let s:is_active = v:false

    " Not needed for this question (restores old option state):
    " call _#restore_option('virtualedit')

    iunmap <buffer> <cr>
    augroup tablemode
        au!
    augroup END
endf


function! tablemode#toggle()
    if s:is_active
        call tablemode#desactivate()
    else
        call tablemode#activate()
    endif
endf

Here's a mapping example, in order to test my script:

:imap <c-b> <c-o>:call tablemode#toggle()<cr>
  • It is not clear why you need this, perhaps you can use :set eventignore=InsertLeave? – Christian Brabandt Mar 22 '18 at 18:12
  • @ChristianBrabandt: Please read my edit; thanks – yolenoyer Mar 22 '18 at 22:25
1

You could try adding a guard:

" Are we currently within a <CR> press?
let s:in_cr = v:false

function! tablemode#deactivate()
    if !s:in_cr
        return
    endif

    " Deactivate stuff
    ...
endfunction

function! tablemode#my_cr_wrapper()
    " At this point, we are still in insert mode
    let s:in_cr = v:true

    " Exit insert mode and call the real function
    return "\<Esc>:call tablemode#my_cr()\<CR>"
endfunction

function! tablemode#my_cr()
    " Do what you need to do
    ...

    " Re-enable the autocommand
    let s:in_cr = v:false
endfunction

inoremap <expr> <CR> tablemode#my_cr_wrapper()

N.B. I haven't tried running the code above. There may be minor errors/typos you need to fix, but it should give you the idea.

You can't edit the buffer or persistently move the cursor inside the expression mapping (see :help :map-expression), but you can include keystrokes which do so in your return value. Therefore, depending on how complicated your <CR> mapping actually is, you might not need the second my_cr() function at all, including everything in the my_cr_wrapper() return value.

  • The use of inoremap <expr> with the guard is working very well, thank you. – yolenoyer Mar 23 '18 at 14:35
1

Embrace InsertLeave instead of trying to avoid it.

augroup TableMode
    autocmd!
    autocmd InsertLeave *
                \ if get(g:, 'table_mode', 0) |
                \   let g:table_mode = 0 |
                \   execute "normal! \<cr> " |
                \   call search('"', 'e', line('.')+1) |
                \   call feedkeys('a', 't') |
                \ endif
augroup END

inoremap <expr> <cr> <SID>advance()

function! s:advance()
    let pat = '"[^"]*"'
    if getline('.') =~ pat && strpart(getline('.'), 0, col('.')) =~ '^[^"]*"[^"]*' && getline(line('.')+1) =~ pat
        let g:table_mode = 1
        return "\<esc>"
    else
        let g:table_mode = 0
        return "\<cr>"
    endif
endfunction

The secret sauce here is to use feedkeys() to start back into insert mode after the InsertLeave autocmd event. Map <cr> to determine if "table mode" should be active for the next line, otherwise just use <cr>.

For more help see:

:h feedkeys()
:h :map-expression
  • Your code is working nicely, thanks; with Rich you pointed out the same important thing: to use map expressions in order to have control over deciding whether to leave insert mode or not, which is not possible without them. The functionnality you wrote is a bit different from what i was talking about (staying at the same col), but it doesn't matter, it helped me a lot! – yolenoyer Mar 23 '18 at 15:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.