2

I'm looking for an approach, in Vimscript, to operate on the text lines of a buffer by looping through lines of a visual selection.

Specifically, my goal is very simple: I have a (vertical) list of words, which I want to convert into a table; the first word is in the first column, the second is the first word in the second column; the third word is the second word in the first column, the fourth is the second word in the second column; and so on.

So, for the vertical list of letters below, I want to achieve this (in verbatim):

a --> 1. a b
b     2. c d
c     3. e f
d
e
f

My initial thought was to find a way to loop over the lines of a visual selection, after which I apply a macro depending on the whether the current linenumber is even or odd. The iterator variable of the loop can then be used to insert the row numbers.

How does one loop through the lines of a visual selection and operate on the current line, through a Vimscript function?

2
  • On Linux paste could probably do most of the table-formatting work.
    – D. Ben Knoble
    Jul 2, 2018 at 22:09
  • @D.BenKnoble I am currently working on a Windows 10 machine (with ConEMU, as a terminal multiplexer; using git-Bash as my shell). :) Jul 3, 2018 at 9:43

2 Answers 2

4

That would be a pretty straightforward function. I'll cover key parts of it but leave things like validation and error handling to you. For instance, there could be a check for Visual mode not being active or for getline() not returning any lines.

func! VisualToTable()
    let l:out = ""
    let l:idx = 1

    " '< and '> mark begin and end lines of most recent visually selected text.
    " Using those we get text from visual selection and iterate over the lines.
    for l:line in getline(line("'<"), line("'>"))
        if l:idx % 2 == 0  " even numbered lines to column 2
            let l:out .= "\t" . l:line . "\n"
        else
            let l:row = l:idx / 2 + 1
            let l:out .= l:row . ". " . l:line
        endif

        let l:idx += 1
    endfor

    if l:idx % 2 == 0
        " If last col num was 1 we need a closing newline
        let l:out .= "\n"
    endif

    return l:out
endfunc

This implementation is just building the table up in a string and returning it. The basic call is just :echo VisualToTable() .. after you've selected some text. That'll print the table.

You could put it in a register with :let @x = VisToTable() and then paste wherever you want with "xp. If you want the function to actually place the text in the buffer that's something I'll also leave to you.

1
  • 1
    very nice solution! :)
    – Kes
    Jun 10, 2021 at 23:47
2

You can easily join the lines with :g[lobal]/^/join. If you also want to prepend line number, a substitution of the beginning of each line with a sub-replace-expression calling line('.') should work. Something like:

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

This assumes that you want to opreate on all lines in the buffer. If you have other lines you can run global and substitute over a range, for example by visually selecting the lines before issuing the commands (and omitting % from the substitution).

If you want a table of three columns, pass a count to join:

   :g/^/join 3
2
  • You may want to use :g/./join: Your command g//join will use the last search pattern which may or may not be what you want.
    – statox
    Jul 2, 2018 at 11:31
  • 1
    Oops that was a naughty slip. Thanks @statox!
    – jjaderberg
    Jul 2, 2018 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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