Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5375e30bda | |||
| 51e857f3c6 | |||
| 4c187fee3c |
@@ -1159,19 +1159,45 @@ function! s:is_table_row(line) abort
|
|||||||
return nuwiki#util#is_table_row(a:line)
|
return nuwiki#util#is_table_row(a:line)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" 0-based byte offsets of the cell-separator `|` in `s`. Pipes that are literal
|
||||||
|
" cell content are skipped: those inside `[[…]]` wikilinks, `{{…}}`
|
||||||
|
" transclusions, and `` `…` `` inline code. 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.
|
||||||
|
"
|
||||||
|
" Backslash-escaped `\|` is intentionally NOT skipped, to match the server
|
||||||
|
" lexer (nuwiki-rs). See the nuwiki-rs issue for the coordinated `\|` follow-up.
|
||||||
|
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: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
|
function! s:table_cell_starts(line) abort
|
||||||
" 1-based byte positions of each `|`. Cursor lands one past pipe N at
|
" 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
|
" 1-based byte (starts[N] + 1) — that's the first space inside the
|
||||||
" cell the pipe opens.
|
" cell the pipe opens.
|
||||||
let l:out = []
|
return map(s:cell_bar_positions(a:line), 'v:val + 1')
|
||||||
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
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:find_table_range(row) abort
|
function! s:find_table_range(row) abort
|
||||||
@@ -1192,15 +1218,11 @@ function! s:parse_table_row(line) abort
|
|||||||
let l:body = strpart(a:line, len(l:indent))
|
let l:body = strpart(a:line, len(l:indent))
|
||||||
let l:segments = []
|
let l:segments = []
|
||||||
let l:pos = 0
|
let l:pos = 0
|
||||||
while 1
|
for l:bar in s:cell_bar_positions(l:body)
|
||||||
let l:bar = stridx(l:body, '|', l:pos)
|
|
||||||
if l:bar < 0
|
|
||||||
call add(l:segments, strpart(l:body, l:pos))
|
|
||||||
break
|
|
||||||
endif
|
|
||||||
call add(l:segments, strpart(l:body, l:pos, l:bar - l:pos))
|
call add(l:segments, strpart(l:body, l:pos, l:bar - l:pos))
|
||||||
let l:pos = l:bar + 1
|
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
|
" Drop the empty piece before the opening `|` and the trailing piece
|
||||||
" after the closing `|`.
|
" after the closing `|`.
|
||||||
if len(l:segments) > 0 && l:segments[0] ==# ''
|
if len(l:segments) > 0 && l:segments[0] ==# ''
|
||||||
|
|||||||
@@ -336,6 +336,14 @@ call s:run('cr.adds_new_table_row', {
|
|||||||
\ 'keys': "A\<CR>x\<Esc>",
|
\ 'keys': "A\<CR>x\<Esc>",
|
||||||
\ 'expect_lines': ['| a | b |', '|x | |'],
|
\ 'expect_lines': ['| a | b |', '|x | |'],
|
||||||
\ })
|
\ })
|
||||||
|
" Regression: a `|` inside `[[u|t]]` must not split the cell — the link stays
|
||||||
|
" intact and the row keeps 2 columns (fresh row has 2 cells, not 3).
|
||||||
|
call s:run('cr.table_link_cell_pipe_not_a_separator', {
|
||||||
|
\ 'lines': ['| a | [[u|t]] |'],
|
||||||
|
\ 'cursor': [1, 4],
|
||||||
|
\ 'keys': "A\<CR>x\<Esc>",
|
||||||
|
\ 'expect_lines': ['| a | [[u|t]] |', '|x | |'],
|
||||||
|
\ })
|
||||||
" Insert <S-CR>: multiline list item — continue with NO marker, aligned under
|
" Insert <S-CR>: multiline list item — continue with NO marker, aligned under
|
||||||
" the text ("- " → 2 cols; "- [ ] " → 6 cols).
|
" the text ("- " → 2 cols; "- [ ] " → 6 cols).
|
||||||
call s:run('cr.shift_cr_multiline', {
|
call s:run('cr.shift_cr_multiline', {
|
||||||
|
|||||||
@@ -735,6 +735,16 @@ vim.defer_fn(function()
|
|||||||
expect_lines = { '| a | b |', '|x | |' },
|
expect_lines = { '| a | b |', '|x | |' },
|
||||||
wait_ms = 100,
|
wait_ms = 100,
|
||||||
})
|
})
|
||||||
|
run('cr.table_link_cell_pipe_not_a_separator', {
|
||||||
|
-- Regression: the `|` inside `[[u|t]]` must NOT split the cell, so the
|
||||||
|
-- link stays intact and the row keeps its 2 columns (a 2-col fresh row,
|
||||||
|
-- not the mangled 3-col one the old naive `|` scan produced).
|
||||||
|
lines = { '| a | [[u|t]] |' },
|
||||||
|
cursor = { 1, 4 },
|
||||||
|
keys = 'A<CR>x<Esc>',
|
||||||
|
expect_lines = { '| a | [[u|t]] |', '|x | |' },
|
||||||
|
wait_ms = 100,
|
||||||
|
})
|
||||||
|
|
||||||
-- ===== Insert-mode table navigation (Cluster 6) =====
|
-- ===== Insert-mode table navigation (Cluster 6) =====
|
||||||
|
|
||||||
|
|||||||
+36
-15
@@ -821,20 +821,45 @@ end
|
|||||||
|
|
||||||
local is_table_row = require('nuwiki.util').is_table_row
|
local is_table_row = require('nuwiki.util').is_table_row
|
||||||
|
|
||||||
|
-- 1-based byte positions of the cell-separator `|` characters in `s`. Pipes
|
||||||
|
-- that are literal cell content are skipped: those inside `[[…]]` wikilinks,
|
||||||
|
-- `{{…}}` transclusions, and `` `…` `` inline code. This keeps a
|
||||||
|
-- `[[url|title]]` link in one cell instead of splitting on its internal pipe.
|
||||||
|
--
|
||||||
|
-- Backslash-escaped `\|` is intentionally NOT skipped, to match the server
|
||||||
|
-- lexer (nuwiki-rs), which can't skip it yet without leaving a stray backslash
|
||||||
|
-- in the output. See nuwiki-rs issue for the coordinated `\|` follow-up.
|
||||||
|
local function cell_bar_positions(s)
|
||||||
|
local out = {}
|
||||||
|
local i = 1
|
||||||
|
local n = #s
|
||||||
|
while i <= n do
|
||||||
|
local c = s:sub(i, i)
|
||||||
|
if c == '`' then
|
||||||
|
local close = s:find('`', i + 1, true)
|
||||||
|
i = (close or n) + 1
|
||||||
|
elseif s:sub(i, i + 1) == '[[' then
|
||||||
|
local close = s:find(']]', i + 2, true)
|
||||||
|
i = close and close + 2 or n + 1
|
||||||
|
elseif s:sub(i, i + 1) == '{{' then
|
||||||
|
local close = s:find('}}', i + 2, true)
|
||||||
|
i = close and close + 2 or n + 1
|
||||||
|
elseif c == '|' then
|
||||||
|
table.insert(out, i)
|
||||||
|
i = i + 1
|
||||||
|
else
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
local function table_cell_starts(line)
|
local function table_cell_starts(line)
|
||||||
-- Returns 1-based byte positions of each `|` separator. Cell N spans
|
-- Returns 1-based byte positions of each `|` separator. Cell N spans
|
||||||
-- (starts[N], starts[N+1]); placing the cursor at 0-based byte column
|
-- (starts[N], starts[N+1]); placing the cursor at 0-based byte column
|
||||||
-- `starts[i]` lands one position past the i-th `|` (the first space
|
-- `starts[i]` lands one position past the i-th `|` (the first space
|
||||||
-- of the cell that the `|` opens).
|
-- of the cell that the `|` opens).
|
||||||
local out = {}
|
return cell_bar_positions(line)
|
||||||
local pos = 0
|
|
||||||
while true do
|
|
||||||
local bar = line:find('|', pos + 1, true)
|
|
||||||
if not bar then break end
|
|
||||||
table.insert(out, bar)
|
|
||||||
pos = bar
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local function find_table_range(row)
|
local function find_table_range(row)
|
||||||
@@ -856,15 +881,11 @@ local function parse_table_row(line)
|
|||||||
local body = line:sub(#indent + 1)
|
local body = line:sub(#indent + 1)
|
||||||
local segments = {}
|
local segments = {}
|
||||||
local pos = 1
|
local pos = 1
|
||||||
while true do
|
for _, bar in ipairs(cell_bar_positions(body)) do
|
||||||
local bar = body:find('|', pos, true)
|
|
||||||
if not bar then
|
|
||||||
table.insert(segments, body:sub(pos))
|
|
||||||
break
|
|
||||||
end
|
|
||||||
table.insert(segments, body:sub(pos, bar - 1))
|
table.insert(segments, body:sub(pos, bar - 1))
|
||||||
pos = bar + 1
|
pos = bar + 1
|
||||||
end
|
end
|
||||||
|
table.insert(segments, body:sub(pos))
|
||||||
-- Drop the empty piece before the opening `|` and the trailing piece
|
-- Drop the empty piece before the opening `|` and the trailing piece
|
||||||
-- after the closing `|`.
|
-- after the closing `|`.
|
||||||
if #segments > 0 and segments[1] == '' then table.remove(segments, 1) end
|
if #segments > 0 and segments[1] == '' then table.remove(segments, 1) end
|
||||||
|
|||||||
+1
-1
@@ -16,7 +16,7 @@ let g:loaded_nuwiki = 1
|
|||||||
" Plugin version, surfaced by :VimwikiShowVersion / :NuwikiShowVersion.
|
" Plugin version, surfaced by :VimwikiShowVersion / :NuwikiShowVersion.
|
||||||
" Set before the Neovim early-return below so both clients see the global.
|
" Set before the Neovim early-return below so both clients see the global.
|
||||||
if !exists('g:nuwiki_version')
|
if !exists('g:nuwiki_version')
|
||||||
let g:nuwiki_version = '0.5.1'
|
let g:nuwiki_version = '0.5.2'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Resolve the plugin root NOW, while this script is being sourced and
|
" Resolve the plugin root NOW, while this script is being sourced and
|
||||||
|
|||||||
Reference in New Issue
Block a user