How can I get vim to preserve marks when I delete the line the mark is on (i.e., automatically move the mark to the line immediately above or below the marked line)
Maybe the easiest way is to use capital letter marks when you don't want them deleted. If the line the mark is on is deleted, it gets moved to the next line.
Another option is the lockmarks
command. lockmarks
takes a command to run and locks most marks at their current line number until the command completes. If you wanted to do that often, you'd probably need to add some mappings that would lockmarks
for you, for example this turns dd
into mark-preserving:
nnoremap dd :lockmarks normal! dd<cr>
or
nnoremap dd :lockmarks delete<cr>
The problem is that you lose the ability to specify a count for dd
. It's possible to get around that by using v:count
, but then we'll need execute
to get that interpolated:
nnoremap dd :<c-u>execute 'lockmarks normal! ' . v:count . 'dd'<cr>
And then you'd have to define a mapping like this for every deletion operation you use regularly.
So, probably using capital letter marks will be easier. You'll just have to remember to use them.
One addition to @Kurt Hutchinson answer: there is a possibility to define an operator function. In this case you don’t need to define a mapping for every deletion command, but instead redefine d
:
let s:typetochar={
\ 'char': 'v',
\ 'line': 'V',
\'block': "\<C-v>",
\}
function! DeleteOperator(type)
execute 'lockmarks normal! `["'.v:register.'d'.s:typetochar[a:type].'`]'
endfunction
function! s:Delete()
set opfunc=DeleteOperator
return 'g@'
endfunction
nnoremap <expr> d <SID>Delete()
nnoremap <expr> dd <SID>Delete().'g@'
vnoremap d :<C-u>lockmarks normal! gvd<CR>
Note the dd
mapping: as d
is actually using g@
, there should be dg@
(expanded to g@g@
) where you type dd
. So in order to use cleaner dd
you have to create a mapping.