Skip to content

Instantly share code, notes, and snippets.

@swarn
Created April 6, 2026 16:50
Show Gist options
  • Select an option

  • Save swarn/7b30ce77332051127290c2737a4ba6a7 to your computer and use it in GitHub Desktop.

Select an option

Save swarn/7b30ce77332051127290c2737a4ba6a7 to your computer and use it in GitHub Desktop.
My neovim terminal config
local group = vim.api.nvim_create_augroup("my_terminal", { clear = true })
------------------------------------------------------------------------------
-- TERMINAL EXIT
------------------------------------------------------------------------------
-- Remove the built-in autocommand to delete the buffer on shell exit, which
-- deletes the scrollback history and alters window layout.
pcall(function() vim.cmd [[au! nvim.terminal TermClose]] end)
-- Instead, scrape the scrollback and copy it to a scratch buffer, then swap
-- the scratch buffer in for the terminal buffer in all windows.
vim.api.nvim_create_autocmd("TermClose", {
group = group,
callback = function(args)
local lines = vim.api.nvim_buf_get_lines(args.buf, 0, -1, false)
local scrollback_buf = vim.api.nvim_create_buf(true, true)
vim.api.nvim_buf_set_lines(scrollback_buf, 0, -1, false, lines)
vim.api.nvim_buf_set_name(scrollback_buf, "term-log-" .. args.buf)
vim.bo[scrollback_buf].buftype = 'nowrite'
for _, win in ipairs(vim.fn.win_findbuf(args.buf)) do
vim.api.nvim_win_set_buf(win, scrollback_buf)
end
vim.api.nvim_buf_delete(args.buf, { force = true })
end,
})
------------------------------------------------------------------------------
-- TERMINAL MODE MANAGEMENT
------------------------------------------------------------------------------
-- Enter terminal mode in new terminals.
vim.api.nvim_create_autocmd("TermOpen", {
group = group,
callback = function(args)
if args.buf == vim.api.nvim_get_current_buf() then vim.cmd.startinsert() end
vim.b[args.buf].restore_term_mode = true
end,
})
-- Save terminal mode status when leaving a terminal buffer.
vim.api.nvim_create_autocmd("BufLeave", {
group = group,
pattern = "term://*",
callback = function(args)
local mode = vim.api.nvim_get_mode().mode
vim.b[args.buf].restore_term_mode = (mode == "t")
end,
})
-- Restore terminal mode status when returning to a terminal buffer.
vim.api.nvim_create_autocmd("BufEnter", {
group = group,
pattern = "term://*",
callback = function(args)
if vim.b[args.buf].restore_term_mode then vim.cmd.startinsert() end
end,
})
------------------------------------------------------------------------------
-- TERMINAL MAPPINGS
------------------------------------------------------------------------------
local set = vim.keymap.set
set("t", "<c-w>n", "<C-\\><C-n>", { desc = "Normal mode" })
set("t", "<c-w><c-n>", "<C-\\><C-n>", { desc = "Normal mode" })
set("t", "<c-w>:", "<c-\\><c-o>:", { desc = "Do a command" })
set("t", "<c-w>.", "<c-w>", { desc = "Send ctrl-w to terminal" })
set("t", '<c-w>"', function()
local char = vim.fn.getcharstr()
local valid_register, _ = pcall(vim.fn.getregtype, char)
if not valid_register then return "" end
return '<C-\\><C-n>"' .. char .. "pi"
end, { desc = "Paste from register", expr = true })
set("t", "<c-w>gt", "<C-\\><C-n>gt", { desc = "go to next tabpage" })
set("t", "<c-w>gT", "<C-\\><C-n>gT", { desc = "go to previous tabpage" })
-- Standard window navigation and management works in terminal mode.
for c in ("hjklptbxrwsvqco="):gmatch "." do
local rhs = string.format("<cmd>wincmd %s<cr>", c)
set("t", string.format("<c-w><c-%s>", c), rhs)
set("t", string.format("<c-w>%s", c), rhs)
end
for c in ("KJHLT"):gmatch "." do
local rhs = string.format("<cmd>wincmd %s<cr>", c)
set("t", string.format("<c-w>%s", c), rhs)
end
------------------------------------------------------------------------------
-- FLOATING TERMINAL
------------------------------------------------------------------------------
-- A floating window that you can toggle. It starts a terminal, but is
-- otherwise a normal window: you can change its layout, view the terminal
-- buffer in other windows, open a new terminal with :term, etc.
local float_buf = -1
local terminal_win = -1
local function toggle_term_window()
local exists = vim.api.nvim_win_is_valid(terminal_win)
local floating = exists and vim.api.nvim_win_get_config(terminal_win).relative ~= ""
local current = vim.api.nvim_get_current_win() == terminal_win
if exists and floating then
if current then
float_buf = vim.api.nvim_win_get_buf(terminal_win)
vim.api.nvim_win_close(terminal_win, true)
else
vim.api.nvim_set_current_win(terminal_win)
end
return
end
local make_new_term = not vim.api.nvim_buf_is_valid(float_buf)
if make_new_term then float_buf = vim.api.nvim_create_buf(true, false) end
terminal_win = vim.api.nvim_open_win(float_buf, true, {
relative = "editor",
row = math.floor(vim.o.lines * 0.1),
col = math.floor(vim.o.columns * 0.1),
height = math.floor(vim.o.lines * 0.8),
width = math.floor(vim.o.columns * 0.8),
border = "solid",
})
if make_new_term then vim.fn.jobstart(vim.o.shell, { term = true }) end
end
set({ "n", "t" }, "<C-/>", toggle_term_window)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment