Search code examples
vim

How to throw error in vim that silently fails without printing any message?


I have a vim function that moves the cursor. It may also throw an error. A simplified version for illustrative purpose is:

" Move cursor to the 1st line. Errors if the cursor was on the 2nd line.
function! FirstLineErrorOut()
    let l:curr_line = line(".")
    call cursor(1, 1)
    if l:curr_line == 2
        throw "error"
    endif
endfunction

I then bind a key to it in operator-pending mode:

onoremap lb :call FirstLineErrorOut()<cr>

What I want to achieve is to complete the actions before the error (i.e. positioning the cursor), and then abort the operator-pending mode by throwing an error (see https://vimhelp.org/map.txt.html#map-error), yet without showing any error message (i.e. "Error detected while processing function ... E605: Exception not caught: error").

The mapping currently positions the cursor, aborts the operator-pending mode, but does show error messages and needs the user to press <enter> to continue, which I'd like to avoid.

Note that this effect is achievable with builtin vim keys like b and ge (see discussion at https://stackoverflow.com/a/79261518/7881370), where the cursor is positioned to the first line while deletion (d) is not executed.

Things I've tried with no luck:

  • onoremap <silent> lb :call FirstLineErrorOut()<cr>: the error message still shows up.
  • onoremap <silent> lb :silent call FirstLineErrorOut()<cr>: the error message still shows up.
  • onoremap lb :silent! call FirstLineErrorOut()<cr>: the error message is suppressed, but the operator-pending mode is not aborted either.
  • Add redir @a and redir END before and after throw "error" in FirstLineErrorOut() definition: the error message still shows up, although it's also sent to register @a.
  • Surround throw "error" with try ... catch ... endtry: the operator-pending mode won't get aborted of course.

Thank you so much!


Solution

  • The trick is to first cancel the operator-pending mode, and then reactivate it depending on certain condition. Inspired by the example at vimhelp. Code:

    " Move cursor to the 1st line, run the operator
    " unless the cursor was on the 2nd line.
    function! FirstLineErrorOut(operator)
        let l:curr_line = line(".")
        if l:curr_line == 2
            call cursor(1, 1)
        else
            execute "normal! " . a:operator . ":call cursor(1, 1)\<cr>"
        endif
    endfunction
    
    onoremap lb <esc>:call FirstLineErrorOut(v:operator)<cr>