blog/content/posts/vim-stt.md

118 lines
3.3 KiB
Markdown
Raw Normal View History

2023-07-21 22:46:10 -04:00
---
2023-07-22 00:15:29 -04:00
title: Vim Send To Terminal
2023-07-21 22:46:10 -04:00
date: 2023-07-15T20:18:03-04:00
2023-07-24 23:54:51 -04:00
asciinema: true
tags:
- vim
categories:
- development
2023-07-21 22:46:10 -04:00
---
2023-07-22 00:15:29 -04:00
### Semi automatic scripts with vim `:terminal`
<!--more-->
2023-07-24 23:54:51 -04:00
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
2023-07-22 00:15:29 -04:00
```vim
:call term_list()[0]->term_sendkeys(getline('.') .. "\<CR>")
```
2023-07-24 23:54:51 -04:00
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 @`.
2023-07-22 00:15:29 -04:00
2023-07-24 23:54:51 -04:00
To do the same another day, `:call te<UP arrow>` to recall from vim's command
history. Or better, add a function and mapping.
2023-07-22 00:15:29 -04:00
```vim
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>
```
2023-07-24 23:54:51 -04:00
### 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
2023-07-22 00:15:29 -04:00
2023-07-24 23:54:51 -04:00
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,
2023-07-22 00:15:29 -04:00
```vim
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>
```
2023-07-24 23:54:51 -04:00
### More cool mapping
2023-07-22 00:15:29 -04:00
Wouldn't it be nice to just double-click commands with mouse? Like a simple GUI! ;)
```vim
nnoremap <silent><2-LeftMouse> :SendToTerm<CR>
```
Or just `Enter`?
```vim
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
```vim
autocmd BufNewFile,BufRead cheat.sh {
nnoremap <buffer> <silent><2-LeftMouse> :SendToTerm<CR>
nnoremap <buffer> <CR> :SendToTerm \| norm j<CR>
}
```
2023-07-24 23:54:51 -04:00
### Demo
{{< asciinema key="vimstt" >}}
### Getting the scripts
2023-07-22 00:15:29 -04:00
### What about neovim/tmux/screen?
I am not the only one who thought about this. See
[vim-slime](https://github.com/jpalardy/vim-slime) since 2007. However it does
not support adding a sleep or sending arbitrary `ctrl` characters without
additional mappings