Skip to content

Instantly share code, notes, and snippets.

@ram535
Last active February 2, 2023 12:25
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save ram535/b1b7af6cd7769ec0481eb2eed549ea23 to your computer and use it in GitHub Desktop.
Save ram535/b1b7af6cd7769ec0481eb2eed549ea23 to your computer and use it in GitHub Desktop.
Reuse the same terminal in neovim.
" 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
@esn89
Copy link

esn89 commented May 10, 2018

I managed to use wincmd J to change it so that the terminal opens on the bottom. How can I adjust height?

@ram535
Copy link
Author

ram535 commented May 12, 2018

I have this mapping to resize the windows manually.

      nnoremap <Left> :vertical resize +2<CR>
      nnoremap <Right> :vertical resize -2<CR>
      nnoremap <Up> :resize -2<CR>
      nnoremap <Down> :resize +2<CR>

@esn89
Copy link

esn89 commented May 14, 2018

Thanks. Though I'm looking for a way to have it initialize as a certain height, is that possible?

@danmikita
Copy link

@esn89 If you want to resize you can add

wincmd J
resize 15

To the open command if and else.

@thewalldao
Copy link

thewalldao commented Apr 24, 2020

hey guys, how to split below with specify number of row (Ex: 8sp) , please help me

@attomos
Copy link

attomos commented May 5, 2020

Hey @thewalldao

You can try the solution from @danmikita
https://gist.github.com/ram535/b1b7af6cd7769ec0481eb2eed549ea23#gistcomment-2771529

wincmd J " split below (Move the current window to be at the very bottom)
resize 8 " resize to 8

@DanielCardonaRojas
Copy link

DanielCardonaRojas commented Nov 5, 2020

Any way of getting this to work on Vim as well ? I get an error indicating that termopen is an unkown function.

@ram535
Copy link
Author

ram535 commented Jan 22, 2021

No clue how make it work in vim, sorry.

@marcoaaguiar
Copy link

marcoaaguiar commented Feb 11, 2021

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

function! MonkeyTerminalKillChild()
    " Close any process that is running in the terminal
    call system('kill $(ps --ppid ' . jobpid(s:monkey_terminal_job_id) .' -o pid --no-headers)')
endfunction

@PSeitz
Copy link

PSeitz commented Apr 14, 2021

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>

@sigmaSd
Copy link

sigmaSd commented Jul 11, 2022

I added this at the end of terminalopen function

  "always enter insert mode
  startinsert

@sigmaSd
Copy link

sigmaSd commented Jul 13, 2022

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)

@plusiv
Copy link

plusiv commented Nov 19, 2022

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