How to generate a number sequence in file using vi or Vim?
Asked Answered
R

11

85

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.

Roasting answered 28/3, 2012 at 8:23 Comment(1)
This plugin is not a complete solution for your problem, but it's tremendously useful for adding columns of numbers: VisIncr. Docs here. FWIW.Dereism
C
65

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.

Cark answered 28/3, 2012 at 8:29 Comment(7)
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 Teatime
Can anyone explain this?Borlow
@Borlow Check this post for the g #28404122 Page me if you need any further hint.Unexperienced
What about if I want the prefix number to be zero-padded?Gillyflower
When I try this on Vim 7.4 on Windows 7 I get E481: No range allowedGillyflower
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 <Leader>nl :call NumberedList()<CR>. I added a period after each numeral to create a numbered list.Forefinger
@Gillyflower 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.Zaidazailer
K
140

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

Kimberlite answered 23/1, 2018 at 17:55 Comment(5)
Wonderful. After 20 years of using Vim, I am still amazed by its capabilities.Airwoman
Did you use a plugin or some trick to get the text of each key press to show up when you made that animated gif?Reactance
Tool is called screenkey and can be found on gitlab.Kimberlite
It did not work for me either on gvim or vim. Here is a solution that worked for me vim.fandom.com/wiki/Increasing_or_decreasing_numbers. E.g. qa Y p Ctrl-A q 5@aPeppy
@Peppy Interesting, could you share the output of vim --version?Kimberlite
C
65

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.

Cark answered 28/3, 2012 at 8:29 Comment(7)
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 Teatime
Can anyone explain this?Borlow
@Borlow Check this post for the g #28404122 Page me if you need any further hint.Unexperienced
What about if I want the prefix number to be zero-padded?Gillyflower
When I try this on Vim 7.4 on Windows 7 I get E481: No range allowedGillyflower
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 <Leader>nl :call NumberedList()<CR>. I added a period after each numeral to create a numbered list.Forefinger
@Gillyflower 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.Zaidazailer
T
34

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.

Triplicate answered 28/3, 2012 at 8:32 Comment(6)
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?Hadria
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.Triplicate
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.Hadria
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.Triplicate
@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.Asterism
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.Hallelujah
H
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.

Hersh answered 28/3, 2012 at 8:36 Comment(1)
This is approach is simple, versatile, and self explanatory.Snelling
U
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.

Uproot answered 28/3, 2012 at 8:29 Comment(0)
A
2

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

:'<,'>s/^/\=(line('.')-line("'<")+1).' '
Asterism answered 28/3, 2012 at 8:28 Comment(0)
Q
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.

Queue answered 15/1, 2014 at 13:47 Comment(0)
A
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.).

Amberjack answered 28/3, 2012 at 11:31 Comment(0)
F
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
Fireproof answered 23/1, 2018 at 12:41 Comment(0)
V
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

Vashti answered 31/1, 2023 at 17:13 Comment(2)
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.Slugabed
@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 unix.stackexchange.com/a/222219/355088Vashti
J
-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

Jameejamel answered 28/3, 2012 at 10:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.