How to indent what is pasted

I’ve read and suffered and failed for a good amount of hours. Nothing “just works” that I try, it works maybe 80%.

After a decade + of using MacVim I decided to go through my config/plugins in detail and freshen/clean everything up, and that includes fixing common annoyances that I’ve tolerated.

If it matters, I’m using Neovide and remapping to paste.

Heres my current nightmare of attempts. Note that they dont really work properly, I just wanted to show the context of how im binding the keys.

  vim.keymap.set('v', '<D-v>', '"+P`]')               -- paste visual mode
  vim.keymap.set('c', '<D-v>', '<C-r>+')              -- paste command mode
  -- create arbitrary _ char to avoid autoindent issues
  vim.keymap.set('i', '<D-v>', '_<ESC>xi<C-r><C-o>+') -- paste insert mode <C-r><C-o>+
  vim.keymap.set('n', '<D-v>', 'i<C-r><C-o>+<ESC>l')  -- paste normal mode m0"+P`]m`v`0=``

One of the big ones is things don’t indent properly that I paste.

Ideally, it should ignore the indentation of what I paste, by running the = command in the context of my code.

Consider copying a part of a Lua neovim plugin config from github, so you start the cursor here:

    { <-- copy this block only, start cursor before the |{
      some = 'thing'

It will actually look like this:

{ <-- copy this block only, start cursor before the |{
      some = 'thing'

This is just a side example of the headaches of trusting the indentation of the thing you’re copying.

So that leaves indenting it ourself.

I’ve read that <C-r><C-p>+ works but it doesnt for me unless I’m doing it wrong.

My next thought is “what if i use marks and just indent it myself?”

One way is to use the `] and `[ marks to move to the start/end of the pastel the problem is ONCE its indented, you can’t return your cursor to immediately after what was just pasted, which is ridiculous.

What am I missing and why is this such a pain in the ass?


Were you able to get this working? When I paste from somewhere, the indentation sticks as long as I’m not appending to an otherwise occupied line. What options do you have set?

My first thought was that you’d need to write an autocommand that runs something like gg=G after you paste. I haven’t toyed around with anything; the closest things I found were FileAppendPost and TextYankPost (albeit the latter one probably isn’t the one we need). I’ll attach a relevant video here (not because you need it, but because this is what my idea would look like in practice) about autocommands from TJ Devries.

If I have time, I’ll test out some things for myself to see if I can get anything working. (Not likely, but will be fun!)

Hey yo,

I’ve spent about 10 days now fighting it and I’ve learned a significant amount of things.

The biggest thing I realized is that…

What we really are wanting is to auto-indent code AND move the cursor to the exact last spot of the indented code.

I don’t think this is possible after all the stuff I’ve looked at, but I would love to be wrong.

There are 2 scenarios here that apply to pasting, given this above fact, and this is what I realized.

  1. you want to paste something which doesn’t really involve indenting, ie: this: Floccinaucinihilipilification. In this case, you want to be able to paste this thing and have the cursor go exactly where you expect it. Imagine how frustrating it would be when you just want to copy/paste someones name or something, or a function name, and it resets the cursor every time.

  2. you want to paste some block of code or something with indenting, probably from docs or a readme. In this case, the cursor position isn’t super important but moreso that it pastes properly and you dont have to go spend tons of time fixing it.

So, I have made hotkeys for each of these scenarios which I’ll put below. I have <D-v> (Command+V on mac) do scenario 1, and I have <Leader>v do scenario 2.

I believe the reason its so hard to indent and keep the cursor the same is because imagine there is code involving lots of braces or something, like:

}} ) } )

It would be quite difficult for a simple ‘paste’ mechanism to try to interpret and make sense of this to know where exactly the cursor was. For example imagine the cursor is lying in between 2 spaces, and now those spaces are gone. I dont think this is how the paste mechanism works.

Anyway here are the hotkeys has I have them so far. Im always refining and refining them. These are specifically for Neovide on OSX which allow mapping the command key.

  -- command mapping
  vim.keymap.set({ 'i', 'n' }, '<D-a>', '<ESC>ggVG')                              -- select all
  vim.keymap.set({ 'i', 'n' }, '<D-w>', function() close_tab() end)               -- close tab
  vim.keymap.set({ 'i', 'n' }, '<D-[>', function() vim.cmd('BufferPrevious') end) -- previous tab
  vim.keymap.set({ 'i', 'n' }, '<D-]>', function() vim.cmd('BufferNext') end)     -- next tab
  vim.keymap.set('i', '<D-t>', '<C-o>:tabnew<CR><ESC>')                           -- new tab (insert)
  vim.keymap.set('n', '<D-t>', ':tabnew<CR>')                                     -- new tab (insert)
  vim.keymap.set('i', '<D-s>', '<C-o>:w<CR>')                                     -- save (insert)
  vim.keymap.set('n', '<D-s>', ':w<CR>')                                          -- save (normal)
  vim.keymap.set('x', '<D-x>', '"+d')                                             -- cut
  vim.keymap.set('x', '<D-c>', '"+y')                                             -- copy
  vim.keymap.set('i', '<D-v>', '<C-r><C-o>+')                                     -- paste (insert)
  vim.keymap.set('n', '<D-v>', 'i<C-r><C-o>+<ESC>l')                              -- paste (normal)
  vim.keymap.set('x', '<D-v>', '"+P')                                             -- paste (visual)
  vim.keymap.set('c', '<D-v>', '<C-r>+')                                          -- paste (command)
  -- vim.keymap.set('n', '<D-q>', ':q<CR>')                                          -- quit


-- paste and indent
vim.keymap.set('n', '<Leader>v', 'i<C-r><C-o>+<ESC>l=`[`]$', { desc = 'Paste block and indent'})

Ah ha! After writing this I changed what I was searching to be more refined, and instead of googling about searching and indenting, I simply googled “Preserve cursor when indenting” and found this:

I havent tried it yet, but if it does what it says it should then you could combine scenarios 1 and 2 into a single key.

Just to be sure: have you had a look at :set paste?

Options paste and nopaste

For that I’m using the neovim keys below. That’s not the full picture though as…

  1. This doesn’t account for the fact that the indenting of the thing you’re pasting is probably wrong (and indenting below automatically doesnt seem to work well)
  2. The cursor position is lost on indenting (see my last comment)
CTRL-R CTRL-R {register}			i_CTRL-R_CTRL-R
		Insert the contents of a register.  Works like using a single
		CTRL-R, but the text is inserted literally, not as if typed.
		This differs when the register contains characters like <BS>.
		Example, where register a contains "ab^Hc":
CTRL-R a                results in "ac".
CTRL-R CTRL-R a                results in "ab^Hc".
		Options 'textwidth', 'formatoptions', etc. still apply.  If
		you also want to avoid these, use CTRL-R CTRL-O, see below.
		The '.' register (last inserted text) is still inserted as
		After this command, the '.' register contains the text from
		the register as if it was inserted by typing it.
CTRL-R CTRL-O {register}			i_CTRL-R_CTRL-O
		Insert the contents of a register literally and don't
		auto-indent.  Does the same as pasting with the mouse
		<MiddleMouse>. When the register is linewise this will
		insert the text above the current line, like with P.
		Does not replace characters!
		The '.' register (last inserted text) is still inserted as
		After this command, the '.' register contains the command
		typed and not the text. I.e., the literals "^R^O" and not the
		text from the register.
CTRL-R CTRL-P {register}			i_CTRL-R_CTRL-P
		Insert the contents of a register literally and fix the
		indent, like [<MiddleMouse>.
		Does not replace characters!
		The '.' register (last inserted text) is still inserted as
		After this command, the '.' register contains the command
		typed and not the text. I.e., the literals "^R^P" and not the
		text from the register.