14

I know I can use Awk, but I am on a Windows box, and I am making a function for others that may not have Awk. I also know I can write a C program, but I would love not to have something that requires compilation and maintenance for a little Vim utility I am making.

The original file might be:

THE DAY WAS LONG 
THE WAY WAS FAST

and after the transposition, it should become:

TT
HH
EE

DW
AA
YY

WW
AA
SS

LF
OA
NS
GT

Update

ib.
  • 27,830
  • 11
  • 80
  • 100
ojblass
  • 21,146
  • 22
  • 83
  • 132
  • 2
    Lowest number of characters... lowest score wins kind of thing. – ojblass Apr 01 '09 at 12:45
  • It would have been even better if the solution transposed only the selected text... But, okay, the current solution is good enough for me. :) – Denilson Sá Maia May 15 '10 at 16:15
  • The script in [my answer below](https://stackoverflow.com/a/7320629/254635) seems to be the shortest Vim-only solution in the “golf” sense—that you have declared as the deciding factor for selecting between correct answers—among those posted so far, is it not? – ib. Aug 01 '20 at 03:04

6 Answers6

12

Here is a command in Vim language. So you don't have to compile Vim with +python support.

function! s:transpose()
    let maxcol = 0
    let lines = getline(1, line('$'))

    for line in lines
        let len = len(line)
        if len > maxcol 
            let maxcol = len
        endif
    endfor

    let newlines = []
    for col in range(0, maxcol - 1)
        let newline = ''
        for line in lines
            let line_with_extra_spaces = printf('%-'.maxcol.'s', line)
            let newline .= line_with_extra_spaces[col]
        endfor
        call add(newlines, newline)
    endfor

    1,$"_d
    call setline(1, newlines)
endfunction

command! TransposeBuffer call s:transpose()

Put this in newly created .vim file inside vim/plugin dir or put this to your [._]vimrc.
Execute :TransposeBuffer to transpose current buffer

Mykola Golubyev
  • 57,943
  • 15
  • 89
  • 102
  • Pure vim gets you the nod. Thank you so much! – ojblass Apr 02 '09 at 06:28
  • An unnecessarily wordy solution, in my opinion. By the way, technically speaking your solution does not work, since `1,$"_d` does not do what you probably want it to do. I would write that line as `sil %d_`. – ib. Sep 10 '11 at 14:02
  • @ib why you say it doesn't? It works for me and it delete the whole file into anonymous register, does it not? – Mykola Golubyev Sep 13 '11 at 07:16
  • No, it does not: Open a nonempty buffer and run the Ex command you propose: `:1,$"_d`—nothing will be deleted. You are trying to use the Ex command `:delete` the same way as the Normal mode command `d`. However, both syntax and semantics of those commands are different! The `:delete` command accepts an optional range before the command name, and an optional register name *after* (without `"`). What you have written is the range `1,$` followed by a the *commented* text `_d` (see `:quote`). Thus, the only effect your command has is moving the cursor to the last line of the current buffer. – ib. Mar 13 '12 at 02:37
10

Vim support for a number of scripting languages built in -- see the Python interface as an example.

Just modify vim.current.buffer appropriately and you're set.

To be a little more specific:

function! Rotate()
python <<EOF
import vim, itertools
max_len = max((len(n) for n in vim.current.buffer))

vim.current.buffer[:] = [
    ''.join(n) for n in itertools.izip(
        *( n + ' ' * (max_len - len(n))
           for n in vim.current.buffer))]
EOF
endfunction
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    ++ oooh - was unaware that vim supported more than vimscript! Also perl, tcl, and ruby -- awesome! – guns Apr 01 '09 at 06:29
  • 1
    It supports those scripts but not by default. You have to turn them on during build. – Mykola Golubyev Apr 01 '09 at 08:37
  • Right. Just about every distribution has a vim-full or vim-enhanced version, and I tested what I posted here against the Windows gvim – Charles Duffy Apr 01 '09 at 16:10
  • Crazy Kudos I am going to wait to see if any golf answer like four key strokes and your done win out. I wonder what assumptions I should be allowed to make about the vim bulid itself to still be considered portable. – ojblass Apr 02 '09 at 00:20
  • You still have to have python2X.dll somewhere on your computer for this to work. Python itself is not embedded in Vim. – George V. Reilly Apr 02 '09 at 05:47
  • @CharlesDuffy: Just to simplify your code, you can use `max(map(len, vim.current.buffer))` instead of `max((len(n) for n in vim.current.buffer))`, I believe. – Tadeck Dec 26 '11 at 20:49
5

If scripts don't do it for you, you could record the actions to a register (the carriage returns are added for readability):

qa
1G0
xGo<Esc>p
1G0j
xGp
q

This will give you a macro that you could run against the example above, or any 2-line strings of the same length. You only need to know the length of the string so you can iterate the operation the correct number of time

16@a

A fairly basic solution, but it works.

seanhodges
  • 17,426
  • 15
  • 71
  • 93
1

The following function performs the required editing operations to “transpose“ the contents of the current buffer, regardless of the number of lines in it.

fu!T()
let[m,n,@s]=[0,line('$'),"lDG:pu\r``j@s"]
g/^/let m=max([m,col('$')])
exe'%norm!'.m."A \e".m.'|D'
sil1norm!@s
exe'%norm!'.n.'gJ'
endf

For the sake of “golfing”, here is its one-line version:

let[m,n,@s]=[0,line('$'),"lDG:pu\r``j@s"]|exe'g/^/let m=max([m,col("$")])'|exe'%norm!'.m."A \e".m.'|D'|exe'sil1norm!@s'|exe'%norm!'.n.'gJ'
ib.
  • 27,830
  • 11
  • 80
  • 100
0

I've developed a vim plugin to do it. You can find it here. Run :Transpose to transpose the whole file.

Benoit
  • 76,634
  • 23
  • 210
  • 236
0

Charles Duffy's code could be shortened/improved using izip_longest instead of izip:

function! Rotate()
    :py import vim, itertools
    :py vim.current.buffer[:] = [''.join(c) for c in itertools.izip_longest(*vim.current.buffer, fillvalue=" ")]
endfunction
Community
  • 1
  • 1
etuardu
  • 5,066
  • 3
  • 46
  • 58