Terminal Mode
Neovim has a built-in terminal emulator (:terminal). You can run shells, scripts, compilers, and test runners in a split window alongside your code.
Core Idea
The terminal buffer is a regular Neovim buffer in "terminal mode." You can switch between Normal mode and terminal input using Ctrl+\ Ctrl+n.
Opening a Terminal
:terminal → open terminal in current window
:split | terminal → horizontal split with terminal
:vsplit | terminal → vertical split with terminal
:tabnew | terminal → terminal in new tab
Recommended keymaps
vim.keymap.set("n", "<leader>tt", "<cmd>terminal<cr>", { desc = "New terminal" })
vim.keymap.set("n", "<leader>th", "<cmd>split | terminal<cr>", { desc = "Terminal H-split" })
vim.keymap.set("n", "<leader>tv", "<cmd>vsplit | terminal<cr>", { desc = "Terminal V-split" })
Terminal Mode Keys
| Key | Action |
|---|---|
i or a | Enter terminal mode (start typing to shell) |
Ctrl+\ Ctrl+n | Exit terminal mode → Normal mode |
Esc | Only exits if configured (see below) |
Map Esc to exit terminal mode:
~/.config/nvim/lua/config/keymaps.lua
-- Escape terminal with Esc
vim.keymap.set("t", "<Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
-- Or Esc Esc (double-press to exit)
vim.keymap.set("t", "<Esc><Esc>", "<C-\\><C-n>", { desc = "Exit terminal mode" })
Auto-Enter Insert Mode on Terminal Open
~/.config/nvim/lua/config/autocmds.lua
vim.api.nvim_create_autocmd("TermOpen", {
group = vim.api.nvim_create_augroup("terminal_auto", { clear = true }),
callback = function()
vim.opt_local.number = false
vim.opt_local.relativenumber = false
vim.opt_local.signcolumn = "no"
vim.cmd("startinsert")
end,
})
Navigating Between Terminal and Editor
In terminal:
Ctrl+\ Ctrl+n → enter Normal mode
Ctrl+w h/j/k/l → navigate to adjacent window (from Normal)
From editor window:
<leader>tt → open terminal
<C-h>/<C-l> → move between editor and terminal
Running Commands Without a Full Terminal
You can run shell commands from command mode:
:!ls -la → run command, show output
:!make → run Makefile
:!python % → run current file
:r !date → insert command output into buffer
Toggleterm — Better Terminal Management
lua/plugins/editor.lua
{
"akinsho/toggleterm.nvim",
version = "*",
keys = {
{ "<C-\\>", desc = "Toggle terminal" },
{ "<leader>lg", desc = "Lazygit" },
},
opts = {
size = function(term)
if term.direction == "horizontal" then
return 15
elseif term.direction == "vertical" then
return vim.o.columns * 0.4
end
end,
open_mapping = [[<C-\>]],
direction = "horizontal",
border = "curved",
shade_terminals = true,
persist_mode = true,
},
config = function(_, opts)
require("toggleterm").setup(opts)
-- Lazygit integration
local Terminal = require("toggleterm.terminal").Terminal
local lazygit = Terminal:new({
cmd = "lazygit",
direction = "float",
float_opts = { border = "curved" },
})
vim.keymap.set("n", "<leader>lg", function() lazygit:toggle() end, { desc = "Lazygit" })
end,
},