Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Tabstop Alignment #39

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
86 changes: 73 additions & 13 deletions autoload/tabular.vim
Expand Up @@ -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.

Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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, ''))
Expand Down
65 changes: 47 additions & 18 deletions doc/Tabular.txt
Expand Up @@ -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
Expand All @@ -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
Expand Down