fix(wiki): build wiki picker list from config so it works before opening a file
The picker fetched the wiki list via workspace/executeCommand, but the LSP only starts once a vimwiki buffer exists — so a wiki could not be selected until one was already open (chicken-and-egg). Read the list straight from config instead (g:nuwiki_wikis / scalar fallback, the same source as open_wiki_path) and open the chosen index directly; that auto-starts the server via the FileType autocmd. - Vim: new public nuwiki#commands#wiki_list(); wiki_ui_select() uses it + inputlist() + :edit. Global :VimwikiUISelect / :NuwikiUISelect commands. - Neovim: config.wiki_cfg(n) + config.wiki_list() (init.lua delegates); commands.wiki_ui_select() uses them + vim.ui.select + :edit; global VimwikiUISelect / NuwikiUISelect user commands registered in setup(). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -119,33 +119,29 @@ function! nuwiki#commands#wiki_index(count) abort
|
|||||||
\ function('s:open_uri_from'))
|
\ function('s:open_uri_from'))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Vim equivalent of the Lua `wiki_ui_select` — receives the list, asks
|
" `:VimwikiUISelect` — pick a wiki from the configured list and open its
|
||||||
" via `inputlist()` (no `vim.ui.select` on plain Vim), then opens.
|
" index. The list is read straight from config (see wiki_list below), not
|
||||||
|
" from the server, so the picker works from any buffer before a wiki file is
|
||||||
|
" ever opened — opening the chosen index then auto-starts the LSP via the
|
||||||
|
" FileType autocmd. Plain Vim has no `vim.ui.select`, so use `inputlist()`.
|
||||||
function! nuwiki#commands#wiki_ui_select() abort
|
function! nuwiki#commands#wiki_ui_select() abort
|
||||||
call s:exec('nuwiki.wiki.select', [{}],
|
let l:wikis = nuwiki#commands#wiki_list()
|
||||||
\ function('s:wiki_pick'))
|
if empty(l:wikis)
|
||||||
endfunction
|
echohl WarningMsg | echom 'nuwiki: no wikis configured' | echohl None
|
||||||
|
|
||||||
function! s:wiki_pick(notification) abort
|
|
||||||
let l:rows = get(get(a:notification, 'response', {}), 'result', [])
|
|
||||||
if type(l:rows) != type([]) || empty(l:rows)
|
|
||||||
echom 'nuwiki: no wikis configured'
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
let l:prompt = ['Pick a wiki:']
|
let l:prompt = ['Select a wiki:']
|
||||||
let l:i = 0
|
let l:i = 0
|
||||||
for l:w in l:rows
|
for l:w in l:wikis
|
||||||
let l:i += 1
|
let l:i += 1
|
||||||
call add(l:prompt, l:i . '. ' . get(l:w, 'name', '?')
|
call add(l:prompt, printf('%d. %s (%s)', l:i, l:w.name, l:w.root))
|
||||||
\ . ' (' . get(l:w, 'root', '?') . ')')
|
|
||||||
endfor
|
endfor
|
||||||
let l:choice = inputlist(l:prompt)
|
let l:choice = inputlist(l:prompt)
|
||||||
if l:choice < 1 || l:choice > len(l:rows)
|
if l:choice < 1 || l:choice > len(l:wikis)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
let l:wiki_id = get(l:rows[l:choice - 1], 'id', l:choice - 1)
|
let l:w = l:wikis[l:choice - 1]
|
||||||
call s:exec('nuwiki.wiki.openIndex', [{ 'wiki': l:wiki_id }],
|
execute 'edit ' . fnameescape(l:w.root . '/' . l:w.idx . l:w.ext)
|
||||||
\ function('s:open_uri_from'))
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" `:VimwikiNextLink` / `:VimwikiPrevLink` — pure-VimL `[[…]]` jumper.
|
" `:VimwikiNextLink` / `:VimwikiPrevLink` — pure-VimL `[[…]]` jumper.
|
||||||
@@ -427,6 +423,28 @@ function! s:wiki_cfg(n) abort
|
|||||||
\ 'diary_rel': l:diary_rel, 'diary_idx': l:diary_idx }
|
\ 'diary_rel': l:diary_rel, 'diary_idx': l:diary_idx }
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Build the list of configured wikis as {name, root, ext, idx, diary_rel,
|
||||||
|
" diary_idx} dicts, straight from config — no LSP round-trip. Drives the wiki
|
||||||
|
" picker so it works before any wiki buffer (and therefore the server) exists.
|
||||||
|
" Falls back to a single entry from the scalar `g:nuwiki_wiki_root` config.
|
||||||
|
function! nuwiki#commands#wiki_list() abort
|
||||||
|
let l:list = []
|
||||||
|
if exists('g:nuwiki_wikis') && !empty(g:nuwiki_wikis)
|
||||||
|
let l:n = 0
|
||||||
|
for l:w in g:nuwiki_wikis
|
||||||
|
let l:c = s:wiki_cfg(l:n)
|
||||||
|
let l:c['name'] = get(l:w, 'name', fnamemodify(l:c.root, ':t'))
|
||||||
|
call add(l:list, l:c)
|
||||||
|
let l:n += 1
|
||||||
|
endfor
|
||||||
|
else
|
||||||
|
let l:c = s:wiki_cfg(0)
|
||||||
|
let l:c['name'] = fnamemodify(l:c.root, ':t')
|
||||||
|
call add(l:list, l:c)
|
||||||
|
endif
|
||||||
|
return l:list
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! nuwiki#commands#open_wiki_path(tab) abort
|
function! nuwiki#commands#open_wiki_path(tab) abort
|
||||||
let l:c = s:wiki_cfg(0)
|
let l:c = s:wiki_cfg(0)
|
||||||
execute (a:tab ? 'tabedit' : 'edit') . ' '
|
execute (a:tab ? 'tabedit' : 'edit') . ' '
|
||||||
|
|||||||
+21
-22
@@ -247,30 +247,29 @@ function M.wiki_tab_index(count)
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Pick a wiki and open its index. The list is read straight from config
|
||||||
|
-- (no LSP round-trip) so this works from any buffer before a wiki file is
|
||||||
|
-- ever opened; opening the index then auto-starts the server.
|
||||||
function M.wiki_ui_select()
|
function M.wiki_ui_select()
|
||||||
exec('nuwiki.wiki.select', { {} }, function(r)
|
local wikis = require('nuwiki.config').wiki_list()
|
||||||
if type(r) ~= 'table' or #r == 0 then
|
if #wikis == 0 then
|
||||||
vim.notify('nuwiki: no wikis configured')
|
vim.notify('nuwiki: no wikis configured', vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
|
end
|
||||||
|
vim.ui.select(
|
||||||
|
wikis,
|
||||||
|
{
|
||||||
|
prompt = 'Select a wiki',
|
||||||
|
format_item = function(w)
|
||||||
|
return string.format('%s (%s)', w.name, w.root)
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
function(choice)
|
||||||
|
if not choice then return end
|
||||||
|
local path = choice.root .. '/' .. choice.idx .. choice.ext
|
||||||
|
vim.cmd('edit ' .. vim.fn.fnameescape(vim.fn.expand(path)))
|
||||||
end
|
end
|
||||||
vim.ui.select(
|
)
|
||||||
r,
|
|
||||||
{
|
|
||||||
prompt = 'Pick a wiki',
|
|
||||||
format_item = function(w)
|
|
||||||
return string.format('%d. %s (%s)', (w.id or 0) + 1, w.name or '?', tostring(w.root))
|
|
||||||
end,
|
|
||||||
},
|
|
||||||
function(choice)
|
|
||||||
if not choice then return end
|
|
||||||
exec(
|
|
||||||
'nuwiki.wiki.openIndex',
|
|
||||||
{ { wiki = choice.id } },
|
|
||||||
function(rr) if rr and rr.uri then open_uri(rr.uri) end end
|
|
||||||
)
|
|
||||||
end
|
|
||||||
)
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.wiki_goto_page(name)
|
function M.wiki_goto_page(name)
|
||||||
|
|||||||
@@ -59,4 +59,39 @@ function M.apply(user)
|
|||||||
M.options = vim.tbl_deep_extend('force', M.defaults, user or {})
|
M.options = vim.tbl_deep_extend('force', M.defaults, user or {})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Resolve the config for wiki #n (0-based) without touching the LSP.
|
||||||
|
-- Priority: setup() opts.wikis → g:nuwiki_wikis (VimL) → scalar opts/g: vars.
|
||||||
|
-- Returns { name, root, ext, idx, diary_rel, diary_idx }.
|
||||||
|
function M.wiki_cfg(n)
|
||||||
|
local opts = M.options
|
||||||
|
local wikis = opts.wikis or vim.g.nuwiki_wikis or nil
|
||||||
|
local w = wikis and wikis[(n or 0) + 1] or nil
|
||||||
|
local root = (w and w.root) or opts.wiki_root or vim.g.nuwiki_wiki_root or '~/vimwiki'
|
||||||
|
local ext = (w and w.file_extension) or opts.file_extension or vim.g.nuwiki_file_extension or '.wiki'
|
||||||
|
if not ext:match('^%.') then ext = '.' .. ext end
|
||||||
|
return {
|
||||||
|
name = (w and w.name) or vim.fn.fnamemodify(vim.fn.expand(root), ':t'),
|
||||||
|
root = root,
|
||||||
|
ext = ext,
|
||||||
|
idx = (w and w.index) or 'index',
|
||||||
|
diary_rel = (w and w.diary_rel_path) or vim.g.nuwiki_diary_rel_path or 'diary',
|
||||||
|
diary_idx = (w and w.diary_index) or 'diary',
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The full list of configured wikis as wiki_cfg dicts. Falls back to a single
|
||||||
|
-- entry built from the scalar wiki_root config when no list is configured.
|
||||||
|
function M.wiki_list()
|
||||||
|
local wikis = M.options.wikis or vim.g.nuwiki_wikis or nil
|
||||||
|
local list = {}
|
||||||
|
if type(wikis) == 'table' and #wikis > 0 then
|
||||||
|
for i = 1, #wikis do
|
||||||
|
list[i] = M.wiki_cfg(i - 1)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
list[1] = M.wiki_cfg(0)
|
||||||
|
end
|
||||||
|
return list
|
||||||
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|||||||
+13
-24
@@ -18,31 +18,9 @@ local function _open(path, open_cmd)
|
|||||||
vim.cmd((open_cmd or 'edit') .. ' ' .. vim.fn.fnameescape(vim.fn.expand(path)))
|
vim.cmd((open_cmd or 'edit') .. ' ' .. vim.fn.fnameescape(vim.fn.expand(path)))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Return the config dict for wiki #n (0-based).
|
-- Return the config dict for wiki #n (0-based). See config.wiki_cfg.
|
||||||
-- Priority: setup() opts.wikis → g:nuwiki_wikis (VimL) → scalar opts/g: vars.
|
|
||||||
local function _wiki_cfg(n)
|
local function _wiki_cfg(n)
|
||||||
local opts = config.options
|
return config.wiki_cfg(n)
|
||||||
local wikis = opts.wikis
|
|
||||||
or (vim.g.nuwiki_wikis and vim.g.nuwiki_wikis or nil)
|
|
||||||
local w = wikis and wikis[(n or 0) + 1] or nil
|
|
||||||
local root = (w and w.root)
|
|
||||||
or opts.wiki_root
|
|
||||||
or vim.g.nuwiki_wiki_root
|
|
||||||
or '~/vimwiki'
|
|
||||||
local ext = (w and w.file_extension)
|
|
||||||
or opts.file_extension
|
|
||||||
or vim.g.nuwiki_file_extension
|
|
||||||
or '.wiki'
|
|
||||||
if not ext:match('^%.') then ext = '.' .. ext end
|
|
||||||
return {
|
|
||||||
root = root,
|
|
||||||
ext = ext,
|
|
||||||
idx = (w and w.index) or 'index',
|
|
||||||
diary_rel = (w and w.diary_rel_path)
|
|
||||||
or vim.g.nuwiki_diary_rel_path
|
|
||||||
or 'diary',
|
|
||||||
diary_idx = (w and w.diary_index) or 'diary',
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Compute the wiki index path for wiki #n (0-based) from config.
|
-- Compute the wiki index path for wiki #n (0-based) from config.
|
||||||
@@ -81,6 +59,16 @@ local function _setup_global_mappings()
|
|||||||
kmap('n', '<Leader>w<Leader>i', function() require('nuwiki.commands').diary_generate_index() end, o('rebuild diary index'))
|
kmap('n', '<Leader>w<Leader>i', function() require('nuwiki.commands').diary_generate_index() end, o('rebuild diary index'))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Global wiki-picker commands so a wiki can be chosen from any buffer before
|
||||||
|
-- a .wiki file exists. The picker reads its list from config (no LSP); the
|
||||||
|
-- buffer-local versions in ftplugin/vimwiki.vim shadow these inside wiki
|
||||||
|
-- buffers but dispatch to the same function.
|
||||||
|
local function _setup_global_commands()
|
||||||
|
local function ui_select() require('nuwiki.commands').wiki_ui_select() end
|
||||||
|
vim.api.nvim_create_user_command('VimwikiUISelect', ui_select, {})
|
||||||
|
vim.api.nvim_create_user_command('NuwikiUISelect', ui_select, {})
|
||||||
|
end
|
||||||
|
|
||||||
function M.setup(opts)
|
function M.setup(opts)
|
||||||
config.apply(opts or {})
|
config.apply(opts or {})
|
||||||
|
|
||||||
@@ -99,6 +87,7 @@ function M.setup(opts)
|
|||||||
|
|
||||||
lsp.register()
|
lsp.register()
|
||||||
_setup_global_mappings()
|
_setup_global_mappings()
|
||||||
|
_setup_global_commands()
|
||||||
end
|
end
|
||||||
|
|
||||||
function M.install()
|
function M.install()
|
||||||
|
|||||||
@@ -39,6 +39,14 @@ augroup nuwiki_vim_lsp_start
|
|||||||
autocmd FileType vimwiki call nuwiki#lsp#start()
|
autocmd FileType vimwiki call nuwiki#lsp#start()
|
||||||
augroup END
|
augroup END
|
||||||
|
|
||||||
|
" 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
|
||||||
|
" buffer-local :VimwikiUISelect in ftplugin/vimwiki.vim shadows these inside
|
||||||
|
" wiki buffers but dispatches to the same function.
|
||||||
|
command! -nargs=0 VimwikiUISelect call nuwiki#commands#wiki_ui_select()
|
||||||
|
command! -nargs=0 NuwikiUISelect call nuwiki#commands#wiki_ui_select()
|
||||||
|
|
||||||
" Global entry-point mappings — work from any buffer, not just .wiki files.
|
" Global entry-point mappings — work from any buffer, not just .wiki files.
|
||||||
" Files are opened directly from config (no LSP required); the server
|
" Files are opened directly from config (no LSP required); the server
|
||||||
" auto-starts via the FileType autocmd once the vimwiki buffer loads.
|
" auto-starts via the FileType autocmd once the vimwiki buffer loads.
|
||||||
|
|||||||
@@ -321,6 +321,30 @@ call s:record(
|
|||||||
\ 'conceal.cursor_line_revealed_in_normal_mode',
|
\ 'conceal.cursor_line_revealed_in_normal_mode',
|
||||||
\ '&l:concealcursor=' . &l:concealcursor)
|
\ '&l:concealcursor=' . &l:concealcursor)
|
||||||
|
|
||||||
|
" ===== Wiki picker (config-driven, no LSP) =====
|
||||||
|
|
||||||
|
" The picker builds its list straight from config so it works from any buffer
|
||||||
|
" before the LSP (which only starts on a wiki buffer) is running.
|
||||||
|
let g:nuwiki_wikis = [
|
||||||
|
\ {'name': 'Personal', 'root': '/tmp/nuwiki-a'},
|
||||||
|
\ {'name': 'Work', 'root': '/tmp/nuwiki-b', 'file_extension': 'md'},
|
||||||
|
\ ]
|
||||||
|
let s:wl = nuwiki#commands#wiki_list()
|
||||||
|
call s:record(
|
||||||
|
\ len(s:wl) == 2 ? 1 : 0,
|
||||||
|
\ 'wiki_select.lists_all_configured_wikis',
|
||||||
|
\ 'count=' . len(s:wl))
|
||||||
|
call s:record(
|
||||||
|
\ get(get(s:wl, 0, {}), 'name', '') ==# 'Personal'
|
||||||
|
\ && get(get(s:wl, 1, {}), 'name', '') ==# 'Work' ? 1 : 0,
|
||||||
|
\ 'wiki_select.preserves_names_and_order',
|
||||||
|
\ 'names=' . string(map(copy(s:wl), 'get(v:val, "name", "")')))
|
||||||
|
call s:record(
|
||||||
|
\ get(get(s:wl, 1, {}), 'ext', '') ==# '.md' ? 1 : 0,
|
||||||
|
\ 'wiki_select.honours_per_wiki_extension',
|
||||||
|
\ 'ext=' . get(get(s:wl, 1, {}), 'ext', ''))
|
||||||
|
unlet g:nuwiki_wikis
|
||||||
|
|
||||||
" ===== Wrap up =====
|
" ===== Wrap up =====
|
||||||
|
|
||||||
call add(s:results, '')
|
call add(s:results, '')
|
||||||
|
|||||||
@@ -492,6 +492,25 @@ vim.defer_fn(function()
|
|||||||
-- a populated diary, but at least confirm the maps fire without
|
-- a populated diary, but at least confirm the maps fire without
|
||||||
-- error.
|
-- error.
|
||||||
|
|
||||||
|
-- ===== Wiki picker (config-driven, no LSP) =====
|
||||||
|
-- The picker must build its list from config so it works from any buffer
|
||||||
|
-- before the server attaches.
|
||||||
|
tobj_case('wiki_select.config_list_reads_g_var', function()
|
||||||
|
vim.g.nuwiki_wikis = {
|
||||||
|
{ name = 'Personal', root = '/tmp/nuwiki-a' },
|
||||||
|
{ name = 'Work', root = '/tmp/nuwiki-b', file_extension = 'md' },
|
||||||
|
}
|
||||||
|
local list = require('nuwiki.config').wiki_list()
|
||||||
|
vim.g.nuwiki_wikis = nil
|
||||||
|
if #list ~= 2 then error('want 2 wikis, got ' .. #list) end
|
||||||
|
if list[1].name ~= 'Personal' or list[2].name ~= 'Work' then
|
||||||
|
error('names/order broken: ' .. vim.inspect(list))
|
||||||
|
end
|
||||||
|
if list[2].ext ~= '.md' then
|
||||||
|
error('per-wiki extension not honoured: ' .. tostring(list[2].ext))
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
-- ===== Wrap up =====
|
-- ===== Wrap up =====
|
||||||
|
|
||||||
table.insert(out_lines, '')
|
table.insert(out_lines, '')
|
||||||
|
|||||||
Reference in New Issue
Block a user