diff --git a/autoload/tablemode/align.vim b/autoload/tablemode/align.vim index d618f45..5f44b1d 100644 --- a/autoload/tablemode/align.vim +++ b/autoload/tablemode/align.vim @@ -64,11 +64,21 @@ function! s:Padding(string, length, where) "{{{3 endif endfunction -" function! s:Split() - Split a string into fields and delimiters {{{2 +" Public Functions {{{1 +function! tablemode#align#sid() "{{{2 + return maparg('', 'n') +endfunction +nnoremap + +function! tablemode#align#scope() "{{{2 + return s: +endfunction + +" 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! s:Split(string, delim) +function! tablemode#align#Split(string, delim) let rv = [] let beg = 0 @@ -107,59 +117,61 @@ function! s:Split(string, delim) return rv endfunction -" Public Functions {{{1 -function! tablemode#align#sid() "{{{2 - return maparg('', 'n') -endfunction -nnoremap - -function! tablemode#align#scope() "{{{2 - return s: +function! tablemode#align#alignments(lnum, ncols) "{{{2 + let alignments = repeat(['l'], a:ncols) " For each column + if tablemode#table#IsHeader(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 + if hcols[idx] =~# g:table_mode_align_char . '$' | let alignments[idx] = 'r' | endif + endfor + end + return alignments endfunction function! tablemode#align#Align(lines) "{{{2 - let lines = map(a:lines, 's:Split(v:val, g:table_mode_separator)') + 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 - if len(line) <= 1 | continue | endif + let stext = line.text + if len(stext) <= 1 | continue | endif - if line[0] !~ tablemode#table#StartExpr() - let line[0] = s:StripTrailingSpaces(line[0]) + if stext[0] !~ tablemode#table#StartExpr() + let stext[0] = s:StripTrailingSpaces(stext[0]) endif - if len(line) >= 2 - for i in range(1, len(line)-1) - let line[i] = tablemode#utils#strip(line[i]) + 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 - if len(line) <= 1 | continue | endif - for i in range(len(line)) + let stext = line.text + if len(stext) <= 1 | continue | endif + for i in range(len(stext)) if i == len(maxes) - let maxes += [ s:Strlen(line[i]) ] + let maxes += [ s:Strlen(stext[i]) ] else - let maxes[i] = max([ maxes[i], s:Strlen(line[i]) ]) + let maxes[i] = max([ maxes[i], s:Strlen(stext[i]) ]) endif endfor endfor + let alignments = tablemode#align#alignments(lines[0].lnum, len(lines[0].text)) + for idx in range(len(lines)) - let line = lines[idx] + let tlnum = lines[idx].lnum + let tline = lines[idx].text - if len(line) <= 1 | continue | endif - for i in range(len(line)) - if line[i] !~# '[^0-9\.]' - let field = s:Padding(line[i], maxes[i], 'r') - else - let field = s:Padding(line[i], maxes[i], 'l') - endif - - let line[i] = field . (i == 0 || i == len(line) ? '' : ' ') + if len(tline) <= 1 | continue | endif + for i in range(len(tline)) + let field = s:Padding(tline[i], maxes[i], alignments[i]) + let tline[i] = field . (i == 0 || i == len(tline) ? '' : ' ') endfor - let lines[idx] = s:StripTrailingSpaces(join(line, '')) + let lines[idx].text = s:StripTrailingSpaces(join(tline, '')) endfor return lines diff --git a/autoload/tablemode/table.vim b/autoload/tablemode/table.vim index 2871817..551d072 100644 --- a/autoload/tablemode/table.vim +++ b/autoload/tablemode/table.vim @@ -21,7 +21,7 @@ function! s:HeaderBorderExpr() "{{{2 return tablemode#table#StartExpr() . \ '[' . g:table_mode_corner . g:table_mode_corner_corner . ']' . - \ '[' . g:table_mode_fillchar . g:table_mode_corner . ']*' . + \ '[' . g:table_mode_fillchar . g:table_mode_corner . g:table_mode_align_char . ']*' . \ '[' . g:table_mode_corner . g:table_mode_corner_corner . ']' . \ tablemode#table#EndExpr() endfunction @@ -45,10 +45,29 @@ function! s:GenerateHeaderBorder(line) "{{{2 let line_val = getline(line - 1) endif if tablemode#utils#strlen(line_val) <= 1 | return s:DefaultHeaderBorder() | 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') let border = substitute(border, '[^' . g:table_mode_corner . ']', g:table_mode_fillchar, 'g') 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 + if 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()) @@ -134,15 +153,14 @@ function! tablemode#table#EndExpr() "{{{2 endif endfunction -function! tablemode#table#IsRow(line) "{{{2 - return getline(a:line) =~# (tablemode#table#StartExpr() . g:table_mode_separator . '[^' . - \ g:table_mode_fillchar . ']*[^' . g:table_mode_corner . ']*$') -endfunction - function! tablemode#table#IsHeader(line) "{{{2 return getline(a:line) =~# s:HeaderBorderExpr() endfunction +function! tablemode#table#IsRow(line) "{{{2 + return !tablemode#table#IsHeader(a:line) && getline(a:line) =~# (tablemode#table#StartExpr() . g:table_mode_separator) +endfunction + function! tablemode#table#AddHeaderBorder(line) "{{{2 call setline(a:line, s:GenerateHeaderBorder(a:line)) endfunction @@ -150,41 +168,36 @@ endfunction function! tablemode#table#Realign(line) "{{{2 let line = tablemode#utils#line(a:line) - let [lnums, lines] = [[], []] - let [tline, blines] = [line, []] - while tablemode#table#IsRow(tline) || tablemode#table#IsHeader(tline) - if tablemode#table#IsHeader(tline) - call insert(blines, tline) - let tline -= 1 + let lines = [] + let [lnum, blines] = [line, []] + while tablemode#table#IsRow(lnum) || tablemode#table#IsHeader(lnum) + if tablemode#table#IsHeader(lnum) + call insert(blines, lnum) + let lnum -= 1 continue endif - call insert(lnums, tline) - call insert(lines, getline(tline)) - let tline -= 1 + call insert(lines, {'lnum': lnum, 'text': getline(lnum)}) + let lnum -= 1 endwhile - let tline = line + 1 - - while tablemode#table#IsRow(tline) || tablemode#table#IsHeader(tline) - if tablemode#table#IsHeader(tline) - call insert(blines, tline) - let tline += 1 + let lnum = line + 1 + while tablemode#table#IsRow(lnum) || tablemode#table#IsHeader(lnum) + if tablemode#table#IsHeader(lnum) + call add(blines, lnum) + let lnum += 1 continue endif - call add(lnums, tline) - call add(lines, getline(tline)) - let tline += 1 + call add(lines, {'lnum': lnum, 'text': getline(lnum)}) + let lnum += 1 endwhile let lines = tablemode#align#Align(lines) - for lnum in lnums - let index = index(lnums, lnum) - call setline(lnum, lines[index]) + for aline in lines + call setline(aline.lnum, aline.text) endfor for bline in blines call tablemode#table#AddHeaderBorder(bline) endfor endfunction - diff --git a/plugin/table-mode.vim b/plugin/table-mode.vim index 7230de9..41938bd 100644 --- a/plugin/table-mode.vim +++ b/plugin/table-mode.vim @@ -42,6 +42,7 @@ call s:SetGlobalOptDefault('table_mode_toggle_map', 'm') call s:SetGlobalOptDefault('table_mode_always_active', 0) call s:SetGlobalOptDefault('table_mode_delimiter', ',') call s:SetGlobalOptDefault('table_mode_corner_corner', '|') +call s:SetGlobalOptDefault('table_mode_align_char', ':') function! s:TableEchoCell() "{{{1 if tablemode#table#IsRow('.') diff --git a/t/align.vim b/t/align.vim index 479f81a..60ca2f8 100644 --- a/t/align.vim +++ b/t/align.vim @@ -3,12 +3,20 @@ source t/config/options.vim call vspec#hint({'scope': 'tablemode#align#scope()', 'sid': 'tablemode#align#sid()'}) +function! ConvertLines2Dict(lines) + let lines = [] + for idx in range(len(a:lines)) + call insert(lines, {"lnum": idx+1, "text": a:lines[idx]}) + endfor + return lines +endfunction + describe 'Align' it 'should align table content correctly' - Expect tablemode#align#Align(readfile('t/fixtures/align/simple_before.txt')) == readfile('t/fixtures/align/simple_after.txt') + Expect tablemode#align#Align(ConvertLines2Dict(readfile('t/fixtures/align/simple_before.txt'))) == ConvertLines2Dict(readfile('t/fixtures/align/simple_after.txt')) end it 'should align table content with unicode characters correctly' - Expect tablemode#align#Align(readfile('t/fixtures/align/unicode_before.txt')) == readfile('t/fixtures/align/unicode_after.txt') + Expect tablemode#align#Align(ConvertLines2Dict(readfile('t/fixtures/align/unicode_before.txt'))) == ConvertLines2Dict(readfile('t/fixtures/align/unicode_after.txt')) end end diff --git a/t/config/options.vim b/t/config/options.vim index 550ce28..a277b0f 100644 --- a/t/config/options.vim +++ b/t/config/options.vim @@ -6,3 +6,4 @@ let g:table_mode_toggle_map = 'm' let g:table_mode_always_active = 0 let g:table_mode_delimiter = ',' let g:table_mode_corner_corner = '|' +let g:table_mode_align_char = ':'