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:
2026-05-12 21:34:36 +00:00
parent fd4d902fde
commit e347f605ff
5 changed files with 125 additions and 0 deletions
+42
View File
@@ -684,6 +684,48 @@ function! nuwiki#commands#paste_url() abort
call s:exec('nuwiki.link.pasteUrl', l:args)
endfunction
" Smart <CR> for insert mode — vimwiki `:VimwikiReturn` parity.
" Used as `inoremap <expr> <CR> nuwiki#commands#smart_return()`. The
" returned string is fed into Vim's input stream after the function
" finishes, keeping the undo block coherent.
function! nuwiki#commands#smart_return() abort
let l:line = getline('.')
if l:line =~# '^\s*|.*|\s*$'
let l:n = max([len(split(l:line, '|', 1)) - 2, 1])
let l:new_row = '|' . repeat(' |', l:n)
let l:row = line('.')
call append(l:row, l:new_row)
call cursor(l:row + 1, 3)
return ''
endif
let l:m = matchlist(l:line, '^\(\s*\)\([-*#]\)\s\(.*\)$')
if empty(l:m)
let l:m2 = matchlist(l:line, '^\(\s*\)\(\d\+\)\([.)]\)\s\(.*\)$')
if !empty(l:m2)
let l:m = [l:line, l:m2[1], l:m2[2] . l:m2[3], l:m2[4]]
endif
endif
if !empty(l:m)
let l:indent = l:m[1]
let l:marker = l:m[2]
let l:rest = l:m[3]
let l:cb = matchlist(l:rest, '^\(\[[ xXoO.\-]\]\)\s*\(.*\)$')
let l:has_cb = !empty(l:cb)
let l:body = l:has_cb ? l:cb[2] : l:rest
if l:body =~# '^\s*$'
call setline('.', '')
call cursor(line('.'), 1)
return "\<CR>"
endif
let l:cont = l:indent . l:marker . ' '
if l:has_cb
let l:cont .= '[ ] '
endif
return "\<CR>" . l:cont
endif
return "\<CR>"
endfunction
" `:VimwikiNormalizeLink` — wrap the word at cursor as `[[word]]`
" without following. Pure-VimL.
function! nuwiki#commands#normalize_link() abort