Search code examples
vimpositionselectionblock

Column position visual selection line by line


example:

text téxt text |text| text
text text text |text| text
text téxt téxt |text| text
text téxt text |text| téxt

I'm selecting a block of text '|text|' of above lines

What I want to know is the column position of the startcolumn and endcolumn of every single line.

If I use:

echo col('"<') --> 17  
echo col('">') --> 20  

it gives me the startcolumn of the 1st line of the block and the endcolumn of the last line of the block.

However there are multibytes characters before my block selection in every line. I want to know col('"<') and col('">') of every single line:

17, 20
16, 19
18, 21
17, 20

How can I obtain these?


Solution

  • We start with the begin and end columns of the visual (blockwise) selection. These are screen columns, i.e. counting screen cells. You want to translate them into byte indexes, for each covered line.

    So, we need a way to translate screen columns into byte indexes. If we use the cursor, there are convenient functions to position it on a particular screen column (the [N]| command), and functions to query the byte index (col()). So, the overall algorithm transforms the screen columns of the start and end of the selection (marks '< and '>, respectively), obtained via virtcol(), into the byte index by positioning the cursor.

    Here's one way to implement this:

    :'<,'>global/^/
    \   let positions = [] |
    \   for mark in ['<', '>'] |
    \       exe 'normal!' virtcol("'" . mark) . '|' |
    \       call add(positions, col('.')) |
    \   endfor |
    \   echo positions
    [17, 20]
    [16, 19]
    [18, 21]
    [17, 20]
    

    The start and end screen columns of the selection can be obtained through virtcol(). We can then position the cursor on the start and then the end position, and obtain the byte index via col('.').

    For simplicity, I here used :global to iterate over all selected lines, and put the columns into a list.