Merge remote-tracking branch 'origin/calendar-support' into feature-gap
CI / cargo fmt --check (push) Successful in 35s
CI / cargo clippy (push) Successful in 32s
CI / cargo test (push) Successful in 39s
CI / editor keymaps (push) Successful in 1m32s

# Conflicts:
#	development/vimwiki-gap.md
This commit is contained in:
2026-06-03 01:46:07 +00:00
18 changed files with 682 additions and 45 deletions
+6 -1
View File
@@ -79,7 +79,12 @@ nuwiki/
│ ├── test-keymaps.sh # Neovim keymap harness
│ ├── test-keymaps.lua
│ ├── test-keymaps-vim.sh # Vim keymap harness
── test-keymaps-vim.vim
── test-keymaps-vim.vim
│ ├── test-calendar.sh # Neovim calendar-vim integration harness
│ ├── test-calendar.lua
│ ├── test-calendar-vim.sh # Vim calendar-vim integration harness
│ ├── test-calendar-vim.vim
│ └── test-calendar-vim-optout.vim
└── .gitea/
└── workflows/
+14
View File
@@ -290,6 +290,20 @@ reset_dirs() {
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() {
if [[ "${NUWIKI_DEV_NO_SEED:-0}" == "1" ]]; then
log "NUWIKI_DEV_NO_SEED=1 — skipping wiki seed (using $WIKI_DIR as-is)"
+15 -7
View File
@@ -4,10 +4,15 @@
This document describes how nuwiki (vimwiki) integrates with calendar-vim to provide graphical calendar navigation for wiki diary entries.
## Integration Mechanism
Nuwiki activates the integration by setting calendar-vim's hook variables when `g:vimwiki_use_calendar` is enabled (default: true):
Nuwiki auto-detects calendar-vim on the runtimepath (via a `FileType vimwiki`
autocmd in `plugin/nuwiki.vim`) and wires calendar-vim's hook variables, unless
the integration is disabled (default: enabled):
- `g:calendar_action = 'vimwiki#diary#calendar_action'`
- `g:calendar_sign = 'vimwiki#diary#calendar_sign'`
These hooks are only set when unset or still at calendar-vim's own defaults
(`calendar#diary` / `calendar#sign`), so a user-chosen custom hook is preserved.
## Primary Use Case: Viewing/Create Diary Entries
1. User executes `:Calendar` command
2. Calendar-vim displays navigable calendar interface
@@ -15,9 +20,9 @@ Nuwiki activates the integration by setting calendar-vim's hook variables when `
4. User presses `<Enter>` on selected date
5. `vimwiki#diary#calendar_action` function executes:
- Formats date as YYYY-MM-DD
- Opens existing diary file or creates new one
- Applies appropriate wiki filetype
- Sets up autocmd to refresh calendar on file close
- Opens existing diary file or creates new one (split/vsplit per `dir`)
- Applies the vimwiki filetype to a brand-new entry
- Closes the calendar window once the diary entry is open
6. User views/edits diary entry for selected date
## Date Marking Functionality
@@ -30,12 +35,15 @@ Nuwiki activates the integration by setting calendar-vim's hook variables when `
- Automatic file creation/opening for selected dates
- Visual indicators for dates with existing content
- Proper window splitting based on calendar orientation
- Automatic calendar refresh after editing diary entries
- Calendar window closed automatically once the diary entry opens
## Configuration
Controlled by:
- `g:vimwiki_use_calendar` (global toggle)
- Individual wiki configurations in `g:vimwiki_list` (path, diary_rel_path, ext)
- `use_calendar` Lua option (Neovim `setup()`), default `true`
- `g:nuwiki_use_calendar` / `g:nuwiki_no_calendar` (VimL global toggles)
- Individual wiki configurations in `g:nuwiki_wikis` (root, diary_rel_path,
file_extension), or the scalar `g:nuwiki_wiki_root` / `g:nuwiki_diary_rel_path`
/ `g:nuwiki_file_extension` fallbacks
- Standard calendar-vim variables for appearance/customization
This integration leverages calendar-vim's strong date presentation while using nuwiki's diary system for file management, creating a cohesive experience for journal-style wiki usage.
+7 -10
View File
@@ -54,17 +54,13 @@ vim.env.XDG_DATA_HOME = '$DATA_DIR'
vim.env.XDG_CONFIG_HOME = '$CONFIG_DIR'
vim.opt.runtimepath:prepend('$REPO_ROOT')
vim.opt.runtimepath:append('$DEV_DIR/calendar-vim')
vim.g.nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls'
-- Friendly defaults for an interactive smoke test.
vim.opt.number = true
vim.opt.signcolumn = 'yes'
vim.opt.termguicolors = true
vim.opt.swapfile = false
vim.opt.writebackup = false
vim.opt.undofile = false
vim.g.mapleader = ' '
vim.g.nuwiki_wiki_root = '$WIKI_DIR'
vim.g.nuwiki_file_extension = '.wiki'
vim.g.nuwiki_syntax = 'vimwiki'
vim.g.nuwiki_diary_rel_path = 'diary'
require('nuwiki').setup({
wiki_root = '$WIKI_DIR',
@@ -74,13 +70,14 @@ require('nuwiki').setup({
-- they're swallowed; with it they land in :messages.
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')
EOF
}
main() {
ensure_binary
ensure_calendar_vim
seed_wiki
write_init
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
" install.
set viminfo='100,n$VIM_STATE/viminfo
let &directory = '$VIM_STATE/swap//'
let &backupdir = '$VIM_STATE/backup//'
let &undodir = '$VIM_STATE/undo//'
set viminfo='100,n${VIM_STATE}/viminfo
let &directory = '${VIM_STATE}/swap//'
let &backupdir = '${VIM_STATE}/backup//'
let &undodir = '${VIM_STATE}/undo//'
" Make Vim look for runtime paths in the dev cache. vim-lsp is intentionally
" absent so nuwiki dispatches jump-to-definition through coc.
set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath
let &runtimepath .= ',$COC_DIR'
let &runtimepath = '${REPO_ROOT}' . ',' . &runtimepath
let &runtimepath .= ',${COC_DIR}'
let &runtimepath .= ',${DEV_DIR}/calendar-vim'
" Point coc at the generated settings + a private data dir.
let g:coc_config_home = '$DEV_DIR'
let g:coc_data_home = '$COC_DATA'
let g:coc_config_home = '${DEV_DIR}'
let g:coc_data_home = '${COC_DATA}'
let g:nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls'
let g:nuwiki_wiki_root = '$WIKI_DIR'
let g:nuwiki_binary_path = '${REPO_ROOT}/bin/nuwiki-ls'
let g:nuwiki_wiki_root = '${WIKI_DIR}'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_syntax = 'vimwiki'
let g:nuwiki_diary_rel_path = 'diary'
let g:nuwiki_log_level = 'info'
filetype plugin indent on
@@ -153,11 +155,8 @@ augroup NuwikiFtdetect
autocmd BufRead,BufNewFile *.wiki setfiletype vimwiki
augroup END
" coc starts the server automatically from coc-settings.json once a vimwiki
" buffer loads — no explicit nuwiki#lsp#start() needed.
echomsg '[nuwiki-dev] ready (coc.nvim) — wiki root: $WIKI_DIR'
echomsg '[nuwiki-dev] coc.preferences.jumpCommand = $COC_JUMP'
echo
echomsg '[nuwiki-dev] coc.preferences.jumpCommand = ' . '\$COC_JUMP'
echomsg '[nuwiki-dev] <CR> on a [[link]] should open in the CURRENT window'
echomsg '[nuwiki-dev] :CocList services for status, :messages for log'
EOF
@@ -167,6 +166,7 @@ main() {
ensure_node
ensure_binary
ensure_coc
ensure_calendar_vim
write_coc_settings
seed_wiki
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
" install.
set viminfo='100,n$VIM_STATE/viminfo
let &directory = '$VIM_STATE/swap//'
let &backupdir = '$VIM_STATE/backup//'
let &undodir = '$VIM_STATE/undo//'
set viminfo='100,n${VIM_STATE}/viminfo
let &directory = '${VIM_STATE}/swap//'
let &backupdir = '${VIM_STATE}/backup//'
let &undodir = '${VIM_STATE}/undo//'
" Make Vim look for runtime paths in the dev cache.
set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath
let &runtimepath .= ',$VIM_LSP_DIR'
let &runtimepath .= ',$ASYNC_DIR'
let &runtimepath = '${REPO_ROOT}' . ',' . &runtimepath
let &runtimepath .= ',${VIM_LSP_DIR}'
let &runtimepath .= ',${ASYNC_DIR}'
let &runtimepath .= ',${DEV_DIR}/calendar-vim'
let g:nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls'
let g:nuwiki_wiki_root = '$WIKI_DIR'
let g:nuwiki_binary_path = '${REPO_ROOT}/bin/nuwiki-ls'
let g:nuwiki_wiki_root = '${WIKI_DIR}'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_syntax = 'vimwiki'
let g:nuwiki_log_level = 'info'
@@ -113,8 +114,7 @@ augroup NuwikiStart
autocmd FileType vimwiki call nuwiki#lsp#start()
augroup END
" Status messages go to :messages so they don't trigger "Press ENTER".
echomsg '[nuwiki-dev] ready — wiki root: $WIKI_DIR'
echomsg '[nuwiki-dev] ready — wiki root: ' . g:nuwiki_wiki_root
echomsg '[nuwiki-dev] :LspStatus for diagnostics, :messages for log'
EOF
}
@@ -122,6 +122,7 @@ EOF
main() {
ensure_binary
ensure_vim_lsp
ensure_calendar_vim
seed_wiki
write_vimrc
reset_dirs "$VIM_STATE"
@@ -0,0 +1,40 @@
" development/tests/test-calendar-vim-optout.vim — driven by test-calendar-vim.sh.
"
" Asserts that the calendar-vim integration stays OFF when the user opts out
" (g:nuwiki_use_calendar = 0 or g:nuwiki_no_calendar = 1). In that case the
" FileType autocmd must leave calendar-vim's own defaults untouched, so the
" hooks still point at 'calendar#diary' / 'calendar#sign' (set by the stub).
let s:results = []
let s:pass = 0
let s:fail = 0
let s:out = $NUWIKI_CALENDAR_RESULTS
function! s:record(ok, name, detail) abort
let l:line = (a:ok ? 'PASS' : 'FAIL') . ' ' . a:name
if a:detail !=# ''
let l:line .= ' — ' . a:detail
endif
call add(s:results, l:line)
if a:ok
let s:pass += 1
else
let s:fail += 1
endif
endfunction
" The buffer is already a loaded vimwiki file (FileType has fired). nuwiki must
" NOT have rewired the hooks — they should still be calendar-vim's defaults.
call s:record(&filetype ==# 'vimwiki', 'ftplugin.attached',
\ &filetype ==# 'vimwiki' ? '' : 'filetype=' . &filetype)
call s:record(get(g:, 'calendar_action', '') !=# 'vimwiki#diary#calendar_action',
\ 'optout.action_not_wired',
\ 'g:calendar_action=' . get(g:, 'calendar_action', ''))
call s:record(get(g:, 'calendar_sign', '') !=# 'vimwiki#diary#calendar_sign',
\ 'optout.sign_not_wired',
\ 'g:calendar_sign=' . get(g:, 'calendar_sign', ''))
call add(s:results, '')
call add(s:results, printf('SUMMARY: %d passed, %d failed', s:pass, s:fail))
call writefile(s:results, s:out)
execute (s:fail == 0 ? 'qall!' : 'cquit!')
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env bash
#
# development/tests/test-calendar-vim.sh — calendar-vim integration harness (Vim).
#
# Exercises the pure-VimL diary hooks (nuwiki#diary#calendar_action /
# nuwiki#diary#calendar_sign) plus plugin/nuwiki.vim's auto-wiring of
# g:calendar_action / g:calendar_sign. A stub calendar-vim is placed on the
# runtimepath so :Calendar exists and the hooks start at calendar-vim's own
# defaults — the exact state nuwiki must detect and override.
#
# Two runs:
# 1. default → integration ON, hooks rewired, diary paths from wiki root
# 2. opt-out → g:nuwiki_use_calendar=0 / g:nuwiki_no_calendar=1 leave
# calendar-vim's defaults untouched
#
# No LSP binary is needed — the diary hooks are entirely client-side VimL.
# Exit code: 0 when all assertions pass, 1 otherwise. Skips (0) without vim.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
TMP="$(mktemp -d -t nuwiki-calendar-vim-test-XXXXXX)"
trap 'rm -rf "$TMP"' EXIT
log() { printf '\033[1;34m[calendar-vim]\033[0m %s\n' "$*"; }
if ! command -v vim >/dev/null 2>&1; then
log 'vim not installed — skipping'
exit 0
fi
# --- Scratch wiki with one seeded diary entry (2026-05-30) ----------------
WIKI="$TMP/wiki"
mkdir -p "$WIKI/diary"
echo "= index =" > "$WIKI/index.wiki"
echo "= 2026-05-30 =" > "$WIKI/diary/2026-05-30.wiki"
# --- Stub calendar-vim ----------------------------------------------------
# Mimics the real plugin closely enough for detection + the override path:
# defines :Calendar and seeds calendar-vim's default hook names.
CAL="$TMP/calendar-vim"
mkdir -p "$CAL/plugin" "$CAL/autoload"
cat > "$CAL/plugin/calendar.vim" <<'EOF'
if exists('g:loaded_calendar_stub') | finish | endif
let g:loaded_calendar_stub = 1
let g:calendar_action = get(g:, 'calendar_action', 'calendar#diary')
let g:calendar_sign = get(g:, 'calendar_sign', 'calendar#sign')
command! -nargs=* Calendar echo 'stub calendar'
EOF
cat > "$CAL/autoload/calendar.vim" <<'EOF'
function! calendar#open(...) abort
endfunction
function! calendar#diary(...) abort
endfunction
function! calendar#sign(...) abort
return 0
endfunction
EOF
run_harness() {
local label="$1" vimrc="$2" harness="$3"
local results="$TMP/results-$label.txt"
log "running $label harness…"
NUWIKI_CALENDAR_RESULTS="$results" \
NUWIKI_CALENDAR_WIKI="$WIKI" \
timeout 30 vim -e -s -u "$vimrc" -c "edit $WIKI/index.wiki" \
-c "source $harness" </dev/null >"$TMP/$label.log" 2>&1 || true
if [[ ! -f "$results" ]]; then
echo "calendar $label harness produced no output" >&2
echo "--- vim log ---" >&2
cat "$TMP/$label.log" >&2 || true
return 1
fi
cat "$results"
grep -qE '^SUMMARY: [0-9]+ passed, 0 failed' "$results"
}
# --- Run 1: default (integration ON) --------------------------------------
VIMRC="$TMP/vimrc"
cat > "$VIMRC" <<EOF
set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath
let &runtimepath .= ',$CAL'
let g:nuwiki_wiki_root = '$WIKI'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_diary_rel_path = 'diary'
set hidden
filetype plugin indent on
syntax enable
EOF
# --- Run 2: opt-out via g:nuwiki_use_calendar = 0 -------------------------
VIMRC_OPTOUT="$TMP/vimrc-optout"
cat > "$VIMRC_OPTOUT" <<EOF
set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath
let &runtimepath .= ',$CAL'
let g:nuwiki_use_calendar = 0
let g:nuwiki_wiki_root = '$WIKI'
filetype plugin indent on
syntax enable
EOF
# --- Run 3: opt-out via g:nuwiki_no_calendar = 1 --------------------------
VIMRC_NOCAL="$TMP/vimrc-nocal"
cat > "$VIMRC_NOCAL" <<EOF
set nocompatible
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath
let &runtimepath .= ',$CAL'
let g:nuwiki_no_calendar = 1
let g:nuwiki_wiki_root = '$WIKI'
filetype plugin indent on
syntax enable
EOF
rc=0
run_harness 'default' "$VIMRC" "$REPO_ROOT/development/tests/test-calendar-vim.vim" || rc=1
run_harness 'optout-use' "$VIMRC_OPTOUT" "$REPO_ROOT/development/tests/test-calendar-vim-optout.vim" || rc=1
run_harness 'optout-no' "$VIMRC_NOCAL" "$REPO_ROOT/development/tests/test-calendar-vim-optout.vim" || rc=1
exit $rc
+97
View File
@@ -0,0 +1,97 @@
" development/tests/test-calendar-vim.vim — driven by development/tests/test-calendar-vim.sh.
"
" Pure-VimL regression suite for the calendar-vim integration. The diary
" hooks (nuwiki#diary#calendar_action / nuwiki#diary#calendar_sign) are
" implemented entirely in VimL and shared verbatim by the Vim and Neovim
" clients, so this harness is the canonical coverage for them. The Vim
" plugin-side hook wiring (plugin/nuwiki.vim's FileType autocmd) is also
" asserted here; the Neovim setup() wiring has its own harness in
" development/tests/test-calendar.lua.
"
" A stub calendar-vim is placed on the runtimepath by the shell wrapper so
" :Calendar exists and g:calendar_action/g:calendar_sign already hold
" calendar-vim's own defaults ('calendar#diary'/'calendar#sign') — exactly
" the state nuwiki must detect and override.
let s:results = []
let s:pass = 0
let s:fail = 0
let s:out = $NUWIKI_CALENDAR_RESULTS
function! s:record(ok, name, detail) abort
let l:line = (a:ok ? 'PASS' : 'FAIL') . ' ' . a:name
if a:detail !=# ''
let l:line .= ' — ' . a:detail
endif
call add(s:results, l:line)
if a:ok
let s:pass += 1
else
let s:fail += 1
endif
endfunction
let s:wiki_root = $NUWIKI_CALENDAR_WIKI
" ===== Hook wiring: nuwiki overrides calendar-vim's defaults =====
" The stub set g:calendar_action='calendar#diary' / g:calendar_sign='calendar#sign'
" at load. Opening this .wiki buffer fired plugin/nuwiki.vim's FileType autocmd,
" which must have rewired both to the nuwiki delegators.
call s:record(&filetype ==# 'vimwiki', 'ftplugin.attached',
\ &filetype ==# 'vimwiki' ? '' : 'filetype=' . &filetype)
call s:record(get(g:, 'calendar_action', '') ==# 'vimwiki#diary#calendar_action',
\ 'hook.action_wired', 'g:calendar_action=' . get(g:, 'calendar_action', ''))
call s:record(get(g:, 'calendar_sign', '') ==# 'vimwiki#diary#calendar_sign',
\ 'hook.sign_wired', 'g:calendar_sign=' . get(g:, 'calendar_sign', ''))
" ===== calendar_sign: marker reflects diary-file existence =====
" The shell seeded a diary entry for 2026-05-30 and left 2026-05-31 absent.
call s:record(nuwiki#diary#calendar_sign(30, 5, 2026) ==# '*',
\ 'sign.existing_date_marked',
\ 'got ' . string(nuwiki#diary#calendar_sign(30, 5, 2026)))
call s:record(nuwiki#diary#calendar_sign(31, 5, 2026) ==# '',
\ 'sign.absent_date_unmarked',
\ 'got ' . string(nuwiki#diary#calendar_sign(31, 5, 2026)))
" The vimwiki# alias must return the identical result.
call s:record(vimwiki#diary#calendar_sign(30, 5, 2026) ==# '*',
\ 'sign.alias_matches', '')
" ===== calendar_action: opens WIKI_ROOT/diary/<date>.wiki, not ~/diary =====
" Set up two windows so the "previous window" branch runs and we can assert
" the calendar window is closed afterwards.
" Window A: the seed wiki (already current). Open a fresh window to stand in
" for the calendar, then invoke the action from it.
new
file [stub-calendar]
let s:cal_winid = win_getid()
let s:wins_before = winnr('$')
call nuwiki#diary#calendar_action(31, 5, 2026, 0, '')
let s:opened = expand('%:p')
let s:want = s:wiki_root . '/diary/2026-05-31.wiki'
call s:record(s:opened ==# s:want, 'action.opens_wiki_diary_path',
\ 'opened=' . s:opened)
call s:record(s:opened !~# '/diary/2026-05-31.wiki$' ? 0 : 1,
\ 'action.path_has_date_filename', 'opened=' . s:opened)
call s:record(s:opened !~# expand('~') . '/diary',
\ 'action.not_home_diary', 'opened=' . s:opened)
" The calendar window must be closed once the diary is open.
call s:record(win_id2win(s:cal_winid) == 0, 'action.closes_calendar_window',
\ 'cal still at win ' . win_id2win(s:cal_winid))
call s:record(winnr('$') == s:wins_before - 1, 'action.window_count_decremented',
\ 'before=' . s:wins_before . ' after=' . winnr('$'))
" ===== calendar_action sets the wiki filetype on a new entry =====
call s:record(&filetype ==# 'vimwiki', 'action.sets_filetype',
\ 'filetype=' . &filetype)
" ===== Wrap up =====
call add(s:results, '')
call add(s:results, printf('SUMMARY: %d passed, %d failed', s:pass, s:fail))
call writefile(s:results, s:out)
execute (s:fail == 0 ? 'qall!' : 'cquit!')
+92
View File
@@ -0,0 +1,92 @@
-- development/tests/test-calendar.lua — driven by development/tests/test-calendar.sh.
--
-- Headless Neovim harness for the calendar-vim integration wired through
-- `require('nuwiki').setup()`. The diary hooks themselves are pure VimL and
-- covered exhaustively by development/tests/test-calendar-vim.vim; this
-- harness focuses on the Neovim-only pieces:
-- * setup() registering the FileType autocmd that overrides calendar-vim's
-- own default hooks ('calendar#diary' / 'calendar#sign'),
-- * the window-close behaviour of calendar_action under the real (non-ex)
-- window API,
-- * disabling via use_calendar = false.
--
-- The opt-out case runs in a second nvim invocation (see the shell wrapper)
-- because setup() registers the autocmd once per process.
--
-- Results are written to $NUWIKI_CALENDAR_RESULTS so the wrapper can render
-- them without scraping nvim's output.
local OUT = vim.env.NUWIKI_CALENDAR_RESULTS or '/tmp/nuwiki-calendar-results.txt'
local WIKI = vim.env.NUWIKI_CALENDAR_WIKI
local MODE = vim.env.NUWIKI_CALENDAR_MODE or 'default'
local out_lines = {}
local pass, fail = 0, 0
local function record(ok, name, detail)
local suffix = (detail and detail ~= '') and ('' .. detail) or ''
table.insert(out_lines, (ok and 'PASS' or 'FAIL') .. ' ' .. name .. suffix)
if ok then pass = pass + 1 else fail = fail + 1 end
end
local function finish()
table.insert(out_lines, '')
table.insert(out_lines, string.format('SUMMARY: %d passed, %d failed', pass, fail))
vim.fn.writefile(out_lines, OUT)
vim.cmd(fail == 0 and 'qall!' or 'cquit!')
end
-- Open the seeded wiki index so the FileType vimwiki autocmd fires.
vim.cmd('edit ' .. vim.fn.fnameescape(WIKI .. '/index.wiki'))
if MODE == 'optout' then
-- use_calendar = false: setup() must not register the wiring autocmd, so
-- calendar-vim's own defaults (set by the stub) survive untouched.
record(vim.bo.filetype == 'vimwiki', 'optout.ftplugin_attached',
'filetype=' .. vim.bo.filetype)
record(vim.g.calendar_action ~= 'vimwiki#diary#calendar_action',
'optout.action_not_wired', 'g:calendar_action=' .. tostring(vim.g.calendar_action))
record(vim.g.calendar_sign ~= 'vimwiki#diary#calendar_sign',
'optout.sign_not_wired', 'g:calendar_sign=' .. tostring(vim.g.calendar_sign))
finish()
return
end
-- ===== Hook wiring overrides calendar-vim defaults =====
record(vim.bo.filetype == 'vimwiki', 'ftplugin_attached',
'filetype=' .. vim.bo.filetype)
record(vim.g.calendar_action == 'vimwiki#diary#calendar_action',
'hook.action_wired', 'g:calendar_action=' .. tostring(vim.g.calendar_action))
record(vim.g.calendar_sign == 'vimwiki#diary#calendar_sign',
'hook.sign_wired', 'g:calendar_sign=' .. tostring(vim.g.calendar_sign))
-- ===== calendar_sign reflects diary-file existence =====
record(vim.fn['nuwiki#diary#calendar_sign'](30, 5, 2026) == '*',
'sign.existing_date_marked',
'got ' .. tostring(vim.fn['nuwiki#diary#calendar_sign'](30, 5, 2026)))
record(vim.fn['nuwiki#diary#calendar_sign'](31, 5, 2026) == '',
'sign.absent_date_unmarked',
'got ' .. tostring(vim.fn['nuwiki#diary#calendar_sign'](31, 5, 2026)))
-- ===== calendar_action: path + window-close (real window API) =====
-- Open a second window to act as the calendar, invoke from it, and confirm
-- the diary lands at WIKI/diary/<date>.wiki while the calendar window closes.
vim.cmd('new')
local cal_win = vim.api.nvim_get_current_win()
local wins_before = #vim.api.nvim_list_wins()
vim.fn['nuwiki#diary#calendar_action'](31, 5, 2026, 0, '')
local opened = vim.fn.expand('%:p')
local want = WIKI .. '/diary/2026-05-31.wiki'
record(opened == want, 'action.opens_wiki_diary_path', 'opened=' .. opened)
record(not opened:match(vim.fn.expand('~') .. '/diary'),
'action.not_home_diary', 'opened=' .. opened)
record(not vim.api.nvim_win_is_valid(cal_win),
'action.closes_calendar_window',
'cal_win valid=' .. tostring(vim.api.nvim_win_is_valid(cal_win)))
record(#vim.api.nvim_list_wins() == wins_before - 1,
'action.window_count_decremented',
'before=' .. wins_before .. ' after=' .. #vim.api.nvim_list_wins())
record(vim.bo.filetype == 'vimwiki', 'action.sets_filetype',
'filetype=' .. vim.bo.filetype)
finish()
+106
View File
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
#
# development/tests/test-calendar.sh — calendar-vim integration harness (Neovim).
#
# Drives development/tests/test-calendar.lua under headless Neovim to cover the
# pieces specific to the Lua entry point:
# * require('nuwiki').setup() wiring g:calendar_action / g:calendar_sign and
# overriding calendar-vim's own defaults,
# * calendar_action's window-close behaviour under the real window API,
# * opting out with use_calendar = false.
#
# A stub calendar-vim on the runtimepath provides :Calendar and seeds the
# default hook names. The LSP binary is built (setup() registers the client);
# the calendar paths themselves don't need the server.
#
# Exit: 0 when both runs are green, 1 otherwise. Skips (0) without nvim.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
TMP="$(mktemp -d -t nuwiki-calendar-test-XXXXXX)"
trap 'rm -rf "$TMP"' EXIT
log() { printf '\033[1;34m[calendar]\033[0m %s\n' "$*"; }
if ! command -v nvim >/dev/null 2>&1; then
log 'nvim not installed — skipping'
exit 0
fi
# setup() registers the LSP client, so build the binary like the other
# Neovim harnesses do (incremental, fast).
BIN="$REPO_ROOT/bin/nuwiki-ls"
log 'building nuwiki-ls (release, incremental)…'
cargo build --release -p nuwiki-ls --manifest-path "$REPO_ROOT/Cargo.toml" >/dev/null
mkdir -p "$REPO_ROOT/bin"
ln -sf "$REPO_ROOT/target/release/nuwiki-ls" "$BIN"
# --- Scratch wiki with one seeded diary entry (2026-05-30) ----------------
WIKI="$TMP/wiki"
mkdir -p "$WIKI/diary"
echo "= index =" > "$WIKI/index.wiki"
echo "= 2026-05-30 =" > "$WIKI/diary/2026-05-30.wiki"
# --- Stub calendar-vim ----------------------------------------------------
CAL="$TMP/calendar-vim"
mkdir -p "$CAL/plugin" "$CAL/autoload"
cat > "$CAL/plugin/calendar.vim" <<'EOF'
if exists('g:loaded_calendar_stub') | finish | endif
let g:loaded_calendar_stub = 1
let g:calendar_action = get(g:, 'calendar_action', 'calendar#diary')
let g:calendar_sign = get(g:, 'calendar_sign', 'calendar#sign')
command! -nargs=* Calendar echo 'stub calendar'
EOF
cat > "$CAL/autoload/calendar.vim" <<'EOF'
function! calendar#open(...) abort
endfunction
function! calendar#diary(...) abort
endfunction
function! calendar#sign(...) abort
return 0
endfunction
EOF
write_init() {
local file="$1" use_calendar="$2"
cat > "$file" <<EOF
vim.opt.runtimepath:prepend('$REPO_ROOT')
vim.opt.runtimepath:append('$CAL')
vim.g.nuwiki_binary_path = '$BIN'
vim.g.nuwiki_wiki_root = '$WIKI'
vim.g.nuwiki_file_extension = '.wiki'
vim.g.nuwiki_diary_rel_path = 'diary'
require('nuwiki').setup({ wiki_root = '$WIKI', use_calendar = $use_calendar })
EOF
}
run() {
local label="$1" init="$2" mode="$3"
local results="$TMP/results-$label.txt"
log "running $label harness…"
NUWIKI_CALENDAR_RESULTS="$results" \
NUWIKI_CALENDAR_WIKI="$WIKI" \
NUWIKI_CALENDAR_MODE="$mode" \
timeout 60 nvim --clean -u "$init" --headless \
-c "luafile $REPO_ROOT/development/tests/test-calendar.lua" \
>"$TMP/$label.log" 2>&1 || true
if [[ ! -f "$results" ]]; then
echo "calendar $label harness produced no output" >&2
echo "--- nvim log ---" >&2
cat "$TMP/$label.log" >&2 || true
return 1
fi
cat "$results"
grep -qE '^SUMMARY: [0-9]+ passed, 0 failed' "$results"
}
INIT_ON="$TMP/init-on.lua"
INIT_OFF="$TMP/init-off.lua"
write_init "$INIT_ON" 'true'
write_init "$INIT_OFF" 'false'
rc=0
run 'default' "$INIT_ON" 'default' || rc=1
run 'optout' "$INIT_OFF" 'optout' || rc=1
exit $rc
+6 -1
View File
@@ -611,7 +611,6 @@ audits don't re-flag them.
backed by LSP foldingRange.
- `key_mappings` (dict) — replaced by Lua `mappings.<group>` + the
`g:nuwiki_no_<group>_mappings` globals.
- `use_calendar` — no calendar.vim integration.
- `auto_tags` (upstream `0`) — upstream auto-updates an on-disk tag *metadata*
file on save. nuwiki has no such file: the LSP re-indexes tags on every
`didChange`, so tag search/jump/completion are always fresh. The setting's
@@ -708,3 +707,9 @@ audits don't re-flag them.
fallback, now via `table_align_or_cmd`); `:VimwikiSearch`/`VWS` scoped to the
wiki root; and global `Index`/`TabIndex` entry points. All client-side (no
server change). Both harnesses green (Neovim 299, Vim 291/18/21).
- Merged the `calendar-support` branch (2026-06-03): calendar-vim integration
now wires `g:calendar_action`/`g:calendar_sign` to nuwiki's diary paths via a
`FileType vimwiki` autocmd (`plugin/nuwiki.vim`, both clients), with a
`use_calendar` opt-out (`g:nuwiki_no_calendar` / setup `use_calendar = false`).
So `use_calendar` is no longer an intentional divergence — removed from that
list above. See `development/calendar-vim-integration.md`.