Skip to content

Instantly share code, notes, and snippets.

@kamichidu
Last active August 29, 2015 14:06
Show Gist options
  • Save kamichidu/4017dd7ff2d0bdfd2794 to your computer and use it in GitHub Desktop.
Save kamichidu/4017dd7ff2d0bdfd2794 to your computer and use it in GitHub Desktop.
let s:S= vital#of('vital').import('Data.String')
let s:L= vital#of('vital').import('Data.List')
let s:tabular= {
\ '__column_defs': [],
\ '__hborder': 1,
\ '__vborder': 1,
\ '__header': [],
\ '__rows': [],
\ '__footer': [],
\}
let s:default_column_def= {
\ 'halign': 'left',
\ 'valign': 'top',
\ 'width': 0,
\}
function! s:tabular.header(...)
if a:0 == 0
return deepcopy(self.__header)
else
let header= deepcopy(a:1)
if len(header) != len(self.__column_defs)
throw "vital: Text.Table: Not match column size."
endif
let self.__header= header
endif
endfunction
function! s:tabular.add_column(def)
let self.__column_defs+= [deepcopy(a:def)]
endfunction
function! s:tabular.add_row(row)
let row= deepcopy(a:row)
if len(row) != len(self.__column_defs)
throw "vital: Text.Table: Not match column size."
endif
let self.__rows+= [row]
endfunction
function! s:tabular.footer(...)
if a:0 == 0
return deepcopy(self.__footer)
else
let footer= deepcopy(a:1)
if len(footer) != len(self.__column_defs)
throw "vital: Text.Table: Not match column size."
endif
let self.__footer= footer
endif
endfunction
function! s:tabular.stringify()
let context= {}
let context.header= self.__header
let context.footer= self.__footer
let context.rows= self.__rows
let context.column_defs= []
for col in range(len(self.__column_defs))
let orig= self.__column_defs[col]
let def= extend(deepcopy(s:default_column_def), orig)
if def.width == 0
" `1' is nl
let def.width= max(map(copy(context.rows), 'strlen(v:val[col])')) + 1
endif
let context.column_defs+= [def]
endfor
let context.hborder= self.__hborder
let context.vborder= self.__vborder
return s:_stringify(context)
endfunction
function! s:_stringify(context)
let buffer= []
let buffer+= s:_make_border_string(a:context)
if !empty(a:context.header)
let buffer+= s:_make_row_string(a:context, a:context.header)
let buffer+= s:_make_border_string(a:context)
endif
for row in a:context.rows
let buffer+= s:_make_row_string(a:context, row)
endfor
if !empty(a:context.footer)
let buffer+= s:_make_border_string(a:context)
let buffer+= s:_make_row_string(a:context, a:context.footer)
endif
let buffer+= s:_make_border_string(a:context)
return join(buffer, "\n")
endfunction
function! s:_make_border_string(context)
if !a:context.hborder
return []
endif
let buffer= []
for def in a:context.column_defs
let buffer+= [repeat('-', def.width + 2)]
endfor
if a:context.vborder
return ['+' . join(buffer, '+') . '+']
else
return ['-' . join(buffer, '-') . '-']
endif
endfunction
function! s:_make_row_string(context, row)
let buffer= []
for col in range(len(a:context.column_defs))
let def= a:context.column_defs[col]
let cell= a:row[col]
let buffer+= [s:_make_cell_string(def, cell)]
unlet cell
endfor
" vertical align
let tmp= []
let col= 0
for cells in s:_make_equals_size(buffer)
let def= a:context.column_defs[col]
let tmp+= [s:_valign(def, cells)]
let col+= 1
endfor
let buffer= tmp
unlet tmp
let out= []
for cells in s:_zip(buffer)
let cellstrs= []
for col in range(len(a:context.column_defs))
let def= a:context.column_defs[col]
let cell= cells[col]
" horizontal align
let cellstrs+= [s:_halign(def, cell)]
endfor
if a:context.vborder
let out+= ['| ' . join(cellstrs, ' | ') . ' |']
else
let out+= [join(cell, ' ')]
endif
endfor
return out
endfunction
function! s:_make_cell_string(def, expr)
let cellstr= s:_to_string(a:expr)
return s:S.wrap(cellstr, a:def.width)
endfunction
function! s:_halign(def, expr)
if a:def.halign ==# 'left'
return printf('%-*s', a:def.width, s:_to_string(a:expr))
elseif a:def.halign ==# 'center'
throw printf("vital: Text.Table: Sorry, halign `%s' is unimplemented yet.", a:def.halign)
elseif a:def.halign ==# 'right'
return printf('% *s', a:def.width, s:_to_string(a:expr))
else
throw printf("vital: Text.Table: Unknown halign `%s'", a:def.halign)
endif
endfunction
function! s:_valign(def, list)
if a:def.valign ==# 'top'
return a:list
elseif a:def.valign ==# 'center'
throw printf("vital: Text.Table: Sorry, valign `%s' is unimplemented yet.", a:def.valign)
elseif a:def.valign ==# 'bottom'
let buffer= []
for e in reverse(copy(a:list))
let buffer+= [e]
endfor
return buffer
else
throw printf("vital: Text.Table: Unknown valign `%s'", a:def.valign)
endif
endfunction
function! s:_to_string(expr)
if type(a:expr) == type('')
return a:expr
elseif type(a:expr) == type(0)
return '' . a:expr
elseif type(a:expr) == type(0.0)
return printf('%f', a:expr)
else
throw 'vital: Text.Table: Unsupported type'
endif
endfunction
function! s:_make_equals_size(list)
let mlen= max(map(copy(a:list), 'len(v:val)'))
let res= []
for l in a:list
let res+= [map(range(mlen), 'get(l, v:val, "")')]
endfor
return res
endfunction
function! s:_zip(list)
let mlen= max(map(copy(a:list), 'len(v:val)'))
let zip= []
for i in range(mlen)
let buf= []
for l in a:list
let buf+= [get(l, i, '')]
endfor
let zip+= [buf]
endfor
return zip
endfunction
" test
try
call s:tabular.add_column({ })
call s:tabular.add_column({ 'width': 10})
call s:tabular.add_column({'halign': 'right', 'valign': 'bottom', 'width': 8})
call s:tabular.header(['h1', 'h2', 'h3'])
call s:tabular.add_row(['r1c1', 'r1c2', 'r1c3'])
call s:tabular.add_row(['r2c1', 'r2c2', 'r2c3'])
call s:tabular.add_row(['r2c1', 'ああああああああああああああああ', 'r2c3'])
call s:tabular.footer(['f1', 'f2', 'f3'])
echo s:tabular.stringify()
catch
echo v:throwpoint
echo v:exception
endtry
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment