Navigate to a heading when following [[Note#Heading]] master
The `#Heading` half of a wikilink parsed and resolved the note but did nothing with the anchor, and a bare same-document `[[#Heading]]` bailed out entirely. Following one now scrolls the heading to the top of the viewport and puts the caret on the line *below* it. Below, not on it: `headingDecoration` reveals a heading line's raw `## ` markers while the cursor sits there, so landing on the heading would change its appearance and reflow the line at the moment the scroll settles. A heading near the end of a document lands mid-viewport, since CodeMirror cannot scroll past the end and forcing it would mean permanent blank space under every document. For a document being opened, the heading is resolved BEFORE the view exists and applied through `EditorViewConfig.scrollTo` at construction. Dispatching the scroll straight after `new EditorView(...)` does not work: CodeMirror only applies a pending scroll target once `viewState.editorHeight` is non-zero, which is not guaranteed in the tick the view is created, so the request is silently dropped and the document stays at the top. `resolveHeadingPosition` therefore takes a `Text` rather than an `EditorState`, so it can run before construction. The dispatch path remains for a reveal arriving at an already-mounted editor, where the view has long since been measured. Heading extraction is a line scan, not a Lezer walk. `syntaxTree` is parsed viewport-first and can be truncated on a long document, and forcing it with `ensureSyntaxTree` blocks the thread and may still time out; a scan is O(lines) with no failure mode. It also lets one implementation serve completion for notes that are not open, where there is no `EditorState` at all. Since that makes it a second heading implementation alongside the grammar the editor renders with, a parity test asserts both find headings on the same lines across a corpus — that is what stops the two drifting. The anchor rides on `OpenDocRequest.nav` rather than a separate channel: the workspace already decides which tab an open lands in, and a parallel channel matching on providerId+entryId would scroll *every* tile showing that document. `nav` is excluded from dedup (which compares providerId+entryId) and from persistence, via an explicit allowlist in serializeLayout so a future transient field cannot leak into localStorage silently — a test caught exactly that leaking while writing this. Two things had to be fixed for it to arrive at all: - `useTilingDocumentSlots` early-returned when the document was already open, leaving the tab holding its original request and dropping the newer anchor. New pure `tabsModel.retargetTab` swaps the request while preserving the slot kind and handle, so the editor is not remounted. - A reveal must not replay. The editor records the applied `seq` in the tab's view snapshot, so returning to a tab restores where you left it rather than re-running the jump that first brought you there. `[[#Heading]]` short-circuits inside the editor — no vault lookup, no workspace round trip — and works in view mode, where `readOnly` blocks document changes but not selection or scrolling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>