Relative copy and paste in vim

5 minute read

Sometimes there seems to be so much hidden power in vim that it’s just stunning. It’s like there’s a hidden reservoir of power just underneath the surface of the commands one uses every day.

A case in point: I was recently editing a document where I often needed to copy lines of text from one location to another, and although I was using relative jumps (e.g. via <relative-number>j or <relative-number>k) to get to the locations I needed to go within the document, yanking the text and then moving to where I needed to paste the text, and pasting it. After a while I thought “there must be an easier way!”. And there is! It took a while for me to put the correct search term into Google and StackOverflow, but I eventually stumbled on the solution:

:t

It’s likely that you’re laughing now at how overly concise and cryptic vim can be. Yes, vim has this reputation, however this level of concision can be so wonderfully powerful; I’m really pleased that I found this seemingly dusty corner of vim’s features.

There’s more of a story here, though. Many vim users know that copy and paste is performed by first yanking (copying) a piece of text with e.g. yy (yank a line) and then pasting it with either p (paste after the cursor) or P (paste before the cursor). One also knows that j moves down in the file and k moves up in the file. These commands are sufficient for a large number of editing tasks. However, for the use case mentioned above, only using this basic set results in a command sequence like this:

kkkkkkkkkkkkyyjjjjjjjjjjjjjjjjjjjP

which isn’t particularly efficient (and that’s putting it mildly!).

In my case, this was the kind of thing I used to do. Nevertheless, after having been introduced to the relativenumber setting I was able to improve upon this sequence of commands and consequently the abomination above now became:

12kyy19jP

and this pattern has served me well for many years. Of course, right up until that point in time where I had to do this really often and then I wished for something much more concise. Obviously it had never been sufficiently annoying for me to consider changing my habits. But what was the more concise solution? Time to turn to Dr. Google.

Unfortunately, searching for “copy and paste in vim” only turned up beginner articles about what I already knew! Talk about frustrating! Nevertheless, after lots of trying and many search term permutations along the lines of “relative copy paste vim”, I eventually found the solution to my problem on Unix StackExchange.

I found that the command can be simplified to:

:-12copy.<cr>

which makes the assumption that the cursor is already positioned on the line above where one wants to paste the copied line.

Let’s disect this command a bit.

  • The colon (:) means that one is entering command-line mode.
  • The minus sign (-) means that the following number refers to the number of lines above the current position; a plus sign (+), therefore, signifies copying from a certain number of lines below the current cursor position.
  • Thus, -12 means “twelve lines above the current cursor position”.
  • copy is the editing command to be executed.
  • The dot (.) is, technically speaking, the address below which to paste the copied line. Dot (.) basically means “here” in vim-speak, so practically speaking it means to paste the copied line after “here”, which is the current line in this context.
  • Finally, the <cr> just means that one has to hit enter (i.e. carriage return) for the command as a whole to be executed.

So how did I get to :t being the solution to my problem? Well, it turns out that :t is just a synonym for

:copy

(which can be shortened to :co, but why use two characters when one will do? :wink:). As mentioned in a comment to the Unix StackExchange answer, the :t command is a left-over from ed, the first editor on Unix systems. Although today one thinks of copying text from one place to another (hence co for the copy command), the t means transfer, which in essence is what a copy-paste operation is.

If you’re looking for this information in the vim documentation, you’ll find it as part of the copy-move documentation, which contains a huge amount of information. By the way, you can get directly to the documentation for :copy with the command :help :copy from within vim. Also, if you’re interested in digging deeper into vim commands that use ranges, be sure to have a look at the command ranges (:help 10.3) and the ex command-line ranges documentation (:help [range]).

So, in the end, the required command is:

:-12t.<cr>

But what if the cursor isn’t located on the line above where we want the copied text to be pasted? What if it’s, say, located somewhere between the copy and the paste locations? Well, instead of using dot (.), just specify the relative number of lines to the location where the text should be pasted after. In other words, to copy (erm, transfer) the line from 12 lines above to the line after 19 lines below, use:

:-12t+19<cr>

Somehow, the concision and generality of this command really impressed me. It certainly helped me perform fairly repetitive editing tasks more easily and gave me the opportunity to learn and look more deeply into the vim documentation. I hope that my new knowledge helped you in some way too!

Postscript

While researching some of the links for this post I found out that Drew Neil had already discussed this topic in episode 40 of his excellent vimcasts videos. I’m fairly sure that I would have seen this episode, but obviously I needed to “discover” it again. Oops.

As Drew mentions in his Core Vim Class:

You only have a finite number of keystrokes before you die. Make them count!

Update

Many thanks to -romainl- for pointing out and correcting errors in this post and to --Antony for suggesting the links to the range documentation!

Categories:

Updated:

Comments