17

I have a markdown file like this:

Irish Bands:
1. U2
2. The Cranberries
3. The Dubliners
English Bands:
1. Queen
2. Duran Duran
3. The Beatles
American Bands:
1. Nirvana
2. Blondie
3. The Doors

I would like to modify it to look something like this:

Irish Bands:
1. U2
2. The Cranberries
3. The Dubliners
English Bands:
4. Queen
5. Duran Duran
6. The Beatles
American Bands:
7. Nirvana
8. Blondie
9. The Doors

What would be the best way to go about making a change like this? The actual file is a lot bigger

0

4 Answers 4

21

Another simple way to accomplish this is to use the g CTRL-A command in Visual mode, which increments all numbers by successively higher counts on each line that starts with a number. (See :help v_g_CTRL-A.)

In order to be able to use it, start by replacing all numbers with zeroes:

:%s/^\d\+\./0./

Then visually select everything:

ggVG

And finally increment them using g<C-A>.

The first numbered line will become 1. the second 2., the third 3. and so on, which matches what you requested.

I'd say one advantage of this approach is that it doesn't require any plug-ins and will work in any Vim. Additionally, it builds on simple commands (a very simple substitution, visual mode and a very handy increment command that can be super helpful in many situations!), rather than using heavier Vimscript and Vim variables to accomplish the task.


One problem with this approach is if you have categories that contain numbers, such as 80's Bands or Bands from the 90's, in which case g CTRL-A will increment those numbers too. (It acts on the first number it finds on a line of text.) So such categories need to be avoided, or preprocessed in a way that will prevent the command from acting on them.

As pointed out by @Rich, using Visual Block selection can help avoid the problem with the latter form (Bands from the 90's), by doing a visual block selection of the first few columns instead of a linewise selection of the entire file.

So, in the middle step you could use something like gg, followed by CTRL-V (for visual block mode), G and then 3l to move three characters right, selecting the four first columns and incrementing numbers appearing on those columns only.

2
  • 4
    Worth noting that you can avoid the problem with Bands from the 90's (but not 80's Bands) by doing a visual block selection of the first few columns instead of a linewise selection of the entire file.
    – Rich
    Jan 12, 2020 at 23:04
  • 1
    @rainman_s Glad you liked the answer! I'll edit it to incorporate Rich's recommendation. g is a fairly known command prefix with many commands "stacked" behind it. For example, gg to go to top, gU to turn uppercase, ga to display info on character under cursor, gt to go to next tab... There are only so many keys, so some keys are used to stack more commands behind them. z is another such example.
    – filbranden
    Jan 13, 2020 at 13:39
11

Try this:

let c=0|g/^\d\+\ze\./let c+=1|s//\=c
├─────┘ ├───────────┘├──────┘ ├─┘├─┘
│       │            │        │  └ with the current value of the counter (i.e. new number)
│       │            │        └ replace last used pattern (i.e. old number)
│       │            └ for every matched line, increment the counter
│       └ iterate over the lines starting with a number followed by a dot
└ initialize counter to 0

See:

:h :g
:h :s
:h :s\=
:h /^
:h /\d
:h /\+
:h /\ze
2

Here's a couple of different (but related) methods, both of which perform the edits using regular normal mode editing commands that yank each line's number and then paste and increment it on the next line:

Using :global and :normal

First, populate the unnamed register with a 0 by typing i0Escx.

Then run the following command:

:g/^\d/norm!viwp^Ayiw

                ^^

This works by running a series of normal mode commands on each line that matches the regular expression ^\d i.e. has a number at the start of the line:

  • viw Visually select the first word on the line.
  • p Paste the contents of the unnamed register. On the first matching line, this is 0.
  • Ctrl-A Increment the number you just pasted.
  • yiw Yank the incremented number, ready to paste (and then increment) on the next matching line.

Note that the ^A highlighted above denotes a literal Ctrl-A character, which you enter by typing Ctrl-VCtrl-A. An alternative, copy-pastable method of entering the same command is:

:g/^\d/exe "norm!viwp\<C-A>yiw"

Using a recursive macro

First switch off 'wrapscan', so that searches won't wrap around from the end to the start of the file:

:setl nows

Then type the following:

/^\dEnterqqqqqyiwnviwpCtrl-A@qq@q

Broken down, these commands do the following:

  1. /^\dEnter Search for a number at the start of the line. Presuming your cursor begins at the start of the file, this will be a 1.
  2. qqq Clear out the "q register by starting a macro recording and immediately finishing it. This will be necessary later on.
  3. qq Start recording a macro in register "q.
  4. yiw Yank the number the cursor is currently on.
  5. n Jump to the next number.
  6. viwp Select and replace the current number with the one we yanked in step 4.
  7. Ctrl-A Increment it.
  8. @q Playback the macro in the "q register. Because we emptied it in step 2, this step does nothing right now, but when we run the macro, it will cause the macro to repeat.
  9. q End the recording.
  10. @q Playback the recursive macro.
0

As usual, not the vim-way but fast.
use vim-visual-multi (https://github.com/mg979/vim-visual-multi) . \\/^\d*\. followed by \\A will mark all numbers (if I am not mistaken). Then simply delete all of them with d. Now use \\n to replace them with numbers using 1/1/.. See https://github.com/mg979/vim-visual-multi/wiki/Special-commands

Your Answer

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

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