feat(config): P3 config batch — group 2 (create_link, dir_link, auto_header)
- 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 <dir>/<dir_link><ext> 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) <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 `<dir>/<dir_link>`
|
||||
// (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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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', {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user