Search code examples
vimstatusbarstderr

How to split stdout and stderr of shell command output cleanly and show the latter in Vim status line?


I use Vim as my dash board and monitor/update many tables with various external(filtering) commands, run from macros. They work terrifically, except the error output (not yet perfect, though I've resolved 80% of this message integration. See p.s. for the story for such application).

For example, the following table is for "I love coffee" game:

TYPE  | STAT |    NAME    | GOAL | DONE | TODO | S1 | S2 | S3
drink | .    | Latte      |  250 |  178 |   72 | .  | .  | .
drink | .    | Cocoa      |   99 |   46 |   50 | .  | 3  | .
drink | .    | Mocha      |  250 |  190 |   40 | 12 | 8  | .
drink | .    | Espresso   |   30 |    0 |   20 | 10 | .  | .
drink | .    | Iced Latte |   54 |    8 |   37 | .  | .  | 9
cake  | .    | Egg Tart   |   25 |   15 |    4 | 6  | .  | .
cake  | .    | Tiramisu   |   36 |    6 |   20 | 4  | 6  | .
cake  | .    | Brûlée     |   11 |    0 |    9 | .  | 2  | .
cake  | v    | Pudding    |   20 |   20 |    0 | .  | .  | .

I have the following macro in register s:

'oV}!order-update.pl 2>/tmp/vim.err ^M:if getfsize('/tmp/vim.err')>0|echo join(readfile('/tmp/vim.err'),"\|")|endif ^M'o

(^M is the character ctrl-M), which helps update the DONE and TODO columns when I've touched the quantity in Store1/2/3. (And if finished, will make a v check and move the line down to the top of historical area.)

When there is something wrong in the numbers of S1/S2/S3, the redirection 2> will spilt the stderr output from stdout output and save in vim.err file. (not to mess up the table)

The if...endif block will get the error output back and echo it to status line. It will print Store3 drink: 9 > 8 max when I put 9 in S3. (But S3 can hold a maximum of 8 drinks.)

My problem is that the last ex command line:

:if getfsize('/tmp/vim.err')>0|echo join(readfile('/tmp/vim.err'),"\|")|endif

will be left in the status line if no error occur.

How can I improve it?


p.s.

Story of such an application

The above (simplified) example demonstrates how I use Vim as a quick spreadsheet, for many tables I update every day. The benefit is - I don't have to leave my favorite Vim.

With some syntax highlighting, Vim can present those tables in the way best preferable to my eyes. (depending on those values)

With related filters, every table can be correctly updated according to the values' dependency.

I've developed many syntax/filter pairs for various projects over the years. And recently I got aware that I've not yet integrate the filter's error messages into Vim's environment. Such messages will be randomly merged into the normal output and make a messed-up table. (with Vim's default setting shellredir=">%s 2>$1")

My intention is to collect filter's stderr messages and fit them in Vim's status line.

Some results have been achieved in my experiments:

  1. The "2>vim.err" specified in macro can overrule the default "2>&1" and split the stderr into the tempfile.

  2. The readfile() function is great to read in the whole file.

  3. The filter had better not exit with non-zero value for minor errors detected. Such value will make Vim do more things and raise a "Hit-enter" prompt.

And fitting the stderr messages smoothly in status line is my last piece of puzzle.


Solution

  • I don't think the "else|echo" is good solution to clear the status line all the time. It's ugly and not optimized for your macro.

    Look at the content of your tmpfile vim.err, from stderr of your filter program. It should contains nothing if the filter doesn't see anything strange. Its empty content will help you clear the status line in the OK condition. I would rewrite your macro as (much simpler and cleaner):

    'oV}!order-update.pl 2>/tmp/vim.err ^M:echo join(readfile('/tmp/vim.err'),"\|") ^M'o
    

    In addtion, I don't agree to Mr. Karkat's comment: "Macro kept in a register is only good for a single editing session". In fact, macros are kept in .viminfo and available from session to session. They will never disappear unless you assign empty to them. In my mind, macro is as powerful as mapping. And further, macro is more convenient (easier to create and maintain) than mapping. At least you can save the escaping nightmare of mapping. I would transcribe a macro to a mapping only when it's mature enough and globally useful.