0

I'm trying to write a function to open a split terminal in Vim and then run the current file in that terminal. A non-working example trying to explain what I'm trying to do is

function! Foo()
    :bel term
    %
endfunction

This just opens up the terminal. I tried with another command, dir, and it seems it's fails because dir is not a Vim command, so it's still trying to run things in Vim rather than the terminal. I also tried putting the command on the same line e.g. :bel term % but I get an error:

Error detected while processing function Foo:
line 2:
CreateProcess failed

And the terminal doesn't even open.

I also tried

function! Foo()
    :bel term
    !dir
endfunction

though that had the expected result of essentially running !dir and then returning me to a split window with nothing in the terminal there.

I've also tried to use the feedkeys() function but I can't find a way to automatically pass the file name in.

I can't see anything relevant on :help terminal. I've also found two relevant posts (post 1, post 2) but both seem to use Linux/bash and I'm on Windows, and those solutions aren't working.

How could I achieve this? If it's relevant I'm running Vim on Windows through cmd.

Alira
  • 3
  • 1

1 Answers1

1

Each line in vimscript is a vim command, not a series of keystrokes. Running :terminal doesn't automatically mean any following commands will be sent to the terminal buffer.

In order to send keystrokes to a terminal, we need its buffer number and term_sendkeys(). The easiest way to get a terminal's buffer number programatically is to start it with term_start():

function Foo() abort
  " replace with let if your vim is old
  const term_buf = term_start(&shell)
  call term_sendkeys(term_buf, expand('#')."\<CR>")
endfunction

You can also use method syntax in this case:

call term_start(&shell)->term_sendkeys(expand('#')."\<CR>")
  • expand('#') gets the previous files name (term_start() has already switched to the terminal buffer)
  • ."\<CR>" embeds a carriage-return (enter) keypress, too
  • term_start() takes a fair number of options, so you can use, e.g., #{vertical: v:true} for a vertical split.

When you do :terminal %, it tries to run the file expanded to by % as a program; it's possible the file isn't found (e.g., if the file is in the current directory and that directory is not in PATH). Using :terminal %:p might work around that.

D. Ben Knoble
  • 26,070
  • 3
  • 29
  • 65
  • That function you posted does open a terminal, but returns the file path of cmd rather than the file name. However, I managed to fix it by with const file_name = expand('%') and then call term_start(&shell)->term_sendkeys(file_name)! So thank you for that. A couple more questions: (1) how do I send an Enter keypress afterwards? Currently it fills the filename in the terminal but doesn't run it. (2) Is there a way to make the terminal split to be below or alongside the code, rather than above it, similar to :bel term and :vert term? – Alira Jul 28 '21 at 15:35
  • Also, :terminal %:p sadly does not work, and gives the same error as the similar attempts mentioned in the question. – Alira Jul 28 '21 at 15:37
  • Ick, good call on the file-name—term_start() switches windows, so the old one is lost. I'll use # here for the alternate file. For an enter keypress, you need a string with "\<cr>" in it probably. Have a look at term_start()s options and see if there's a splitting option. – D. Ben Knoble Jul 28 '21 at 16:38
  • Hadn't seen # before, good to know! Much cleaner. As for the term_start() options: I'd already found them so I knew there was an option for a vertical split called vertical, but being new to Vim/Vimscript I couldn't figure out the syntax. After an embarassing amount of time I've finally got it: term_start(&shell, {'vertical' : 1}). Thanks for all the help! – Alira Jul 28 '21 at 17:02
  • Good stuff. I haven't spent near enough time playing around with :term and all that goes with it. – B Layer Jul 28 '21 at 21:59
  • I haven’t done too much with it other than simpl, which i have more plans for, but :terminal+tmux is pretty much my daily “i need to run something” when I’m in vim @BLayer – D. Ben Knoble Jul 28 '21 at 22:21
  • 1
    @D.BenKnoble Actually, that's probably why I haven't spent much time with it...99% of the time that I'm in (terminal) Vim it's in tmux and my custom stuff involving "shell from Vim" mostly predates :term and/or is easier to do with :!. – B Layer Jul 29 '21 at 00:18