3.1 KiB
title, date
| 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