DEV Community

Sérgio Araújo
Sérgio Araújo

Posted on • Edited on

Vim: Math on vim substitution

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)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

\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)\)
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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='
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)))/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

The simple solution is:

Jump to the first 1 on on the second line:

jf1
Enter fullscreen mode Exit fullscreen mode

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

4j
Enter fullscreen mode Exit fullscreen mode

Now just press:

g Ctrl-a
Enter fullscreen mode Exit fullscreen mode

Top comments (2)

Collapse
 
filbranden profile image
Filipe Brandenburger

Great tips overall! Very nice article. It has most of my favorite math tricks in Vim.

One important caveat to watch for is that Vim will interpret numbers like 047 as octal (so that's actually 39 rather than 47.) In your particular examples, that wouldn't matter, because you only have two digit seconds there, and 01 through 07 will behave the same whether octal or decimal, and Vim will interpret 08 and 09 as decimal. So it all looks good... But when you start having three-digit numbers with leading zeroes, that's something to watch for...

I love the g Ctrl-A trick in block visual mode! (Or, more accurately, I just love block visual mode! So cool!)

Thanks for the article again. Looking forward to reading more Vim articles from you.

Collapse
 
voyeg3r profile image
Sérgio Araújo • Edited

Hi, Filipe!

I have found a great article I backed up some time ago about "how amazing vim regexes are" and I think you will like reading it:
bitbucket.org/snippets/sergio/7nzqxx

I suggest increasing the font size for better reading.