Reorder A List Of Numbers in Vim
Tags: vim, neovim
Published: 2020-03-28 23:39:00

---
title: Reorder A List Of Numbers in Vim
published: true
description: Avoid macros when it's possible
tags: vim, neovim
---

## 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;

### 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;

### The solution
 
    gg
    :let @r=0 | g/./ let @r=@r+1 | exec "norm t;ciw\<c-r>r"

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"

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](https://vi.stackexchange.com/a/22516/7339) to a similar problem and how amazing it can be:

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

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

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](http://deliberate-software.com/vim-refactoring-patterns/) 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
+ https://vi.stackexchange.com/a/22516/7339

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

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

Solution:

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

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

## Som tips from vim wiki
+ https://vim.fandom.com/wiki/Making_a_list_of_numbers

```viml
    " 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
```

Select the function with `yip`, then run

    :@0

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

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.

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

Basically:

     nnoremap <expr> "what evaluate" == "with what" ? true : false

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