[1]Skip to content

  Site big logo

  [2]Niko, doko?

  sorcerers conjure spirits with spells, but programmers invoke processes
  with code

                                Ranges in Vim

  Posted on Feb 9, 2020
  [3]VimExplorer [4]vim [5]vimscript [6]tricks

  Although I briefly mentioned the concept of range in my [7]previous
  article about substitutions, I simply described them as a “set of
  lines”, on each of which the :substitute command would be called. But
  amongst the (much appreciated) feedback I received on [8]reddit, some
  mentioned the fact that this somewhat hand-wavy explanation was not
  enough, and that I should elaborate.

  This in turn made me realize that everything about ranges was not clear
  for me. What exactly is a range? How is it different from an address? I
  decided to do some research, leading to the article you are reading
  right now!

Range or Address?

  As with any article of the [9]VimExplorer series, I started by looking
  at the help. This time however, I quickly ran into a problem: :help
  address and :help range refer to the exact same location! Does this
  mean that ranges and addresses are the same thing?

  As you might expect, the answer to the previous question is no.
  Consider the % commonly used in :%s/pat/repl/ to replace a given pat
  with repl over the whole file: because the syntax of the substitute
  command is :[range]s[ubstitute]/{pattern}/{string}/[flags] [count], it
  is easy to understand that % is a range. But is it also an address?
  Using it in a command that takes an address like :move demonstrates the
  opposite, as :m % will result in E14: Invalid address (which is not too
  surprising - “move to the whole file” does not make much sense!).

  So what is an address then? It turns out that it is simply a line
  number. Nothing more, nothing less. This also means that any positive
  number smaller or equal to the number of lines in the current buffer is
  an address! But dealing only in terms of absolute numbers is not really
  convenient, so Vim introduces several special characters that (when
  used as addresses) will be translated to a given line number. Here are
  some examples:
    * . is the current line,
    * $ is the last line in the buffer,
    * 1 is the first line in the buffer,
    * 0 is a special “virtual” line before the first line in the buffer
      (some commands, like :move will interpret it as “before the first
      line”, while others like :substitute will treat it as the first
      line),
    * 't is the line in which mark t is placed,
    * '< and '> are, respectively, the line on which the previous
      selection begins and ends,
    * /pat/ (note the second / !) is the next line where pat matches,
    * \/ is the next line in which the previous search pattern matches,
      and
    * \& is the next line in which the previous substitute pattern
      matches (if the difference with search pattern is not clear for
      you, check out [10]my previous article!)

  In addition, for any address, {address}+{number} and {address}-{number}
  are also valid addresses, pointing respectively to the line {number}
  lines after, and the line {number} lines before address (for example,
  .+5 means “5 lines after the current line”).

  There is nothing like a little practice to remember, so let’s try a few
  common commands that use addresses.
This is the first line.

This is the third line.
And a fourth one.

  Given the above file, with the cursor on the first line, :m 3 will move
  it to below the third line:
This is the third line.
This is the first line.
And a fourth one.

  Notice how your cursor also got moved with the line! Now, try :co
  /fourth/:
This is the third line.
This is the first line.
And a fourth one.
This is the first line.

  Once again, your cursor moved. Finally, do :m .-5:
This is the first line.

This is the third line.
This is the first line.
And a fourth one.

  Your cursor was on the line number five, so :m .-5 becomes :m 0, and
  the line is moved to the first line of the buffer.

  All of this is nice, but fairly limited: what if you wanted to move all
  the lines below the second one? For this, and for a lot of more
  advanced operations, you’ll need a range.

A Short History Lesson

  Before we move on to ranges, let’s take a moment to think about why
  addresses came to be defined this way. As some of you might have
  noticed, while '< (first line of the last selected visual area) is a
  valid address, `< (first character of the last selected visual area) is
  not. Why is that so? After all, Vim supports character-oriented marks,
  so why keep addresses line-oriented?

  To understand the origins of this limitation, and many other Vim quirks
  (why would you use :wq to leave?), it is necessary to take a look at
  UNIX history and the evolution of text editors in general. For those of
  you who have no interest in this, you can directly skip to the [11]next
  section.

 The Dark Ages, and the Emergence of Time-sharing Systems

  Back in the 1960’s, computers were enormous, room-filling machines
  without words, images, sounds nor music: just pure number crushing. Of
  course, they were also prohibitively expensive, so only universities,
  government agencies and big corporations could afford them.

  Ranges in Vim /img/ibm-7094.jpeg An IBM 7094 computer, released in
  1962. The magnetic tapes in the background were used for storage!

  This is fairly known. What is not as much however, is that those
  computers could only run one program at a time! Although techniques
  like batch-processing existed to reduce downtimes, this still implied
  that only one user could use the computer at a time. People used to
  write their code on paper tape then give it to an operator that would
  enqueue them on the physical queue of code the computer had to process.
  It could take days, or weeks, before your code would be executed and
  the result returned to you (usually printed).

  Researchers and computer scientists at the time quickly realized this
  could be optimized by sharing resources across multiple users (giving
  CPU time to process A while process B is waiting for disk, for
  instance), which lead to the development of time-sharing systems.
  [12]Project Genie is a great example of such research efforts, funded
  by the US Defense Advanced Research Program Agency and carried by the
  University of Berkeley in 1963-1965. This program ended up pioneering
  many techniques, culminating in the creation of the [13]SDS-940, one of
  the first commercial machines using time-sharing and memory
  paging^[14]1.

  What we are more interested in today however, is that the SDS-940 also
  came with a built-in text editor, called QED, for “Quick EDitor” (to be
  exact, QED was not at its first iteration as it was originally
  developed on the Berkeley Timesharing System, precursor of the
  SDS-940). Let me reproduce here one excerpt of the original [15]QED
  manual:

    [In QED] lines can be referred to by number, although line numbers
    are subject to change if carriage returns are added or deleted
    within the string during the course of editing.

    QED has three modes of operations:
   1. The command mode
   2. The text mode
   3. The line edit mode

  Does that sound familiar? It gets better in the appendix:

    Line Addressing Methods 1. By position (line number). 2. By .
    (symbol for current line). 3. By $ (symbol for last line). 4. By
    :label: (where label is the first word or string of characters in a
    line). 5. By [text] (where text is a unique word or string of
    characters in a line). 6. By adding or substracting lines from any
    of the above addresses, e.g., [text]+2

  Wait. Isn’t that exactly the same as Vim? Almost, but with one big
  difference: regular expressions are not supported. And the missing link
  to explain the similarities between QED and Vim, as well as the usage
  of regular expressions in addresses, is a computer scientist named Ken
  Thompson.

 The Age of Unix: ed, sed and grep

  Having just graduated from the University of California Berkeley with a
  Master’s degree in Electrical Engineering and Computer Science, Ken
  Thompson was hired by Bell Labs in 1966 and promptly started working on
  another time-sharing system called [16]Multics. Along the way, he
  proceeded to reimplement the QED editor he worked on during his time as
  a student for the Multics operating system, adding regular expression
  searching and substitution^[17]2.

  Although Bell Labs pulled out of the Multics project in 1969, Ken
  Thompson and some fellow researchers still felt the need for a
  time-sharing operating system, and started developing their own version
  on a spare [18]PDP-7: this new operating system would end up becoming
  Unix^[19]3. The first things that Thompson implemented on this new
  system were the shell, an assembler… and an editor, called ed.

  ed was yet another take on QED, and while it ditched many features for
  the sake of simplicity, it kept its parent’s core philosophy, at least
  regarding line addressing. It had, amongst other things, a global
  command, similar to the one in current day vim, that allowed to run
  commands like substitutes on all lines in a file. For a while, Bell
  researchers were happy with ed, but it was not long before more
  specific needs arise.

  ed was a heavy beast: it required loading each file one by one in a
  buffer of limited size, meaning that one often had to split a file in
  multiple chunks and load them in succession in order to edit them, a
  very tedious and time-consuming process. The need for a simpler,
  quicker program that simply searches a file for a regular expression
  and prints every line that matches lead Thompson to implement grep in
  1973, short for the g/re/p (global regular expression print), the ed
  command that it emulated^[20]4.

  grep was great, but the demand for g/re/s (global regular expression
  substitution) soon arose, and Lee McMahon, another Unix pioneer working
  on it realized that it was just a matter of time before people would
  ask him for g/re/a, g/re/d, etc. Anticipating that the greX family
  would have no end, he instead chose to develop the utility now know as
  sed.

 The Dawn of Visual Editors: vi and vim

  One thing you have to keep in mind is that early Unix software like ed,
  grep and the likes were written for hardware that had no screens. You
  read that right: the only way to know what you were editing was to
  output the current line on paper, using a teletype printer. With ink.
  This, amongst other things, explain why addressing text in terms of
  characters was not really convenient: most of the time, you could not
  see where in the line you were, and if you changed part of that line,
  the entire line would have to be printed out anyway.

  Ranges in Vim /img/teletype-asr33.jpg The ASR-33 teletype, from the
  1960s. If you were a Unix pioneer, then this was your screen.

  As you would guess, while such a system worked fine for Thompson and
  friends, a lot of people found it hard to use, if not outright hostile
  to the novice^[21]5. As video displays started to appear, a British man
  named George Coulouris wrote an improved version of ed, called em, or
  “editor for mortals”. Unlike ed, em came with a mode allowing users to
  edit a single line in place on screen (pretty similar to Vim, but one
  line at a time).

  In 1976, Coulouris brought his code back to Berkeley, where a graduate
  student named Bill Joy (who later carried on to co-found Sun
  Microsystems in 1982) decided to build upon it. The resulting editor
  became known as ex, for “extended ed”. While largely compatible with
  ed, ex shipped with an “open” mode for line by line editing like em,
  along with a “visual” mode which used the whole screen to enable live
  editing of an entire file like we are used to today.

  vi was added to BSD Unix in May 1979, being a shortcut that took users
  directly into ex’s visual mode. A lot of Vim’s normal and visual modes
  come directly from vi, such as navigation with h, j, k, l, and the use
  of the escape key to exit modes. Oh, and before I forget: the reason
  why h, j, k and l were picked is because Joy used an [22]ADM-3A, which
  had arrows painted on these keys. Perhaps this little fact will help
  hjkl maniacs to be a little more understanding towards people using
  arrow keys :)

  Ranges in Vim /img/adm-3a-keyboard.jpg The ADM-3A keyboard, that Bill
  Joy used to develop vi in the late 1970s.

  vi quickly became enormously popular, but suffered from two main
  limitations: it was a direct descendant of ed, and thus any
  modification required an AT&T license, and it was only available on
  Unix. The first motivated the creation of many vi clones like STEVIE or
  nvi, and the second imitation is what pushed Bram Moolenar to write
  Vim.

  In 1988, Moolenar was working on an Amiga 2000, but was disappointed by
  the fact that it did not include vi. Using STEVIE as a starting point,
  he started creating vim, for “Vi Imitation”, ensuring full
  compatibility with vi before adding new features such as multi-level
  undo. Released in 1993, Vim 2.0 saw the name change to “Vi Improved”,
  and from this point the editor grew at a steady pace, adding support
  for buffers in Vim 3.0, and Vimscript and syntax highlighting in Vim
  5.0. Quickly becoming popular, Vim was ported to many platforms,
  including Unix where it competed with vi for a while before overtaking
  it completely, to the point that vi is now a simple simlink to vim on
  many distributions.

  Computer science history is fascinating, and there would be a lot more
  to talk about, but I’ll keep it to that for now. I personally find it
  pretty cool to think that by using Vim everyday, I am perpetuating a
  tradition of more than 50 years, older than Unix itself! It is also
  satisfying to finally understand why sed commands like sed -i
  's/pat/replacement/' <file> seemed weirdly familiar: they stem from the
  same common ancestor.

  Besides, I am convinced that, if some features like line addressing
  survived for such a long time (in computer science, 50 years is pretty
  much like going back to the Paleolithic), then they were probably good
  ideas.

Ranges

  Now that we’ve got the definition of address out of the way,
  understanding ranges is a lot easier, as it is simply all the lines
  between two addresses (including the first and last line!). In other
  words, true to their ed and ex heritage, ranges only operate on lines.
  Even if you were to select two words out of a line in visual mode then
  press :, the resulting range would still be the entire line!

 Syntax

  There are not one, but two ways to write a range:
    * with a , (example: 1,$ for “first line to end of buffer”)
    * with a ; (example: 1;$ for “first line to end of buffer”)

  Wait, what is the difference? The only thing that changes is where your
  cursor will go. With , it will stay where it was, while ; will make it
  go to whatever address came before it. For example,1;$ will move to the
  address given by 1 (so the first line of the buffer).

  You might think that it does not impact range definition, and that the
  lines included in a range do not change if you use , or ;… In fact, it
  can. Of course, absolute addresses (5, $ and the like) will behave the
  same way, but for addresses defined from the cursor position (such as
  searches and .), the result may be different! The reason for this is
  that with ;, the cursor will be moved during the command, just after
  Vim parses the first address (although it is not visible in the UI
  until when the command completes).

  To understand the implications, consider this file (reminder from the
  previous section: the /pat/ address is defined as “the next line where
  pat matches!):
banana apple
apple banana
apple apricot banana

  Running :/apple/,/apple/s/banana/pear/ with your cursor on the first
  line will do the following:
   1. from the current line (the first one), find the first match on the
      line below (so the second line),
   2. from the current line (the first one), find the first match on the
      line below (so the second line),
   3. perform the replacement on the range 2,2

  However, running :/apple/;/apple/s/banana/pear/ with your cursor on the
  first line will do:
   1. from the current line (the first one), find the first match on the
      line below (so the second line),
   2. move the cursor to that first address (the second line),
   3. from the current line (the second one), find the first match on the
      line below (so the third line),
   4. Perform the replacement on the range 2,3

 Shortcuts

  Manually typing the first and second addresses each time you want to
  create a range can quickly prove annoying. Fortunately, Vim offers
  several shortcuts to make the process smoother:
    * to create a range of one single line, writing one address is enough
      (:5 p is equivalent to :5,5 p),
    * the . is implied when using modifiers such a + or - (so :.,+2 p is
      like :.,.+2 p and prints three lines starting from the current
      one),
    * when in visual mode, pressing : will append '<,'> which means that
      the command will operate on that selection
    * using :* is a shorthand for :'<,'>, which can be convenient to run
      a second command on the same selection without re-selecting it (It
      is like using gv to re-select then :, but with one key less!)
    * pressing any number then : will automatically append .,.+(number -
      1), and the command will affect a total of number lines starting at
      the cursor.

  Do remember however that ex commands can only affect complete lines,
  and visual mode is no exception: as you now should know, '<,'> is the
  range starting at the “first line of the previous selection” and ending
  at the “last line of the last selection”, which means that even if your
  selection only includes one character on line 2 and about half of line
  5, the resulting range will include the full lines 2 and 5!

  I want to end this section with a tip for people who like me who like
  using set relativenumber: because relative lines start at 0, it means
  that to include up to the line number X, I have to remember to type
  (X+1):. That’s annoying, so I’ve remapped : using nnoremap :
  :<C-R>=v:count ==# 0 ? "" : "+1"<CR>, to add a +1 to the second address
  whenever I use a count before : (if you’ve never seen it before, have a
  read at :help c_CTRL-R_=, it’s pretty interesting).

 A few useful range commands

  You probably think, like I used to, that substitute is the range
  command you use the most day to day. In fact, I am pretty sure that it
  is :w. The formal syntax is indeed :[range]w[rite], meaning that you
  can control what part of the file you write (although the default is
  the entire file)!

  There are a lot of other range commands. Some useful examples include:
    * :[range]m[ove] {address} to move text contained in range below
      address, like :,/pat/m $ to move the text from the current line to
      the next line containing pat (inclusive) to the end of the buffer,
    * :[range]co[py] {address} to copy text contained in range below
      address,
    * :[range]d[elete] [x] to delete text contained in range into
      register x, like :1,-1d to delete all lines before the current one,
      and more!

  It is also interesting to note that although most range commands
  default to .,. (current line only) when no range is given, some behave
  differently, like :w defaulting to :%w.

Final Words

  There are more things that you can do with ranges and addresses, such
  as pre-pending an address in front of a search to say “find the first
  line containing this pattern, starting from this address” (/pat1//pat2/
  is in fact the line number of the first line containing pat2 after the
  first line containing pat1), but I’ll leave them for you to explore!
    __________________________________________________________________

  I hope you learned one thing or two with me today! May the hjkl be with
  you, and please shoot me a message or tweet [23]@nicol4s_c if you want
  to chat about any of this, if you spotted any mistakes or typos, or if
  you want to show me other cool vim tricks! Have a great day :)
    __________________________________________________________________

   1. Paul Spinrad and Patti Meagher, [24]“Project Genie: Berkeley’s
      piece of the computer revolution”, archived from the original on
      July 19, 2011, and accessed on February 6, 2020. [25]^[return]
   2. Tom Van Vleck, [26]“Glossary of Multics acronyms and terms”,
      accessed on February 7, 2020. [27]^[return]
   3. Ritchie, Dennis M. [28]“The Evolution of the Unix Time-sharing
      System”, archived from the original on April 3, 2017, and accessed
      on February 7, 2020. [29]^[return]
   4. Michael and Ronda Hauben, “Netizens: On the History and Impact of
      Usenet and the Internet”, [30]“Chapter 9 - On the Early History and
      Impact of UNIX: Tools to Build the Tools for a New Millennium”,
      accessed on February 5, 2020. [31]^[return]
   5. Donald A. Norman, [32]“The truth about Unix: The user interface is
      horrid”, accessed on February 7, 2020. [33]^[return]

  [34]Older

 See Also

    * [35]Vim Substitute Tricks

  Author Avatar

  Nicolas Couvrat

  Hi! I am a software engineer at Playstation, Japan. I mostly talk about
  backend stuff, golang and vim. I also like cooking. My views are my
  own.

    *
    *
    *
    *

  © Nikodoko 2019 - 2020 | [36]RSS

References

  Visible links
  1. https://nikodoko.com/posts/vim-ranges/#main
  2. https://nikodoko.com/
  3. https://nikodoko.com/tags/vimexplorer
  4. https://nikodoko.com/tags/vim
  5. https://nikodoko.com/tags/vimscript
  6. https://nikodoko.com/tags/tricks
  7. https://nikodoko.com/posts/vim-substitute-tricks
  8. https://www.reddit.com/r/vim/comments/epig4f/learn_about_the_darker_corners_of_substitute/
  9. https://nikodoko.com/tags/vimexplorer
 10. https://nikodoko.com/posts/vim-substitute-tricks
 11. https://nikodoko.com/posts/vim-ranges/#ranges
 12. https://en.wikipedia.org/wiki/Project_Genie
 13. https://en.wikipedia.org/wiki/SDS_940
 14. https://nikodoko.com/posts/vim-ranges/#fn:1
 15. https://archive.org/stream/bitsavers_sds9xx9409an69_1668444/901112B_940_QED_RefMan_Jan69#page/n17/mode/1up
 16. https://en.wikipedia.org/wiki/Multics
 17. https://nikodoko.com/posts/vim-ranges/#fn:2
 18. https://en.wikipedia.org/wiki/PDP-7
 19. https://nikodoko.com/posts/vim-ranges/#fn:3
 20. https://nikodoko.com/posts/vim-ranges/#fn:4
 21. https://nikodoko.com/posts/vim-ranges/#fn:5
 22. https://en.wikipedia.org/wiki/ADM-3A
 23. https://twitter.com/@nicol4s_c
 24. https://web.archive.org/web/20110719144454/http://coe.berkeley.edu/news-center/publications/forefront/archive/forefront-fall-2007/features/berkeley2019s-piece-of-the-computer-revolution
 25. https://nikodoko.com/posts/vim-ranges/#fnref:1
 26. https://multicians.org/mgq.html
 27. https://nikodoko.com/posts/vim-ranges/#fnref:2
 28. https://www.bell-labs.com/usr/dmr/www/hist.pdf
 29. https://nikodoko.com/posts/vim-ranges/#fnref:3
 30. http://www.columbia.edu/~rh120/ch106.x09
 31. https://nikodoko.com/posts/vim-ranges/#fnref:4
 32. http://www.ceri.memphis.edu/people/smalley/ESCI7205_misc_files/The_truth_about_Unix_cleaned.pdf
 33. https://nikodoko.com/posts/vim-ranges/#fnref:5
 34. https://nikodoko.com/posts/vim-substitute-tricks/
 35. https://nikodoko.com/posts/vim-substitute-tricks/
 36. https://nikodoko.com/index.xml

  Hidden links:
 38. https://medium.com/@nicol4s_c
 39. https://twitter.com/@nicol4s_c
 40. https://github.com/nicolascouvrat
 41. https://linkedin.com/in/nicolascouvrat