Consider the following:
function! RunWithErrorHandling(command) abort
let view = winsaveview()
let errorOutputFile = tempname()
let shellredir_save = &shellredir
let &shellredir = '>%s 2>' . errorOutputFile
silent execute a:command
let &shellredir = shellredir_save
if v:shell_error != 0
silent undo
echohl ErrorMsg
for error in readfile(errorOutputFile)
echoerr error
endfor
echohl None
endif
call delete(errorOutputFile)
call winrestview(view)
endfunction
command! JsonToDict call RunWithErrorHandling('% ! python -c "import json; import sys; as_dict = json.load(sys.stdin); print(as_dict)"')
I've got multiple of these commands calling external commands (not just python) and I have wrapped them with RunWithErrorHandling (adapted from https://github.com/hashivim/vim-terraform) so that I can get an error message instead of replacing the buffer content with the error.
I've been struggling with getting an error message displayed properly because multiline messages would have \n
replaced with ^@
instead of adding a line break when using echoerr
and echom
.
Using echo
in combination with echohl ErrorMsg
displays everything the way I want, but the message is not available using :messages
.
I ended up with the for loop and multiple calls to echoerr
as shown above, but it feels wrong and a multiline message is broken up into several "vim messages" so :1messages
shows me only the last line of a multiline message instead of the whole message.
It would be nice if I could echo the message so it's displayed the same way it is now and :1messages
shows me the whole message.
It seems like it should be possible because when I get an error running the following command:
command! DictToJson % python3 function_that_raises_an_exception()
I do get a nice stacktrace and :1messages
shows me the whole thing.
let &shellredir = shellredir_save
,delete(errorOutputFile)
,winrestview(view)
) in afinally
block so it always gets run. Example. Also see:help try-finally
.silent execute a:command
never throws an exception, no matter what goes wrong?