8

Is it possible to send the contents of a buffer to a running terminal window. That window can be running e.g a REPL for python code.

I mean the new terminal feature of VIM rather than external plugins or previous versions.

Saveen
  • 4,120
  • 14
  • 38
  • 41
Stagrovin
  • 177
  • 1
  • 5
  • 1
    Will this terminal buffer already be running? May want to look at `:h term_sendkeys()`. Or will this terminal buffer be ran each time you want to send input? Simply use `:terminal` with a range. See `:h :terminal` – Peter Rincker Mar 16 '18 at 14:27
  • It's a running terminal. I'd like to send my whole code or selected parts of it to the running python session without closing it. I've seen the term_sendkeys function, but not sure how I can use it to send the my editing buffer to terminal. – Stagrovin Mar 19 '18 at 15:14

1 Answers1

16

You can use term_sendkeys() to send data to a terminal buffer. However there are some considerations:

  • Need to capture data to use term_sendkeys() often this is via yanking text
  • Need to know which terminal buffer to send to

Here is some code simplify and automate the send to terminal buffer workflow. Put inside vimrc file or make a small plugin.

augroup send_to_term
  autocmd!
  autocmd TerminalOpen * if &buftype ==# 'terminal' |
        \   let t:send_to_term = +expand('<abuf>') |
        \ endif
augroup END


function! s:op(type, ...)
  let [sel, rv, rt] = [&selection, @@, getregtype('"')]
  let &selection = "inclusive"

  if a:0 
    silent exe "normal! `<" . a:type . "`>y"
  elseif a:type == 'line'
    silent exe "normal! '[V']y"
  elseif a:type == 'block'
    silent exe "normal! `[\<C-V>`]y"
  else
    silent exe "normal! `[v`]y"
  endif

  call s:send_to_term(@@)

  let &selection = sel
  call setreg('"', rv, rt)
endfunction

function! s:send_to_term(keys)
  let bufnr = get(t:, 'send_to_term', 0)
  if bufnr > 0 && bufexists(bufnr) && getbufvar(bufnr, '&buftype') ==# 'terminal'
    let keys = substitute(a:keys, '\n$', '', '')
    call term_sendkeys(bufnr, keys . "\<cr>")
    echo "Sent " . len(keys) . " chars -> " . bufname(bufnr)
  else
    echom "Error: No terminal"
  endif
endfunction

command! -range -bar SendToTerm call s:send_to_term(join(getline(<line1>, <line2>), "\n"))
nmap <script> <Plug>(send-to-term-line) :<c-u>SendToTerm<cr>
nmap <script> <Plug>(send-to-term) :<c-u>set opfunc=<SID>op<cr>g@
xmap <script> <Plug>(send-to-term) :<c-u>call <SID>op(visualmode(), 1)<cr>

You can make setup your own mappings. Example:

nmap yrr <Plug>(send-to-term-line)
nmap yr <Plug>(send-to-term)
xmap R <Plug>(send-to-term)

You can now use :[range]SendToTerm to send a [range] of lines to the last used terminal buffer in a tab-page. You can also use yrr to send a line, yr{motion} to send a {motion} text, or use R to send visually selected text to the terminal buffer. Note: You must have a terminal buffer opened beforehand in the current tab-page.

Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
  • Hi, I'm very glad you shared this code! I assume it is your own creation. I myself have hacked together something like this, but of inferior quality. I'm going to integrate it into my setup - and doing so I would like to ask if you would be willing to share updated versions of that code if there were any! BTW: There is a vim plug-in pertaining to that (rather sad ^^) issue https://github.com/jalvesaq/vimcmdline/issues/31 which seems to do a great job sending bits of code to a tmux or terminal instance, but the case I am interested in - vim 8 :term - is unfortunately not supported. – simlei May 10 '18 at 10:11
  • 1
    @simlei I made this code, but do not personally use it. Feel free to use it and even roll it up into a plugin of your own if you want – Peter Rincker May 10 '18 at 21:00
  • 1
    An alternative to `cr` (used by *vim-abolish* for *coerce*), you can also use `yr` (mnemonic *you run*). – Rastapopoulos Aug 22 '18 at 09:51
  • And in visual mode, for consistency with *vim-surround* (which uses `S`) and *vim-exchange* (which uses `X`), I like to use `R`. (The default wasn't very useful.) – Rastapopoulos Aug 22 '18 at 09:56
  • Hi ! I'm using your proposed script with a recently compiled vim8 and I can only trigger the 'Error: No terminal' message. Do you know how to fix this ? I tried to change BufWinEnter with BufEnter but it doesn't work either – Vinz Nov 19 '18 at 13:41
  • @Vinz: "You must have a terminal buffer opened beforehand in the current tab-page." I imagine you didn't call `:term` beforehand in the same tab-page – Peter Rincker Nov 19 '18 at 15:07
  • I do (or at least I think so): I'm starting vim, I open a terminal with `:term`, move back to my test editor, enter some text and then I try to send it. It doesn't work with this pattern, but I might be doing something wrong ? – Vinz Nov 19 '18 at 16:36
  • 1
    @Vinz, you are correct. It seems that newer Vim's do not call `BufWinEnter` for terminal windows anymore. You must now use `TerminalOpen` autocmd. I have updated the code. Thank you – Peter Rincker Nov 19 '18 at 17:02