From 87d14310fc23546150b615cdd2c9048a4f6a668a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Wed, 3 Jun 2026 12:19:37 +0000 Subject: [PATCH] =?UTF-8?q?feat(config):=20P3=20config=20batch=20=E2=80=94?= =?UTF-8?q?=20group=202=20(create=5Flink,=20dir=5Flink,=20auto=5Fheader)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - create_link (default true): server resolve_target_uri now only synthesises a missing-page URI when create_link is set; when off, following a link to a non-existent page returns no location (no-op). - dir_link: a [[dir/]] link now resolves to / when dir_link is set, else opens the directory itself. Uses the already-parsed LinkTarget.is_directory. - auto_header (default off): new wiki pages get a `= Stem =` header derived from the filename, skipping the wiki index + diary index. Client-side BufNewFile autocmd in both clients (init.lua + plugin/nuwiki.vim), with auto_header handlers in both command layers (trailing-slash-robust root matching). Tests: cmd.auto_header_inserts + cmd.auto_header_skips_diary_index (nvim) / cmd.auto_header_skips_index (vim). Full rust suite + both harnesses green (Neovim 307, Vim 301/18/21). Co-Authored-By: Claude Opus 4.8 (1M context) --- autoload/nuwiki/commands.vim | 24 ++++++++++++++++++++ crates/nuwiki-lsp/src/lib.rs | 24 +++++++++++++++++++- development/tests/test-keymaps-vim.vim | 17 ++++++++++++++ development/tests/test-keymaps.lua | 25 +++++++++++++++++++++ lua/nuwiki/commands.lua | 31 ++++++++++++++++++++++++++ lua/nuwiki/init.lua | 16 +++++++++++++ plugin/nuwiki.vim | 13 +++++++++++ 7 files changed, 149 insertions(+), 1 deletion(-) diff --git a/autoload/nuwiki/commands.vim b/autoload/nuwiki/commands.vim index f1efa8a..dc4ad85 100644 --- a/autoload/nuwiki/commands.vim +++ b/autoload/nuwiki/commands.vim @@ -190,6 +190,30 @@ function! nuwiki#commands#auto_chdir() abort endif endfunction +" vimwiki `auto_header`: on a new wiki page, insert a level-1 header derived +" from the filename. Skips the wiki index + diary index pages; only acts on an +" empty new buffer. Wired from a BufNewFile autocmd. +function! nuwiki#commands#auto_header() abort + if line('$') > 1 || getline(1) !=# '' + return + endif + let l:file = expand('%:p') + for l:c in nuwiki#commands#wiki_list() + let l:root = fnamemodify(expand(l:c.root), ':p') + if l:root[len(l:root) - 1:] !=# '/' | let l:root .= '/' | endif + if l:file[: len(l:root) - 1] ==# l:root + let l:index_path = l:root . l:c.idx . l:c.ext + let l:diary_index_path = l:root . l:c.diary_rel . '/' . l:c.diary_idx . l:c.ext + if l:file ==# l:index_path || l:file ==# l:diary_index_path + return + endif + break + endif + endfor + call setline(1, '= ' . expand('%:t:r') . ' =') + call append(1, '') +endfunction + " :VimwikiShowVersion / :NuwikiShowVersion — echo the nuwiki version and the " host editor, mirroring upstream's version banner. function! nuwiki#commands#show_version() abort diff --git a/crates/nuwiki-lsp/src/lib.rs b/crates/nuwiki-lsp/src/lib.rs index 931cec9..1326b02 100644 --- a/crates/nuwiki-lsp/src/lib.rs +++ b/crates/nuwiki-lsp/src/lib.rs @@ -269,7 +269,29 @@ impl Backend { } } let path = target.path.as_deref()?; - return synthesise_page_uri(&source_wiki.config, path); + // vimwiki `dir_link`: a `[[dir/]]` link opens `/` + // (e.g. `index` → `dir/index.wiki`); empty → the directory itself. + if target.is_directory { + let dl = source_wiki.config.dir_link.trim(); + if dl.is_empty() { + let mut p = source_wiki.config.root.clone(); + for seg in path.split('/').filter(|s| !s.is_empty()) { + p.push(seg); + } + return Url::from_file_path(p).ok(); + } + let joined = format!("{}/{dl}", path.trim_end_matches('/')); + return synthesise_page_uri(&source_wiki.config, &joined); + } + // vimwiki `create_link`: when off, only follow to a page that + // already exists on disk — never synthesise a new one. + let uri = synthesise_page_uri(&source_wiki.config, path)?; + if !source_wiki.config.create_link + && !uri.to_file_path().map(|p| p.exists()).unwrap_or(false) + { + return None; + } + return Some(uri); } // No wiki registered (no `initializationOptions`, no // workspace folder). Fall back to the source buffer's diff --git a/development/tests/test-keymaps-vim.vim b/development/tests/test-keymaps-vim.vim index f796b02..8d83251 100644 --- a/development/tests/test-keymaps-vim.vim +++ b/development/tests/test-keymaps-vim.vim @@ -728,6 +728,23 @@ endtry call s:record(s:nm_err ==# '' ? 1 : 0, \ 'cmd.VimwikiSearch_no_match_graceful', 'err=' . s:nm_err) +" ===== auto_header inserts `= Stem =` on a new wiki page; skips the index ===== +let s:ah_root = fnamemodify(expand(nuwiki#commands#wiki_list()[0].root), ':p') +if s:ah_root[len(s:ah_root)-1:] !=# '/' | let s:ah_root .= '/' | endif +new +execute 'file ' . fnameescape('/tmp/nuwiki-ah-plain/MyNewPage.wiki') +call nuwiki#commands#auto_header() +call s:record(getline(1) ==# '= MyNewPage =' ? 1 : 0, + \ 'cmd.auto_header_inserts', 'line1=' . getline(1)) +bwipeout! +new +execute 'file ' . fnameescape(s:ah_root . 'index.wiki') +call nuwiki#commands#auto_header() +call s:record(getline(1) ==# '' ? 1 : 0, + \ 'cmd.auto_header_skips_index', 'line1=' . getline(1)) +bwipeout! +unlet s:ah_root + " ===== :VimwikiGoto -nargs=* ===== " Was -nargs=1, so a page name with a space raised E488 (and the empty-arg " prompt fallback was unreachable). Now -nargs=*: a multi-word arg must parse. diff --git a/development/tests/test-keymaps.lua b/development/tests/test-keymaps.lua index 5e7fd64..c0e68ae 100644 --- a/development/tests/test-keymaps.lua +++ b/development/tests/test-keymaps.lua @@ -305,6 +305,31 @@ vim.defer_fn(function() vim.cmd('bwipeout!') end + -- auto_header: the function inserts `= Stem =` on an empty new wiki buffer, + -- and skips the wiki index page. Operate on dedicated buffer handles so the + -- harness's own buffer is untouched. + do + local wroot = require('nuwiki.config').wiki_cfg(0).root + local root = vim.fn.fnamemodify(vim.fn.expand(wroot), ':p') + if root:sub(-1) ~= '/' then root = root .. '/' end + local function ah_line(name) + local buf = vim.api.nvim_create_buf(true, false) + vim.api.nvim_buf_set_name(buf, name) + require('nuwiki.commands').auto_header(buf) + local line = vim.api.nvim_buf_get_lines(buf, 0, 1, false)[1] or '' + vim.api.nvim_buf_delete(buf, { force = true }) + return line + end + -- A normal page (outside any wiki root) → header inserted. + local got = ah_line('/tmp/nuwiki-ah-plain/MyNewPage.wiki') + record(got == '= MyNewPage =', 'cmd.auto_header_inserts', 'line1=' .. got) + -- The diary index page → skipped (no header). (The wiki index itself is the + -- harness's open buffer, so use the diary index to avoid an E95 collision.) + local c = require('nuwiki.config').wiki_cfg(0) + local idx_line = ah_line(root .. c.diary_rel .. '/' .. c.diary_idx .. '.wiki') + record(idx_line == '', 'cmd.auto_header_skips_diary_index', 'line1=' .. idx_line) + end + -- ===== Lists ===== run('list.toggle_via_C-Space', { diff --git a/lua/nuwiki/commands.lua b/lua/nuwiki/commands.lua index b48c631..eee6e6d 100644 --- a/lua/nuwiki/commands.lua +++ b/lua/nuwiki/commands.lua @@ -285,6 +285,37 @@ function M.show_version() }, false, {}) end +-- vimwiki `auto_header`: on a new wiki page, insert a level-1 header derived +-- from the filename. Skips the wiki index + diary index pages, and only acts +-- on a genuinely empty buffer. Called from a BufNewFile autocmd. +function M.auto_header(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + if #lines > 1 or (lines[1] or '') ~= '' then + return -- not an empty new buffer + end + local file = vim.api.nvim_buf_get_name(bufnr) + local config = require('nuwiki.config') + for i = 0, 64 do + local c = config.wiki_cfg(i) + if not c then break end + local root = vim.fn.fnamemodify(vim.fn.expand(c.root), ':p') + if root:sub(-1) ~= '/' then root = root .. '/' end + if file:sub(1, #root) == root then + -- Skip the wiki index and the diary index pages. + local index_path = root .. c.idx .. c.ext + local diary_index_path = root .. c.diary_rel .. '/' .. c.diary_idx .. c.ext + if file == index_path or file == diary_index_path then + return + end + break + end + if not (config.options.wikis or vim.g.nuwiki_wikis) then break end + end + local title = vim.fn.fnamemodify(file, ':t:r') + vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, { '= ' .. title .. ' =', '' }) +end + -- :VimwikiSearch / :VWS {pattern} — search the current wiki's files (upstream -- scopes to the wiki, not the editor CWD). Resolves the wiki whose root holds -- the current file, else the first configured wiki, else CWD. Empty pattern diff --git a/lua/nuwiki/init.lua b/lua/nuwiki/init.lua index be37b71..9443799 100644 --- a/lua/nuwiki/init.lua +++ b/lua/nuwiki/init.lua @@ -113,6 +113,22 @@ function M.setup(opts) vim.g.nuwiki_no_calendar = 1 end + -- vimwiki `auto_header` (default off): insert a level-1 header from the + -- filename on new wiki pages. BufNewFile (not FileType) so it only fires for + -- genuinely new files. + if config.options.auto_header == true then + local ext = (config.options.file_extension or '.wiki'):gsub('^%.', '') + local pats = { '*.wiki' } + if ext ~= 'wiki' and ext ~= '' then + pats[#pats + 1] = '*.' .. ext + end + vim.api.nvim_create_autocmd('BufNewFile', { + pattern = pats, + group = vim.api.nvim_create_augroup('nuwiki_auto_header', { clear = true }), + callback = function(a) require('nuwiki.commands').auto_header(a.buf) end, + }) + end + lsp.register() _setup_global_mappings() _setup_global_commands() diff --git a/plugin/nuwiki.vim b/plugin/nuwiki.vim index 003afdb..d70300b 100644 --- a/plugin/nuwiki.vim +++ b/plugin/nuwiki.vim @@ -69,6 +69,19 @@ augroup nuwiki_vim_lsp_start autocmd FileType vimwiki call nuwiki#lsp#start() augroup END +" vimwiki `auto_header` (default off): insert a level-1 header from the filename +" on new wiki pages. BufNewFile fires only for genuinely new files. Matches the +" configured extension(s). +if get(g:, 'nuwiki_auto_header', 0) + let s:awh_ext = get(g:, 'nuwiki_file_extension', '.wiki') + let s:awh_ext = s:awh_ext[0] ==# '.' ? s:awh_ext : '.' . s:awh_ext + augroup nuwiki_auto_header + autocmd! + execute 'autocmd BufNewFile *' . s:awh_ext . ' call nuwiki#commands#auto_header()' + augroup END + unlet s:awh_ext +endif + " Global wiki-picker commands — available from any buffer so a wiki can be " chosen before a .wiki file exists. The picker reads its list from config " (no LSP); opening the chosen index then auto-starts the server. The