hhh
hhh

Reputation: 52820

Vim: Columnvise Increment inside and outside?

By outside, I want solutions that does not use Vim's scripting hacks but try to reuse certain basic *ix tools. Inside Vim stuff asks for solutions to get the column-increment with inside stuff such as scripting.

1           1 
1           2 
1           3 
1   --->    4
1           5
1           6
.           .
.           .

Vim has a script that does column-vise incrementing, VisIncr. It has gathered about 50/50 ups and down, perhaps tasting a bit reinventing-the-wheel. How do you column-increment stuff in Vim without using such script? Then the other question is, how do you column-increment stuff without/outside Vim?

Most elegant, reusable and preferably-small wins the race!

Upvotes: 1

Views: 1466

Answers (3)

ib.
ib.

Reputation: 28944

The optimal choice of a technique highly depends on the actual circumstances of the transformation. There are at least two points variations affecting implementation:

  1. Whether the lines to operate on are the only ones in a file? If not, is the range of lines defined by context (i.e. it separated by blank lines, like a paragraph) or is it arbitrary and should be specified by user?

  2. Are those lines already contain numbers that should be changed or is it necessary to insert new ones leaving the text on the lines in tact?

Since there is no information to answer these questions, below we will try to construct a flexible solution.

A general solution is a substitution operating on the beginnings of the lines in the range specified by the user. Visual mode is probably the simplest way of selecting an arbitrary range of lines, so we assume here that boundaries of the range are defined by the visual selection.

:'<,'>s/^\d\+/\=line(".")-line("''")+1/

If it is necessary to number every line in a buffer, the command can be simplified as follows.

:%s/^\d\+/\=line('.')/

In any case, if the number should be merely inserted at the beginnings of the lines (without modifying the ones that already exist), one can change the pattern from ^\d\+ to ^, and optionally add a separator:

:'<,'>s/^\d\+/\=(line(".")-line("''")+1).' '/

or

:%s/^/\=line('.').' '/

respectively.

For a solution based on command-line tools, one can consider using stream editors like Sed or text extraction and reporting tools like AWK.

To number each of the lines in a file using Sed, run the commands

$ sed = filename | sed 'N;s/\n/ /'

In order to do the same in AWK, use the command

$ awk '{print NR " " $0}' filename

which could be easily modfied to limit numbering to a particular range of lines satisfying a certain condition. For example, the following command numbers the lines two through eight.

$ awk '{print (2<=NR && NR<=8 ? ++n " " : "") $0}' filename

Having an interest in how commands similar to those from the script linked in the question statement are implemented, one can use the following command as a reference.

vnoremap <leader>i :call EnumVisualBlock()<cr>
function! EnumVisualBlock() range
    if visualmode() != "\<c-v>"
        return
    endif
    let [l, r] = [virtcol("'<"), virtcol("'>")]
    let [l, r] = [min([l, r]), max([l, r])]
    let start = matchstr(getline("'<"), '^\d\+', col("'<")-1)
    let off = start - line("'<")
    let w = max(map([start, line("'>") + off], 'len("".v:val)'))
    exe "'<,'>" 's/\%'.l.'v.*\%<'.(r+1).'v./'.
    \   '\=printf("%'.w.'d",line(".")+off).repeat(" ",r-l+1-w)'
endfunction

Upvotes: 3

kev
kev

Reputation: 161634

If you want change 1 1 1 1 ... to 1 2 3 4 .... (Those numbers should be on different lines.)

:let i=1 | g/1/s//\=i/g | let i+=1

If some of 1 1 1 1 ... are in the same line:

:let g:i = 0

:func! Inc()
:    let g:i+=1
:    return g:i
:endfun

:%s/1/\=Inc()/g

Upvotes: 1

Rook
Rook

Reputation: 62538

I don't see a need for a script, a simple macro would do

 "a   yyp^Ayy

then play it, or map to play it.

Of course, there is always the possibility that I misunderstood the question entirely...

Upvotes: 3

Related Questions