1

I want to automate something I started using.

Short story is I have a file with some lines and I want to execute each line every now and then and see its' output, but I don't want it to override the contents of the buffer containing the commands. Instead, I rather store it in a new buffer/tab so that when I close everything nothing is stored.

I did something with mktemp, but I want to use vim's internal capabilities.

My workflow is currently as follows:

  • select the command I want to execute (example: ping 8.8.8.8)
  • execute :tabnew | r! <YANKED TEXT> where I get <YANKED TEXT> from using ctrl+r

Output is stored in a new tab, but I want to automate it, preferably using vmap <somekey> somehow to be able to press a key to evaluate a command and see its' output in a new buffer/tab.

I don't know how to mimic what I currently do to a vmap binding. I tried vmap <F2> tabnew | r! @" which failed (probably for some obvious reason).

Nice to have:

  • I don't want to use plugins for this, seems simple enough to accomplish.
  • I don't want to write complex vimscripts.
  • I want to understand each step.

Pointers to what to read to accomplish this would be great as well.

2

Solution

You can achieve this with the following mapping:

vmap <F2> y:tabnew<CR>:r! <C-R>"<CR>

This simply mimics the keystrokes that you were previously entering manually, including the presses of the Enter key: <CR>. (You should note therefore that, like your manual steps, this has the side-effect of setting the yank and unnamed registers to the yanked text.)

Why Your Command Didn't Work

Your command was failing for a couple of reasons:

  1. The bar character | in your command is interpreted as ending the mapping, so the following r! @" is then run immediately as a separate command. An alternative solution is to use the <Bar> stand in:

    vnoremap <F2> y:tabnew <Bar> r! <C-R>"<CR>
    

    This will create a mapping that actually uses a | in the ex command that is run. (If the difference is unclear, try removing the final <CR> from the end of the mapping and then invoke it.)

  2. You can't use @ in that context. It doesn't expand into the contents of the register: instead the entire @" is passed to the shell as the command to run.

Suggestion: Use nore

As a side-note, it's generally better to use the non-recursive forms of the various map commands:

vnoremap <F2> y:tabnew<CR>:r! ^R"<CR>

You should only use the plain forms if you have a specific reason for doing so (which will be almost never.)

Further Reading

The part of the documentation that explains all of the above is Chapter 40 of the user manual:

:help usr_40
  • Thanks! It works, but I wonder what the <CR>s are for... – Reut Sharabani Jan 9 '18 at 10:10
  • @ReutSharabani See my edit: this method mimics all the keystrokes you would enter manually. – Rich Jan 9 '18 at 10:17
  • 2
    @ReutSharabani Prepend a ^ motion too, and it'll work regardless of current cursor position within the line: nnoremap <F2> ^y$:tabnew<CR>:r! ^R"<CR> – Rich Jan 9 '18 at 12:01
  • 1
    Few things: 1) You may want to use key-notation instead of ^R. 2) Use xnoremap instead of vnoremp as you probably only want this to work in visual mode and not select mode as well. e.g. xnoremap <F2> y:tabnew<CR>:r! <C-R>"<CR>. 3) Might also want to mention this has the side-effect of mutating the unnamed register which can be a bit of a surprise to some folks – Peter Rincker Jan 9 '18 at 15:32
  • 1
    @PeterRincker Thanks. 1 & 3 are good points: I'll update my answer. I thought about 2 when writing the post, and respectfully disagree. – Rich Jan 9 '18 at 15:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.