I have this mapping in my .vimrc file for swapping one line with the line above it.

:map <c-k> dd-P

It work just fine almost in any case except for two cases. when I select the first line I and press I expect nothing to happen and in that case if I manually do dd-P the line will be removed and pasted again (so nothing happens) but if I press the line will be removed!(I guess it's because in that case - will lead to an error message or something!

the other problem is when I'm at the last line of the file dd-P will swap the last line with the two lines above it instead of only the last line above it. (Which is understandable but I want to find a workaround for it.)

So what I have in mind is to make vim do ddP instead of dd-P if the cursor is either at the first or the last line of the file.(or maybe just do nothing in case of the first line but I think this way I'll have fewer exceptions to make)

How can I get what I want?

Also it would be great if someone could tell me a way to make this swapping work smartly and automatically indent itself(I have a plugin that does thisone for me and I can simply call the plugin after that but I was just wondering what would the .vimrc approach for this.)


1 Answer 1


Actually you can just use the :m[ove] command to accomplish what you want:

:noremap <silent> <c-k> :.m-2<cr>

This will move the current line (.) to "below the line given by address". The address here (-2) is two lines up from current position. I added <silent> to prevent all this from appearing on the command line when you use it.

One noteworthy thing here is that an error message will appear if you try it on line 1. If you ask me, that's the way it ought to be. Still, if you'd prefer not to see the error then use this variation:

:noremap <silent> <c-k> :sil! .m-2<cr>

The :sil[ent] command when followed by a bang (!) will suppress errors.

If you want to use this in Insert mode you'll need to get out of Insert (with <esc>) before doing the move. Then you can get back to Insert with :startinsert:

:inoremap <silent> <c-k> <esc>:silent! .m-2 \| startinsert<cr>

If you want to do any more customization I recommend you use my original solution below.

A few more things...

  • Note my use of :noremap. You should always use this (and :nnoremap, :inoremap, etc.) unless you definitively want to do otherwise.
  • I'm assuming you intentionally selected :map over :nmap. But if you only intend to use this for Normal mode it would be better to use :nnoremap.
  • Regarding your secondary request, given that it's probably not just a "here's a one line fix, no questions asked" kind of thing I suggest you post another question for that.

Note: I didn't think to use :move at first and had written a function to address this question. I'll leave it here in case anyone's interested.

The cleanest way, IMO, is to use a function. Put something like this in your vimrc file:

function! SmartLineSwap()
  let lineno = line(".")  " the current line
  if lineno == line("$")  " the last line in the buffer
    exe "normal! ddP"
  elseif lineno != 1
    exe "normal! dd-P"

And then your mapping:

:noremap <c-k> :call SmartLineSwap()<cr>
  • 1
    Great suggestion on the move command; I have mappings which use normal mode keystrokes to move lines up and down—will be converting them today
    – D. Ben Knoble
    Commented Dec 30, 2017 at 15:48
  • @DavidBenKnoble Excellent. Can't believe I got so far with the other solution before thinking of it!
    – B Layer
    Commented Dec 30, 2017 at 16:46
  • Can you add some more explanation on the use of "$" please!? Thanks for your solution:) Commented Dec 30, 2017 at 19:05
  • 1
    mo[ve] vs m[ove] isn't significant. It just means I thought the shortest abbreviation allowed was :mo when :m will do (:mov and :move work, too). For Insert mode you need to take it out of Insert, do the move command, then go back to Insert: inoremap <silent> <c-k> <esc>:silent! .mo-2 \| startinsert<cr>.
    – B Layer
    Commented Dec 30, 2017 at 21:15
  • 1
    I added the inoremap version to my answer and updated to the shorter abbreviation of :move. Sounds like you shouldn't need the solution with the function at this point but I added a couple comments to it nonetheless.
    – B Layer
    Commented Dec 30, 2017 at 21:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.