Last active
February 2, 2023 12:25
Reuse the same terminal in neovim.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
" With this function you can reuse the same terminal in neovim. | |
" You can toggle the terminal and also send a command to the same terminal. | |
let s:monkey_terminal_window = -1 | |
let s:monkey_terminal_buffer = -1 | |
let s:monkey_terminal_job_id = -1 | |
function! MonkeyTerminalOpen() | |
" Check if buffer exists, if not create a window and a buffer | |
if !bufexists(s:monkey_terminal_buffer) | |
" Creates a window call monkey_terminal | |
new monkey_terminal | |
" Moves to the window the right the current one | |
wincmd L | |
let s:monkey_terminal_job_id = termopen($SHELL, { 'detach': 1 }) | |
" Change the name of the buffer to "Terminal 1" | |
silent file Terminal\ 1 | |
" Gets the id of the terminal window | |
let s:monkey_terminal_window = win_getid() | |
let s:monkey_terminal_buffer = bufnr('%') | |
" The buffer of the terminal won't appear in the list of the buffers | |
" when calling :buffers command | |
set nobuflisted | |
else | |
if !win_gotoid(s:monkey_terminal_window) | |
sp | |
" Moves to the window below the current one | |
wincmd L | |
buffer Terminal\ 1 | |
" Gets the id of the terminal window | |
let s:monkey_terminal_window = win_getid() | |
endif | |
endif | |
endfunction | |
function! MonkeyTerminalToggle() | |
if win_gotoid(s:monkey_terminal_window) | |
call MonkeyTerminalClose() | |
else | |
call MonkeyTerminalOpen() | |
endif | |
endfunction | |
function! MonkeyTerminalClose() | |
if win_gotoid(s:monkey_terminal_window) | |
" close the current window | |
hide | |
endif | |
endfunction | |
function! MonkeyTerminalExec(cmd) | |
if !win_gotoid(s:monkey_terminal_window) | |
call MonkeyTerminalOpen() | |
endif | |
" clear current input | |
call jobsend(s:monkey_terminal_job_id, "clear\n") | |
" run cmd | |
call jobsend(s:monkey_terminal_job_id, a:cmd . "\n") | |
normal! G | |
wincmd p | |
endfunction | |
" With this maps you can now toggle the terminal | |
nnoremap <F7> :call MonkeyTerminalToggle()<cr> | |
tnoremap <F7> <C-\><C-n>:call MonkeyTerminalToggle()<cr> | |
" This an example on how specify command with different types of files. | |
augroup go | |
autocmd! | |
autocmd BufRead,BufNewFile *.go set filetype=go | |
autocmd FileType go nnoremap <F5> :call MonkeyTerminalExec('go run ' . expand('%'))<cr> | |
augroup END | |
I made minor adjustments so that the terminal opens at the bottom and the windows size is saved and restored when being toggled
" With this function you can reuse the same terminal in neovim.
" You can toggle the terminal and also send a command to the same terminal.
let s:monkey_terminal_window = -1
let s:monkey_terminal_buffer = -1
let s:monkey_terminal_job_id = -1
let s:monkey_terminal_window_size = -1
function! MonkeyTerminalOpen()
" Check if buffer exists, if not create a window and a buffer
if !bufexists(s:monkey_terminal_buffer)
" Creates a window call monkey_terminal
new monkey_terminal
" Moves the window to the bottom
wincmd J
resize 15
let s:monkey_terminal_job_id = termopen($SHELL, { 'detach': 1 })
" Change the name of the buffer to "Terminal 1"
silent file Terminal\ 1
" Gets the id of the terminal window
let s:monkey_terminal_window = win_getid()
let s:monkey_terminal_buffer = bufnr('%')
" The buffer of the terminal won't appear in the list of the buffers
" when calling :buffers command
set nobuflisted
else
if !win_gotoid(s:monkey_terminal_window)
sp
" Moves to the window below the current one
wincmd J
execute "resize " . s:monkey_terminal_window_size
buffer Terminal\ 1
" Gets the id of the terminal window
let s:monkey_terminal_window = win_getid()
endif
endif
endfunction
function! MonkeyTerminalToggle()
if win_gotoid(s:monkey_terminal_window)
call MonkeyTerminalClose()
else
call MonkeyTerminalOpen()
endif
endfunction
function! MonkeyTerminalClose()
if win_gotoid(s:monkey_terminal_window)
let s:monkey_terminal_window_size = winheight(s:monkey_terminal_window)
" close the current window
hide
endif
endfunction
function! MonkeyTerminalExec(cmd)
if !win_gotoid(s:monkey_terminal_window)
call MonkeyTerminalOpen()
endif
" clear current input
call jobsend(s:monkey_terminal_job_id, "clear\n")
" run cmd
call jobsend(s:monkey_terminal_job_id, a:cmd . "\n")
normal! G
wincmd p
endfunction
" With this maps you can now toggle the terminal
nnoremap <F7> :call MonkeyTerminalToggle()<cr>
tnoremap <F7> <C-\><C-n>:call MonkeyTerminalToggle()<cr>
nnoremap <M-CR> :call MonkeyTerminalToggle()<cr>
tnoremap <M-CR> <C-\><C-n>:call MonkeyTerminalToggle()<cr>
I added this at the end of terminalopen function
"always enter insert mode
startinsert
I ported the code to lua and made the terminal size reactif to resize
local lua_terminal_window = nil
local lua_terminal_buffer = nil
local lua_terminal_job_id = nil
local function lua_terminal_window_size()
return tonumber(vim.api.nvim_exec("echo &lines", true)) / 4
end
local function LuaTerminalOpen()
if vim.fn.bufexists(lua_terminal_buffer) == 0 then
vim.api.nvim_command("new lua_terminal")
vim.api.nvim_command("wincmd J")
vim.api.nvim_command("resize " .. lua_terminal_window_size())
lua_terminal_job_id = vim.fn.termopen(os.getenv("SHELL"), {
detach = 1
})
vim.api.nvim_command("silent file Terminal 1")
lua_terminal_window = vim.fn.win_getid()
lua_terminal_buffer = vim.fn.bufnr('%')
vim.opt.buflisted = false
else
if vim.fn.win_gotoid(lua_terminal_window) == 0 then
vim.api.nvim_command("sp")
vim.api.nvim_command("wincmd J")
vim.api.nvim_command("resize " .. lua_terminal_window_size())
vim.api.nvim_command("buffer Terminal 1")
lua_terminal_window = vim.fn.win_getid()
end
end
vim.cmd("startinsert")
end
local function LuaTerminalClose()
if vim.fn.win_gotoid(lua_terminal_window) == 1 then
vim.api.nvim_command("hide")
end
end
local function LuaTerminalToggle()
if vim.fn.win_gotoid(lua_terminal_window) == 1 then
LuaTerminalClose()
else
LuaTerminalOpen()
end
end
---@diagnostic disable-next-line: unused-local, unused-function
local function LuaTerminalExec(cmd)
if vim.fn.win_gotoid(lua_terminal_window) == 0 then
LuaTerminalOpen()
end
vim.fn.jobsend(lua_terminal_job_id, 'clear\n')
vim.fn.jobsend(lua_terminal_job_id, cmd .. '\n')
vim.api.nvim_command("normal! G")
vim.api.nvim_command("wincmd p")
end
vim.keymap.set("n", "<space>t", LuaTerminalToggle)
vim.keymap.set("t", "<space>t", LuaTerminalToggle)
For those who doesn't like numbers in terminal, I added
" disables numbers or/and relative numbers just for terminal
setlocal nonumber norelativenumber
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for posting this. I've been using it for some months now.
I included a function to kill all child process of the terminal, if I forgot to do close a REPL.
I call it in the MonkeyTerminalExec function