4

I have a file like the following : two different files were yanked one after another, and the numbers subsequently don't match anymore.

                ...   46
atom   142   option   47
atom   143   option   48       
atom    12   option    7  # There's a discontinuity in the numbers at this line
atom    13   option    8
atom    14   option    9
atom    ...

I would like to take the columns of numbers using visual-selection, and add the missing constant (in my example, for the first column, adding 132 to the last three lines would be perfect).

I don't know if regexp allow mathematical operations (otherwise I could try a macro using Ctrl-X / Ctrl-A, but it won't use visual selection anymore and I'd like to keep it).

(The expected result would be something like:)

                ...   46
atom   142   option   47
atom   143   option   48       
atom   144   option    7  # Discontinuity in the first numbers column is gone !
atom   145   option    8
atom   146   option    9
atom    ...
  • 3
    As of Vim 7.4.754+ you can use <c-a>/<c-x> in visual mode. See :h v_CTRL-A – Peter Rincker Jun 15 '16 at 16:07
  • According to vim --version I only have Vim 7.2, and I cant update that (I do not have admin rights). – Feffe Jun 15 '16 at 16:11
  • 2
    The easy way: install the VisIncr plugin, select the column from the last good number down, and run :II. The harder way: if the first bad number is on line 73, do something like this: :73,$s/\d\+/\=submatch(0)+132/, then fix the align. – Sato Katsura Jun 15 '16 at 16:52
  • 5
    I only have Vim 7.2, and I cant update that - The last release of Vim 7.2 was 7.2.433, in 2010. Motion to fire your sysadmin. :) – Sato Katsura Jun 15 '16 at 17:00
9

Visually select all the lines you want to increment, and do the following:

:s/\d\+/\=submatch(0) + 132

Does exactly what you describe. Visually selects a bunch of numbers, and adds a mathematical constant to each of them. If you leave of the /g flag, it will only increment the first number on each line. This uses the "expression register". To learn more, :help "= has much more useful info.

Since you mentioned trying a macro, here's what you were probably missing from your macro. Add gv to the beginning of your macro. This reselects your last visual selection. For example:

qqgv<C-a>q132@q

(Note, I don't know if this works in 7.2, and since I don't have it installed I can't test it)

7

As of Vim 7.4.754+ you can use <c-a>/<c-x> in visual mode. See :h v_CTRL-A.

However since you can not upgrade Vim you may want to look into speeddating.vim which does some visual incrementing.

There are other plugins that might work as well:

Otherwise you need to use a macro or use visual mode and then use :normal.

:norm 132<c-v><c-a>

For more help see:

:h :norm
:h v_CTRL-A
  • Beware that <c-a>/<c-x> in visual mode took a while to actually become usable. They used to segfault and behaved in unexpected ways in their early days. Just FYI. – Sato Katsura Jun 16 '16 at 18:22
  • @SatoKatsura, would you recommend version 7.4.1634+ instead? I am reading the patches from here. – Peter Rincker Jun 16 '16 at 18:31
  • I haven't paid much attention to this particular point, but there were several patches posted by several people affecting it. 7.4.1634 was indeed relevant, but I don't know if it was the last one. Vim 8+ should be a safer test soon. :) – Sato Katsura Jun 16 '16 at 19:02
4

edit : answer posted before knowing OP had no support for the visual increment.


Here is a possible solution:

  1. Select the last good number, 143 and yank it.
  2. In visual-block (C-V), select the column under the 143 you just yanked (where you want to update the numbers).
  3. p will past 143 over the entire selection.
  4. Select again the pasted 143 column.
  5. gC-A will increment gradually the selection, giving you overall increment.
2

A possible solution is to yank the number before and after the sequential break and let vim do the math for you.

vnoremap <C-a> :call CalculatedIncrement(@p, @o)<CR>

function! CalculatedIncrement(first, second)
   if (a:first > a:second)
      exe 'normal! '. (a:first-a:second+1) .'^A'
   else
      exe 'normal! '. (a:second-a:first+1) .'^X'
   endif
endfunction

Note that you can't just copy and paste this. You'll need to re-input ^A and ^X yourself with <C-v>+<C-A>, etc.

To use this, yank the first number into buffer p: "pyiw, and the second number into o: "oyiw. Then just visually select the lines you want to change and press Ctrl+a.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.