feat: wiki-links by id — id-first resolution + heph.nvim [[ picker (§8.4)
Some checks failed
Build / validate (pull_request) Failing after 6m34s

Backend: `links::resolve_id` now checks for an exact live node id before
alias/title, so a canonical `[[NODEID]]` link resolves to its node and
can't be shadowed by a like-named node. Legacy `[[Name]]` links still
resolve by name (until the migration), so this is additive.

heph.nvim: `link.insert` (bound to insert-mode `[[` and `:Heph link`)
searches via the `search` RPC and inserts `[[NODEID]]`, with a "+ Create
new doc" entry; `<CR>` follow resolves the id directly. e2e covers
search→insert→materialize and the create path.

Remaining (§8.4): read-expansion/conceal display + the one-time
[[Title]]→[[NODEID]] migration (then retire name-resolution + the hack).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-06-03 12:07:46 -07:00
commit 4e8f6743cf
7 changed files with 139 additions and 9 deletions

View file

@ -68,12 +68,48 @@ function M.follow()
require("heph.node").open(node.id)
end
--- Attach the buffer-local `<CR>` follow keymap and inline-`#hashtag`
--- Pick a node (by full-text search) and insert a canonical `[[NODEID]]` link at
--- the cursor — the authoring path for wiki-links-by-id (§8.4); a node id is the
--- only thing that ever enters a stored link, so there's no name ambiguity. A
--- "Create" entry mints a new doc named after the query. No-op if cancelled.
function M.insert()
vim.ui.input({ prompt = "Link to: " }, function(query)
if not query or query == "" then
return
end
local items = {}
for _, hit in ipairs(rpc.call("search", { query = query }) or {}) do
items[#items + 1] = hit
end
items[#items + 1] = { __create = true, title = query }
require("heph.picker").select(items, {
prompt = "Link to",
format = function(it)
if it.__create then
return "+ Create new doc: " .. it.title
end
return it.title .. " [" .. (it.kind or "node") .. "]"
end,
}, function(choice)
if not choice then
return
end
local id = choice.__create and rpc.call("node.create", { kind = "doc", title = choice.title }).id or choice.id
vim.api.nvim_put({ "[[" .. id .. "]]" }, "c", true, true)
end)
end)
end
--- Attach the buffer-local follow/insert keymaps and inline-`#hashtag`
--- highlighting (only on heph:// buffers).
function M.attach(buf)
vim.keymap.set("n", "<CR>", function()
M.follow()
end, { buffer = buf, desc = "heph: follow [[link]]" })
-- Typing `[[` opens the node picker (Obsidian-style), inserting `[[NODEID]]`.
vim.keymap.set("i", "[[", function()
M.insert()
end, { buffer = buf, desc = "heph: insert [[link]]" })
-- Render inline #hashtags in italics so they stand out — matching the
-- save-time tag detection (whitespace-prefixed `#word`, never a `# heading`).