···11+" Vim transposition plugin.
22+" Plugin author: Benoit Mortgat
33+" Main git repository: http://github.com/salsifis/vim-transpose
44+55+" this script should be pure ASCII but just in case
66+scriptencoding utf-8
77+88+" == Main function == {{{
99+" This function does the work, all other functions call it.
1010+"
1111+" * first_line , last_line | range of lines on which to operate
1212+" * isp | Input separator pattern (tokenizes input lines)
1313+" | This is a Vim pattern.
1414+" * ofs | Output field separator
1515+" * dfv | Default field value when transposed data has not
1616+" | The same number of fields on each line
1717+function transpose#t(first_line, last_line, isp, ofs, dfv)
1818+ let whole_buffer = (a:first_line <= 1 && a:last_line == line('$')) ? 1 : 0
1919+2020+ let cursor_was_at_first_line = (line('.') == a:first_line ? 1 : 0)
2121+ let cursor_was_at_last_line = (line('.') == a:last_line ? 1 : 0)
2222+ let old_mode = mode()
2323+2424+ let input_lines = getline(a:first_line, a:last_line)
2525+2626+ let indentation = 0
2727+ if exists('g:transpose_keepindent') && g:transpose_keepindent > 0
2828+ " find the first non-space on each line; the indentation is the
2929+ " lowest value found. TODO: accept tabs
3030+ let indentation = min(map(copy(input_lines), 'match(v:val,"[^ ]")'))
3131+ if indentation > 0
3232+ " de-indent each input line
3333+ call map(input_lines, 'v:val['.indentation.':]')
3434+ endif
3535+ endif
3636+3737+ " Split each line into fields according to the input pattern separator
3838+ let tokenized_lines = map(input_lines, 'len(v:val)?split(v:val, a:isp, 1):[]')
3939+4040+ " Delete input lines from buffer, to the black hole register
4141+ silent! execute a:first_line . ',' . a:last_line . 'd _'
4242+4343+ " There are as many lines in output as number of fields in the line that has
4444+ " the greatest amount of fields
4545+ let nb_output_lines = max(map(copy(tokenized_lines), 'len(v:val)'))
4646+4747+ " For each output line (line i) take i^th field of each input line. If it does
4848+ " not exist, take default value.
4949+ for i in range(0, nb_output_lines - 1)
5050+ call append( a:first_line - 1 + i
5151+ \ , repeat(' ', indentation)
5252+ \ . join( map(copy(tokenized_lines)
5353+ \ ,'(i < len(v:val)) ? v:val[i] : a:dfv'
5454+ \ )
5555+ \ , a:ofs
5656+ \ )
5757+ \ )
5858+ endfor
5959+6060+ " If the whole buffer was transposed, there was a moment after deleting
6161+ " all input lines, where there was an empty line. Now time to remove it.
6262+ if(whole_buffer)
6363+ execute '$d _'
6464+ else
6565+ execute '' . a:first_line . 'mark <'
6666+ execute '' . (a:first_line + nb_output_lines - 1) . 'mark >'
6767+ endif
6868+6969+ if cursor_was_at_first_line == 1
7070+ execute a:first_line
7171+ elseif cursor_was_at_last_line == 1
7272+ execute '' . (a:first_line + nb_output_lines - 1)
7373+ endif
7474+7575+ if old_mode ==? 'v'
7676+ execute 'normal gv'
7777+ endif
7878+7979+endfunction " }}}
8080+8181+" == Specialized function: Array of characters == {{{
8282+function transpose#block(first_line, last_line)
8383+ " Input separator pattern: any zero-width match between two chars => .\zs\ze.
8484+ " Output separator: empty
8585+ " Default field value: a space.
8686+ call transpose#t(a:first_line, a:last_line, '.\zs\ze.', '', ' ')
8787+endfunction " }}}
8888+8989+" == Specialized function: Words (separated by whitespace) {{{
9090+function transpose#words(first_line, last_line, ...)
9191+ " If an argument is provided, this is the default field value
9292+ if a:0 > 1
9393+ throw 'Only one additional argument allowed (default field value)'
9494+ endif
9595+ let dfv = (a:0 > 0) ? a:1 : '?'
9696+ " Input separator pattern: whitespace
9797+ " Output separator: one space
9898+ " Default field value: nonempty.
9999+ call transpose#t(a:first_line, a:last_line, '\s\+', ' ', dfv)
100100+endfunction " }}}
101101+102102+" == Specialized function: Tabs (separated by tabulation character) {{{
103103+function transpose#tab(first_line, last_line)
104104+ " Input separator pattern: single tab
105105+ " Output separator: tab
106106+ " Default field value: empty
107107+ call transpose#t(a:first_line, a:last_line, "\x09", "\x09", '')
108108+endfunction " }}}
109109+110110+" == Specialized function: Separated fields == {{{
111111+" There are two special sequences of characters in the input:
112112+" * The separator (typically semicolon)
113113+" * The delimiter; when fields contain the separator their contents can be
114114+" surrounded with two delimiters. If they must contain the delimiter itself,
115115+" it has to be doubled.
116116+" If the delimiter is empty, then no field can contain the separator.
117117+"
118118+" Example: Separator and delimiter being resp ; and '
119119+" 'a;b';c;'d;e''f'
120120+" Contains three fields : a;b | c | d;e'f
121121+function transpose#delimited(first_line, last_line, separator, delimiter)
122122+ let separator = escape(a:separator, '\*[].^$')
123123+ let delimiter = escape(a:delimiter, '\*[].^$')
124124+125125+ " Vim pattern that matches a field:
126126+ " Either:
127127+ " * It starts with the delimiter
128128+ " * It globs as many non-delimiters and double-delimiters as it can
129129+ " * It ends with the delimiter
130130+ " * There is no delimiter afterwards
131131+ " Or:
132132+ " * It consists of characters that are neither the delimiter nor the
133133+ " separator
134134+ "
135135+ " And there is always a separator afterwards.
136136+ "
137137+ " We put \zs in the pattern to only match the separator.
138138+139139+ if delimiter !=# ''
140140+ let isp = '\%('
141141+ \ . delimiter
142142+ \ . '\%([^' . delimiter . ']\|' . delimiter . delimiter . '\)*'
143143+ \ . delimiter . '\%(' . delimiter . '\)\@!'
144144+ \ . '\|[^' . separator.delimiter . ']*\)\zs' . separator
145145+ else
146146+ let isp = separator
147147+ endif
148148+ let ofs = a:separator
149149+ let placeholder = ''
150150+ call transpose#t(a:first_line, a:last_line, isp, ofs, placeholder)
151151+endfunction " }}}
152152+153153+" == Specialized function: CSV input == {{{
154154+" Calls the previous function, with variable number of arguments. Number of
155155+" additional arguments shall not exceed 2.
156156+" 1st argument: separator, default is semicolon
157157+" 2nd argument: delimiter, default is none.
158158+function transpose#csv(first_line, last_line, ...)
159159+ if a:0 > 2
160160+ throw 'Optional arguments: separator, delimiter. Only 2 allowed.'
161161+ endif
162162+ let separator = (a:0 > 0) ? a:1 : (exists('g:transpose_csv_default_separator') ? g:transpose_csv_default_separator : ';')
163163+ let delimiter = (a:0 > 1) ? a:2 : ''
164164+ call transpose#delimited(a:first_line, a:last_line, separator, delimiter)
165165+endfunction " }}}
166166+167167+" == Interactive way == {{{
168168+" Asks for parameters
169169+function transpose#interactive(first_line, last_line)
170170+ let isp = input('What Vim pattern separates input fields? ')
171171+ let ofs = input('What string will join fields in output? ')
172172+ let dfv = input('What is default field value? ')
173173+ call transpose#t(a:first_line, a:last_line, isp, ofs, dfv)
174174+endfunction " }}}
175175+176176+" vim: ts=2 sw=2 et tw=80 colorcolumn=+1 fdm=marker fmr={{{,}}}
+152
.vim/doc/transpose.txt
···11+*transpose.txt* For Vim version 7.3 Last change: 2015 Jul 1
22+33+Plugin author: Benoit Mortgat
44+ *transpose-tutorial*
55+ *transpose*
66+Welcome to the transpose plugin tutorial.
77+88+Note~
99+For your convenience, tab characters appear as > throughout this tutorial, and
1010+trailing spaces as *, except if you don't have `:set modeline` in your vimrc.
1111+1212+First, ensure that |g:transpose_keepindent| is equal to 1. This should be the
1313+case if you did not mess with the plugin settings.
1414+1515+===============================================================================
1616+Basic transposition~
1717+ *:Transpose*
1818+1919+Visually select the following lines (V2j) and use the |:Transpose| command
2020+(you will see :'<,'>Transpose in the command bar)
2121+Then, Use :Transpose another time on the result.
2222+>
2323+ abcd
2424+ efghij
2525+ klmno
2626+<
2727+As you see, the :Transpose command takes an array of characters, and swaps
2828+lines with columns. When you run :Transpose a second time, you will get the
2929+original text back, except that extra spaces will have been inserted, which
3030+is because :Transpose needs to work on rectangular data
3131+3232+===============================================================================
3333+Word transposition~
3434+ *:TransposeWords*
3535+3636+Visually select the following lines (V3j) and use the |:TransposeWords|
3737+command.
3838+Then, Use :TransposeWords another time on the result.
3939+>
4040+ Eeny meenie miny moe
4141+ Catch a tiger by the toe
4242+ If he hollers let him go
4343+ Eeny meenie miny moe
4444+<
4545+You can see that this command will insert interrogation marks when an input
4646+line was not long enough and there is a missing field. If it did not, the
4747+command would not be invertible.
4848+4949+However you can provide an additional argument to :TransposeWords to set that
5050+default value and override the interrogation mark. Since this command can be
5151+chained with another command, you must escape vertical bars with backslashes
5252+if you need them in the default value.
5353+5454+===============================================================================
5555+Tab-separated fields transposition~
5656+ *:TransposeTab*
5757+5858+Visually select the following lines (V3j) and use the |:TransposeTab| command.
5959+Then, Use :TransposeTab another time on the result.
6060+>
6161+ Eeny meenie miny moe
6262+ Catch a tiger by the toe
6363+ If he hollers let him go
6464+ Eeny meenie miny moe
6565+<
6666+This is similar to :TransposeWords, but any tabulation is a separator.
6767+Multiple tabs are considered as many separators. Therefore, there is no need
6868+for a default value.
6969+7070+===============================================================================
7171+Separated fields transposition~
7272+ *:TransposeCSV*
7373+7474+Visually select the following lines (V2j) and use the |:TransposeCSV| command.
7575+Then, Use :TransposeCSV another time on the result.
7676+>
7777+ ab;cd
7878+ e;f;gh;ij
7979+ klm;no
8080+<
8181+This is like the tab-separated case, but the separator is by default the
8282+semicolon. However you can specify the separator of your choice in a first
8383+argument. You can Use :TransposeCSV \ instead of :TransposeTab but as you
8484+can see it is necessary to escape the tabulation character, which is messy.
8585+8686+Visually select the following lines (V2j) and type >
8787+ :TransposeCSV ,
8888+<and another time to switch back:
8989+>
9090+ ab,cd
9191+ e,f,gh,ij
9292+ klm,no
9393+<
9494+You can also provide a second argument, which is the delimiter. In this case:
9595+ - A field will be delimited when the first character in the field is the
9696+ delimiter.
9797+ - When a field is delimited, it must end with the delimiter
9898+ - When a field is delimited, it can include the separator
9999+ - When a field is delimited, it can include the delimiter, escaped by itself
100100+ (like SQL string literals).
101101+102102+Try to transpose this CSV separated by tab and delimited by apostrophe: you
103103+will have to escape the tab with a backslash in the command line.
104104+:TransposeCSV \ '
105105+>
106106+ 'I''m a field with a tab' I'm a field without tab
107107+ No tab here 'There is a tab here' A third field
108108+<
109109+Since this command can be chained with another command, you must escape
110110+vertical bars with backslashes if you need them in the delimiter or separator.
111111+112112+ *g:transpose_csv_default_separator*
113113+114114+You can override the default separator. If you want it to be a comma by
115115+default, put the following line into your vimrc:
116116+>
117117+ let g:transpose_csv_default_separator = ','
118118+<
119119+===============================================================================
120120+Interactive transposition~
121121+ *:TransposeInteractive*
122122+123123+Assume this input
124124+>
125125+ A b c
126126+ D e f
127127+ ghi
128128+<
129129+You want this output:
130130+>
131131+ A,D,
132132+ b,e,ghi
133133+ c,f,xxx
134134+<
135135+It is possible! Basically, your input fields are separated by one or more
136136+tabs, you want to have a table with commas in the output, and nonexistent
137137+fields (the third input line had only two fields) will come as xxx
138138+139139+Select the input lines and use |:TransposeInteractive|, you will be prompted
140140+for:
141141+ - A vim pattern that will be used to separate input fields: type >
142142+143143+ \t\+
144144+<
145145+ - An output separator, type a comma
146146+ - A default field value, type xxx
147147+148148+That's all.
149149+150150+===============================================================================
151151+152152+vim: readonly noexpandtab ft=help modifiable list listchars=tab\:>\ ,trail\:*