" scripts/test-keymaps-vim.vim — driven by scripts/test-keymaps-vim.sh. " " Pure-VimL keymap regression suite. Plain Vim's LSP path " (vim-lsp + async.vim) talks to the server via timers, which won't " reliably fire inside `vim -e -s` headless mode — so the cases here " are restricted to the bindings whose RHS is implemented entirely in " VimL (header/link nav, `o`/`O` bullet continuation, the wrap step " of ``). LSP-roundtrip bindings (``, `=`, `-`, …) get " their coverage from the Neovim harness in scripts/test-keymaps.lua " — same Lua codepath, same server. let s:results = [] let s:pass = 0 let s:fail = 0 let s:out = $NUWIKI_KEYMAP_RESULTS function! s:record(ok, name, detail) abort let l:line = (a:ok ? 'PASS' : 'FAIL') . ' ' . a:name if a:detail !=# '' let l:line .= ' — ' . a:detail endif call add(s:results, l:line) if a:ok let s:pass += 1 else let s:fail += 1 endif endfunction function! s:set_buf(lines) abort silent %delete _ call setline(1, a:lines) endfunction function! s:lines() abort return getline(1, '$') endfunction function! s:run(name, opts) abort try call s:set_buf(a:opts.lines) let l:cursor = get(a:opts, 'cursor', [1, 1]) call cursor(l:cursor[0], l:cursor[1]) " feedkeys with 'tx' — t: typed (respects maps), x: execute now. let l:keys = a:opts.keys call feedkeys(l:keys, 'tx') let l:detail = '' if has_key(a:opts, 'expect_lines') if s:lines() !=# a:opts.expect_lines let l:detail = 'lines mismatch — expected ' . string(a:opts.expect_lines) \ . ', actual ' . string(s:lines()) call s:record(0, a:name, l:detail) return endif endif if has_key(a:opts, 'expect_line') && has_key(a:opts, 'expect_at') let l:got = getline(a:opts.expect_at) if l:got !=# a:opts.expect_line let l:detail = 'line ' . a:opts.expect_at . ' — expected ' . string(a:opts.expect_line) \ . ', got ' . string(l:got) call s:record(0, a:name, l:detail) return endif endif if has_key(a:opts, 'expect_cursor_line') let l:lnum = line('.') if l:lnum != a:opts.expect_cursor_line let l:detail = 'cursor line — expected ' . a:opts.expect_cursor_line \ . ', got ' . l:lnum call s:record(0, a:name, l:detail) return endif endif call s:record(1, a:name, '') catch call s:record(0, a:name, v:exception) endtry endfunction " ===== Smoke: ftplugin loaded + commands defined ===== if &filetype !=# 'vimwiki' call s:record(0, 'ftplugin.attached', 'filetype is ' . &filetype . ' (expected vimwiki)') else call s:record(1, 'ftplugin.attached', '') endif if exists(':VimwikiTOC') == 2 call s:record(1, 'cmd.VimwikiTOC_defined', '') else call s:record(0, 'cmd.VimwikiTOC_defined', ':VimwikiTOC not registered') endif if exists(':NuwikiIndex') == 2 call s:record(1, 'cmd.NuwikiIndex_defined', '') else call s:record(0, 'cmd.NuwikiIndex_defined', ':NuwikiIndex not registered') endif " ===== Header nav (pure VimL) ===== call s:run('headers.]]_jumps_to_next', { \ 'lines': ['= One =', 'body', '== Two ==', 'body', '= Three ='], \ 'cursor': [1, 1], \ 'keys': ']]', \ 'expect_cursor_line': 3, \ }) call s:run('headers.[[_jumps_to_prev', { \ 'lines': ['= One =', 'body', '== Two ==', 'body', '= Three ='], \ 'cursor': [5, 1], \ 'keys': '[[', \ 'expect_cursor_line': 3, \ }) call s:run('headers.]u_jumps_to_parent', { \ 'lines': ['= Parent =', '== Child ==', 'body'], \ 'cursor': [3, 1], \ 'keys': ']u', \ 'expect_cursor_line': 1, \ }) call s:run('headers.]=_jumps_to_next_sibling', { \ 'lines': ['= A =', '== child ==', 'body', '= B ='], \ 'cursor': [1, 1], \ 'keys': ']=', \ 'expect_cursor_line': 4, \ }) " ===== Link nav (pure VimL) ===== call s:run('links._finds_next_wikilink', { \ 'lines': ['intro [[A]] more [[B]] end'], \ 'cursor': [1, 1], \ 'keys': "\", \ 'expect_cursor_line': 1, \ }) " ===== first press wraps bare word ===== call s:run('cr.wraps_bare_word_first_press', { \ 'lines': ['MyPage rest'], \ 'cursor': [1, 3], \ 'keys': "\", \ 'expect_line': '[[MyPage]] rest', \ 'expect_at': 1, \ }) " ===== Bullet continuation ===== call s:run('lists.o_continues_list_marker', { \ 'lines': ['- first'], \ 'cursor': [1, 1], \ 'keys': "o\", \ 'expect_lines': ['- first', '- '], \ }) call s:run('lists.o_continues_checkbox', { \ 'lines': ['- [ ] first'], \ 'cursor': [1, 1], \ 'keys': "o\", \ 'expect_lines': ['- [ ] first', '- [ ] '], \ }) call s:run('lists.O_continues_above', { \ 'lines': ['- second'], \ 'cursor': [1, 1], \ 'keys': "O\", \ 'expect_lines': ['- ', '- second'], \ }) " ===== Insert-mode smart_return (Cluster 5) ===== call s:run('cr.continues_list_marker', { \ 'lines': ['- first'], \ 'cursor': [1, 7], \ 'keys': "A\second\", \ 'expect_lines': ['- first', '- second'], \ }) call s:run('cr.continues_checkbox', { \ 'lines': ['- [ ] first'], \ 'cursor': [1, 11], \ 'keys': "A\second\", \ 'expect_lines': ['- [ ] first', '- [ ] second'], \ }) call s:run('cr.breaks_on_empty_list_line', { \ 'lines': ['- '], \ 'cursor': [1, 2], \ 'keys': "A\plain\", \ 'expect_lines': ['', 'plain'], \ }) call s:run('cr.adds_new_table_row', { \ 'lines': ['| a | b |'], \ 'cursor': [1, 4], \ 'keys': "A\x\", \ 'expect_lines': ['| a | b |', '|x | |'], \ }) " ===== Insert-mode smart_tab / smart_shift_tab (Cluster 6) ===== call s:run('tab.next_cell_in_table', { \ 'lines': ['| a | b |'], \ 'cursor': [1, 3], \ 'keys': "A\x\", \ 'expect_lines': ['| a | b |', '|x | |'], \ }) call s:run('tab.cycles_to_next_row_when_not_last', { \ 'lines': ['| a | b |', '|---|---|', '| c | d |'], \ 'cursor': [1, 7], \ 'keys': "i\x\", \ 'expect_lines': ['| a | b |', '|---|---|', '|x c | d |'], \ }) call s:run('tab.inserts_row_only_when_on_last_row', { \ 'lines': ['| a | b |', '|---|---|', '| c | d |'], \ 'cursor': [3, 7], \ 'keys': "i\x\", \ 'expect_lines': ['| a | b |', '|---|---|', '| c | d |', '|x | |'], \ }) call s:run('tab.from_first_cell_moves_to_second', { \ 'lines': ['| a | b |'], \ 'cursor': [1, 3], \ 'keys': "i\x\", \ 'expect_lines': ['| a |x b |'], \ }) call s:run('shift_tab.prev_cell_in_table', { \ 'lines': ['| a | b |'], \ 'cursor': [1, 7], \ 'keys': "i\x\", \ 'expect_lines': ['|x a | b |'], \ }) " ===== Named link commands (Cluster 1) ===== if exists(':VimwikiNextLink') == 2 call s:record(1, 'cmd.VimwikiNextLink_defined', '') else call s:record(0, 'cmd.VimwikiNextLink_defined', ':VimwikiNextLink not registered') endif if exists(':VimwikiPrevLink') == 2 call s:record(1, 'cmd.VimwikiPrevLink_defined', '') else call s:record(0, 'cmd.VimwikiPrevLink_defined', ':VimwikiPrevLink not registered') endif if exists(':VimwikiBaddLink') == 2 call s:record(1, 'cmd.VimwikiBaddLink_defined', '') else call s:record(0, 'cmd.VimwikiBaddLink_defined', ':VimwikiBaddLink not registered') endif " ===== Text objects (Cluster 3) ===== " Exercise the pure helpers directly — visual-mode mark inspection in " `vim -e -s` is unreliable for the same reasons the Lua harness uses " the helper-level tests. call s:set_buf(['= h1 =', 'a', '== h2 ==', 'b', '== h3 ==', 'c']) call s:record( \ nuwiki#textobjects#_heading_block(1, 0) ==# [1, 2] \ ? 1 : 0, \ 'textobj.heading_block_section_only', \ 'got ' . string(nuwiki#textobjects#_heading_block(1, 0))) call s:record( \ nuwiki#textobjects#_heading_block(1, 1) ==# [1, 6] \ ? 1 : 0, \ 'textobj.heading_block_subtree', \ 'got ' . string(nuwiki#textobjects#_heading_block(1, 1))) let s:cells = nuwiki#textobjects#_cell_ranges('| a | b | c |') call s:record( \ len(s:cells) == 3 ? 1 : 0, \ 'textobj.cell_ranges_three_cells', \ len(s:cells) == 3 ? '' : 'got ' . string(s:cells)) let s:cell_idx = nuwiki#textobjects#_current_cell_for_line('| a | b | c |', 8) call s:record( \ s:cell_idx == 2 ? 1 : 0, \ 'textobj.current_cell_picks_middle', \ s:cell_idx == 2 ? '' : 'got idx=' . s:cell_idx) call setline(1, ['before', '| a | b |', '| c | d |', '', 'after']) let s:bnd = nuwiki#textobjects#_table_bounds(2) call s:record( \ s:bnd ==# [2, 3] ? 1 : 0, \ 'textobj.table_bounds_walks_contig_block', \ s:bnd ==# [2, 3] ? '' : 'got ' . string(s:bnd)) " Operator-pending: `dah` should delete the heading section. call s:set_buf(['= h1 =', 'body', '= h2 =', 'rest']) call cursor(1, 1) call feedkeys('dah', 'tx') let s:dah = getline(1, '$') call s:record( \ s:dah ==# ['= h2 =', 'rest'] || s:dah ==# ['', '= h2 =', 'rest'] \ ? 1 : 0, \ 'textobj.dah_deletes_heading_section', \ 'got ' . string(s:dah)) " ===== Folding ===== call setline(1, ['= h1 =', 'a', '== h2 ==', 'b', 'c']) let &l:foldmethod = 'expr' let &l:foldexpr = 'nuwiki#folding#expr()' call s:record( \ foldlevel(1) > 0 ? 1 : 0, \ 'folding.heading_starts_fold', \ 'foldlevel(1)=' . foldlevel(1)) call s:record( \ foldlevel(2) > 0 ? 1 : 0, \ 'folding.body_inherits_fold', \ 'foldlevel(2)=' . foldlevel(2)) " ftplugin sets `foldlevel=99` so every heading-block fold starts open " rather than collapsed on file open. call s:record( \ &l:foldlevel == 99 ? 1 : 0, \ 'folding.starts_with_all_folds_open', \ '&l:foldlevel=' . &l:foldlevel) " ===== Conceal ===== " The ftplugin reveals the cursor line in normal mode (concealcursor has no " `n`) so left/right motions move over visible characters instead of stalling " on hidden conceal regions like a wikilink's `[[`/`]]`. call s:record( \ &l:conceallevel == 2 ? 1 : 0, \ 'conceal.level_is_two', \ '&l:conceallevel=' . &l:conceallevel) call s:record( \ &l:concealcursor !~# 'n' ? 1 : 0, \ 'conceal.cursor_line_revealed_in_normal_mode', \ '&l:concealcursor=' . &l:concealcursor) " ===== Wrap up ===== call add(s:results, '') call add(s:results, printf('SUMMARY: %d passed, %d failed', s:pass, s:fail)) call writefile(s:results, s:out) execute (s:fail == 0 ? 'qall!' : 'cquit!')