parity(5): smart <CR> in insert mode (VimwikiReturn)
Bind `<CR>` in insert mode as an `<expr>` map to a `smart_return`
helper. Behaviour matches upstream vimwiki's `:VimwikiReturn`:
- On a list line with content → continue the list with the same
marker on a new line (preserving leading checkbox `[ ]` if any).
- On an empty list line (marker only) → clear the marker and break
out of the list with a plain newline.
- Inside a `|…|` table row → insert a fresh empty row below with
the same column count, cursor lands inside the first cell.
- Otherwise → plain `<CR>`.
Implementation note: the helper is `<expr>`-bound, which means
textlock is active and the buffer can't be mutated from inside the
callback. The function returns key sequences only — including
`<Esc>0DA<CR>` for the empty-list break and `<Esc>0li` for the
table-row cursor jump — so every effect flows through Vim's normal
keystroke pipeline and stays undo-coherent.
Both Lua and VimL counterparts shipped.
Tests: 4 new in scripts/test-keymaps.lua covering list continuation,
checkbox preservation, empty-marker break-out, and new table row.
Gates: 421 Rust / 35 Neovim / 12 Vim.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -582,6 +582,51 @@ function M.list_toggle_or_add_checkbox()
|
||||
vim.api.nvim_buf_set_lines(0, row - 1, row, false, { new_line })
|
||||
end
|
||||
|
||||
--- Smart `<CR>` for insert mode — vimwiki parity for `:VimwikiReturn`.
|
||||
--- Used as an `<expr>` keymap so the function is side-effect-free on
|
||||
--- the buffer (textlock is active). The returned string is fed into
|
||||
--- Vim's input stream — we encode everything (including the table-row
|
||||
--- insertion) as keystrokes so the user's typed keys after `<CR>` land
|
||||
--- at the right cursor position.
|
||||
function M.smart_return()
|
||||
local line = vim.api.nvim_get_current_line()
|
||||
local function tc(s) return vim.api.nvim_replace_termcodes(s, true, false, true) end
|
||||
local cr = tc('<CR>')
|
||||
local esc = tc('<Esc>')
|
||||
|
||||
-- Table row → newline + empty row + cursor inside first cell.
|
||||
if line:match('^%s*|.*|%s*$') then
|
||||
local cell_count = 0
|
||||
for _ in line:gmatch('|') do cell_count = cell_count + 1 end
|
||||
cell_count = math.max(cell_count - 1, 1)
|
||||
local new_row = '|' .. string.rep(' |', cell_count)
|
||||
-- After `<CR>` + new_row, cursor sits at end of new row. Bounce to
|
||||
-- normal, jump to col 0, step right past the leading `|`, re-enter
|
||||
-- insert: `<Esc>0li` puts the cursor immediately after the `|`.
|
||||
return cr .. new_row .. esc .. '0li'
|
||||
end
|
||||
|
||||
-- List item with marker?
|
||||
local indent, marker, after = line:match('^(%s*)([%-%*%#])%s(.*)$')
|
||||
if not marker then
|
||||
local n
|
||||
indent, n, marker, after = line:match('^(%s*)(%d+)([%.%)])%s(.*)$')
|
||||
if marker then marker = n .. marker end
|
||||
end
|
||||
if marker then
|
||||
local cb = after:match('^%[[%sxXoO%.%-]%]%s*(.*)$')
|
||||
local body = cb or after
|
||||
if body:match('^%s*$') then
|
||||
-- Empty item → drop to normal, clear the line, re-enter insert,
|
||||
-- then a fresh newline. Indent is dropped (matches vimwiki's
|
||||
-- "break out of the list" behaviour on an empty bullet).
|
||||
return esc .. '0DA' .. cr
|
||||
end
|
||||
return cr .. indent .. marker .. ' ' .. (cb and '[ ] ' or '')
|
||||
end
|
||||
return cr
|
||||
end
|
||||
|
||||
-- §13.1 Cluster B — table rewriters.
|
||||
|
||||
function M.table_insert(cols, rows)
|
||||
|
||||
@@ -254,6 +254,11 @@ function M.attach(bufnr, mappings)
|
||||
{ desc = 'nuwiki: cycle list symbol backward (insert)' }, bufnr)
|
||||
map('i', '<C-L><C-M>', cmd.list_toggle_or_add_checkbox,
|
||||
{ desc = 'nuwiki: toggle/add checkbox (insert)' }, bufnr)
|
||||
|
||||
-- Smart <CR>: continue list / table rows. `<expr>` so the function
|
||||
-- result is the keystrokes fed — keeps undo coherent.
|
||||
map('i', '<CR>', cmd.smart_return,
|
||||
{ desc = 'nuwiki: smart return (insert)', expr = true }, bufnr)
|
||||
end
|
||||
|
||||
-- ===== Header levels + nav =====
|
||||
|
||||
Reference in New Issue
Block a user