aboutsummaryrefslogtreecommitdiff
path: root/vim/bundle/vim-table-mode/autoload/tablemode/align.vim
blob: 9e6330c627608f8736b3fa125e8ae21f2f332d9d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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