DEV Community

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

Posted on

4 1

Reorder A List Of Numbers in Vim

Let's say you have this (consider a Huge file with a pattern like this)

required string query = 1;
optional int32 pagenumber = 32;
optional optional result_per_page = 93;
optional Corpus corpus = 4;
required string url = 5;
Enter fullscreen mode Exit fullscreen mode

And you want something like this

required string query = 1;
optional int32 pagenumber = 2;
optional optional result_per_page = 3;
optional Corpus corpus = 4;
required string url = 5;
Enter fullscreen mode Exit fullscreen mode

The solution

gg
:let @r=0 | g/./ let @r=@r+1 | exec "norm t;ciw\<c-r>r"
Enter fullscreen mode Exit fullscreen mode

We start by moving to the first line, after that we create a variable to store the number sequence we want.

We have a regex to match any character (which means it will match on all lines) /./ and we increment the register "r" let @r=@r+1.

The normal command does the following

exec ................ execute the following
"norm ............... in normal mode
t; .................. jump til before ;
ciw ................. change inner word
\<c-r>r ............. now we insert the register "r"
Enter fullscreen mode Exit fullscreen mode

In the "normal" part we use double quotes that allow us to use keystrokes like \<c-r>, which means Ctrl-r.

I stumbled upon a solution to a similar problem and how amazing it can be:

:let c=0 | g/\d\+\ze;$/ let c+=1 | s//\=c
Enter fullscreen mode Exit fullscreen mode

In this case, we really need to specify the location of the numbers!

Another approach:

:let @a=1 | %s/\d\+\ze;$/\=(@a+setreg('a',@a+1))/g
Enter fullscreen mode Exit fullscreen mode

In this case, we exchange the number /\d\+\ze;$/ for register a and increase the register a for the next match.

OBS: The idea for this article came from this post and possible I will change it to include more examples. in the original article, the author shows how to solve this problem using macros.

An elegant solution

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

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

Solution:

:let c=0 | g/^\d\+\ze\./ let c+=1 |s//\=c
Enter fullscreen mode Exit fullscreen mode

Explanation:

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

Som tips from vim wiki

    " Add argument (can be negative, default 1) to global variable i.
    " Return value of i before the change.
    function! Inc(...)
        let result = g:i
        let g:i += a:0 > 0 ? a:1 : 1
        return result
    endfunction
Enter fullscreen mode Exit fullscreen mode

Select the function with yip, then run

:@0

:let i=1 | %s/\d\+\ze;$/\=Inc()
Enter fullscreen mode Exit fullscreen mode

The option g:i creates a "global" variable i. The a: stands for function argument.

let g:i += a:0 > 0 ? a:1 : 1

If the first argument `a:0` is bigger than zero use the second argument `a:1`, otherwise use one.
Enter fullscreen mode Exit fullscreen mode

TIP: Understanding vimscript ternary operator

Let's start by creating a normal map to jump to a certain line if the user types a number before hitting Enter.

" 6 Enter goes to the line 6
nnoremap <expr> <cr> v:count == 0 ? "\<cr>" : "G"

nnoremap ............. normal map that does not allow recursion
<expr> ............... It means we are gonna use an expression 
v:count .............. The count given for the last Normal mode command
== ................... compares things
Enter fullscreen mode Exit fullscreen mode

Basically:

 nnoremap <expr> "what evaluate" == "with what" ? true : false
Enter fullscreen mode Exit fullscreen mode

If our evaluation is true it runs "true" otherwise it runs "false"

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay