ahe
ahe

Reputation: 2359

vim filters and stdout/stderr

When I use :%! to run the contents of a file through a filter and the filter fails (it returns another code than 0) and prints an error message to stderr I get my file replaced with this error message. Is there a way to tell vim to skip the filtering if the filter returns an status code that indicates an error and/or ignore output the filter program writes to stderr?

There are cases where you want your file to replaced with the output of the filter but most often this behavior is wrong. Of course I can just undo the filtering with one keypress but it isn't optimal.

Also I have a similar problem when writing a custom vim script to do the filtering. I have a script that calls a filter program with system() and replaces the file in the buffer with its output but there doesn't seem to be a way to detect if the lines returned by system() where written to stdout or to stderr. Is there a way to tell them apart in vim script?

Upvotes: 13

Views: 3486

Answers (5)

Jorenar
Jorenar

Reputation: 2884

To Vim 7 were added new autocommand events: ShellCmdPost and ShellFilterPost

augroup FILTER_ERROR
  au!
  autocmd ShellFilterPost * if v:shell_error | undo | endif
augroup END

Upvotes: 3

Peter Kay
Peter Kay

Reputation: 143

This is what I ended up doing:

function MakeItAFunction(line1, line2, args)
  let l:results=system() " call filter via system or systemlist
  if v:shell_error
    "no changes were ever actually made!
    echom "Error with etc etc"
    echom results
  endif
  "process results if anything needed?

  " delete lines but don't put in register:
  execute a:line1.",".a:line2." normal \"_dd"
  call append(a:line1-1, l:result)  " add lines
  call cursor(a:line1, 1)  " back to starting place
  " echom any messages
endfunction
command -range <command keys> MakeItAFunction(<line1>,<line2>,<q-args>) 
"                                         or <f-args>, etc.

You can see my full code at http://vim.wikia.com/wiki/Perl_compatible_regular_expressions

It's complicated, but it works and when it's used, it's fairly transparent and graceful. Hope that helps in any way!

Upvotes: 1

sergeant
sergeant

Reputation: 11

An alternative would be to run the filter command such as it modifies the file on disk.

For example, for gofmt (www.golang.org) I have these mappings in place:

map <f9> :w<CR>:silent !gofmt -w=true %<CR>:e<CR>
imap <f9> <ESC>:w<CR>:silent !gofmt -w=true %<CR>:e<CR>

Explanation: :w - save file :silent - avoid pressing enter at the end % - passes the file to gofmt -w=true - tells gofmt to write back to the file :e - tells vim to reload modified file

Upvotes: 1

Curt Nelson
Curt Nelson

Reputation: 3192

:!{cmd} Executes {cmd} with the shell and sets v:shell_error.

If you happen to set up mappings to call your filters, you could do something like the following:

function! UndoIfShellError()
    if v:shell_error
        undo
    endif
endfuntion

nmap <leader>filter :%!/path/to/filter<CR>:call UndoIfShellError()<CR>

Upvotes: 6

ZyX
ZyX

Reputation: 53644

You can use Python to distinguish between stdout and stderr:

python import vim, subprocess
python b=vim.current.buffer
python line=vim.current.range.start
python p=subprocess.Popen(["command", "argument", ...], stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True)
python returncode=p.poll()
python if not returncode: b.append(("STDOUT:\n"+p.stdout.read()+"\nSTDERR:\n"+p.stderr.read()).split("\n"), line)

Upvotes: 3

Related Questions