-
-
Save romainl/eae0a260ab9c135390c30cd370c20cd7 to your computer and use it in GitHub Desktop.
| function! Redir(cmd) | |
| for win in range(1, winnr('$')) | |
| if getwinvar(win, 'scratch') | |
| execute win . 'windo close' | |
| endif | |
| endfor | |
| if a:cmd =~ '^!' | |
| execute "let output = system('" . substitute(a:cmd, '^!', '', '') . "')" | |
| else | |
| redir => output | |
| execute a:cmd | |
| redir END | |
| endif | |
| vnew | |
| let w:scratch = 1 | |
| setlocal nobuflisted buftype=nofile bufhidden=wipe noswapfile | |
| call setline(1, split(output, "\n")) | |
| endfunction | |
| command! -nargs=1 -complete=command Redir silent call redir#Redir(<f-args>) | |
| " Usage: | |
| " :Redir hi ............. show the full output of command ':hi' in a scratch window | |
| " :Redir !ls -al ........ show the full output of command ':!ls -al' in a scratch window |
@3N4N I try to share portable snippets so :help execute() is too recent for me.
Checking for % my be of some interest, too:
[...]
if cmd =~ '^!'
if cmd =~' %'
let cmd = substitute(cmd, ' %', ' ' . expand('%:p'), '')
endif
let output = system(matchstr(cmd, '^!\zs.*'))
else
[...]
@097115 good point. #, %< and friends should probably be transformed, too.
By the way, this was the very first gist notification I've ever get. Did they change something? Oh, right, there's an "Unsubscribe" button, now. Good.
Checking for
%my be of some interest, too:[...] if cmd =~ '^!' if cmd =~' %' let cmd = substitute(cmd, ' %', ' ' . expand('%:p'), '') endif let output = system(matchstr(cmd, '^!\zs.*')) else [...]
Can you paste the full script? Sorry, I can't execute if after insert your snippets. Thank you.
Checking for
%my be of some interest, too:[...] if cmd =~ '^!' if cmd =~' %' let cmd = substitute(cmd, ' %', ' ' . expand('%:p'), '') endif let output = system(matchstr(cmd, '^!\zs.*')) else [...]Can you paste the full script? Sorry, I can't execute if after insert your snippets. Thank you.
097115's snippet incorrectly uses cmd instead of a:cmd. Check the main snippet for a working version.
Checking for
%my be of some interest, too:[...] if cmd =~ '^!' if cmd =~' %' let cmd = substitute(cmd, ' %', ' ' . expand('%:p'), '') endif let output = system(matchstr(cmd, '^!\zs.*')) else [...]Can you paste the full script? Sorry, I can't execute if after insert your snippets. Thank you.
097115's snippet incorrectly uses
cmdinstead ofa:cmd. Check the main snippet for a working version.
It works! Thank you for this. It has wasted me several hours.
I'm curious why you added the -bar option to the command definition?
Im on both MacOS and Windows -- with most of my time in Windows. When I try to redirect a shell command that has an argument with a space, it fails because the " I try to use just becomes the beginning of the Ex command's comment.
:Redir !p4 annotate -u "file with spaces"Ends up trying to invoke:
system('p4 annotate -u')...because the <q-args> never includes anything from the first " onwards.
Do you have a use case of invoking this command in a | joined chain of commands?
Side note...
I also changed the expand('%:p') to be "more" compatible for windows, into: shellescape(escape(expand('%:p'), '\'))
I "think" this change would still remain compatible with the *nix systems most people use most of the time.
@nebbish The use case for -bar is to allow things like:
:Redir <cmd> | v/foo/d " insert output of <cmd> in the buffer then delete every line matching foo
:Redir !<cmd> | $ " insert output of <cmd> in the buffer then move cursor to some arbitrary line
With -bar I can "postprocess" the buffer on the command-line using Vim tools. That's the whole point.
The problem is that I overlooked this sentence from :help :command-bar:
Also checks for a " to start a comment.
which is not super intuitive when using something called -bar.
Anyway, right now I don't see a possible middle ground:
- on one hand, I really want to be able to do the things described above,
- on the other hand, your example is very real and reasonable and should work and
-barprevents it.
For now, I think I'm going to keep the -bar and provide a non-bar alternative.
As for your escaping snippets… my gists often miss that kind of refinement because I usually stop at "works for me" and "me". A plugin would be treated differently :-). It seems to work fine on my end so I will add it ASAP.
Thank you.
👍
How to capture the output of the job? In this case, redir is finished before out_cb is called.
call job_start("python -m this", {'out_cb': {_, msg -> execute('echom ' .. msg)}})
@younger-1 Redir() would have to be rewritten to make that possible. As of now, it is 100% synchronous.
not exactly the same, but similar in vim9script: https://github.com/habamax/vim-shout
The same in Vim9script:
export def g:Redir(cmd: string, rng: number, start: number, end: number)
for win in range(1, winnr('$'))
if !empty(getwinvar(win, 'scratch'))
execute win .. 'windo close'
endif
endfor
var output = []
if cmd =~ '^!'
var cmd_filt = cmd =~ ' %'
\ ? matchstr(substitute(cmd, ' %', ' ' .. shellescape(escape(expand('%:p'), '\')), ''), '^!\zs.*')
\ : matchstr(cmd, '^!\zs.*')
if rng == 0
output = systemlist(cmd_filt)
else
var joined_lines = join(getline(start, end), '\n')
var cleaned_lines = substitute(shellescape(joined_lines), "'\\\\''", "\\\\'", 'g')
output = systemlist(cmd_filt .. " <<< $" .. cleaned_lines)
endif
else
var tmp: string
redir => tmp
execute cmd
redir END
output = split(tmp, "\n")
endif
vnew
w:scratch = 1
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile
setline(1, output)
enddef
In fact, things can be easier now use the vim command read. For example, :read !ls -arlh can exec the external shell command ls -arlh to the buffer in one step directly. If you wanna redirect the command result from the vim ex mode, you can also use the vim command like :put=execute('ls') or :put=execute('buffers'). The variable put is very special in vim. I have used this feature to make my "quickfix" with ripgrep. Here is my vim config -> https://github.com/HCY-ASLEEP/NVIM-Config/blob/main/init.vim.
Well… of course :read !foo can be used to insert the output of external command foo in the buffer, or :put=execute('hi') to insert the output of Ex command :hi. But that's, like… in the current buffer, and 10% of what this :Redir does.
:Redir !foo is functionally similar to the following crude command:
:vnew | read !foo
and :Redir foo is functionally "similar" to:
:vnew | put=execute('foo')
which is already a bit more than :read.
That is more or less what I did for years but it had a bunch of issues that I fixed gradually to obtain this command, which now fits my workflow perfectly. Those issues, in no particular order:
-
Whether you do
:reador:0read, you always end up with an extraneous empty line, which is something I don't like at all. My first attempt to fix that was to switch to:put, which allowed me to usesystem()or:redir, which allowed me to add a third command that removed the extra line::vnew | :put=system('foo') | 1d_Which worked, but it started to become quite a mouthful. That is the point where you start to consider turning it into a an Ex command. And of course, it couldn't be turned into a command as-is because, depending on whether the command was internal or external, I would have to decide whether to use
system()or:redir.Note that I wrote this command before
execute()was introduced, but that wouldn't change much.:rediris perfectly fine.Taking one path or another depending on the argument is not super hard, mind you, but solving that specific "extra line" issue, made the problem—and the solution—a little bit more complex than a "simple"
:reador a "simple":put. -
The resulting buffer lingered behind because it was a regular one. Since I intended it to be a "scratch buffer", I had to set a few options. Once again, that's not hard, but that's more complexity added to the mix. It makes the whole thing much cleaner, though, so the added complexity is very much worth it.
And that's one more step further away from a simple
:reador:put. -
In some cases,
:readchanges the alternate file, which is something I wanted to avoid in this scenario but not all the time. Another reason why suggesting a simple:readas an alternative would be disingenuous at best. -
I didn't want the command to spawn more than one scratch window. Solving it was, again, relatively easy, but that's still more complexity than that mythical
:read. -
I wanted my command to be usable as a filter, which, yet again, added more complexity because I needed to handle ranges.
-
Etc.
So no, :read and :put don't make things easier at all.
Thanks for your serious reply, now that I have a better understanding of these commands, I'm considering taking your methods to improve my config.
guys can you give me something in luascript?!
guys can you give me something in luascript?!
Thanks romainl for amazing work! I implement this command with lua script, with some extra commands to get repl-like features.
Also, this lua implementations support vertical and horizontal modifiers.
And note that this lua script doesn't use sh -c to do things, it spawns process directly, so no shell commands available but also no shell escaping needed.
Thanks, @Leenuus.
@gachikuku it looks like your wish came true.
Thanks, @Leenuus.
@gachikuku it looks like your wish came true.
Hi, @gachikuku , I rewrite my ugly codes and write a README like @romainl did.
And with vim.uv power, this implementation is totally async.
Split direction and stderr are handled too.
Can't thank @romainl more for such a simple, elegant, and powerful solution.
You are very welcome, @Leenuus. Your gist seems almost plugin-ready.
You are very welcome, @Leenuus. Your gist seems almost plugin-ready.
Hope that I don't overengineer. 😂
@romainl Would you be open to turning this gist into a plugin? I would do it (even just for my own convenience), but not being the author, I couldn't properly license it.
any idea how to do this when you're already in the 'hit-enter' / 'more-prompt'
@MicahMartin The output of the previous command is not accessible so we have to re-run it. I have added that feature: at the "hit enter" or "more" prompt, do :Redir!.
FWIW, I already refactored this into a plugin some time ago. I finally published it here.
awesome!
Hi, @romainl,
I learnt about
:h execute()recently and noticed that it can be used instead of:h redir. Any thoughts?