aboutsummaryrefslogtreecommitdiff
path: root/vim/bundle/vim-table-mode/autoload
diff options
context:
space:
mode:
Diffstat (limited to 'vim/bundle/vim-table-mode/autoload')
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode.vim195
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/align.vim134
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/logger.vim5
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet.vim189
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/cell.vim258
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/formula.vim122
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/table.vim198
-rw-r--r--vim/bundle/vim-table-mode/autoload/tablemode/utils.vim49
8 files changed, 1150 insertions, 0 deletions
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode.vim b/vim/bundle/vim-table-mode/autoload/tablemode.vim
new file mode 100644
index 0000000..defc3dd
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode.vim
@@ -0,0 +1,195 @@
+" Private Functions {{{1
+function! s:SetBufferOptDefault(opt, val) "{{{2
+ if !exists('b:' . a:opt)
+ let b:{a:opt} = a:val
+ endif
+endfunction
+
+function! s:Map(map, to, mode) "{{{2
+ if !empty(a:to) && !hasmapto(a:map, a:mode)
+ for l:mode in split(a:mode, '.\zs')
+ execute l:mode . 'map <buffer>' a:to a:map
+ endfor
+ endif
+endfunction
+
+function! s:UnMap(map, mode) "{{{2
+ if !empty(maparg(a:map, a:mode))
+ for mode in split(a:mode, '.\zs')
+ execute l:mode . 'unmap <buffer>' a:map
+ endfor
+ endif
+endfunction
+
+function! s:ToggleMapping() "{{{2
+ let separator_map = g:table_mode_separator
+ " '|' is a special character, we need to map <Bar> instead
+ if g:table_mode_separator ==# '|' | let separator_map = '<Bar>' | endif
+
+ if !g:table_mode_disable_mappings
+ if tablemode#IsActive()
+ call s:Map('<Plug>(table-mode-tableize)', separator_map, 'i')
+ call s:Map('<Plug>(table-mode-motion-up)', g:table_mode_motion_up_map, 'n')
+ call s:Map('<Plug>(table-mode-motion-down)', g:table_mode_motion_down_map, 'n')
+ call s:Map('<Plug>(table-mode-motion-left)', g:table_mode_motion_left_map, 'n')
+ call s:Map('<Plug>(table-mode-motion-right)', g:table_mode_motion_right_map, 'n')
+
+ call s:Map('<Plug>(table-mode-cell-text-object-a)', g:table_mode_cell_text_object_a_map, 'ox')
+ call s:Map('<Plug>(table-mode-cell-text-object-i)', g:table_mode_cell_text_object_i_map, 'ox')
+
+ call s:Map('<Plug>(table-mode-realign)', g:table_mode_realign_map, 'n')
+ call s:Map('<Plug>(table-mode-delete-row)', g:table_mode_delete_row_map, 'n')
+ call s:Map('<Plug>(table-mode-delete-column)', g:table_mode_delete_column_map, 'n')
+ call s:Map('<Plug>(table-mode-add-formula)', g:table_mode_add_formula_map, 'n')
+ call s:Map('<Plug>(table-mode-eval-formula)', g:table_mode_eval_formula_map, 'n')
+ call s:Map('<Plug>(table-mode-echo-cell)', g:table_mode_echo_cell_map, 'n')
+ call s:Map('<Plug>(table-mode-sort)', g:table_mode_sort_map, 'n')
+ else
+ call s:UnMap(separator_map, 'i')
+ call s:UnMap(g:table_mode_motion_up_map, 'n')
+ call s:UnMap(g:table_mode_motion_down_map, 'n')
+ call s:UnMap(g:table_mode_motion_left_map, 'n')
+ call s:UnMap(g:table_mode_motion_right_map, 'n')
+
+ call s:UnMap(g:table_mode_cell_text_object_a_map, 'ox')
+ call s:UnMap(g:table_mode_cell_text_object_i_map, 'ox')
+
+ call s:UnMap(g:table_mode_realign_map, 'n')
+ call s:UnMap(g:table_mode_delete_row_map, 'n')
+ call s:UnMap(g:table_mode_delete_column_map, 'n')
+ call s:UnMap(g:table_mode_add_formula_map, 'n')
+ call s:UnMap(g:table_mode_eval_formula_map, 'n')
+ call s:UnMap(g:table_mode_echo_cell_map, 'n')
+ call s:UnMap(g:table_mode_sort_map, 'n')
+ endif
+ endif
+endfunction
+
+function! tablemode#SyntaxEnable()
+ exec 'syntax match Table'
+ \ '/' . tablemode#table#StartExpr() . '\zs|.\+|\ze' . tablemode#table#EndExpr() . '/'
+ \ 'contains=TableBorder,TableSeparator,TableColumnAlign containedin=ALL'
+ syntax match TableSeparator /|/ contained
+ syntax match TableColumnAlign /:/ contained
+ syntax match TableBorder /[\-+]\+/ contained
+
+ hi! link TableBorder Delimiter
+ hi! link TableSeparator Delimiter
+ hi! link TableColumnAlign Type
+endfunction
+
+function! s:ToggleSyntax()
+ if tablemode#IsActive()
+ call tablemode#SyntaxEnable()
+ else
+ syntax clear Table
+ syntax clear TableBorder
+ syntax clear TableSeparator
+ syntax clear TableColumnAlign
+
+ hi! link TableBorder NONE
+ hi! link TableSeparator NONE
+ hi! link TableColumnAlign NONE
+ endif
+endfunction
+
+function! s:SetActive(bool) "{{{2
+ let b:table_mode_active = a:bool
+ call s:ToggleSyntax()
+ call s:ToggleMapping()
+ if tablemode#IsActive()
+ doautocmd User TableModeEnabled
+ else
+ doautocmd User TableModeDisabled
+ endif
+endfunction
+
+function! s:ConvertDelimiterToSeparator(line, ...) "{{{2
+ let old_gdefault = &gdefault
+ set nogdefault
+
+ let delim = g:table_mode_delimiter
+ if a:0 | let delim = a:1 | endif
+ if delim ==# ','
+ silent! execute a:line . 's/' . "[\'\"][^\'\"]*\\zs,\\ze[^\'\"]*[\'\"]/__COMMA__/g"
+ endif
+
+ let [cstart, cend] = [tablemode#table#GetCommentStart(), tablemode#table#GetCommentEnd()]
+ let [match_char_start, match_char_end] = ['.', '.']
+ if tablemode#utils#strlen(cend) > 0 | let match_char_end = '[^' . cend . ']' | endif
+ if tablemode#utils#strlen(cstart) > 0 | let match_char_start = '[^' . cstart . ']' | endif
+
+ silent! execute a:line . 's/' . tablemode#table#StartExpr() . '\zs\ze' . match_char_start .
+ \ '\|' . delim . '\|' . match_char_end . '\zs\ze' . tablemode#table#EndExpr() . '/' .
+ \ g:table_mode_separator . '/g'
+
+ if delim ==# ','
+ silent! execute a:line . 's/' . "[\'\"][^\'\"]*\\zs__COMMA__\\ze[^\'\"]*[\'\"]/,/g"
+ endif
+
+ let &gdefault=old_gdefault
+endfunction
+
+function! s:Tableizeline(line, ...) "{{{2
+ let delim = g:table_mode_delimiter
+ if a:0 && type(a:1) == type('') && !empty(a:1) | let delim = a:1[1:-1] | endif
+ call s:ConvertDelimiterToSeparator(a:line, delim)
+endfunction
+
+" Public API {{{1
+function! tablemode#IsActive() "{{{2
+ if g:table_mode_always_active | return 1 | endif
+
+ call s:SetBufferOptDefault('table_mode_active', 0)
+ return b:table_mode_active
+endfunction
+
+function! tablemode#TableizeInsertMode() "{{{2
+ if tablemode#IsActive() && getline('.') =~# (tablemode#table#StartExpr() . g:table_mode_separator . g:table_mode_separator)
+ call tablemode#table#AddBorder('.')
+ normal! A
+ elseif tablemode#IsActive() && getline('.') =~# (tablemode#table#StartExpr() . g:table_mode_separator)
+ let column = tablemode#utils#strlen(substitute(getline('.')[0:col('.')], '[^' . g:table_mode_separator . ']', '', 'g'))
+ let position = tablemode#utils#strlen(matchstr(getline('.')[0:col('.')], '.*' . g:table_mode_separator . '\s*\zs.*'))
+ call tablemode#table#Realign('.')
+ normal! 0
+ call search(repeat('[^' . g:table_mode_separator . ']*' . g:table_mode_separator, column) . '\s\{-\}' . repeat('.', position), 'ce', line('.'))
+ endif
+endfunction
+
+function! tablemode#Enable() "{{{2
+ call s:SetActive(1)
+endfunction
+
+function! tablemode#Disable() "{{{2
+ call s:SetActive(0)
+endfunction
+
+function! tablemode#Toggle() "{{{2
+ if g:table_mode_always_active
+ return 1
+ endif
+
+ call s:SetBufferOptDefault('table_mode_active', 0)
+ call s:SetActive(!b:table_mode_active)
+endfunction
+
+function! tablemode#TableizeRange(...) range "{{{2
+ let lnum = a:firstline
+ while lnum < (a:firstline + (a:lastline - a:firstline + 1))
+ call s:Tableizeline(lnum, a:1)
+ undojoin
+ let lnum += 1
+ endwhile
+
+ call tablemode#table#Realign(lnum - 1)
+endfunction
+
+function! tablemode#TableizeByDelimiter() "{{{2
+ let delim = input('/')
+ if delim =~# "\<Esc>" || delim =~# "\<C-C>" | return | endif
+ let vm = visualmode()
+ if vm ==? 'line' || vm ==? 'V'
+ exec line("'<") . ',' . line("'>") . "call tablemode#TableizeRange('/' . delim)"
+ endif
+endfunction
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/align.vim b/vim/bundle/vim-table-mode/autoload/tablemode/align.vim
new file mode 100644
index 0000000..9e6330c
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/align.vim
@@ -0,0 +1,134 @@
+" Borrowed from Tabular
+" Private Functions {{{1
+" function! s:StripTrailingSpaces(string) - Remove all trailing spaces {{{2
+" from a string.
+function! s:StripTrailingSpaces(string)
+ return matchstr(a:string, '^.\{-}\ze\s*$')
+endfunction
+
+function! s:Padding(string, length, where) "{{{3
+ let gap_length = a:length - tablemode#utils#StrDisplayWidth(a:string)
+ if a:where =~# 'l'
+ return a:string . repeat(" ", gap_length)
+ elseif a:where =~# 'r'
+ return repeat(" ", gap_length) . a:string
+ elseif a:where =~# 'c'
+ let right = gap_length / 2
+ let left = right + (right * 2 != gap_length)
+ return repeat(" ", left) . a:string . repeat(" ", right)
+ endif
+endfunction
+
+" Public Functions {{{1
+" function! tablemode#align#Split() - Split a string into fields and delimiters {{{2
+" Like split(), but include the delimiters as elements
+" All odd numbered elements are delimiters
+" All even numbered elements are non-delimiters (including zero)
+function! tablemode#align#Split(string, delim)
+ let rv = []
+ let beg = 0
+
+ let len = len(a:string)
+ let searchoff = 0
+
+ while 1
+ let mid = match(a:string, a:delim, beg + searchoff, 1)
+ if mid == -1 || mid == len
+ break
+ endif
+
+ let matchstr = matchstr(a:string, a:delim, beg + searchoff, 1)
+ let length = strlen(matchstr)
+
+ if length == 0 && beg == mid
+ " Zero-length match for a zero-length delimiter - advance past it
+ let searchoff += 1
+ continue
+ endif
+
+ if beg == mid
+ let rv += [ "" ]
+ else
+ let rv += [ a:string[beg : mid-1] ]
+ endif
+
+ let rv += [ matchstr ]
+
+ let beg = mid + length
+ let searchoff = 0
+ endwhile
+
+ let rv += [ strpart(a:string, beg) ]
+
+ return rv
+endfunction
+
+function! tablemode#align#alignments(lnum, ncols) "{{{2
+ let achr = g:table_mode_align_char
+ let alignments = []
+ if tablemode#table#IsBorder(a:lnum+1)
+ let hcols = tablemode#align#Split(getline(a:lnum+1), '[' . g:table_mode_corner . g:table_mode_corner_corner . ']')
+ for idx in range(len(hcols))
+ " Right align if header
+ call add(alignments, 'l')
+ if hcols[idx] =~# achr . '[^'.achr.']\+' . achr
+ let alignments[idx] = 'c'
+ elseif hcols[idx] =~# achr . '$'
+ let alignments[idx] = 'r'
+ endif
+ " if hcols[idx] !~# '[^0-9\.]' | let alignments[idx] = 'r' | endif
+ endfor
+ end
+ return alignments
+endfunction
+
+function! tablemode#align#Align(lines) "{{{2
+ if empty(a:lines) | return [] | endif
+ let lines = map(a:lines, 'map(v:val, "v:key =~# \"text\" ? tablemode#align#Split(v:val, g:table_mode_separator) : v:val")')
+
+ for line in lines
+ let stext = line.text
+ if len(stext) <= 1 | continue | endif
+
+ if stext[0] !~ tablemode#table#StartExpr()
+ let stext[0] = s:StripTrailingSpaces(stext[0])
+ endif
+ if len(stext) >= 2
+ for i in range(1, len(stext)-1)
+ let stext[i] = tablemode#utils#strip(stext[i])
+ endfor
+ endif
+ endfor
+
+ let maxes = []
+ for line in lines
+ let stext = line.text
+ if len(stext) <= 1 | continue | endif
+ for i in range(len(stext))
+ if i == len(maxes)
+ let maxes += [ tablemode#utils#StrDisplayWidth(stext[i]) ]
+ else
+ let maxes[i] = max([ maxes[i], tablemode#utils#StrDisplayWidth(stext[i]) ])
+ endif
+ endfor
+ endfor
+
+ let alignments = tablemode#align#alignments(lines[0].lnum, len(lines[0].text))
+
+ for idx in range(len(lines))
+ let tlnum = lines[idx].lnum
+ let tline = lines[idx].text
+
+ if len(tline) <= 1 | continue | endif
+ for jdx in range(len(tline))
+ " Dealing with the header being the first line
+ if jdx >= len(alignments) | call add(alignments, 'l') | endif
+ let field = s:Padding(tline[jdx], maxes[jdx], alignments[jdx])
+ let tline[jdx] = field . (jdx == 0 || jdx == len(tline) ? '' : ' ')
+ endfor
+
+ let lines[idx].text = s:StripTrailingSpaces(join(tline, ''))
+ endfor
+
+ return lines
+endfunction
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/logger.vim b/vim/bundle/vim-table-mode/autoload/tablemode/logger.vim
new file mode 100644
index 0000000..ab3986d
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/logger.vim
@@ -0,0 +1,5 @@
+function! tablemode#logger#log(message)
+ if g:table_mode_verbose
+ echom message
+ endif
+endfunction
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet.vim b/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet.vim
new file mode 100644
index 0000000..6f79d5c
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet.vim
@@ -0,0 +1,189 @@
+" Private Functions {{{1
+function! s:Sum(list) "{{{2
+ let result = 0.0
+ for item in a:list
+ if type(item) == type(1) || type(item) == type(1.0)
+ let result += item
+ elseif type(item) == type('')
+ let result += str2float(item)
+ elseif type(item) == type([])
+ let result += s:Sum(item)
+ endif
+ endfor
+ return result
+endfunction
+
+function! s:Average(list) "{{{2
+ return s:Sum(a:list)/len(a:list)
+endfunction
+
+" Public Functions {{{1
+function! tablemode#spreadsheet#GetFirstRow(line) "{{{2
+ if tablemode#table#IsRow(a:line)
+ let line = tablemode#utils#line(a:line)
+
+ while !tablemode#table#IsHeader(line - 1) && (tablemode#table#IsRow(line - 1) || tablemode#table#IsBorder(line - 1))
+ let line -= 1
+ endwhile
+ if tablemode#table#IsBorder(line) | let line += 1 | endif
+
+ return line
+ endif
+endfunction
+
+function! tablemode#spreadsheet#MoveToFirstRow() "{{{2
+ if tablemode#table#IsRow('.')
+ call cursor(tablemode#spreadsheet#GetFirstRow('.'), col('.'))
+ endif
+endfunction
+
+function! tablemode#spreadsheet#GetLastRow(line) "{{{2
+ if tablemode#table#IsRow(a:line)
+ let line = tablemode#utils#line(a:line)
+
+ while tablemode#table#IsTable(line + 1)
+ let line += 1
+ endwhile
+ if tablemode#table#IsBorder(line) | let line -= 1 | endif
+
+ return line
+ endif
+endfunction
+
+function! tablemode#spreadsheet#MoveToLastRow() "{{{2
+ if tablemode#table#IsRow('.')
+ call cursor(tablemode#spreadsheet#GetLastRow('.'), col('.'))
+ endif
+endfunction
+
+function! tablemode#spreadsheet#LineNr(line, row) "{{{2
+ if tablemode#table#IsRow(a:line)
+ let line = tablemode#spreadsheet#GetFirstRow(a:line)
+ let row_nr = 0
+
+ while tablemode#table#IsTable(line + 1)
+ if tablemode#table#IsRow(line)
+ let row_nr += 1
+ if a:row ==# row_nr | break | endif
+ endif
+ let line += 1
+ endwhile
+
+ return line
+ endif
+endfunction
+
+function! tablemode#spreadsheet#RowNr(line) "{{{2
+ let line = tablemode#utils#line(a:line)
+
+ let rowNr = 0
+ while !tablemode#table#IsHeader(line) && tablemode#table#IsTable(line)
+ if tablemode#table#IsRow(line) | let rowNr += 1 | endif
+ let line -= 1
+ endwhile
+
+ return rowNr
+endfunction
+
+function! tablemode#spreadsheet#RowCount(line) "{{{2
+ let line = tablemode#utils#line(a:line)
+
+ let [tline, totalRowCount] = [line, 0]
+ while !tablemode#table#IsHeader(tline) && tablemode#table#IsTable(tline)
+ if tablemode#table#IsRow(tline) | let totalRowCount += 1 | endif
+ let tline -= 1
+ endwhile
+
+ let tline = line + 1
+ while !tablemode#table#IsHeader(tline) && tablemode#table#IsTable(tline)
+ if tablemode#table#IsRow(tline) | let totalRowCount += 1 | endif
+ let tline += 1
+ endwhile
+
+ return totalRowCount
+endfunction
+
+function! tablemode#spreadsheet#ColumnNr(pos) "{{{2
+ let pos = []
+ if type(a:pos) == type('')
+ let pos = [line(a:pos), col(a:pos)]
+ elseif type(a:pos) == type([])
+ let pos = a:pos
+ else
+ return 0
+ endif
+ let row_start = stridx(getline(pos[0]), g:table_mode_separator)
+ return tablemode#utils#strlen(substitute(getline(pos[0])[(row_start):pos[1]-2], '[^' . g:table_mode_separator . ']', '', 'g'))
+endfunction
+
+function! tablemode#spreadsheet#ColumnCount(line) "{{{2
+ let line = tablemode#utils#line(a:line)
+
+ return tablemode#utils#strlen(substitute(getline(line), '[^' . g:table_mode_separator . ']', '', 'g'))-1
+endfunction
+
+function! tablemode#spreadsheet#IsFirstCell() "{{{2
+ return tablemode#spreadsheet#ColumnNr('.') ==# 1
+endfunction
+
+function! tablemode#spreadsheet#IsLastCell() "{{{2
+ return tablemode#spreadsheet#ColumnNr('.') ==# tablemode#spreadsheet#ColumnCount('.')
+endfunction
+
+function! tablemode#spreadsheet#MoveToStartOfCell() "{{{2
+ if getline('.')[col('.')-1] ==# g:table_mode_separator && !tablemode#spreadsheet#IsLastCell()
+ normal! 2l
+ else
+ execute 'normal! F' . g:table_mode_separator . '2l'
+ endif
+endfunction
+
+function! tablemode#spreadsheet#DeleteColumn() "{{{2
+ if tablemode#table#IsRow('.')
+ for i in range(v:count1)
+ call tablemode#spreadsheet#MoveToStartOfCell()
+ call tablemode#spreadsheet#MoveToFirstRow()
+ silent! execute "normal! h\<C-V>f" . g:table_mode_separator
+ call tablemode#spreadsheet#MoveToLastRow()
+ normal! d
+ endfor
+
+ call tablemode#table#Realign('.')
+ endif
+endfunction
+
+function! tablemode#spreadsheet#DeleteRow() "{{{2
+ if tablemode#table#IsRow('.')
+ for i in range(v:count1)
+ if tablemode#table#IsRow('.')
+ normal! dd
+ endif
+
+ if !tablemode#table#IsRow('.')
+ normal! k
+ endif
+ endfor
+
+ call tablemode#table#Realign('.')
+ endif
+endfunction
+
+function! tablemode#spreadsheet#Sum(range, ...) abort "{{{2
+ let args = copy(a:000)
+ call insert(args, a:range)
+ return s:Sum(call('tablemode#spreadsheet#cell#GetCellRange', args))
+endfunction
+
+function! tablemode#spreadsheet#Average(range, ...) abort "{{{2
+ let args = copy(a:000)
+ call insert(args, a:range)
+ return s:Average(call('tablemode#spreadsheet#cell#GetCellRange', args))
+endfunction
+
+function! tablemode#spreadsheet#Sort(bang, ...) "{{{2
+ let opts = a:0 ? a:1 : ''
+ let bang = a:bang ? '!' : ''
+ let [firstRow, lastRow] = [tablemode#spreadsheet#GetFirstRow('.'), tablemode#spreadsheet#GetLastRow('.')]
+ call tablemode#spreadsheet#MoveToStartOfCell()
+ exec ':'.firstRow.','.lastRow . 'sort'.bang opts '/.*\%'.col('.').'v/'
+endfunction
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/cell.vim b/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/cell.vim
new file mode 100644
index 0000000..d1d912b
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/cell.vim
@@ -0,0 +1,258 @@
+" Private Functions {{{1
+" function! s:ParseRange(range, ...) {{{2
+" range: A string representing range of cells.
+" - Can be row1:row2 for values in the current columns in those rows.
+" - Can be row1,col1:row2,col2 for range between row1,col1 till
+" row2,col2.
+function! s:ParseRange(range, ...)
+ if a:0 < 1
+ let default_col = tablemode#spreadsheet#ColumnNr('.')
+ elseif a:0 < 2
+ let default_col = a:1
+ endif
+
+ if type(a:range) != type('')
+ let range = string(a:range)
+ else
+ let range = a:range
+ endif
+
+ let [rowcol1, rowcol2] = split(range, ':')
+ let [rcs1, rcs2] = [map(split(rowcol1, ','), 'str2nr(v:val)'), map(split(rowcol2, ','), 'str2nr(v:val)')]
+
+ if len(rcs1) == 2
+ let [row1, col1] = rcs1
+ else
+ let [row1, col1] = [rcs1[0], default_col]
+ endif
+
+ if len(rcs2) == 2
+ let [row2, col2] = rcs2
+ else
+ let [row2, col2] = [rcs2[0], default_col]
+ endif
+
+ return [row1, col1, row2, col2]
+endfunction
+
+
+" Public Functions {{{1
+" function! tablemode#spreadsheet#cell#GetCells() - Function to get values of cells in a table {{{2
+" tablemode#spreadsheet#GetCells(row) - Get values of all cells in a row as a List.
+" tablemode#spreadsheet#GetCells(0, col) - Get values of all cells in a column as a List.
+" tablemode#spreadsheet#GetCells(row, col) - Get the value of table cell by given row, col.
+function! tablemode#spreadsheet#cell#GetCells(line, ...) abort
+ let line = tablemode#utils#line(a:line)
+
+ if tablemode#table#IsRow(line)
+ if a:0 < 1
+ let [row, colm] = [line, 0]
+ elseif a:0 < 2
+ let [row, colm] = [a:1, 0]
+ elseif a:0 < 3
+ let [row, colm] = a:000
+ endif
+
+ let first_row = tablemode#spreadsheet#GetFirstRow(line)
+ let last_row = tablemode#spreadsheet#GetLastRow(line)
+ if row == 0
+ let values = []
+ let line = first_row
+ while tablemode#table#IsTable(line)
+ if tablemode#table#IsRow(line)
+ let row_line = getline(line)[stridx(getline(line), g:table_mode_separator):strridx(getline(line), g:table_mode_separator)]
+ call add(values, tablemode#utils#strip(get(split(row_line, g:table_mode_separator), colm>0?colm-1:colm, '')))
+ endif
+ let line += 1
+ endwhile
+ return values
+ else
+ let row_nr = 0
+ let row_diff = row > 0 ? 1 : -1
+ let line = row > 0 ? first_row : last_row
+ while tablemode#table#IsTable(line)
+ if tablemode#table#IsRow(line)
+ let row_nr += row_diff
+ if row ==# row_nr | break | endif
+ endif
+ let line += row_diff
+ endwhile
+
+ let row_line = getline(line)[stridx(getline(line), g:table_mode_separator):strridx(getline(line), g:table_mode_separator)]
+ if colm == 0
+ return map(split(row_line, g:table_mode_separator), 'tablemode#utils#strip(v:val)')
+ else
+ let split_line = split(row_line, g:table_mode_separator)
+ return tablemode#utils#strip(get(split(row_line, g:table_mode_separator), colm>0?colm-1:colm, ''))
+ endif
+ endif
+ endif
+endfunction
+
+function! tablemode#spreadsheet#cell#GetCell(...) "{{{2
+ if a:0 == 0
+ let [row, colm] = [tablemode#spreadsheet#RowNr('.'), tablemode#spreadsheet#ColumnNr('.')]
+ elseif a:0 == 2
+ let [row, colm] = [a:1, a:2]
+ endif
+
+ return tablemode#spreadsheet#cell#GetCells('.', row, colm)
+endfunction
+
+function! tablemode#spreadsheet#cell#GetRow(row, ...) abort "{{{2
+ let line = a:0 ? a:1 : '.'
+ return tablemode#spreadsheet#cell#GetCells(line, a:row)
+endfunction
+
+function! tablemode#spreadsheet#cell#GetRowColumn(col, ...) abort "{{{2
+ let line = a:0 ? a:1 : '.'
+ let row = tablemode#spreadsheet#RowNr('.')
+ return tablemode#spreadsheet#cell#GetCells(line, row, a:col)
+endfunction
+
+function! tablemode#spreadsheet#cell#GetColumn(col, ...) abort "{{{2
+ let line = a:0 ? a:1 : '.'
+ return tablemode#spreadsheet#cell#GetCells(line, 0, a:col)
+endfunction
+
+function! tablemode#spreadsheet#cell#GetColumnRow(row, ...) abort "{{{2
+ let line = a:0 ? a:1 : '.'
+ let col = tablemode#spreadsheet#ColumnNr('.')
+ return tablemode#spreadsheet#cell#GetCells(line, a:row, col)
+endfunction
+
+function! tablemode#spreadsheet#cell#GetCellRange(range, ...) abort "{{{2
+ if a:0 < 1
+ let [line, colm] = ['.', tablemode#spreadsheet#ColumnNr('.')]
+ elseif a:0 < 2
+ let [line, colm] = [a:1, tablemode#spreadsheet#ColumnNr('.')]
+ elseif a:0 < 3
+ let [line, colm] = [a:1, a:2]
+ else
+ call tablemode#utils#throw('Invalid Range')
+ endif
+
+ let values = []
+
+ if tablemode#table#IsRow(line)
+ let [row1, col1, row2, col2] = s:ParseRange(a:range, colm)
+
+ if row1 == row2
+ if col1 == col2
+ call add(values, tablemode#spreadsheet#cell#GetCells(line, row1, col1))
+ else
+ let values = tablemode#spreadsheet#cell#GetRow(row1, line)[(col1-1):(col2-1)]
+ endif
+ else
+ if col1 == col2
+ let values = tablemode#spreadsheet#cell#GetColumn(col1, line)[(row1-1):(row2-1)]
+ else
+ let tcol = col1
+ while tcol <= col2
+ call add(values, tablemode#spreadsheet#cell#GetColumn(tcol, line)[(row1-1):(row2-1)])
+ let tcol += 1
+ endwhile
+ endif
+ endif
+ endif
+
+ return values
+endfunction
+
+function! tablemode#spreadsheet#cell#SetCell(val, ...) "{{{2
+ if a:0 == 0
+ let [line, row, colm] = ['.', tablemode#spreadsheet#RowNr('.'), tablemode#spreadsheet#ColumnNr('.')]
+ elseif a:0 == 2
+ let [line, row, colm] = ['.', a:1, a:2]
+ elseif a:0 == 3
+ let [line, row, colm] = a:000
+ endif
+
+ " Account for negative values to reference from relatively from the last
+ if row < 0 | let row = tablemode#spreadsheet#RowCount(line) + row + 1 | endif
+ if colm < 0 | let colm = tablemode#spreadsheet#ColumnCount(line) + colm + 1 | endif
+
+ if tablemode#table#IsRow(line)
+ let line = tablemode#spreadsheet#LineNr(line, row)
+ let line_val = getline(line)
+ let cstartexpr = tablemode#table#StartCommentExpr()
+ let values = split(getline(line)[stridx(line_val, g:table_mode_separator):strridx(line_val, g:table_mode_separator)], g:table_mode_separator)
+ if len(values) < colm | return | endif
+ let values[colm-1] = a:val
+ let line_value = g:table_mode_separator . join(values, g:table_mode_separator) . g:table_mode_separator
+ if tablemode#utils#strlen(cstartexpr) > 0 && line_val =~# cstartexpr
+ let sce = matchstr(line_val, tablemode#table#StartCommentExpr())
+ let ece = matchstr(line_val, tablemode#table#EndCommentExpr())
+ let line_value = sce . line_value . ece
+ endif
+ call setline(line, line_value)
+ call tablemode#table#Realign(line)
+ endif
+endfunction
+function! tablemode#spreadsheet#cell#TextObject(inner) "{{{2
+ if tablemode#table#IsRow('.')
+ call tablemode#spreadsheet#MoveToStartOfCell()
+ if a:inner
+ normal! v
+ call search('[^' . g:table_mode_separator . ']\ze\s*' . g:table_mode_separator)
+ else
+ execute 'normal! vf' . g:table_mode_separator . 'l'
+ endif
+ endif
+endfunction
+function! tablemode#spreadsheet#cell#Motion(direction, ...) "{{{2
+ let l:count = a:0 ? a:1 : v:count1
+ if tablemode#table#IsRow('.')
+ for ii in range(l:count)
+ if a:direction ==# 'l'
+ if tablemode#spreadsheet#IsLastCell()
+ if !tablemode#table#IsRow(line('.') + 1) && (tablemode#table#IsBorder(line('.') + 1) && !tablemode#table#IsRow(line('.') + 2))
+ return
+ endif
+ call tablemode#spreadsheet#cell#Motion('j', 1)
+ normal! 0
+ endif
+
+ " If line starts with g:table_mode_separator
+ if getline('.')[col('.')-1] ==# g:table_mode_separator
+ normal! 2l
+ else
+ execute 'normal! f' . g:table_mode_separator . '2l'
+ endif
+ elseif a:direction ==# 'h'
+ if tablemode#spreadsheet#IsFirstCell()
+ if !tablemode#table#IsRow(line('.') - 1) && (tablemode#table#IsBorder(line('.') - 1) && !tablemode#table#IsRow(line('.') - 2))
+ return
+ endif
+ call tablemode#spreadsheet#cell#Motion('k', 1)
+ normal! $
+ endif
+
+ " If line ends with g:table_mode_separator
+ if getline('.')[col('.')-1] ==# g:table_mode_separator
+ execute 'normal! F' . g:table_mode_separator . '2l'
+ else
+ execute 'normal! 2F' . g:table_mode_separator . '2l'
+ endif
+ elseif a:direction ==# 'j'
+ if tablemode#table#IsRow(line('.') + 1)
+ " execute 'normal! ' . 1 . 'j'
+ normal! j
+ elseif tablemode#table#IsBorder(line('.') + 1) && tablemode#table#IsRow(line('.') + 2)
+ " execute 'normal! ' . 2 . 'j'
+ normal! 2j
+ endif
+ elseif a:direction ==# 'k'
+ if tablemode#table#IsRow(line('.') - 1)
+ " execute 'normal! ' . 1 . 'k'
+ normal! k
+ elseif tablemode#table#IsBorder(line('.') - 1) && tablemode#table#IsRow(line('.') - 2)
+ " execute 'normal! ' . (1 + 1) . 'k'
+ normal! 2k
+ endif
+ endif
+ endfor
+ endif
+endfunction
+
+" vim: sw=2 sts=2 fdl=0 fdm=marker
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/formula.vim b/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/formula.vim
new file mode 100644
index 0000000..8271b5d
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/spreadsheet/formula.vim
@@ -0,0 +1,122 @@
+" Private Functions {{{1
+
+" Public Functions {{{1
+function! tablemode#spreadsheet#formula#Add(...) "{{{2
+ let fr = a:0 ? a:1 : input('f=')
+ let row = tablemode#spreadsheet#RowNr('.')
+ let colm = tablemode#spreadsheet#ColumnNr('.')
+ let indent = indent('.')
+ let indent_str = repeat(' ', indent)
+
+ if fr !=# ''
+ let fr = '$' . row . ',' . colm . '=' . fr
+ let fline = tablemode#spreadsheet#GetLastRow('.') + 1
+ if tablemode#table#IsBorder(fline) | let fline += 1 | endif
+ let cursor_pos = [line('.'), col('.')]
+ if getline(fline) =~# 'tmf: '
+ " Comment line correctly
+ let line_val = getline(fline)
+ let start_pos = match(line_val, tablemode#table#StartCommentExpr())
+ let end_pos = match(line_val, tablemode#table#EndCommentExpr())
+ if empty(end_pos) | let end_pos = len(line_val) | endif
+ let line_expr = strpart(line_val, start_pos, end_pos)
+ let sce = matchstr(line_val, tablemode#table#StartCommentExpr() . '\zs')
+ let ece = matchstr(line_val, tablemode#table#EndCommentExpr())
+ call setline(fline, sce . line_expr . '; ' . fr . ece)
+ else
+ let cstring = &commentstring
+ let [cmss, cmse] = ['', '']
+ if len(cstring) > 0
+ let cms = split(cstring, '%s')
+ if len(cms) == 2
+ let [cmss, cmse] = cms
+ else
+ let [cmss, cmse] = [cms[0], '']
+ endif
+ endif
+ let fr = indent_str . cmss . ' tmf: ' . fr . ' ' . cmse
+ call append(fline-1, fr)
+ call cursor(cursor_pos)
+ endif
+ call tablemode#spreadsheet#formula#EvaluateFormulaLine()
+ endif
+endfunction
+
+function! tablemode#spreadsheet#formula#EvaluateExpr(expr, line) abort "{{{2
+ let line = tablemode#utils#line(a:line)
+ let [target, expr] = map(split(a:expr, '='), 'tablemode#utils#strip(v:val)')
+ let cell = substitute(target, '\$', '', '')
+ if cell =~# ','
+ let [row, colm] = map(split(cell, ','), 'str2nr(v:val)')
+ else
+ let [row, colm] = [0, str2nr(cell)]
+ endif
+
+ if expr =~# 'Sum(.*)'
+ let expr = substitute(expr, 'Sum(\([^)]*\))', 'tablemode#spreadsheet#Sum("\1",'.line.','.colm.')', 'g')
+ endif
+
+ if expr =~# 'Average(.*)'
+ let expr = substitute(expr, 'Average(\([^)]*\))', 'tablemode#spreadsheet#Average("\1",'.line.','.colm.')', 'g')
+ endif
+
+ if expr =~# '\$\d\+,\d\+'
+ let expr = substitute(expr, '\$\(\d\+\),\(\d\+\)',
+ \ '\=str2float(tablemode#spreadsheet#cell#GetCells(line, submatch(1), submatch(2)))', 'g')
+ endif
+
+ if cell =~# ','
+ if expr =~# '\$'
+ let expr = substitute(expr, '\$\(\d\+\)',
+ \ '\=str2float(tablemode#spreadsheet#cell#GetCells(line, row, submatch(1)))', 'g')
+ endif
+ call tablemode#spreadsheet#cell#SetCell(eval(expr), line, row, colm)
+ else
+ let [row, line] = [1, tablemode#spreadsheet#GetFirstRow(line)]
+ while tablemode#table#IsRow(line)
+ let texpr = expr
+ if expr =~# '\$'
+ let texpr = substitute(texpr, '\$\(\d\+\)',
+ \ '\=str2float(tablemode#spreadsheet#cell#GetCells(line, row, submatch(1)))', 'g')
+ endif
+
+ call tablemode#spreadsheet#cell#SetCell(eval(texpr), line, row, colm)
+ let row += 1
+ let line += 1
+ endwhile
+ endif
+endfunction
+
+function! tablemode#spreadsheet#formula#EvaluateFormulaLine() abort "{{{2
+ let exprs = []
+ let cstring = &commentstring
+ let matchexpr = ''
+ if len(cstring) > 0
+ let cms = split(cstring, '%s')
+ if len(cms) == 2
+ let matchexpr = '^\s*' . escape(cms[0], '/*') . '\s*tmf: \zs.*\ze' . escape(cms[1], '/*') . '\s*$'
+ else
+ let matchexpr = '^\s*' . escape(cms[0], '/*') . '\s*tmf: \zs.*$'
+ endif
+ else
+ let matchexpr = '^\s* tmf: \zs.*$'
+ endif
+ if tablemode#table#IsRow('.') " We're inside the table
+ let line = tablemode#spreadsheet#GetLastRow('.')
+ let fline = line + 1
+ if tablemode#table#IsBorder(fline) | let fline += 1 | endif
+ if getline(fline) =~# 'tmf: '
+ let exprs = split(matchstr(getline(fline), matchexpr), ';')
+ endif
+ elseif getline('.') =~# 'tmf: ' " We're on the formula line
+ let line = line('.') - 1
+ if tablemode#table#IsBorder(line) | let line -= 1 | endif
+ if tablemode#table#IsRow(line)
+ let exprs = split(matchstr(getline('.'), matchexpr), ';')
+ endif
+ endif
+
+ for expr in exprs
+ call tablemode#spreadsheet#formula#EvaluateExpr(expr, line)
+ endfor
+endfunction
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/table.vim b/vim/bundle/vim-table-mode/autoload/tablemode/table.vim
new file mode 100644
index 0000000..82344b1
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/table.vim
@@ -0,0 +1,198 @@
+" Private Functions {{{1
+function! s:BorderExpr() "{{{2
+ return tablemode#table#StartExpr() .
+ \ '[' . g:table_mode_corner . g:table_mode_corner_corner . ']' .
+ \ '[' . escape(g:table_mode_fillchar . g:table_mode_header_fillchar . g:table_mode_corner . g:table_mode_align_char, '-') . ']\+' .
+ \ '[' . g:table_mode_corner . g:table_mode_corner_corner . ']' .
+ \ tablemode#table#EndExpr()
+endfunction
+
+function! s:DefaultBorder() "{{{2
+ if tablemode#IsActive()
+ return g:table_mode_corner_corner . g:table_mode_fillchar . g:table_mode_corner . g:table_mode_fillchar . g:table_mode_corner_corner
+ else
+ return ''
+ endif
+endfunction
+
+function! s:GenerateHeaderBorder(line) "{{{2
+ let line = tablemode#utils#line(a:line)
+ if tablemode#table#IsRow(line - 1) || tablemode#table#IsRow(line + 1)
+ let line_val = ''
+ if tablemode#table#IsRow(line + 1)
+ let line_val = getline(line + 1)
+ endif
+ if tablemode#table#IsRow(line - 1) && tablemode#utils#strlen(line_val) < tablemode#utils#strlen(getline(line - 1))
+ let line_val = getline(line - 1)
+ endif
+ if tablemode#utils#strlen(line_val) <= 1 | return s:DefaultBorder() | endif
+
+ let border = substitute(line_val[stridx(line_val, g:table_mode_separator):strridx(line_val, g:table_mode_separator)], g:table_mode_separator, g:table_mode_corner, 'g')
+ " To accurately deal with unicode double width characters
+ if tablemode#table#IsHeader(line - 1)
+ let fill_columns = map(split(border, g:table_mode_corner), 'repeat(g:table_mode_header_fillchar, tablemode#utils#StrDisplayWidth(v:val))')
+ else
+ let fill_columns = map(split(border, g:table_mode_corner), 'repeat(g:table_mode_fillchar, tablemode#utils#StrDisplayWidth(v:val))')
+ endif
+ let border = g:table_mode_corner . join(fill_columns, g:table_mode_corner) . g:table_mode_corner
+ let border = substitute(border, '^' . g:table_mode_corner . '\(.*\)' . g:table_mode_corner . '$', g:table_mode_corner_corner . '\1' . g:table_mode_corner_corner, '')
+
+ " Incorporate header alignment chars
+ if getline(line) =~# g:table_mode_align_char
+ let pat = '[' . g:table_mode_corner_corner . g:table_mode_corner . ']'
+ let hcols = tablemode#align#Split(getline(line), pat)
+ let gcols = tablemode#align#Split(border, pat)
+
+ for idx in range(len(hcols))
+ if hcols[idx] =~# g:table_mode_align_char
+ " center align
+ if hcols[idx] =~# g:table_mode_align_char . '[^'.g:table_mode_align_char.']\+' . g:table_mode_align_char
+ let gcols[idx] = g:table_mode_align_char . gcols[idx][1:-2] . g:table_mode_align_char
+ elseif hcols[idx] =~# g:table_mode_align_char . '$'
+ let gcols[idx] = gcols[idx][:-2] . g:table_mode_align_char
+ else
+ let gcols[idx] = g:table_mode_align_char . gcols[idx][1:]
+ endif
+ endif
+ endfor
+ let border = join(gcols, '')
+ endif
+
+ let cstartexpr = tablemode#table#StartCommentExpr()
+ if tablemode#utils#strlen(cstartexpr) > 0 && getline(line) =~# cstartexpr
+ let sce = matchstr(line_val, tablemode#table#StartCommentExpr())
+ let ece = matchstr(line_val, tablemode#table#EndCommentExpr())
+ return sce . border . ece
+ elseif getline(line) =~# tablemode#table#StartExpr()
+ let indent = matchstr(line_val, tablemode#table#StartExpr())
+ return indent . border
+ else
+ return border
+ endif
+ else
+ return s:DefaultBorder()
+ endif
+endfunction
+
+" Public Functions {{{1
+function! tablemode#table#GetCommentStart() "{{{2
+ let cstring = &commentstring
+ if tablemode#utils#strlen(cstring) > 0
+ return substitute(split(cstring, '%s')[0], '[^()]', '\\\0', 'g')
+ else
+ return ''
+ endif
+endfunction
+
+function! tablemode#table#StartCommentExpr() "{{{2
+ let cstartexpr = tablemode#table#GetCommentStart()
+ if tablemode#utils#strlen(cstartexpr) > 0
+ return '^\s*' . cstartexpr . '\s*'
+ else
+ return ''
+ endif
+endfunction
+
+function! tablemode#table#GetCommentEnd() "{{{2
+ let cstring = &commentstring
+ if tablemode#utils#strlen(cstring) > 0
+ let cst = split(cstring, '%s')
+ if len(cst) == 2
+ return substitute(cst[1], '[^()]', '\\\0', 'g')
+ else
+ return ''
+ endif
+ else
+ return ''
+ endif
+endfunction
+
+function! tablemode#table#EndCommentExpr() "{{{2
+ let cendexpr = tablemode#table#GetCommentEnd()
+ if tablemode#utils#strlen(cendexpr) > 0
+ return '.*\zs\s\+' . cendexpr . '\s*$'
+ else
+ return ''
+ endif
+endfunction
+
+function! tablemode#table#StartExpr() "{{{2
+ let cstart = tablemode#table#GetCommentStart()
+ if tablemode#utils#strlen(cstart) > 0
+ return '^\s*\(' . cstart . '\)\?\s*'
+ else
+ return '^\s*'
+ endif
+endfunction
+
+function! tablemode#table#EndExpr() "{{{2
+ let cend = tablemode#table#GetCommentEnd()
+ if tablemode#utils#strlen(cend) > 0
+ return '\s*\(\s\+' . cend . '\)\?\s*$'
+ else
+ return '\s*$'
+ endif
+endfunction
+
+function! tablemode#table#IsBorder(line) "{{{2
+ return !empty(getline(a:line)) && getline(a:line) =~# s:BorderExpr()
+endfunction
+
+function! tablemode#table#IsHeader(line) "{{{2
+ let line = tablemode#utils#line(a:line)
+ " if line <= 0 || line > line('$') | return 0 | endif
+ return tablemode#table#IsRow(line)
+ \ && !tablemode#table#IsRow(line-1)
+ \ && !tablemode#table#IsRow(line-2)
+ \ && !tablemode#table#IsBorder(line-2)
+ \ && tablemode#table#IsBorder(line+1)
+endfunction
+
+function! tablemode#table#IsRow(line) "{{{2
+ return !tablemode#table#IsBorder(a:line) && getline(a:line) =~# (tablemode#table#StartExpr() . g:table_mode_separator) . '[^' . g:table_mode_separator . ']\+'
+endfunction
+
+function! tablemode#table#IsTable(line) "{{{2
+ return tablemode#table#IsRow(a:line) || tablemode#table#IsBorder(a:line)
+endfunction
+
+function! tablemode#table#AddBorder(line) "{{{2
+ call setline(a:line, s:GenerateHeaderBorder(a:line))
+endfunction
+
+function! tablemode#table#Realign(line) "{{{2
+ let line = tablemode#utils#line(a:line)
+
+ let lines = []
+ let [lnum, blines] = [line, []]
+ while tablemode#table#IsTable(lnum)
+ if tablemode#table#IsBorder(lnum)
+ call insert(blines, lnum)
+ let lnum -= 1
+ continue
+ endif
+ call insert(lines, {'lnum': lnum, 'text': getline(lnum)})
+ let lnum -= 1
+ endwhile
+
+ let lnum = line + 1
+ while tablemode#table#IsTable(lnum)
+ if tablemode#table#IsBorder(lnum)
+ call add(blines, lnum)
+ let lnum += 1
+ continue
+ endif
+ call add(lines, {'lnum': lnum, 'text': getline(lnum)})
+ let lnum += 1
+ endwhile
+
+ let lines = tablemode#align#Align(lines)
+
+ for aline in lines
+ call setline(aline.lnum, aline.text)
+ endfor
+
+ for bline in blines
+ call tablemode#table#AddBorder(bline)
+ endfor
+endfunction
diff --git a/vim/bundle/vim-table-mode/autoload/tablemode/utils.vim b/vim/bundle/vim-table-mode/autoload/tablemode/utils.vim
new file mode 100644
index 0000000..215da6c
--- /dev/null
+++ b/vim/bundle/vim-table-mode/autoload/tablemode/utils.vim
@@ -0,0 +1,49 @@
+" Private Functions {{{1
+
+" Public Functions {{{1
+function! tablemode#utils#throw(string) abort "{{{2
+ let v:errmsg = 'table-mode: ' . a:string
+ throw v:errmsg
+endfunction
+
+function! tablemode#utils#line(row) "{{{2
+ if type(a:row) == type('')
+ return line(a:row)
+ else
+ return a:row
+ endif
+endfunction
+
+function! tablemode#utils#strip(string) "{{{2
+ return matchstr(a:string, '^\s*\zs.\{-}\ze\s*$')
+endfunction
+
+" function! tablemode#utils#strlen {{{2
+" To count multibyte characters accurately
+function! tablemode#utils#strlen(text)
+ return strlen(substitute(a:text, '.', 'x', 'g'))
+endfunction
+
+function! tablemode#utils#StrDisplayWidth(string) "{{{2
+ if exists('*strdisplaywidth')
+ return strdisplaywidth(a:string)
+ else
+ " Implement the tab handling part of strdisplaywidth for vim 7.2 and
+ " earlier - not much that can be done about handling doublewidth
+ " characters.
+ let rv = 0
+ let i = 0
+
+ for char in split(a:string, '\zs')
+ if char == "\t"
+ let rv += &ts - i
+ let i = 0
+ else
+ let rv += 1
+ let i = (i + 1) % &ts
+ endif
+ endfor
+
+ return rv
+ endif
+endfunction