diff --git a/autoload/tabular.vim b/autoload/tabular.vim index f60a73c..e7af6fd 100644 --- a/autoload/tabular.vim +++ b/autoload/tabular.vim @@ -71,6 +71,18 @@ else endfunction endif +function! s:TabOffset(position, string) + let offset = 0 + let sections = split(a:string, "\t", 1) + for index in range(len(sections)) + let offset += s:Strlen(sections[index]) + if index+1 < len(sections) + let offset += &tabstop - ((a:position + offset) % &tabstop) + endif + endfor + return offset +endfunction + " Align a string within a field {{{2 " These functions do not trim leading and trailing spaces. @@ -94,6 +106,54 @@ function! s:Center(string, fieldwidth) return repeat(" ", left) . a:string . repeat(" ", right) endfunction +function! s:TabToString(position, count) + if a:count <= 0 + throw "Invalid tab count!" + endif + + let offset = &tabstop - (a:position % &tabstop) + if &expandtab + let result = repeat(" ", offset) . repeat(" ", &tabstop * (a:count - 1)) + else + let result = repeat("\t", a:count) + endif + let offset += a:count - 1 + return [offset, result] +endfunction + +" Convert padding specification to string: +function! s:PaddingToString(padding, position) + let result = "" + let offset = 0 + let padding = substitute(a:padding, " ", "", "g") + let sections = split(padding, s:paddingpat . '\zs') + for section in sections + let l:count = matchstr(section, '^\d\+') + let l:count = len(l:count) ? str2nr(l:count) : 1 + let type = matchstr(section, '[st]$') + let type = len(type) ? type : g:tabular_default_padding + if type ==? "s" + let result .= repeat(" ", l:count) + let offset += l:count + else + let [section_offset, section_result] = s:TabToString(a:position + offset, l:count) + let result .= section_result + let offset += section_offset + endif + endfor + return [offset, result] +endfunction + +function! s:FieldToString(field, align, max) + if a:align =~? 'l' + return s:Left(a:field, a:max) + elseif a:align =~? 'r' + return s:Right(a:field, a:max) + elseif a:align =~? 'c' + return s:Center(a:field, a:max) + endif +endfunction + " Remove spaces around a string {{{2 " Remove all trailing spaces from a string. @@ -194,7 +254,12 @@ if !exists("g:tabular_default_format") let g:tabular_default_format = "l1" endif -let s:formatelempat = '\%([lrc]\d\+\)' +if !exists("g:tabular_default_padding") + let g:tabular_default_padding = "s" +endif + +let s:paddingpat = '\%(\%(\d\+[st]\?\|[st]\) *\)' +let s:formatelempat = '\%([lrc]' . s:paddingpat . '\+\)' function! tabular#ElementFormatPattern() return s:formatelempat @@ -268,19 +333,14 @@ function! tabular#TabularizeStrings(strings, delim, ...) continue endif + let l:count = 0 for i in range(len(line)) - let how = format[i % len(format)][0] - let pad = format[i % len(format)][1:-1] - - if how =~? 'l' - let field = s:Left(line[i], maxes[i]) - elseif how =~? 'r' - let field = s:Right(line[i], maxes[i]) - elseif how =~? 'c' - let field = s:Center(line[i], maxes[i]) - endif - - let line[i] = field . (lead_blank && i == 0 ? '' : repeat(" ", pad)) + let align = format[i % len(format)][0] + let field = s:FieldToString(line[i], align, maxes[i]) + let l:count += s:TabOffset(l:count, field) + let padding = s:PaddingToString(format[i % len(format)][1:-1], l:count) + let l:count += padding[0] + let line[i] = field . (lead_blank && i == 0 ? '' : padding[1]) endfor let lines[idx] = s:StripTrailingSpaces(join(line, '')) diff --git a/doc/Tabular.txt b/doc/Tabular.txt index a71e032..734b480 100644 --- a/doc/Tabular.txt +++ b/doc/Tabular.txt @@ -89,11 +89,19 @@ fields, you would provide a different format to the Tabularize command: Some short phrase, some other phrase A much longer phrase here,and another long phrase -< -A format specifier is either l, r, or c, followed by one or more digits. If -the letter is l, the field will be left aligned, similarly for r and right -aligning and c and center aligning. The number following the letter is the -number of spaces padding to insert before the start of the next field. + +A format specifier is composed of an alignment specifier followed by a padding +specifier. The alignment specifier is either l, r, or c. If the letter is l, +the field will be left aligned, similarly for r and right aligning and c and +center aligning. The padding specification is a repeated sequence composed of +one or more digits followed by a whitespace specifier of s or t, terminated by +optional space characters. Either digit sequence or whitespace specifier is +optional, but not both. Omitted, the whitespace specifier defaults to +|g:tabular_default_padding|. Conversely, the digit sequence defaults to one. A +whitespace specifier of s represents space, similarly for t and tab. Each +represented character is repeated as per the digit sequence. Tabs respect the +|shiftwidth| and |expandtab| settings. + Multiple format specifiers can be added to the same command - each field will be printed with the next format specifier in the list; when they all have been used the first will be used again, and so on. So, the last command right @@ -115,20 +123,41 @@ left, or center aligned. Also notice that the 0 padding spaces specified for the 3rd field are unused - but they would be used if there were enough fields to require looping through the fields again. For instance: > - abc,def,ghi - a,b + +.......+.......+.......+.......+.......+ + abc,def,ghi,jkl,mno a,b,c - - :Tabularize /,/r1c1l0 - - abc , def, ghi - a , b - a , b , c -< -Notice that now, the format pattern has been reused; field 4 (the second comma) -is right aligned, field 5 is center aligned. No spaces were inserted between -the 3rd field (containing "def") and the 4th field (the second comma) because -the format specified 'l0'. + a,b,c,d,e + + abc,defghi,jkl + abcdefghijkl,m + a,b,c + +.......+.......+.......+.......+.......+ + + :echo [&tabstop, &expandtab] + [8, 1] + :exec "Tabularize /,/r1 ct l0 ct" | exec "norm }j" | Tabularize + + +.......+.......+.......+.......+.......+ + abc , def, ghi , jkl, mno + a , b , c + a , b , c , d , e + + abc , defghi, jkl + abcdefghijkl , m + a , b , c + +.......+.......+.......+.......+.......+ + +Notice that now, the format pattern has been reused: field 5 (containing +"ghi") is right aligned, field 6 (the third comma) is center aligned, and +field 7 (containing "jkl") is left aligned. A tab is inserted between the 4th +field (the second comma) and the 5th field (containing "ghi") because the +format specifier "ct". + +The "+" characters in the visual guide represent the tabstop locations. Notice +how, irrespective of indent the correct number of spaces are automatically +inserted, so the "ghi" and "defghi" fields are correctly aligned, as well as +both "jkl" entries. Though not demonstrated here, Tabularize correctly handles +any tabs within the original expression. But, what if you only wanted to act on the first comma on the line, rather than all of the commas on the line? Let's say we want everything before the first