fix(vim): follow-link creates missing pages + safe LSP foldexpr
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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user