047c9a3cf3
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>
59 lines
1.8 KiB
Lua
59 lines
1.8 KiB
Lua
-- lua/nuwiki/folding.lua — `foldexpr` fallback when the LSP isn't
|
|
-- attached. Same heading-block model as the server-side `foldingRange`
|
|
-- provider in `crates/nuwiki-lsp/src/folding.rs`, but driven from a
|
|
-- pure regex over `getline()` so it works without a running server.
|
|
--
|
|
-- Activated by `setlocal foldmethod=expr foldexpr=v:lua.require'nuwiki.folding'.expr()`
|
|
-- in `ftplugin/nuwiki.vim` when `folding` is enabled in the user config.
|
|
|
|
local M = {}
|
|
|
|
--- Heading level from one line of source. Returns 0 for non-headings.
|
|
local function heading_level(line)
|
|
local lead = line:match('^%s*(=+)%s')
|
|
if not lead then return 0 end
|
|
local lvl = #lead
|
|
if lvl > 6 then return 0 end
|
|
-- Require a matching trailing run of `=`s.
|
|
local trail = line:match('%s(=+)%s*$')
|
|
if not trail or #trail ~= lvl then return 0 end
|
|
return lvl
|
|
end
|
|
|
|
--- `foldexpr` body. Returns a fold-marker string per `:help fold-expr`.
|
|
function M.expr()
|
|
local lnum = vim.v.lnum
|
|
local line = vim.fn.getline(lnum)
|
|
local lvl = heading_level(line)
|
|
if lvl > 0 then
|
|
return '>' .. lvl
|
|
end
|
|
return '='
|
|
end
|
|
|
|
--- Build a heading-only `foldtext` showing the heading title trimmed.
|
|
function M.foldtext()
|
|
local fs = vim.v.foldstart
|
|
local line = vim.fn.getline(fs):gsub('=', ''):gsub('^%s+', ''):gsub('%s+$', '')
|
|
local count = vim.v.foldend - vim.v.foldstart + 1
|
|
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
|