fix(vim): follow-link creates missing pages + safe LSP foldexpr
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 1m7s
CI / cargo test (push) Successful in 1m13s

Two user-visible bugs:

1. `<CR>` on a wikilink whose target wasn't indexed yet (typical for
   "follow this link → page doesn't exist → I want a fresh buffer for
   it") did nothing. Cause: `Backend::resolve_target_uri` returned
   `None` once `WorkspaceIndex::resolve` came up empty, so
   `goto_definition` had no Location to hand back to the client and
   `vim.lsp.buf.definition()` / `:LspDefinition` silently no-op'd.

   Vimwiki's behaviour is "open a fresh buffer for the future page".
   `Backend::resolve_target_uri` now synthesises a
   `<wiki_root>/<path><file_extension>` URI when the lookup misses,
   for both `LinkKind::Wiki` and the `LinkKind::Interwiki` fallback.
   The editor opens an empty buffer; saving writes the file. New
   helper `synthesise_page_uri` does the path walk so subdirectory
   wikilinks like `[[notes/Daily]]` get the right output path.

2. Folding occasionally surfaced an `E5108` from inside
   `vim.lsp.foldexpr()` — happens when the function fires before the
   LSP client has finished attaching to the buffer, or when the
   server's `foldingRange` response hasn't yet arrived. The error
   gets shouted once per visible line.

   `lua/nuwiki/folding.lua` now exports `lsp_expr` which `pcall`s
   `vim.lsp.foldexpr` and falls back to the regex implementation
   (`M.expr`) on error or when the LSP function isn't available.
   `ftplugin.lua` points `foldexpr` at the wrapper instead of
   `vim.lsp.foldexpr` directly. `foldtext` is set in both branches
   now so collapsed folds keep the nicer summary line.

Tests: 4 new in `phase19_followlink_creates.rs` covering indexed
resolution, missing-page fallback, the canonical
`<root>/<page>.wiki` shape, and the slash-bearing subdirectory link.
Total 381 tests pass; fmt + clippy clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 01:22:35 +00:00
parent c3a8eae9d0
commit 047c9a3cf3
4 changed files with 145 additions and 10 deletions
+16
View File
@@ -39,4 +39,20 @@ function M.foldtext()
return string.format('▸ %s … (%d lines)', line, count)
end
--- LSP-backed foldexpr with a safe fallback.
---
--- Wraps `vim.lsp.foldexpr` so a missing client / un-attached buffer /
--- transient evaluation error falls back to the regex implementation
--- instead of throwing E5108 for every line in the buffer.
function M.lsp_expr()
if not (vim.lsp and vim.lsp.foldexpr) then
return M.expr()
end
local ok, val = pcall(vim.lsp.foldexpr)
if ok then
return val
end
return M.expr()
end
return M
+6 -2
View File
@@ -13,6 +13,10 @@ local function setup_folding(bufnr, folding_mode)
-- The ftplugin runs during BufRead → FileType, by which time the
-- buffer is displayed in the current window. `vim.opt_local` writes
-- to the current window's view of the option.
-- The LSP foldexpr is wrapped via `nuwiki.folding.lsp_expr` so a
-- transient client failure (e.g. server still starting, buffer not
-- yet attached) degrades to the regex variant instead of spamming
-- the user with E5108 errors per line.
local apply = function()
if folding_mode == 'expr'
or not (vim.fn.has('nvim-0.11') == 1 and vim.lsp.foldexpr)
@@ -21,9 +25,9 @@ local function setup_folding(bufnr, folding_mode)
vim.opt_local.foldexpr = 'v:lua.require("nuwiki.folding").expr()'
vim.opt_local.foldtext = 'v:lua.require("nuwiki.folding").foldtext()'
else
-- LSP-backed folding via Neovim 0.11+'s built-in foldexpr.
vim.opt_local.foldmethod = 'expr'
vim.opt_local.foldexpr = 'v:lua.vim.lsp.foldexpr()'
vim.opt_local.foldexpr = 'v:lua.require("nuwiki.folding").lsp_expr()'
vim.opt_local.foldtext = 'v:lua.require("nuwiki.folding").foldtext()'
end
end