Vim Send To Terminal


Semi automatic scripts with vim :terminal

Sometimes, fully automating a task is not worth the effort. So I end up running a set of commands usually from a cheat sheet text file slightly modifying the arguments each time. I used the below in vim to send line under cursor to vim’s :terminal open in a split

:call term_list()[0]->term_sendkeys(getline('.') .. "\<CR>")

To send another line @: and then for every other line @@. This works because, last command run is stored in register : and the last macro executed using @ is stored in register @.

To do the same another day, :call te<UP arrow> to recall from vim’s command history. Or better, add a function and mapping.

def SendToTerminal()
    if term_list()->empty()
        echomsg "No Terminal windows found"
    terms[0]->term_sendkeys(getline(.) .. "\<CR>")

nnoremap <silent><leader>s call SendToTerminal()<CR>

More cool features

For most use-cases that was enough. However added a few more nice features which is very helpful when you need it

  1. Support sending a range of lines (visual selection) at a time. E.g. Send a function block to a python shell
  2. Add a delay between lines in milliseconds. This is useful when the previous command reads from standard input and takes some time to complete
  3. Support sending ctrl characters like ctrl d, ctrl c etc,
def g:SendRangeToTerminal(start_line: number, end_line: number)
    const terms = term_list()
    if terms->empty()
        echomsg "No Terminal windows found"
    var line_num = start_line
    for line in getline(start_line, end_line)
        line_num += 1
        const spl_cmd = line->matchlist('\vVIMST (sleep|ctrl) ([0-9]+|[a-z])')
        if !spl_cmd->empty()
            const [_, cmd, arg1; _] = spl_cmd
            if cmd == "sleep"
                timer_start(str2nr(arg1), (_) => g:SendRangeToTerminal(line_num, end_line))
            elseif cmd == "ctrl"
                terms[0]->term_sendkeys(nr2char(char2nr(arg1) - 96))
        terms[0]->term_sendkeys(line .. "\<CR>")

command -range -bar SendToTerm :call g:SendRangeToTerminal(<line1>, <line2>)
vnoremap <silent><leader>s :SendToTerm<CR>
nnoremap <silent><leader>s :SendToTerm<CR>

More cool mapping

Wouldn’t it be nice to just double-click commands with mouse? Like a simple GUI! ;)

nnoremap <silent><2-LeftMouse> :SendToTerm<CR>

Or just hit Enter?

nnoremap <buffer> <CR> :SendToTerm \| norm j<CR>

Automatically add those mappings for

autocmd BufNewFile,BufRead nnoremap <buffer> <CR> :SendToTerm \| norm j<CR>
autocmd BufNewFile,BufRead nnoremap <buffer> <silent><2-LeftMouse> :SendToTerm<CR>



Copying above snippets to your vimrc should work in a recent vim. Checked in ubuntu 22.04 and archlinux. Git: repo


Since neovim uses a different terminal API, above snippets don’t work in neovim. vim-slime plugin (available since 2007!) supports different types of terminal. However it does not support adding delay and ctrl characters in text.