80

Is there a way to generate a number sequence in vi or Vim?

For example, for an arbitrary range of lines i  through j (where i < j) in a file opened in Vim, is there a way to generate a number sequence from number 1 on line i all the way through number (j − i + 1) on line j?

Say, I have the following lines in a file:

this is line #1
this is line #2
this is line #3
this is line #4
this is line #5
this is line #6
this is line #7
this is line #8
this is line #9
this is line #10

I want to prefix the number sequence from line 4 to line 8 with numbers 1 through 5. After the operation, the resulting file should be as follows:

this is line #1
this is line #2
this is line #3
1 this is line #4
2 this is line #5
3 this is line #6
4 this is line #7
5 this is line #8
this is line #9
this is line #10

If this is possible, is there a way to use different step sizes for the generated sequence? For example, can 2 be used for the step size instead, so that the resulting sequence is 2, 4, 6, 8, etc.?

Note: The question “How to add line numbers to range of lines in Vim?” brings up a similar problem, but it is not the same.

ib.
  • 27,830
  • 11
  • 80
  • 100
Sangeeth Saravanaraj
  • 16,027
  • 21
  • 69
  • 98
  • This plugin is not a complete solution for your problem, but it's tremendously useful for adding columns of numbers: [VisIncr](http://vim.sourceforge.net/scripts/script.php?script_id=670). Docs [here](http://www.drchip.org/astronaut/vim/doc/visincr.txt.html). FWIW. – lcd047 Apr 30 '15 at 06:21

11 Answers11

127

Starting with Vim 7.4.754 one can use g Ctrl-a, see :help v_g_CTRL-A

Go to line #4, use Ctrl-v to blockwise select the first character, go down 4 lines, press Shift i, enter 0 (this is 0, followed by Space) and Esc to exit insert mode.

Now use gv to re-select the previously selected area. Press g Ctrl-a to create a sequence.

I start with a 0 here, so I can re-select by gv. If you start with a 1, you need to re-select by hand while omitting the first 1.

Use 2g Ctrl-a to use a step count of 2.


screen capture demonstrating how to generate a number sequence

rkta
  • 3,959
  • 7
  • 25
  • 37
64

Select several lines with V(Shift-v), then type command bellow:

:let i=1 | '<,'>g/^/ s//\=i . " "/ | let i+=2

Type :help sub-replace-expression to read more.

kev
  • 155,172
  • 47
  • 273
  • 272
  • 2
    Thanks for this! vim is so powerful!! Try this: `:let i=1 | let j=1 | '<,'>g/^/ s//\=i/ | let c=i+j | let j=i | let i=c ` – while Oct 03 '13 at 13:03
  • 1
    Can anyone explain this? – Jin Jan 29 '15 at 02:01
  • @Jin Check this post for the g http://stackoverflow.com/questions/28403622/how-g-makes-loop-in-vim-ex-command-script Page me if you need any further hint. – George Feb 09 '15 at 07:06
  • 1
    What about if I want the prefix number to be zero-padded? – hippietrail Apr 29 '15 at 01:05
  • 1
    When I try this on Vim 7.4 on Windows 7 I get `E481: No range allowed` – hippietrail Apr 29 '15 at 01:06
  • To bind this to a key, create a function first. Don't for get the `range` keyword (like I did) to only apply it once to all lines: `function! NumberedList() range` `:let i=1 | '<,'>g/^/ s//\=i . ". "/ | let i+=1` `endfunction` And then bind it to a key in visual mode `vnoremap nl :call NumberedList()`. I added a period after each numeral to create a numbered list. – joelostblom Sep 23 '16 at 13:57
  • @hippietrail in visual mode Vim will automatically prepend the next command with the visual range `'<,'>` when you hit `:`, which is not what you want in this case. Type `CTRL-U` after `:` to clear the command line and then type the command again. – gbs Dec 02 '16 at 13:41
33

Instead of a complicated construct you could simply use a macro with the CTRL-a function to increment a leading number. Example data:

aaa
bbb
ccc

first insert a start number and a space:

1 aaa
bbb
ccc

then record this macro on line 1 (<C-a> means press CTRL-a):

qq0yf 0j0P0<C-a>q

Explanation:

  1. qq: record macro into register q
  2. 0: go to first column.
  3. yf: yank all until and including the first space (remember your first line has 1 and a space).
  4. 0jP: go down and paste the pattern at the start of the line.
  5. 0<C-a>: go to first column and increment number by one.
  6. q: end macro recording.

this gives:

1 aaa
2 bbb
ccc

now you can apply this macro using @q as long as you want. If you need an increase of two just use CTRL-aCTRL-a instead of just once. Now you could apply this macro to consecutive lines, for example:

:.,$norm @q

will add leading line numbers for the rest of your file.

hochl
  • 12,524
  • 10
  • 53
  • 87
  • If I understand correctly, you are copying the previous number and incrementing it. Does it work if the number has more digits? Is there a reason for the space between `f` and `0`? – Tamás Szelei Mar 28 '12 at 08:43
  • Yes, you understand this correctly. The space is important since you copy anything including the space with `f`, so you copy more digits. So yes, it works for numbers with more than one digit, just tried it until line 1449 in a file :) Of course you can use any other separation char, `tab` comes to mind. – hochl Mar 28 '12 at 08:47
  • Ah yes. For some reason I assumed 0 meant "number", so f would have moved to the first number. I think this is the best solution here. – Tamás Szelei Mar 28 '12 at 08:54
  • 1
    Oh well the one-liners are good too, but it's usually more tedious to think those up than to just record a macro. I like macros for their ease of use and flexibility. – hochl Mar 28 '12 at 08:59
  • 1
    @hochl: It is not any more tedious to think in terms of Ex commands than Normal mode commands. If one of these kinds of commands is tedious for you to use, it means that your Vim experience is not balanced well between those. – ib. Mar 28 '12 at 10:53
  • 2
    I often add `zz` in my macro to see the what is coming next. It's hard when the cursor is at the bottom of the screen. – Luc M Jul 30 '15 at 17:50
12
:4,8s/^/\=line(".")-3." "    

will do what you want

if you need count=2:

:4,8s/^/\=2*(line(".")-3)." " 

this will give you 2,4,6,8,10

line numbers are hard coded in my example, you could use V to select those lines you want to change.

Kent
  • 189,393
  • 32
  • 233
  • 301
5

Here's a dirty trick but then life is composed of these. :)

ESC :r! for i in $(seq 1 10); do echo "This is line \#${i}"; done

Not cross platform.

Noufal Ibrahim
  • 71,383
  • 13
  • 135
  • 169
2

I think all the proposed solutions are too difficult to remember, you can use it once but then you need to go into all details every time you use it (if you use it less than once a day or so).

I found the visual incrementing script really easy to install and use. Once it is installed in vim, you just need to generate a column of 0's, select it with Ctrl-V and write the command :I. It will then automatically generate increasing numbers on each line. There are also other features:

  • start with a number different from 0
  • left or right pad numbers with 0's (like 001, ..., 015)
  • decreasing or increasing numbers
  • increase by more than 1
  • dates (but you need an additional plugin), letters of the alphabet, daynames

This solves a more general problem because it works also at a position different from column 0, the column has just to be selectable with Ctrl-V.

The vimball of the plugin is here or here.

Gismo Ranas
  • 6,043
  • 3
  • 27
  • 39
2

Select the target lines in Visual mode, then run the Ex command

:'<,'>s/^/\=(line('.')-line("'<")+1).' '
ib.
  • 27,830
  • 11
  • 80
  • 100
1

(if your vim has Perl support -- default in many Linux Distributions): Select the lines in visual mode (V) and do

:perldo s/^/++$z . " "/e

or

:4,8 perldo s/^/++$z . " "/e
JJoao
  • 4,891
  • 1
  • 18
  • 20
1

You can also use nl, cat -n etc.

  1. Select lines using V in normal mode Shift-v.
  2. Switch to command mode :
  3. In the prompt :<,> type !nl or something fancier like !nl -s .\

Personally I can never remember the approach of the top voted answer and always end up comming back here

Any of these approaches would work

CervEd
  • 3,306
  • 28
  • 25
  • nice answer. btw how to append the number to the end of line? this approach seems to prepend the number at the begin of a line. – Liu Weibo Mar 01 '23 at 08:16
  • 1
    @LiuWeibo you could pipe it into something like awk to reorder the line number and the line. But then maybe a pure awk solution is better. Like https://unix.stackexchange.com/a/222219/355088 – CervEd Mar 01 '23 at 11:29
1

The Nexus plugin provides the Series type and an object, s1, of that type used like this:

:4,8s/^/\=s1.next().' '/

Nexus also comes with an s0 Series object that yields 0 as its first .next() result. Both s0 and s1 use a 1-step increment. All Series objects have a .reset() method which sets them back to their initiated value. New Series objects can be created like the following call:

let s2 = Series(0, 2)

which creates a 2-step object meeting your second request (yielding: 2, 4, 6, 8, etc.).

ib.
  • 27,830
  • 11
  • 80
  • 100
dahu
  • 329
  • 1
  • 2
-1

A less flexible, but an easy to remember method is to use a renumbering plugin like Renumber.vim http://www.vim.org/scripts/script.php?script_id=189

If there aren't any numbers yet, like in the OP, some number should be inserted in their place. Renumber can handle the actual ordering and it does it based on just the first number.

In this example I'm using <C-v> to insert the starting number on all the lines you want numbered.

4G<C-v>4jGI1 <Esc>gv:Renumber

To use steps of two

:Renumber s2

Heikki Naski
  • 2,610
  • 1
  • 21
  • 13