Search code examples
vimstatusbar

VIM line count in status bar with thousands separator?


Is it possible to display the line count in the VIM status bar with thousands separators, preferably custom thousands separators?

Example:

set statusline=%L

should lead to "1,234,567" instead of "1234567".


Solution

  • I've found a way but it looks a bit crazy:

    set statusline=%{substitute(line('$')\,'\\d\\zs\\ze\\%(\\d\\d\\d\\)\\+$'\,'\,'\,'g')}
    

    The first round of backslashes is just for set (I have to escape , and \ itself).

    What I'm actually setting the option to is this string:

    %{substitute(line('$'),'\d\zs\ze\%(\d\d\d\)\+$',',','g')}
    

    As a format string, this line contains one formatting code, which is %{...}. Everything in ... is evaluated as an expression and the result substituted back in.

    The expression I'm evaluating is (spaces added (if I had added them to the real code, I would've had to escape them for set again, forcing yet more backslashes)):

    substitute(line('$'), '\d\zs\ze\%(\d\d\d\)\+$', ',', 'g')
    

    This is a call to the substitute function. The arguments are the source string, the regex, the replacement string, and a list of flags.

    The string we're starting with is line('$'). This call returns the number of lines in the current buffer (or rather the number of the last line in the buffer). This is what %L normally shows.

    The search pattern we're looking for is \d(\d\d\d)+$ (special vim craziness removed), i.e. a digit followed by 1 or more groups of 3 digits, followed by the end of the string. Grouping is spelled \%( \) in vim, and "1 or more" is \+, which gives us \d\%(\d\d\d\)\+$. The last bit of magic is \zs\ze. \zs sets the start of the matched string; \ze sets the end. This works as if everything before \zs were a look-behind pattern and everything after \ze were a look-ahead pattern.

    What this amounts to is: We're looking for every position in the source string that is preceded by a digit and followed by exactly N digits (where N is a multiple of 3). This works like starting at the right and going left, skipping 3 digits each time. These are the positions where we need to insert a comma.

    That's what the replacement string is: ',' (a comma). Because we're matching a string of length 0, we're effectively inserting into the source string (by replacing '' with ',').

    Finally, the g flag says to do this with all matches, not just the first one.

    TL;DR:

    • line('$') gives us the number of lines
    • substitute(..., '\d\zs\ze\%(\d\d\d\)\+$', ',', 'g') adds commas where we want them
    • %{ } lets us embed arbitrary expressions into statusline