1

Vim removes zeros from in front of some digits when decrementing:

If I take a text file with the following:

a02 
a03
a04
a05
a06
a07
a08
a09  
a10
a11

And use ctrl+V to highlight the second and third columns, and then hit ctrl+X to decrement, I am left with:

a01
a02
a03
a04
a05
a06
a7
a8
a9
a10

I am running Vim version 7.4.1689 and I loaded it without my .vimrc via

$ vim -u NONE
jmlarson
  • 837
  • 8
  • 31

2 Answers2

7

This is happening because Vim will automatically recognize and convert octal values.

From the help (:h variables):

Conversion from a Number to a String is by making the ASCII representation of the Number.

Examples:

   Number 123      -->     String "123"
   Number 0        -->     String "0"
   Number -1       -->     String "-1"

Conversion from a String to a Number is done by converting the first digits to a number. Hexadecimal "0xf9", Octal "017", and Binary "0b10" numbers are recognized. If the String doesn't start with digits, the result is zero.

Examples:

   String "456"    -->     Number 456
   String "6bar"   -->     Number 6
   String "foo"    -->     Number 0
   String "0xf1"   -->     Number 241
   String "0100"   -->     Number 64
   String "0b101"  -->     Number 5
   String "-8"     -->     Number -8
   String "+8"     -->     Number 0

Your values 02 through 07 are being recognized as valid octal values and preserved as such, decremented to octal 01 through 06.

When you reach 08 it is not a valid octal value. It is treated as the string 08, converted to decimal value 8, and decremented to 7. This happens again with 09, which ends up being 8.

The 10 and 11 values are decremented as decimal as well. Because 10 was decimal, not octal, you don't get a leading 0 in the resulting 9 value.

I'm not aware of a way to do what you want with the decrement command.

EDIT: After finding this answer, I tested this expression and it does what you are trying to do in this specific case:

:%s/\v[0-9]+/\=printf("%02d", substitute(submatch(0), '^0\+', '', 0)-1)/

I'm not sure whether this solves your general use case, because it's quite different from the original operation using a selection. But for the file you provided, it achieves the result you were after.

Dissecting this a bit to explain it:

First we start by calling the global sub command %s and passing the \v flag to turn on "very magic" mode. This may or may not change the behavior depending on your settings, but this is a public example, so it is included here to ensure that mode is active.

:%s/\v

Then, we find all the contiguous sequences of digits. This will find 02, 03, and so on from your example.

[0-9]+

Then in the replacement portion we have this command, which does the real work:

\=printf("%02d", substitute(submatch(0), '^0\+', '', 0)-1)

The substitute() function determines what the new value is. submatch(0) means to use the entire match. Using a pattern of ^0\+ and a replacement of (empty string) says to strip the leading zero from any number which has one. The 0 at the end isn't too important; it just says there are no flags to the substitute() function.

The result of the substitute command is a number. Say 02 has been stripped down to be 2. Using the - 1 at the end, we subtract 1 from that result (decrement).

Finally, this result is passed to the printf function. Using a format string %02d says to print the values as decimal, in 2-digit wide format, padding with leading zeroes.

Community
  • 1
  • 1
Dan Lowe
  • 51,713
  • 20
  • 123
  • 112
4

If you want Vim to treat all numbers as decimals, you may want to add the following line to your .vimrc:

set nrformats=
Leandro Mineti
  • 196
  • 1
  • 1
  • 8
  • Or if it's a one-off case like for the OP, execute command `:set nrformats=` before using `Ctrl+X` rather than changing it forever with the .vimrc. – Micah Lindstrom Feb 03 '22 at 14:59