4

Say you have some text like this:

## countries, primary
United States,Washington,North America
Canada,Ottawa,North America
Australia,Canberra,Oceania
## countries, secondary
France,Paris,Europe
Japan,Tokyo,Asia
United Kingdom,London,Europe

And you want to filter only the non-comment lines through an external program, say column -ts,, to align the comma-separated fields. In other words, the expected output is:

## countries, primary
United States   Washington  North America
Canada          Ottawa      North America
Australia       Canberra    Oceania
## countries, secondary
France          Paris       Europe
Japan           Tokyo       Asia
United Kingdom  London      Europe

Is it possible to do this in vim with a single command?

I thought maybe combining :g with :range! would work, but it doesn't:

g!/^\s*#/!column -ts,

The above just runs :!column -ts, once for each line that matched the :g command. In other words, it doesn't actually filter anything, it just runs a system command with stdin connected to the terminal, once for each matching line.

So, the general question is, how can we filter discontinuous line sets through an external program, passing all the lines as one block to the stdin of the external program, and returning the output lines back to their corresponding line numbers in the buffer?

  • 3
    No, :g works line by line. You can however filter the range through sed '/^#/d' | columns -ts,. – Sato Katsura Aug 23 '16 at 6:08
  • 1
    You may like tabularize plugin – SibiCoder Aug 23 '16 at 14:05
2

If you want to have the table spacing inferred using the whole file, you could delete the headlines, run column on the resulting file and reinsert the headlines using something like this:

function! Column()
  let b:headlines={}
  g/^##.*$/let b:headlines[line(".")+len(b:headlines)]=getline(".")|d
  %!column -ts,
  call map(b:headlines, 'append(v:key-1, v:val)')
endfunction

However, if you want each block between to headlines to be a separate table (possibly with different tabstops than the others), you can use

g/^\s*##.*\n\_.*\n\s*##/ .+1,/^\s*##/-1!column -ts,

This will work for all but the last block, which can be selected using visual line mode and treated using :'<,'>!column -ts,.

1

A simple way is to remove commas from comment lines before filtering, then replace them afterwards:

:g/^##/ s/,/COMMA/ | s/^/,,/
:%!column -ts,
:g/^\s*##/ s/\s*// | s/COMMA/,/

You also need to prepend comment lines with commas if you don't want them to affect the alignment; you may find that a bit hacky. It's also not a single command, but why does that need to be a requirement?

1

What I suggest is to save the result of the g into a register. You should use append.

that can be done by

:g/pattern/A

Afterwards, save the register to a file

 :new file | $put a | x 

From here, you are free.

you can also try to run columns directly, but I wasn't really successful.

:execute "!columns ,ts " . @a

maybe with a proper escaping, it would work.

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.