3.1 KiB
title | date |
---|---|
Vim Send To Terminal | 2023-07-15T20:18:03-04:00 |
Semi automatic scripts with vim :terminal
For a long time, I used the below to send current line to vim's :terminal
:call term_list()[0]->term_sendkeys(getline('.') .. "\<CR>")
This is very useful if you have a text file with complex shell commands. To run
again, @:
and then again, @@
. This works because, last command run is
stored in register :
and the last macro executed using @
is stored in
register @
. And then to run another day, do :call te<UP arrow>
to recall
from vim's command history.
Since this was very useful, I then wrapped it in a function and added a mapping
def SendToTerminal()
if term_list()->empty()
echomsg "No Terminal windows found"
return
endif
terms[0]->term_sendkeys(getline(.) .. "\<CR>")
enddef
nnoremap <silent><leader>s call SendToTerminal()<CR>
Adding more features
So far good for shell commands. But when working with python, had to send a
block of functions to the ipython
shell. So added support for range of lines.
But then when sending a range of lines to shell, sometimes there had to be a
small delay (sleep) between commands so that the previous command can complete
and not eat the rest of commands. Then finally added support for sending ctrl
characters like ctrl c
, ctrl l
vim9script
def SendRangeToTerminal(start_line: number, end_line: number, _ = 0)
const terms = term_list()
if terms->empty()
echomsg "No Terminal windows found"
return
endif
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(arg1->str2nr(), funcref('SendRangeToTerminal', [line_num, end_line]))
return
elseif cmd == "ctrl"
terms[0]->term_sendkeys(nr2char(arg1->char2nr() - 96))
continue
endif
endif
terms[0]->term_sendkeys(line .. "\<CR>")
endfor
enddef
command -range -bar SendToTerm :call <SID>SendRangeToTerminal(<line1>, <line2>)
vnoremap <silent><leader>s :SendToTerm<CR>
nnoremap <silent><leader>s :SendToTerm<CR>
Wouldn't it be nice to just double-click commands with mouse? Like a simple GUI! ;)
nnoremap <silent><2-LeftMouse> :SendToTerm<CR>
Or just Enter
?
nnoremap <buffer> <CR> :SendToTerm \| norm j<CR>
Of course, mapping Enter
for any file is a bad idea. So lets just map in our cheat file
autocmd BufNewFile,BufRead cheat.sh {
nnoremap <buffer> <silent><2-LeftMouse> :SendToTerm<CR>
nnoremap <buffer> <CR> :SendToTerm \| norm j<CR>
}
Demo!
What about neovim/tmux/screen?
I am not the only one who thought about this. See
vim-slime since 2007. However it does
not support adding a sleep or sending arbitrary ctrl
characters without
additional mappings