Pasted code automatically lands at the correct indentation level.

p / P / gp / gP so linewise pasted code lands at the right indent level automatically.]p / [p to paste charwise content as a correctly indented new line below/above.indentexpr -> treesitter scope analysis -> heuristic fallback.V + p/P): replace selected lines with correctly indented content from linewise registers..) works naturally.u undoes the entire paste.setup() and paste keys are enhanced.-- lazy.nvim
{
'nemanjamalesija/smart-paste.nvim',
event = 'VeryLazy',
config = true,
}
-- packer.nvim
use {
'nemanjamalesija/smart-paste.nvim',
config = function()
require('smart-paste').setup()
end,
}
" vim-plug
Plug 'nemanjamalesija/smart-paste.nvim'
" then in your init.lua: require('smart-paste').setup()
require('smart-paste').setup()
Default mappings are enabled automatically:
p, P, gp, gP]p, [pOptional setup:
require('smart-paste').setup({
exclude_filetypes = {}, -- filetypes that skip smart indent
})
Indentation settings (shiftwidth, expandtab, tabstop) come from your buffer options. No plugin-specific indent config needed.
| Mode | Key | Action |
|---|---|---|
| Normal | p |
Smart paste after cursor line |
| Normal | P |
Smart paste before cursor line |
| Normal | gp |
Smart paste after cursor line and follow to end |
| Normal | gP |
Smart paste before cursor line and follow to end |
| Normal | ]p |
Paste charwise content as smart-indented new line below (linewise: same as p) |
| Normal | [p |
Paste charwise content as smart-indented new line above (linewise: same as P) |
Visual (linewise V) |
p |
Replace selection with smart-indented linewise content |
Visual (linewise V) |
P |
Replace selection with smart-indented linewise content |
| Normal | <Plug>(smart-paste-raw-p) |
Raw p (bypass smart paste) |
| Normal | <Plug>(smart-paste-raw-P) |
Raw P (bypass smart paste) |
Behavior notes:
yy, dd, 2yy, or linewise Visual V + y).]p and [p convert inline content into smart-indented new lines.p/P/gp/gP and blockwise (<C-v>) paste use native Neovim behavior.V + p/P also falls back to native behavior when the source register is charwise or blockwise.P, so the text you paste over never overwrites the register. This matches the smart path, where registers are read but never rewritten.Example escape-hatch bindings:
vim.keymap.set('n', '<leader>p', '<Plug>(smart-paste-raw-p)')
vim.keymap.set('n', '<leader>P', '<Plug>(smart-paste-raw-P)')
Use paste() when you want smart-paste behavior from a specific register in a custom non-recursive mapping:
vim.keymap.set('n', '<M-p>', function()
require('smart-paste').paste({ register = '+', key = 'p' })
end, { desc = 'Smart paste from system clipboard' })
Supported options:
register (string): register override (+, "a, etc.). Defaults to vim.v.register.key (string or table): paste behavior (p, P, gp, gP, ]p, [p) or structured key entry.count (number): explicit repeat count override.If you set keys, it replaces the defaults. Use this only when you want custom key behavior.
Flat string remap:
require('smart-paste').setup({
keys = { 'p', 'P', ']p', '[p' },
})
Structured remap (custom behavior flags):
require('smart-paste').setup({
keys = {
'p',
{ lhs = '-p', after = true, follow = false, charwise_newline = true },
},
})
charwise_newline controls what happens when the register holds charwise content: true always converts the paste to an indented new line (like ]p), false never does, and 'multiline' converts only when the yank spans multiple lines, so a single yanked word still pastes inline:
require('smart-paste').setup({
keys = {
{ lhs = 'p', like = 'p', charwise_newline = 'multiline' },
{ lhs = 'P', like = 'P', charwise_newline = 'multiline' },
},
})
Shorthand remap by inheriting behavior from a built-in key:
require('smart-paste').setup({
keys = {
'p',
{ lhs = '-p', like = ']p' }, -- behaves like ]p (charwise newline below)
},
})