From 82444f50134e7b897b8319270ba0588c01c025d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Sun, 28 Jun 2026 12:38:04 +0000 Subject: [PATCH] refactor(viml): extract shared heading_level/is_table_row/wiki_cfg; fix cap + spaceless MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the Lua util refactor. Pulls the copy-pasted VimL helpers into autoload/nuwiki/util.vim and points the per-file s: stubs at it: - heading_level (commands/folding/textobjects) — commands.vim's copy was missing the >6 level cap the others had (a 7+ `=` line returned 7). The unified version caps at 6 AND accepts spaceless headings (==Heading==), matching the server lexer + the Lua client (so fold fallback, text objects, and ]] navigation recognize spaceless headings too). - is_table_row (commands/textobjects) — identical copies. - wiki_cfg (commands/diary) — these had DRIFTED: commands.vim's included a `space` (links_space_char) key diary.vim's lacked. Unified to the superset so callers reading `.space` keep working; diary callers ignore the extra key. Also refreshes the stale plugin/nuwiki.vim header ("all logic lives in the Rust language server" → thin client; server is nuwiki-rs, downloaded). Verified: 18-case heading battery in headless vim + full editor harness 10/10. Co-Authored-By: Claude Opus 4.8 --- autoload/nuwiki/commands.vim | 28 ++--------------- autoload/nuwiki/diary.vim | 14 +-------- autoload/nuwiki/folding.vim | 10 +----- autoload/nuwiki/textobjects.vim | 12 ++----- autoload/nuwiki/util.vim | 56 +++++++++++++++++++++++++++++++++ plugin/nuwiki.vim | 5 +-- 6 files changed, 66 insertions(+), 59 deletions(-) create mode 100644 autoload/nuwiki/util.vim diff --git a/autoload/nuwiki/commands.vim b/autoload/nuwiki/commands.vim index 5922c28..ad9e529 100644 --- a/autoload/nuwiki/commands.vim +++ b/autoload/nuwiki/commands.vim @@ -468,15 +468,7 @@ endfunction " ===== Heading navigation (pure VimL) ===== function! s:heading_level(line) abort - let l:lead = matchstr(a:line, '^\s*\zs=\+\ze\s') - if l:lead ==# '' - return 0 - endif - let l:trail = matchstr(a:line, '\s\zs=\+\ze\s*$') - if len(l:trail) != len(l:lead) - return 0 - endif - return len(l:lead) + return nuwiki#util#heading_level(a:line) endfunction function! s:current_heading_level() abort @@ -593,21 +585,7 @@ endfunction " upstream g:vimwiki_list both work — and falls back to the scalar g:nuwiki_* " vars (single-wiki shorthand) and finally built-in defaults. function! s:wiki_cfg(n) abort - let l:wikis = nuwiki#config#wikis() - let l:w = (a:n >= 0 && a:n < len(l:wikis)) ? l:wikis[a:n] : {} - let l:root = expand(get(l:w, 'root', - \ get(g:, 'nuwiki_wiki_root', '~/vimwiki'))) - let l:ext = get(l:w, 'file_extension', - \ get(g:, 'nuwiki_file_extension', '.wiki')) - if l:ext[0] !=# '.' | let l:ext = '.' . l:ext | endif - let l:idx = get(l:w, 'index', 'index') - let l:diary_rel = get(l:w, 'diary_rel_path', - \ get(g:, 'nuwiki_diary_rel_path', 'diary')) - let l:diary_idx = get(l:w, 'diary_index', 'diary') - let l:space = get(l:w, 'links_space_char', - \ get(g:, 'nuwiki_links_space_char', ' ')) - return { 'root': l:root, 'ext': l:ext, 'idx': l:idx, - \ 'diary_rel': l:diary_rel, 'diary_idx': l:diary_idx, 'space': l:space } + return nuwiki#util#wiki_cfg(a:n) endfunction " Build the list of configured wikis as {name, root, ext, idx, diary_rel, @@ -1178,7 +1156,7 @@ endfunction " matches `nuwiki-lsp/src/commands.rs::ops::render_aligned_table` (nuwiki-rs repo). function! s:is_table_row(line) abort - return a:line =~# '^\s*|.*|\s*$' + return nuwiki#util#is_table_row(a:line) endfunction function! s:table_cell_starts(line) abort diff --git a/autoload/nuwiki/diary.vim b/autoload/nuwiki/diary.vim index 55acc25..ccd45a2 100644 --- a/autoload/nuwiki/diary.vim +++ b/autoload/nuwiki/diary.vim @@ -106,17 +106,5 @@ endfunction " nuwiki#config#wikis() so g:nuwiki_wikis AND an upstream g:vimwiki_list both " work, falling back to the scalar g:nuwiki_* single-wiki shorthand. function! s:wiki_cfg(n) abort - let l:wikis = nuwiki#config#wikis() - let l:w = (a:n >= 0 && a:n < len(l:wikis)) ? l:wikis[a:n] : {} - let l:root = expand(get(l:w, 'root', - \ get(g:, 'nuwiki_wiki_root', '~/vimwiki'))) - let l:ext = get(l:w, 'file_extension', - \ get(g:, 'nuwiki_file_extension', '.wiki')) - if l:ext[0] !=# '.' | let l:ext = '.' . l:ext | endif - let l:idx = get(l:w, 'index', 'index') - let l:diary_rel = get(l:w, 'diary_rel_path', - \ get(g:, 'nuwiki_diary_rel_path', 'diary')) - let l:diary_idx = get(l:w, 'diary_index', 'diary') - return { 'root': l:root, 'ext': l:ext, 'idx': l:idx, - \ 'diary_rel': l:diary_rel, 'diary_idx': l:diary_idx } + return nuwiki#util#wiki_cfg(a:n) endfunction diff --git a/autoload/nuwiki/folding.vim b/autoload/nuwiki/folding.vim index 6326167..3be6886 100644 --- a/autoload/nuwiki/folding.vim +++ b/autoload/nuwiki/folding.vim @@ -13,15 +13,7 @@ " end of buffer). function! s:heading_level(line) abort - let l:lead = matchstr(a:line, '^\s*\zs=\+\ze\s') - if empty(l:lead) | return 0 | endif - let l:lvl = strlen(l:lead) - if l:lvl > 6 | return 0 | endif - let l:trail = matchstr(a:line, '\s\zs=\+\ze\s*$') - if empty(l:trail) || strlen(l:trail) != l:lvl - return 0 - endif - return l:lvl + return nuwiki#util#heading_level(a:line) endfunction function! nuwiki#folding#expr() abort diff --git a/autoload/nuwiki/textobjects.vim b/autoload/nuwiki/textobjects.vim index 543091e..c15e80d 100644 --- a/autoload/nuwiki/textobjects.vim +++ b/autoload/nuwiki/textobjects.vim @@ -24,15 +24,7 @@ " ===== Heading helpers ===== function! s:heading_level(line) abort - let l:lead = matchstr(a:line, '^\s*\zs=\+\ze\s') - if empty(l:lead) | return 0 | endif - let l:lvl = strlen(l:lead) - if l:lvl > 6 | return 0 | endif - let l:trail = matchstr(a:line, '\s\zs=\+\ze\s*$') - if empty(l:trail) || strlen(l:trail) != l:lvl - return 0 - endif - return l:lvl + return nuwiki#util#heading_level(a:line) endfunction " Returns `[start, end]` line numbers, or `[]` if no heading found. @@ -130,7 +122,7 @@ endfunction " ===== Table cell / column ===== function! s:is_table_row(line) abort - return a:line =~# '^\s*|.*|\s*$' + return nuwiki#util#is_table_row(a:line) endfunction " Returns a list of `[start_col, end_col]` byte positions (1-based, diff --git a/autoload/nuwiki/util.vim b/autoload/nuwiki/util.vim new file mode 100644 index 0000000..03433ff --- /dev/null +++ b/autoload/nuwiki/util.vim @@ -0,0 +1,56 @@ +" autoload/nuwiki/util.vim — small shared helpers used across the VimL client +" layers (commands, folding, textobjects, diary). Keeps the formerly +" copy-pasted heading / table-row / wiki-config logic in one place. Mirrors +" lua/nuwiki/util.lua so both clients behave identically. + +" Heading level of `line` (1-6), or 0 when it isn't a heading. Mirrors the +" server lexer: balanced leading/trailing runs of `=` around a non-empty +" title, capped at level 6. Accepts both the spaced (`== H ==`) and spaceless +" (`==H==`) forms, matching vimwiki. +function! nuwiki#util#heading_level(line) abort + let l:lead = matchstr(a:line, '^\s*\zs=\+') + if empty(l:lead) + return 0 + endif + let l:lvl = strlen(l:lead) + if l:lvl > 6 + return 0 + endif + " Same-length trailing run of `=` at end of line (after optional trailing ws). + let l:trail = matchstr(a:line, '=\+\ze\s*$') + if empty(l:trail) || strlen(l:trail) != l:lvl + return 0 + endif + " Non-empty title between the runs (rules out marker-only lines like + " `======`). `.\{-}` is non-greedy so the `=\+` runs keep their length. + let l:body = matchstr(a:line, '^\s*=\+\zs.\{-}\ze=\+\s*$') + if l:body =~# '^\s*$' + return 0 + endif + return l:lvl +endfunction + +" True when `line` looks like a table row (`| … |`). +function! nuwiki#util#is_table_row(line) abort + return a:line =~# '^\s*|.*|\s*$' +endfunction + +" Resolve the config dict for wiki #n (0-based), falling back to the scalar +" g:nuwiki_* globals. Returns root/ext/idx/diary_rel/diary_idx/space. +function! nuwiki#util#wiki_cfg(n) abort + let l:wikis = nuwiki#config#wikis() + let l:w = (a:n >= 0 && a:n < len(l:wikis)) ? l:wikis[a:n] : {} + let l:root = expand(get(l:w, 'root', + \ get(g:, 'nuwiki_wiki_root', '~/vimwiki'))) + let l:ext = get(l:w, 'file_extension', + \ get(g:, 'nuwiki_file_extension', '.wiki')) + if l:ext[0] !=# '.' | let l:ext = '.' . l:ext | endif + let l:idx = get(l:w, 'index', 'index') + let l:diary_rel = get(l:w, 'diary_rel_path', + \ get(g:, 'nuwiki_diary_rel_path', 'diary')) + let l:diary_idx = get(l:w, 'diary_index', 'diary') + let l:space = get(l:w, 'links_space_char', + \ get(g:, 'nuwiki_links_space_char', ' ')) + return { 'root': l:root, 'ext': l:ext, 'idx': l:idx, + \ 'diary_rel': l:diary_rel, 'diary_idx': l:diary_idx, 'space': l:space } +endfunction diff --git a/plugin/nuwiki.vim b/plugin/nuwiki.vim index fb370fe..d16648b 100644 --- a/plugin/nuwiki.vim +++ b/plugin/nuwiki.vim @@ -4,8 +4,9 @@ " their config (lazy.nvim does this automatically through `opts`). On Vim " we start the LSP client when a vimwiki buffer is loaded. " -" This file contains wiring only — all logic lives in -" the Rust language server. +" This file contains wiring only. The editor layers (VimL + Lua) are thin +" clients; the heavy lifting is done by the nuwiki-ls language server, which +" is built from the separate nuwiki-rs repo and downloaded at install time. if exists('g:loaded_nuwiki') finish