diff --git a/autoload/nuwiki/commands.vim b/autoload/nuwiki/commands.vim index 99c5730..8fc3914 100644 --- a/autoload/nuwiki/commands.vim +++ b/autoload/nuwiki/commands.vim @@ -119,33 +119,29 @@ function! nuwiki#commands#wiki_index(count) abort \ function('s:open_uri_from')) endfunction -" Vim equivalent of the Lua `wiki_ui_select` — receives the list, asks -" via `inputlist()` (no `vim.ui.select` on plain Vim), then opens. +" `:VimwikiUISelect` — pick a wiki from the configured list and open its +" 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 - call s:exec('nuwiki.wiki.select', [{}], - \ function('s:wiki_pick')) -endfunction - -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' + let l:wikis = nuwiki#commands#wiki_list() + if empty(l:wikis) + echohl WarningMsg | echom 'nuwiki: no wikis configured' | echohl None return endif - let l:prompt = ['Pick a wiki:'] + let l:prompt = ['Select a wiki:'] let l:i = 0 - for l:w in l:rows + for l:w in l:wikis let l:i += 1 - call add(l:prompt, l:i . '. ' . get(l:w, 'name', '?') - \ . ' (' . get(l:w, 'root', '?') . ')') + call add(l:prompt, printf('%d. %s (%s)', l:i, l:w.name, l:w.root)) endfor 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 endif - let l:wiki_id = get(l:rows[l:choice - 1], 'id', l:choice - 1) - call s:exec('nuwiki.wiki.openIndex', [{ 'wiki': l:wiki_id }], - \ function('s:open_uri_from')) + let l:w = l:wikis[l:choice - 1] + execute 'edit ' . fnameescape(l:w.root . '/' . l:w.idx . l:w.ext) endfunction " `: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 } 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 let l:c = s:wiki_cfg(0) execute (a:tab ? 'tabedit' : 'edit') . ' ' diff --git a/lua/nuwiki/commands.lua b/lua/nuwiki/commands.lua index a6f9e73..92923c1 100644 --- a/lua/nuwiki/commands.lua +++ b/lua/nuwiki/commands.lua @@ -247,30 +247,29 @@ function M.wiki_tab_index(count) 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() - exec('nuwiki.wiki.select', { {} }, function(r) - if type(r) ~= 'table' or #r == 0 then - vim.notify('nuwiki: no wikis configured') - return + local wikis = require('nuwiki.config').wiki_list() + if #wikis == 0 then + vim.notify('nuwiki: no wikis configured', vim.log.levels.WARN) + 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 - 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 function M.wiki_goto_page(name) diff --git a/lua/nuwiki/config.lua b/lua/nuwiki/config.lua index 0f1404c..f7d048b 100644 --- a/lua/nuwiki/config.lua +++ b/lua/nuwiki/config.lua @@ -59,4 +59,39 @@ function M.apply(user) M.options = vim.tbl_deep_extend('force', M.defaults, user or {}) 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 diff --git a/lua/nuwiki/init.lua b/lua/nuwiki/init.lua index 06638d1..7a0217b 100644 --- a/lua/nuwiki/init.lua +++ b/lua/nuwiki/init.lua @@ -18,31 +18,9 @@ local function _open(path, open_cmd) vim.cmd((open_cmd or 'edit') .. ' ' .. vim.fn.fnameescape(vim.fn.expand(path))) end --- Return the config dict for wiki #n (0-based). --- Priority: setup() opts.wikis → g:nuwiki_wikis (VimL) → scalar opts/g: vars. +-- Return the config dict for wiki #n (0-based). See config.wiki_cfg. local function _wiki_cfg(n) - local opts = config.options - 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', - } + return config.wiki_cfg(n) end -- Compute the wiki index path for wiki #n (0-based) from config. @@ -81,6 +59,16 @@ local function _setup_global_mappings() kmap('n', 'wi', function() require('nuwiki.commands').diary_generate_index() end, o('rebuild diary index')) 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) config.apply(opts or {}) @@ -99,6 +87,7 @@ function M.setup(opts) lsp.register() _setup_global_mappings() + _setup_global_commands() end function M.install() diff --git a/plugin/nuwiki.vim b/plugin/nuwiki.vim index 793e0f0..840df90 100644 --- a/plugin/nuwiki.vim +++ b/plugin/nuwiki.vim @@ -39,6 +39,14 @@ augroup nuwiki_vim_lsp_start autocmd FileType vimwiki call nuwiki#lsp#start() 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. " Files are opened directly from config (no LSP required); the server " auto-starts via the FileType autocmd once the vimwiki buffer loads. diff --git a/scripts/test-keymaps-vim.vim b/scripts/test-keymaps-vim.vim index 908bed4..7a49e65 100644 --- a/scripts/test-keymaps-vim.vim +++ b/scripts/test-keymaps-vim.vim @@ -321,6 +321,30 @@ call s:record( \ 'conceal.cursor_line_revealed_in_normal_mode', \ '&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 ===== call add(s:results, '') diff --git a/scripts/test-keymaps.lua b/scripts/test-keymaps.lua index db7c62a..d15ba6e 100644 --- a/scripts/test-keymaps.lua +++ b/scripts/test-keymaps.lua @@ -492,6 +492,25 @@ vim.defer_fn(function() -- a populated diary, but at least confirm the maps fire without -- 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 ===== table.insert(out_lines, '')