33

Here's how I usually copy a block of text or code:

  1. Select the block with Visual mode (V) and delete it with d; or if it's just a single line, just use dd.
  2. Go to where I want the text to be, I am too stupid to remember the difference between p and P, so I get this right about 50% of time. (if I get it wrong: undo a try again).
  3. Sometimes, the indentation needs adjusting, so I go to Visual mode with V again and reselect the same block, and use > or < to fix that.

Is there an easier way? In some editors I've seen that you can "pick up" a block of text, as it were, and move it around; here's an example from Emacs (but I've also seen this in other editors):

Emacs

Or perhaps there is another (even better) way?

3
  • 2
    About the indentation issue you can solve it by pasting with ]p or [p rather than p or P. This way the block that you paste will be autoindented. Feb 8, 2015 at 0:28
  • You might want to watch the screencast by Drew Neil Bubbling Text which recommends to use vim-unimpaired.
    – Hotschke
    Apr 12, 2018 at 16:22
  • You're not alone in the P/p thing.
    – MDeBusk
    Sep 21, 2022 at 0:10

8 Answers 8

13

There's a plugin for that: dragvisuals.vim.

It doesn't bind any keys by default, so you'll have to do that yourself. I like to map them to Control+Arrow key:

xmap <expr> <C-Left> DVB_Drag('left')
xmap <expr> <C-Right> DVB_Drag('right')
xmap <expr> <C-Down> DVB_Drag('down')
xmap <expr> <C-Up> DVB_Drag('up')

It also comes with a bonus key to duplicate the current selection:

xmap <expr> D DVB_Duplicate()

Advantages of this plugin over the other answers:

  • Doesn't make any text disappear if you try to move further up than the first line.
  • It will expand the file if you move below the last line.
  • Moving to the left/right works a bit better.

Another useful trick to complement this, are these keybinds:

xnoremap > >gv
xnoremap < <gv

This will re-select the last visual selection after you use > while in Visual mode, so you can adjust the alignment more easily (you can also use the arrow keys, but this will move it by one character, and not one shiftwidth).

2
  • 2
    There's another plugin for this that the author of my suggested plugin describes as "a much improved reworking of my own dragvisuals.vim": github.com/zirrostig/vim-schlepp Feb 21, 2016 at 11:29
  • The plugin linked in the answer is dead (AKA gone off GitHub), and the plugin linked in the comment is stale
    – Zoe
    Mar 12, 2021 at 20:31
10

For step 3, you can use gv= instead (= means indent, and gv means "reselect last visual selection").

For the Emacs behavior, it'd be pretty simple to add some mappings in your .vimrc:

vnoremap <down> dpV`]
vnoremap <up> dkPV`]

You can then use the up and down arrow keys to move around a visual line selection. (Of course, you could map to different keys as well if you hate arrows.)

(You could do this with normal visual selections and moving them left and right too: vnoremap <left> dhPgvhoho and vnoremap <right> dpgvlolo.)

1
  • gv goes to the last visual selection, which in this case would be the paragraph that was deleted. What am I missing?
    – r_31415
    Feb 8, 2015 at 1:36
8

You can remap some keys to move lines around. For example, arrows up and down if you are used to move around through hjkl.

E.g on .vimrc

nnoremap <down> :m .+1<CR>==
nnoremap <up> :m .-2<CR>==
vnoremap <down> :m '>+1<CR>gv=gv
vnoremap <up> :m '<-2<CR>gv=gv

:m, normal command to move a line

+1 or -2 to indicate a line up or down (-1 keeps the line on the same place)

gv to select again the same text in VISUAL mode

This solution does not interfere with the registers, so any copied text will still be available, as well as reformat the code to set proper indentation, etc.

2
  • This is nice, but not for me, since I don't like using = (it often changes too much unrelated formatting for me). Feb 8, 2015 at 21:16
  • I map <C-A-Up> & <C-A-Down>, that way I can map insert mode as well Aug 3, 2020 at 22:05
5

There are few plugins for this but I prefer Tim Pope's vim-unimpaired (github) (Ingo Karkat LineJuggler is similar).

You visually select your block of text then do ]e (e for exchange) to move the block downwards ([e for the other direction). Use . to repeat the action (You do have repeat.vim installed don't you). If you go too far then just undo via u.

Now personally I think p and P are such common commands that it would be best to learn them before creating bad habits. Then you could use ]p to paste at the same indent. If you are using unimpaired you can do >p to paste an indention level deeper or =p to paste and reformat.

If you have trouble remembering p and P then it might help you to think of them in terms of the o and O commands which is a kind of symmetry.

As others have noted you can use gv to reselect a visual selection. However if you just put/pasted text you will have lost your visual selection. Don't fret because every change (and yanks!) will set '[ and '] around the changed area. So you can indent via >'] (read: shift right until end of last change) and use the . to repeat the indention until you have the text in the right spot.

For more help see:

:h p
:h P
:h ]p
:h o
:h O
:h .
:h u
:h >
:h ']
:h gv
3

The easiest way I found to do this is documented here: http://vimrcfu.com/snippet/77

Put the following in your .vimrc

vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv
2
3

If you know the line number that you want to move the block of text to, then you can use a range or visual selection with the :move command to move the block to there.

For example, if I have the following file:

1 aaa
2 bbb
3 ccc
4 ddd

and I want to move the line of a's to after the line of c's, I put the cursor on the line of a's and enter :m3, and the line is then shifted.

This also works with +/- offsets. In the example above, the same result could be accomplished with :m+2 to move the line 2 lines down.

For blocks of text, the only difference is adding in a range or selection. To move the aaa and bbb lines to the end of the file, use a command like :1,2m$ ($ = last line) or visually select the lines and :m$.

1
2

Plugin vim-move by matze

Available from

  1. https://www.vim.org/scripts/script.php?script_id=4687
  2. https://github.com/matze/vim-move

Default mappings in visual and normal mode:

<A-k>   Move current line/selection up
<A-j>   Move current line/selection down
0

Edit: the below is a misunderstanding caused by assuming <cmd> in nvim is the same as :, which is not the case. Using :m '>+1<CR> does work in nvim but <cmd>m '>+1<CR> does not, likely because of some of the subtle differences in behavior. Please see comments for details.

I would comment on @Khelben's post but I don't have enough reputation to. Came across this post from googling how to move multiple lines in vim (I'm using neovim). Khelben's answer didn't work for me - I'm not sure if this is due to a :move behavior change from older versions of vim (or to neovim), but :help :move in neovim now specifies that a [range] should be pre-pended to the move command specifying the range of lines to move. In a visual selection, this range is '<,'> ('< start line of visual block selection, '> end line of visual block selection).

> :[range]m[ove] {address}          *:m* *:mo* *:move* *E134*
>           Move the lines given by [range] to below the line
>           given by {address}.

I was able to get Khelben's answer to work for me by modifying the mapping slightly from

" vnoremap <down> :m '>+1<CR>gv=gv
" vnoremap <up> :m '<-2<CR>gv=gv

" move [range] '<,'> to line at '>+1, then reselect visual block
vnoremap <down> :'<,'>m '>+1<CR>gv=gv
" move [range] '<,'> to line at '<-2, then reselect visual block
vnoremap <up> :'<,'>m '<-2<CR>gv=gv

Hope this helps anybody who would rather have a vanilla mapping set up rather than using a plugin.

4
  • 2
    Khelban's answer works for me, both in vim v8.1 and nvim v8.0. The '<,'> after colon isn't required, because it's automatically added when : is pressed, even if it's pressed by a mapping.
    – 3N4N
    Sep 21, 2022 at 11:38
  • The default range is the current line, and in visual mode the marks are automatically added when pressing : as mentioned. Your answer actually would fail because it would run :'<,'>'<,'>m '<-2<CR>gv=gv
    – D. Ben Knoble
    Sep 21, 2022 at 14:25
  • 1
    Thank you! You both are correct. This was a misunderstanding on my part caused by assuming <cmd> and : lead to the same behavior in nvim which doesn't seem to be true in this case. <cmd>m ... did not work properly for me in nvim w.r.t. visual ranges but :m ... does do the right thing, and I've reverted back to that in my mappings.
    – mparq
    Sep 22, 2022 at 17:26
  • @mparq If you're in Visual mode and you press : you should see :'<,'> already, Vim/NeoVim will already pre-fill it with the range for the Visual selection. That's why simply :m will work in the mapping. This has been the behavior of Vim/NeoVim probably in forever (quite possibly since Visual mode was introduced.) Do you not see that behavior in your Vim/NeoVim for some reason?
    – filbranden
    Sep 22, 2022 at 18:13

Your Answer

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

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