diff --git a/autoload/nuwiki/diary.vim b/autoload/nuwiki/diary.vim new file mode 100644 index 0000000..40bbfa3 --- /dev/null +++ b/autoload/nuwiki/diary.vim @@ -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 diff --git a/autoload/vimwiki/diary.vim b/autoload/vimwiki/diary.vim new file mode 100644 index 0000000..e554c7e --- /dev/null +++ b/autoload/vimwiki/diary.vim @@ -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 diff --git a/development/_common.sh b/development/_common.sh index d48fe30..adab8b7 100644 --- a/development/_common.sh +++ b/development/_common.sh @@ -275,6 +275,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)" diff --git a/development/start-nvim.sh b/development/start-nvim.sh index 6963e04..e7f9ee4 100755 --- a/development/start-nvim.sh +++ b/development/start-nvim.sh @@ -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" diff --git a/development/start-vim-coc.sh b/development/start-vim-coc.sh index a6dd59c..a72b0d2 100755 --- a/development/start-vim-coc.sh +++ b/development/start-vim-coc.sh @@ -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] 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 diff --git a/development/start-vim.sh b/development/start-vim.sh index cc32d28..1311d21 100755 --- a/development/start-vim.sh +++ b/development/start-vim.sh @@ -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" diff --git a/lua/nuwiki/config.lua b/lua/nuwiki/config.lua index 0da64f4..50726b1 100644 --- a/lua/nuwiki/config.lua +++ b/lua/nuwiki/config.lua @@ -56,6 +56,12 @@ M.defaults = { diagnostic = { 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) diff --git a/lua/nuwiki/init.lua b/lua/nuwiki/init.lua index b37a471..444915f 100644 --- a/lua/nuwiki/init.lua +++ b/lua/nuwiki/init.lua @@ -85,6 +85,28 @@ function M.setup(opts) vim.filetype.add({ extension = extensions }) 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() _setup_global_mappings() _setup_global_commands() diff --git a/plugin/nuwiki.vim b/plugin/nuwiki.vim index 8a2a71e..6298c55 100644 --- a/plugin/nuwiki.vim +++ b/plugin/nuwiki.vim @@ -27,6 +27,30 @@ else command! -nargs=0 NuwikiInstall execute 'source' fnameescape(s:plugin_root . '/scripts/download_bin.vim') 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') " Neovim path is initialised by `require('nuwiki').setup()`. Nothing else " to do at vimscript boot — wait until the user runs setup.