smart-paste.nvim

July 18, 2026 ยท View on GitHub

Pasted code automatically lands at the correct indentation level.

CI License Neovim

smart-paste demo

Features

  • Intercepts p / P / gp / gP so linewise pasted code lands at the right indent level automatically.
  • Adds ]p / [p to paste charwise content as a correctly indented new line below/above.
  • Three-tier indent strategy: indentexpr -> treesitter scope analysis -> heuristic fallback.
  • Visual mode (V + p/P): replace selected lines with correctly indented content from linewise registers.
  • Dot-repeat (.) works naturally.
  • Single undo step: one u undoes the entire paste.
  • Register-safe behavior: registers are read, never rewritten.
  • Zero dependencies: pure Lua, no external plugins required.
  • Zero config: call setup() and paste keys are enhanced.

Installation

-- 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()

Setup

require('smart-paste').setup()

Default mappings are enabled automatically:

  • p, P, gp, gP
  • ]p, [p

Optional 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.

Programmatic Paste API

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.

Remapping Keys

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)
  },
})

Mappings

ModeKeyAction
NormalpSmart paste after cursor line
NormalPSmart paste before cursor line
NormalgpSmart paste after cursor line and follow to end
NormalgPSmart paste before cursor line and follow to end
Normal]pPaste charwise content as smart-indented new line below (linewise: same as p)
Normal[pPaste charwise content as smart-indented new line above (linewise: same as P)
Visual (linewise V)pReplace selection with smart-indented linewise content
Visual (linewise V)PReplace 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)

Smart paste applies to linewise registers (for example: yy, dd, 2yy, or linewise Visual V + y). For charwise registers, ]p and [p convert inline content into smart-indented new lines. Characterwise paste on p/P/gp/gP and blockwise (<C-v>) paste use native Neovim behavior. Visual V + p/P also falls back to native behavior when the source register is charwise or blockwise. Visual fallbacks are fed as native 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)')

License

MIT