fix(tables): don't split a cell on a | inside a link/code/escape
CI / editor tests (push) Successful in 46s
CI / editor tests (push) Successful in 46s
Table auto-alignment scanned for cell separators with a naive find('|'),
so a cell containing a vimwiki link `[[url|title]]` was split at the link's
internal pipe — mangling both the link (`[[url` / `title]]`) and the table
(a phantom extra column). Same bug in both the Lua (Neovim) and VimL (Vim)
table code, in both table_cell_starts and parse_table_row.
Add a shared cell-separator scanner (cell_bar_positions) that treats `|` as
literal — not a separator — when it's inside `[[…]]` wikilinks, `{{…}}`
transclusions, `` `…` `` inline code, or backslash-escaped (`\|`), matching
vimwiki. Both scan functions in each client now use it.
Regression tests added on both clients (cr.table_link_cell_pipe_not_a_separator);
full harness green (Neovim 308, Vim 302).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1159,19 +1159,44 @@ function! s:is_table_row(line) abort
|
||||
return nuwiki#util#is_table_row(a:line)
|
||||
endfunction
|
||||
|
||||
" 0-based byte offsets of the cell-separator `|` in `s`. Pipes that are literal
|
||||
" cell content are skipped: those inside `[[…]]` wikilinks, `{{…}}`
|
||||
" transclusions, `` `…` `` inline code, and backslash-escaped `\|`. Byte-based
|
||||
" (strpart/stridx) to match the rest of the table code. Keeps a `[[url|title]]`
|
||||
" link in one cell instead of splitting on its internal pipe.
|
||||
function! s:cell_bar_positions(s) abort
|
||||
let l:out = []
|
||||
let l:i = 0
|
||||
let l:n = strlen(a:s)
|
||||
while l:i < l:n
|
||||
let l:c = strpart(a:s, l:i, 1)
|
||||
let l:two = strpart(a:s, l:i, 2)
|
||||
if l:c ==# '\'
|
||||
let l:i += 2
|
||||
elseif l:c ==# '`'
|
||||
let l:close = stridx(a:s, '`', l:i + 1)
|
||||
let l:i = l:close < 0 ? l:n : l:close + 1
|
||||
elseif l:two ==# '[['
|
||||
let l:close = stridx(a:s, ']]', l:i + 2)
|
||||
let l:i = l:close < 0 ? l:n : l:close + 2
|
||||
elseif l:two ==# '{{'
|
||||
let l:close = stridx(a:s, '}}', l:i + 2)
|
||||
let l:i = l:close < 0 ? l:n : l:close + 2
|
||||
elseif l:c ==# '|'
|
||||
call add(l:out, l:i)
|
||||
let l:i += 1
|
||||
else
|
||||
let l:i += 1
|
||||
endif
|
||||
endwhile
|
||||
return l:out
|
||||
endfunction
|
||||
|
||||
function! s:table_cell_starts(line) abort
|
||||
" 1-based byte positions of each `|`. Cursor lands one past pipe N at
|
||||
" 1-based byte (starts[N] + 1) — that's the first space inside the
|
||||
" cell the pipe opens.
|
||||
let l:out = []
|
||||
let l:pos = 0
|
||||
while 1
|
||||
let l:bar = stridx(a:line, '|', l:pos)
|
||||
if l:bar < 0 | break | endif
|
||||
call add(l:out, l:bar + 1)
|
||||
let l:pos = l:bar + 1
|
||||
endwhile
|
||||
return l:out
|
||||
return map(s:cell_bar_positions(a:line), 'v:val + 1')
|
||||
endfunction
|
||||
|
||||
function! s:find_table_range(row) abort
|
||||
@@ -1192,15 +1217,11 @@ function! s:parse_table_row(line) abort
|
||||
let l:body = strpart(a:line, len(l:indent))
|
||||
let l:segments = []
|
||||
let l:pos = 0
|
||||
while 1
|
||||
let l:bar = stridx(l:body, '|', l:pos)
|
||||
if l:bar < 0
|
||||
call add(l:segments, strpart(l:body, l:pos))
|
||||
break
|
||||
endif
|
||||
for l:bar in s:cell_bar_positions(l:body)
|
||||
call add(l:segments, strpart(l:body, l:pos, l:bar - l:pos))
|
||||
let l:pos = l:bar + 1
|
||||
endwhile
|
||||
endfor
|
||||
call add(l:segments, strpart(l:body, l:pos))
|
||||
" Drop the empty piece before the opening `|` and the trailing piece
|
||||
" after the closing `|`.
|
||||
if len(l:segments) > 0 && l:segments[0] ==# ''
|
||||
|
||||
Reference in New Issue
Block a user