feat(calendar): integrate calendar-vim with wiki diary paths
CI / cargo fmt --check (push) Successful in 34s
CI / cargo clippy (push) Successful in 43s
CI / cargo test (push) Successful in 41s
CI / editor keymaps (push) Successful in 1m29s

Wire g:calendar_action/g:calendar_sign to nuwiki diary hooks that
resolve diary paths from the wiki root config instead of calendar-vim's
default ~/diary. Auto-detected on FileType vimwiki when calendar-vim is
present; opt out with g:nuwiki_use_calendar=0 or g:nuwiki_no_calendar.

- autoload/nuwiki/diary.vim: calendar_action/calendar_sign using wiki cfg;
  open diary in the previous window and close the calendar window after.
- autoload/vimwiki/diary.vim: vimwiki# aliases for drop-in compatibility.
- plugin/nuwiki.vim + lua/nuwiki/init.lua: auto-wire hooks, overwriting
  calendar-vim's defaults while respecting user-set custom hooks.
- lua/nuwiki/config.lua: add use_calendar option (on by default).
- development/start-*.sh + _common.sh: clone calendar-vim, fix vimrc var
  expansion, pass wiki root to the Vim/Neovim clients.
This commit is contained in:
2026-05-31 21:34:41 -03:00
parent 39692ba99f
commit 7a3e11cfeb
9 changed files with 211 additions and 36 deletions
+97
View File
@@ -0,0 +1,97 @@
" autoload/nuwiki/diary.vim — diary-specific functions for nuwiki
" Includes calendar-vim integration functions
" ===== Calendar-Vim Integration =====
" Function: nuwiki#diary#calendar_action(day, month, year, week, dir)
" Called by calendar-vim when user presses Enter on a date.
" day/month/year are numeric integers. week and dir are not used.
" Opens or creates the diary entry for the given date.
function! nuwiki#diary#calendar_action(day, month, year, week, dir) abort
" Build YYYY-MM-DD from the parts passed by calendar-vim
let l:date_str = printf('%04d-%02d-%02d', a:year, a:month, a:day)
" Get wiki configuration for the first wiki (index 0)
let l:c = s:wiki_cfg(0)
" Construct file path: {root}/{diary_rel}/{YYYY-MM-DD}.{ext}
let l:file_path = l:c.root . '/' . l:c.diary_rel . '/' . l:date_str . l:c.ext
" Remember the calendar window so we can close it once the diary opens.
let l:cal_winid = win_getid()
" Open the diary file in a different window. If a previous window exists go
" there; otherwise split / vsplit / new depending on a:dir.
if winnr('#') > 0
execute 'wincmd p'
if !&hidden && &modified
execute 'new'
endif
elseif a:dir ==# 'V'
execute 'vsplit'
else
execute 'split'
endif
execute 'edit ' . fnameescape(l:file_path)
if !filereadable(l:file_path)
if l:c.ext[0] ==# '.'
execute 'setfiletype ' . l:c.ext[1:]
else
execute 'setfiletype ' . l:c.ext
endif
endif
" Close the calendar window now that the diary is open (keep at least one).
if win_getid() != l:cal_winid && win_id2win(l:cal_winid) > 0 && winnr('$') > 1
let l:diary_winid = win_getid()
if win_gotoid(l:cal_winid)
close
endif
call win_gotoid(l:diary_winid)
endif
endfunction
" Function: nuwiki#diary#calendar_sign(day, month, year)
" Called by calendar-vim to determine which dates show markers (*)
" day, month, year are numeric integers (1-based).
" Returns a single-character string to use as marker (e.g. '*' or ''),
" or 1/0 for 1-indexed calendars.
" calendar-vim's default expects the return value to be a single character
" to display; returning any non-empty string shows a marker.
function! nuwiki#diary#calendar_sign(day, month, year) abort
" Build YYYY-MM-DD from the parts passed by calendar-vim
let l:date_str = printf('%04d-%02d-%02d', a:year, a:month, a:day)
" Get wiki configuration for the first wiki (index 0)
let l:c = s:wiki_cfg(0)
" Construct expected file path
let l:file_path = l:c.root . '/' . l:c.diary_rel . '/' . l:date_str . l:c.ext
" Return a marker if the file exists, empty string otherwise
if filereadable(l:file_path)
return '*'
endif
return ''
endfunction
" Helper: get wiki configuration for wiki n (0-based).
" Mirrors the same logic used by nuwiki#commands#open_diary_path etc.
function! s:wiki_cfg(n) abort
let l:w = {}
if exists('g:nuwiki_wikis') && len(g:nuwiki_wikis) > a:n
let l:w = g:nuwiki_wikis[a:n]
endif
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 }
endfunction
+14
View File
@@ -0,0 +1,14 @@
" autoload/vimwiki/diary.vim — vimwiki namespace aliases for nuwiki diary functions
" Provides drop-in compatibility for vimwiki-dependent plugins like calendar-vim
" vimwiki#diary#calendar_action(day, month, year, week, dir)
" Alias to nuwiki#diary#calendar_action for calendar-vim compatibility
function! vimwiki#diary#calendar_action(day, month, year, week, dir) abort
return nuwiki#diary#calendar_action(a:day, a:month, a:year, a:week, a:dir)
endfunction
" vimwiki#diary#calendar_sign(day, month, year)
" Alias to nuwiki#diary#calendar_sign for calendar-vim compatibility
function! vimwiki#diary#calendar_sign(day, month, year) abort
return nuwiki#diary#calendar_sign(a:day, a:month, a:year)
endfunction
+14
View File
@@ -275,6 +275,20 @@ reset_dirs() {
done done
} }
# ensure_calendar_vim — clone calendar-vim into dev dir for testing
ensure_calendar_vim() {
local repo="$DEV_DIR/calendar-vim"
if [[ -d "$repo/.git" ]]; then
return
fi
if ! command -v git >/dev/null 2>&1; then
log "git not found — skipping calendar-vim"
return 1
fi
log "cloning https://github.com/mattn/calendar-vim.git → $repo"
git clone --depth 1 https://github.com/mattn/calendar-vim.git "$repo" >/dev/null 2>&1
}
seed_wiki() { seed_wiki() {
if [[ "${NUWIKI_DEV_NO_SEED:-0}" == "1" ]]; then if [[ "${NUWIKI_DEV_NO_SEED:-0}" == "1" ]]; then
log "NUWIKI_DEV_NO_SEED=1 — skipping wiki seed (using $WIKI_DIR as-is)" log "NUWIKI_DEV_NO_SEED=1 — skipping wiki seed (using $WIKI_DIR as-is)"
+7 -10
View File
@@ -54,17 +54,13 @@ vim.env.XDG_DATA_HOME = '$DATA_DIR'
vim.env.XDG_CONFIG_HOME = '$CONFIG_DIR' vim.env.XDG_CONFIG_HOME = '$CONFIG_DIR'
vim.opt.runtimepath:prepend('$REPO_ROOT') vim.opt.runtimepath:prepend('$REPO_ROOT')
vim.opt.runtimepath:append('$DEV_DIR/calendar-vim')
vim.g.nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls' vim.g.nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls'
vim.g.nuwiki_wiki_root = '$WIKI_DIR'
-- Friendly defaults for an interactive smoke test. vim.g.nuwiki_file_extension = '.wiki'
vim.opt.number = true vim.g.nuwiki_syntax = 'vimwiki'
vim.opt.signcolumn = 'yes' vim.g.nuwiki_diary_rel_path = 'diary'
vim.opt.termguicolors = true
vim.opt.swapfile = false
vim.opt.writebackup = false
vim.opt.undofile = false
vim.g.mapleader = ' '
require('nuwiki').setup({ require('nuwiki').setup({
wiki_root = '$WIKI_DIR', wiki_root = '$WIKI_DIR',
@@ -74,13 +70,14 @@ require('nuwiki').setup({
-- they're swallowed; with it they land in :messages. -- they're swallowed; with it they land in :messages.
vim.lsp.set_log_level('info') vim.lsp.set_log_level('info')
print('[nuwiki-dev] ready — wiki root: $WIKI_DIR') print('[nuwiki-dev] ready — wiki root: ' .. '$WIKI_DIR')
print('[nuwiki-dev] :LspInfo to inspect, :checkhealth nuwiki for diagnostics') print('[nuwiki-dev] :LspInfo to inspect, :checkhealth nuwiki for diagnostics')
EOF EOF
} }
main() { main() {
ensure_binary ensure_binary
ensure_calendar_vim
seed_wiki seed_wiki
write_init write_init
reset_dirs "$STATE_DIR" "$DATA_DIR" "$CONFIG_DIR" reset_dirs "$STATE_DIR" "$DATA_DIR" "$CONFIG_DIR"
+15 -15
View File
@@ -114,25 +114,27 @@ write_vimrc() {
" Confine state to the dev cache so we never touch the user's real Vim " Confine state to the dev cache so we never touch the user's real Vim
" install. " install.
set viminfo='100,n$VIM_STATE/viminfo set viminfo='100,n${VIM_STATE}/viminfo
let &directory = '$VIM_STATE/swap//' let &directory = '${VIM_STATE}/swap//'
let &backupdir = '$VIM_STATE/backup//' let &backupdir = '${VIM_STATE}/backup//'
let &undodir = '$VIM_STATE/undo//' let &undodir = '${VIM_STATE}/undo//'
" Make Vim look for runtime paths in the dev cache. vim-lsp is intentionally " Make Vim look for runtime paths in the dev cache. vim-lsp is intentionally
" absent so nuwiki dispatches jump-to-definition through coc. " absent so nuwiki dispatches jump-to-definition through coc.
set nocompatible set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath let &runtimepath = '${REPO_ROOT}' . ',' . &runtimepath
let &runtimepath .= ',$COC_DIR' let &runtimepath .= ',${COC_DIR}'
let &runtimepath .= ',${DEV_DIR}/calendar-vim'
" Point coc at the generated settings + a private data dir. " Point coc at the generated settings + a private data dir.
let g:coc_config_home = '$DEV_DIR' let g:coc_config_home = '${DEV_DIR}'
let g:coc_data_home = '$COC_DATA' let g:coc_data_home = '${COC_DATA}'
let g:nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls' let g:nuwiki_binary_path = '${REPO_ROOT}/bin/nuwiki-ls'
let g:nuwiki_wiki_root = '$WIKI_DIR' let g:nuwiki_wiki_root = '${WIKI_DIR}'
let g:nuwiki_file_extension = '.wiki' let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_syntax = 'vimwiki' let g:nuwiki_syntax = 'vimwiki'
let g:nuwiki_diary_rel_path = 'diary'
let g:nuwiki_log_level = 'info' let g:nuwiki_log_level = 'info'
filetype plugin indent on filetype plugin indent on
@@ -153,11 +155,8 @@ augroup NuwikiFtdetect
autocmd BufRead,BufNewFile *.wiki setfiletype vimwiki autocmd BufRead,BufNewFile *.wiki setfiletype vimwiki
augroup END augroup END
" coc starts the server automatically from coc-settings.json once a vimwiki echo
" buffer loads — no explicit nuwiki#lsp#start() needed. echomsg '[nuwiki-dev] coc.preferences.jumpCommand = ' . '\$COC_JUMP'
echomsg '[nuwiki-dev] ready (coc.nvim) — wiki root: $WIKI_DIR'
echomsg '[nuwiki-dev] coc.preferences.jumpCommand = $COC_JUMP'
echomsg '[nuwiki-dev] <CR> on a [[link]] should open in the CURRENT window' echomsg '[nuwiki-dev] <CR> on a [[link]] should open in the CURRENT window'
echomsg '[nuwiki-dev] :CocList services for status, :messages for log' echomsg '[nuwiki-dev] :CocList services for status, :messages for log'
EOF EOF
@@ -167,6 +166,7 @@ main() {
ensure_node ensure_node
ensure_binary ensure_binary
ensure_coc ensure_coc
ensure_calendar_vim
write_coc_settings write_coc_settings
seed_wiki seed_wiki
write_vimrc write_vimrc
+12 -11
View File
@@ -70,19 +70,20 @@ write_vimrc() {
" Confine state to the dev cache so we never touch the user's real Vim " Confine state to the dev cache so we never touch the user's real Vim
" install. " install.
set viminfo='100,n$VIM_STATE/viminfo set viminfo='100,n${VIM_STATE}/viminfo
let &directory = '$VIM_STATE/swap//' let &directory = '${VIM_STATE}/swap//'
let &backupdir = '$VIM_STATE/backup//' let &backupdir = '${VIM_STATE}/backup//'
let &undodir = '$VIM_STATE/undo//' let &undodir = '${VIM_STATE}/undo//'
" Make Vim look for runtime paths in the dev cache. " Make Vim look for runtime paths in the dev cache.
set nocompatible set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath let &runtimepath = '${REPO_ROOT}' . ',' . &runtimepath
let &runtimepath .= ',$VIM_LSP_DIR' let &runtimepath .= ',${VIM_LSP_DIR}'
let &runtimepath .= ',$ASYNC_DIR' let &runtimepath .= ',${ASYNC_DIR}'
let &runtimepath .= ',${DEV_DIR}/calendar-vim'
let g:nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls' let g:nuwiki_binary_path = '${REPO_ROOT}/bin/nuwiki-ls'
let g:nuwiki_wiki_root = '$WIKI_DIR' let g:nuwiki_wiki_root = '${WIKI_DIR}'
let g:nuwiki_file_extension = '.wiki' let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_syntax = 'vimwiki' let g:nuwiki_syntax = 'vimwiki'
let g:nuwiki_log_level = 'info' let g:nuwiki_log_level = 'info'
@@ -113,8 +114,7 @@ augroup NuwikiStart
autocmd FileType vimwiki call nuwiki#lsp#start() autocmd FileType vimwiki call nuwiki#lsp#start()
augroup END augroup END
" Status messages go to :messages so they don't trigger "Press ENTER". echomsg '[nuwiki-dev] ready — wiki root: ' . g:nuwiki_wiki_root
echomsg '[nuwiki-dev] ready — wiki root: $WIKI_DIR'
echomsg '[nuwiki-dev] :LspStatus for diagnostics, :messages for log' echomsg '[nuwiki-dev] :LspStatus for diagnostics, :messages for log'
EOF EOF
} }
@@ -122,6 +122,7 @@ EOF
main() { main() {
ensure_binary ensure_binary
ensure_vim_lsp ensure_vim_lsp
ensure_calendar_vim
seed_wiki seed_wiki
write_vimrc write_vimrc
reset_dirs "$VIM_STATE" reset_dirs "$VIM_STATE"
+6
View File
@@ -56,6 +56,12 @@ M.defaults = {
diagnostic = { diagnostic = {
link_severity = 'warn', link_severity = 'warn',
}, },
-- Calendar-vim integration. On by default — set to `false` to opt out.
-- When on, nuwiki detects calendar-vim on the runtimepath and wires
-- `g:calendar_action` / `g:calendar_sign` automatically.
-- Also respects `g:nuwiki_no_calendar` set before setup() to disable.
use_calendar = true,
} }
M.options = vim.deepcopy(M.defaults) M.options = vim.deepcopy(M.defaults)
+22
View File
@@ -85,6 +85,28 @@ function M.setup(opts)
vim.filetype.add({ extension = extensions }) vim.filetype.add({ extension = extensions })
end end
-- Auto-detect calendar-vim and wire hooks when the first vimwiki buffer loads.
-- On by default; disable with use_calendar = false or g:nuwiki_no_calendar.
if config.options.use_calendar ~= false and not config.options.no_calendar then
vim.api.nvim_create_autocmd('FileType', {
pattern = 'vimwiki',
callback = function()
if vim.fn.exists('*calendar#open') == 1 or vim.fn.exists(':Calendar') == 2 then
-- Overwrite calendar-vim's own defaults (which write to ~/diary)
-- but respect a user-set custom hook.
local act = vim.g.calendar_action
if not act or act == '' or act == 'calendar#diary' then
vim.g.calendar_action = 'vimwiki#diary#calendar_action'
end
local sign = vim.g.calendar_sign
if not sign or sign == '' or sign == 'calendar#sign' then
vim.g.calendar_sign = 'vimwiki#diary#calendar_sign'
end
end
end,
})
end
lsp.register() lsp.register()
_setup_global_mappings() _setup_global_mappings()
_setup_global_commands() _setup_global_commands()
+24
View File
@@ -27,6 +27,30 @@ else
command! -nargs=0 NuwikiInstall execute 'source' fnameescape(s:plugin_root . '/scripts/download_bin.vim') command! -nargs=0 NuwikiInstall execute 'source' fnameescape(s:plugin_root . '/scripts/download_bin.vim')
endif endif
" Calendar-Vim Integration — auto-detect when calendar-vim is loaded.
augroup nuwiki_calendar_detect
autocmd!
autocmd FileType vimwiki call s:nuwiki_setup_calendar()
augroup END
function! s:nuwiki_setup_calendar() abort
if !get(g:, 'nuwiki_use_calendar', 1) || get(g:, 'nuwiki_no_calendar', 0)
return
endif
if exists('*calendar#open') || exists(':Calendar') == 2
" Wire nuwiki's diary hooks unless the user set a custom (non-default) one.
" calendar-vim's own default is 'calendar#diary' (writes to ~/diary); we
" overwrite that so paths come from the wiki root config instead.
let l:act = get(g:, 'calendar_action', '')
if l:act ==# '' || l:act ==# 'calendar#diary'
let g:calendar_action = 'vimwiki#diary#calendar_action'
endif
let l:sign = get(g:, 'calendar_sign', '')
if l:sign ==# '' || l:sign ==# 'calendar#sign'
let g:calendar_sign = 'vimwiki#diary#calendar_sign'
endif
endif
endfunction
if has('nvim') if has('nvim')
" Neovim path is initialised by `require('nuwiki').setup()`. Nothing else " Neovim path is initialised by `require('nuwiki').setup()`. Nothing else
" to do at vimscript boot — wait until the user runs setup. " to do at vimscript boot — wait until the user runs setup.