I've been annoyed by this for as long as I can remember. Your question prompted me to scratch this itch.
This script remaps
<c-x> to perform custom incrementing/decrementing, but only when the number is preceded by a keyword character and a dash, or just preceded by a period. If those conditions aren't met, the default
<c-x> keys are used.
In your case with
<c-a> on the
1 would make it
<c-x> would make it
0, and pressing
<c-x> on the
0 would leave it at
0. With the script also recognizing numbers preceded with a period, adjusting floating point numbers will work as expected, too. That would also apply to IP addresses (except for the first octet since it doesn't meet the conditions mentioned above).
The limitation is that this won't work in visual mode. I guess it's also worth mentioning that it updates the
. register, since the default keys don't.
I've added comments to the script to make it a learning experience.
function! s:incr(dir) abort
" Search backwards from the cursor for digits preceded by a keyword
" character and a dash/period, restricted to the current line. Characters
" that come after the cursor must only be digits.
" This is slightly different from how <c-a> and <c-x> works since they
" search forward by default.
" If there are no matches, return the default incr/decr key.
if search('\%(\%(\k-\)\|\.\)\@<=\d\+\&\%#\d*\>', 'cbW', line('.')) == 0
return (a:dir > 0 ? "\<c-a>" : "\<c-x>")
" Get the current number under the cursor.
" Because of the search() call above, the cursor should now be placed at
" the beginning of the number allowing us to use <cword> expansion.
let n = expand('<cword>') + 0
" If the number is already zero and we are subtracting, return nothing to
" avoid creating an undo point.
if n == 0 && a:dir < 0
" Clear pending mode if we started with a count
let seq = "\<esc>"
" The cursor will reset once the function exits. We retain it by using a
" column jump.
let seq .= col('.') . '|'
" Begin a change, throwing the <cword> into the black hole register.
let seq .= '"_cw'
" Incr or decr the number by v:count1, without going negative, then type it.
let seq .= max([0, n + (v:count1 * a:dir)])
" Press escape
return seq . "\<esc>"
nnoremap <expr><silent> <c-a> <sid>incr(1)
nnoremap <expr><silent> <c-x> <sid>incr(-1)