DEV Community

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

Posted on

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"

Top comments (0)