Vim: Math on vim substitution
Tags: vim, regex
Published: 2019-12-30 19:18:45

---
title: Vim: Math on vim substitution
published: true
description: How to use vim submatch()
tags: vim, regex
---

## Target content
I stumbled upon and downloaded an interesting python tutorial on youtube and it brings on its annotations something like:

    1. Variables (2:47)
    2. Functions (8:50)
    3. Calendar (14:10)
    4. If Else Statements (37:17)

I have created a README.md file with this annotations, but what happens is that the youtube video link itself uses a sort of sum-up of the time in seconds, which means that **2 minutes and 47 seconds** are in reality **167 seconds** and I needed to convert it to create links to its parts.

Using vim skills let's see how to convert the time properly.

## Introducing our vim friend `submatch()`
When using **Regular expressions** we can use groups like this

    /\v^(word) (another)

The above search matches "word another" using "very magic" option and also create two regex groups. Now we can reuse the last search like this to exchange words position:

    :%s//\2 \1

`\2` matches the second group and `\1` matches the first one. Now we can use our friend `submatch()`, which uses these groups to perform actions like mathematics.

The only problem is that we already have parenthesis in our file, we just need to protect them this way:

    /\v\((pattern)\)

The `\(` in very magick means a real parenthesis and a non scaped one `(` means start of a regex group.

Actually, if you don't use very magic search you kind of have to invert the use of parentheses. With very magic regex groups are not protected and normal parentheses are if we do not use very magic the logic is the oposite.

## The substitution command
The below substitution only substitues minutes+seconds to seconds, it does not creates markdown links as I wanted

        :'<,'>s/\v\((\d+):(\d+)\)$/\=submatch(1)*60 + submatch(2)

We used a selection to group the lines that have only "minutes + seconds", that's why `'<,'>`.

    \v ............... very magic enabled
    \( ............... literal parenthesis
    ( ................ start of the first regex group
    \d+ .............. at least one digit
    : ................ literal colon
    \) ............... closing literal parenthesis
    $ ................ end of line
    \= ............... expression register
    submatch(1) ...... the first regex group

## Building the final link (markdown)
The video link is https://www.youtube.com/watch?v=4F2m91eKmts and I have saved it on the `a` register plus `&ts=` to indicate the video part in seconds:


    :let @a='https://www.youtube.com/watch?v=4F2m91eKmts&ts='

### the search pattern

    /\v\. ([^(]*)\((\d+):(\d+)\)

    \v ....................... very magic search
    \. ....................... literal dot
    ([^(]*) .................. a regex group that matches everything but opening parenthesis
    \( ....................... a literal parenthesis
    (\d+) ..................... a digit or more as a regex group
    : ......................... literal colon

To be able to creat a markdown link we have to group our `submatch()` so then we can combine submatches with the strings 
we are going to use in our links. The markdown links are like: `[link name](url)`.

    :'<,'>s//\=(' ['. submatch(1) . '('. (submatch(2)*60 + submatch(3)). ']')
    :'<,'>s//\=(' ['. submatch(1) . '('. (submatch(2)*60 + submatch(3)). ')](' . @a . ')')

    '<,'> ................... selected area
    // ...................... uses last search
    \=( ..................... here we open our big grouping pattern

The part were we combine minutes plus seconds is between parenthesis

## How old they are
Let's say you have these lines:

    Stallman   Richard   GNU    1953  USA
    Wall       Larry     Perl   1954  USA
    Moolenar   Bram      Vim    1961  Netherlands
    Tridgell   Andrew    Samba  1967  Australia
    Matsumoto  Yukihiro  Ruby   1965  Japan
    Ritchie    Dennis    C      1941  USA
    Thompson   Ken       Unix   1943  USA

And you want to know how old each one of these guys is right now

    :%s,\d\+,\=strftime("%Y")-submatch(0)
    :'<,'>s/\v(\d+)/\=("Age: ".(strftime("%Y")-submatch(1)))/

In the second example we had to group all into parentesis `\=()` so then we can conatenate the string with the number

Notice: If you want to combine "strings" with numbers you can group the math parth and use "." to concatenate them.

## Just increasing and decreasing numbers

Let's say you have these lines:

    On this line I have 1 and nothing more
    On this line I have 1 and nothing more
    On this line I have 1 and nothing more
    On this line I have 1 and nothing more
    On this line I have 1 and nothing more
    On this line I have 1 and nothing more

And you want change them to this:

    On this line I have 1 and nothing more
    On this line I have 2 and nothing more
    On this line I have 3 and nothing more
    On this line I have 4 and nothing more
    On this line I have 5 and nothing more
    On this line I have 6 and nothing more

The simple solution is:

Jump to the first 1 on on the second line:

    jf1

Start visual block selection with Ctrl-v and go down 4 lines
with:

    4j

Now just press:

    g Ctrl-a