Skip to content

Instantly share code, notes, and snippets.

@cowlicks
Last active November 17, 2024 23:48
Show Gist options
  • Save cowlicks/5d70685346bf341a71f26e564b795a76 to your computer and use it in GitHub Desktop.
Save cowlicks/5d70685346bf341a71f26e564b795a76 to your computer and use it in GitHub Desktop.
Vim Plugin to rerun last shell-command in last vim terminal buffer
-- Rerun last command plugin
-- Unlike the python, this removes the scrollback in the terminal
local M = {}
-- Store the last terminal buffer job id
M.last_terminal_chan_id = nil
-- Function to record terminal job id when terminal is opened
local function record_terminal_id()
local buf = vim.api.nvim_get_current_buf()
local chan_id = vim.b[buf].terminal_job_id
if chan_id then
M.last_terminal_chan_id = chan_id
end
end
-- Function to clear scrollback and screen
local function clear_terminal()
-- Temporarily set scrollback to 1 to clear history
vim.o.scrollback = 1
vim.cmd('sleep 100m')
vim.o.scrollback = 10000
-- Send Ctrl-L to clear the screen
if M.last_terminal_chan_id then
vim.fn.chansend(M.last_terminal_chan_id, vim.api.nvim_replace_termcodes('<C-l>', true, true, true))
end
end
-- Function to rerun last command
function M.rerun_last_command()
if not M.last_terminal_chan_id then
vim.notify("No terminal found", vim.log.levels.ERROR)
return
end
-- Clear terminal
clear_terminal()
-- Send commands to rerun last command
local keys = vim.api.nvim_replace_termcodes('<C-u>!!<CR><CR>', true, true, true)
vim.fn.chansend(M.last_terminal_chan_id, keys)
end
-- Function to send Ctrl-C to terminal
function M.send_ctrl_c()
if not M.last_terminal_chan_id then
vim.notify("No terminal found", vim.log.levels.ERROR)
return
end
local keys = vim.api.nvim_replace_termcodes('<C-c><CR><CR>', true, true, true)
vim.fn.chansend(M.last_terminal_chan_id, keys)
end
-- Setup function
function M.setup()
-- Create autocmd to record terminal job id
vim.api.nvim_create_autocmd("TermOpen", {
callback = record_terminal_id,
})
-- Create user commands
vim.api.nvim_create_user_command('RerunLastThingInLastTerminal', M.rerun_last_command, {})
vim.api.nvim_create_user_command('CancelInLastTerminal', M.send_ctrl_c, {})
-- Add keymaps
vim.keymap.set('n', '<leader>p', ':w<CR>:RerunLastThingInLastTerminal<CR>', { silent = true })
vim.keymap.set('n', '<leader>c', ':w<CR>:CancelInLastTerminal<CR>', { silent = true })
end
return M
'''
This plugin creates a vim command that re-runs the last shell command in the last vim terminal buffer.
This is useful for getting quick feedback while working on something. This workflow would look like:
* open a terminal in a vim window with `:terminal`
* run a shell command like `cargo check`
* Go back to editing and re-run the shell command from the previous step with :RerunLastThingInLastTerminal
Installation:
Save this file to "rplugin/python3" in a vim 'runtimepath' directory (~/.config/nvim/rplugin/python3/runlast.py for example).
Usage:
You probably want to map this command to a keybinding. I use "<leader>p" Drop the following in your .vimrc:
```
" save the current file and re-run the most recent command in the most recent terminal buffer
map <leader>p :w \|:RerunLastThingInLastTerminal <cr>
```
Development:
If you update this file in place you *might* need to run `:UpdateRemotePlugins` for the changes to take effect.
For a long time i've use a similiar command where I re-run the last thing in tmux. I did this with:
map ,t :w \|:! tmux send-keys -t 0:$(tmux display-message -p '\#I').1 C-l C-u "\!\!" Enter Enter <cr><cr>
But I wanted to be able to do this with a vim terminal buffer because it can take up less screen space.
Thank you whoever asked this question:
https://vi.stackexchange.com/questions/21449/send-keys-to-a-terminal-buffer
https://www.reddit.com/r/neovim/comments/qjkx12/send_upcr_to_term_buffer_using_chansend_to_run/
'''
import pynvim
rerun_command_name = 'RerunLastThingInLastTerminal'
cancel_command_name = 'CancelInLastTerminal'
string_to_rerun_last_thing = '"\\<C-l>\\<C-u>!!\\<cr>\\<cr>"'
string_to_cancel = '"\\<C-c>\\<cr>\\<cr>"'
global_terminal_channel_id_variable_name = 'g:last_terminal_chan_id'
@pynvim.plugin
class Rerunner(object):
def __init__(self, nvim):
self.nvim = nvim
@pynvim.autocmd('TermOpen', sync=True)
def record_terminal_channel_id(self):
self.nvim.command(f'let {global_terminal_channel_id_variable_name} = b:terminal_job_id')
@pynvim.command(rerun_command_name)
def rerun_last_thing_in_last_terminal(self):
self.nvim.command_output(f'call chansend(g:last_terminal_chan_id, {string_to_rerun_last_thing})')
@pynvim.command(cancel_command_name)
def cancel_in_last_terminal(self):
self.nvim.command_output(f'call chansend(g:last_terminal_chan_id, {string_to_cancel})')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment