开发者

In Vim, can I "stream" the output of e.g. "w ! ruby" into a buffer, line by line as they're output?

开发者 https://www.devze.com 2023-02-17 10:15 出处:网络
I\'m currently doing something like this: redir => m silent w ! ruby redir END new put=m It executes the contents of the current buffer as Ruby code and puts the output in a new buffer.

I'm currently doing something like this:

redir => m
  silent w ! ruby
redir END
new
put=m

It executes the contents of the current buffer as Ruby code and puts the output in a new buffer.

But if the Ruby code I run is something like

puts "start"
sleep 10
puts "end"

then I will see no output for 10 seconds, then both "start" and "end" all at once.

Is there instead some way to "stream" the output to a buffer, line by line as it appears? So that I would see "start", then 10 seconds later I would see "end"? Similar t开发者_C百科o what happens if I just do

w ! ruby

and look at the output under the command line.


Vim does not really expose primitives for this kind of I/O, but you can do it from one of the embedded language interfaces.

Here's a sketch using Python and subprocess (Ruby would look similar; see if_ruby):

python << EOF
import vim
import subprocess

def output_lines_to_buffer(cmd):
    """
    Append the given shell command's output linewise to the current buffer.
    """
    p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True)
    for line in iter(p.stdout.readline, ''):
        vim.current.buffer.append(line)
        vim.command('redraw')
EOF

To use:

:python output_lines_to_buffer('my command')

This will simply append each line of output to the end of the current buffer, as my command emits it. You can extend it fairly straightforwardly to support input and output ranges and so on, depending on what you need.


So one possible solution (hopefully not the best one) is to write to a file on disk, open that file in a buffer and do :setlocal autoread so the file autoupdates when it changes.

This autoupdating seems pretty rough, though. Tail Bundle.vim seems to improve a little on it.

Hoping for a better answer.


How about using tee or :make, which uses tee.

let fname = tempname()
exec "w ! ruby 2>&1 | tee ".fname
exec "new ".fname

Edit: seems like you're on Linux, or an operating system that creates 4K buffers between pipelined processes. To work around this, install expect (sudo apt-get install expect-dev) and use the unbuffer command to allow streaming on a line-by-line basis (see: https://stackoverflow.com/questions/1000674/turn-off-buffering-in-pipe).

let fname = tempname()
exec "! unbuffer ruby % 2>&1 | tee ".fname
exec "new ".fname
0

精彩评论

暂无评论...
验证码 换一张
取 消