--- title: Vim Send To Terminal date: 2023-07-26T15:23:22-04:00 asciinema: true tags: - vim categories: - development --- ### 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 ```vim :call term_list()[0]->term_sendkeys(getline('.') .. "\") ``` 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` to recall from vim's command history. Or better, add a function and mapping. ```vim def SendToTerminal() if term_list()->empty() echomsg "No Terminal windows found" return endif terms[0]->term_sendkeys(getline(.) .. "\") enddef nnoremap s call SendToTerminal() ``` ### 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, ```vim def g:SendRangeToTerminal(start_line: number, end_line: number) 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(str2nr(arg1), (_) => g:SendRangeToTerminal(line_num, end_line)) return elseif cmd == "ctrl" terms[0]->term_sendkeys(nr2char(char2nr(arg1) - 96)) continue endif endif terms[0]->term_sendkeys(line .. "\") endfor enddef command -range -bar SendToTerm :call g:SendRangeToTerminal(, ) vnoremap s :SendToTerm nnoremap s :SendToTerm ``` ### More cool mapping Wouldn't it be nice to just double-click commands with mouse? Like a simple GUI! ;) ```vim nnoremap <2-LeftMouse> :SendToTerm ``` Or just hit `Enter`? ```vim nnoremap :SendToTerm \| norm j ``` Automatically add those mappings for `cheat.sh` ```vim autocmd BufNewFile,BufRead cheat.sh nnoremap :SendToTerm \| norm j autocmd BufNewFile,BufRead cheat.sh nnoremap <2-LeftMouse> :SendToTerm ``` ### Demo {{< asciinema key="vimstt" >}} ### Using Copying above snippets to your vimrc should work in a recent vim. Checked in ubuntu 22.04 and archlinux. Git: [repo](https://gitea.balki.me/balki/vimfun/src/branch/main/stt/stt8.vim) ### neovim/tmux/screen Since neovim uses a different terminal API, above snippets don't work in neovim. [vim-slime](https://github.com/jpalardy/vim-slime) plugin (available since 2007!) supports different types of terminal. However it does not support adding delay and `ctrl` characters in text.