You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Feature] Add source-position navigation and reading anchors / 增加源码位置导航与阅读锚点 API #6
A Markdown editor needs to synchronize source caret/scroll with the rendered preview and restore the reader's location after changing layout, font, theme, or the document text. This is needed for Vex's split view and detachable reading windows.
MarkdownView already exposes GetOutline(), ScrollToPoint(TextPoint) and ScrollToBlock(int). TextPoint identifies an entry/block/segment/plain-text offset, and model blocks have SourceRange. These are useful building blocks, but are not a public bidirectional mapping between arbitrary Markdown source offsets and rendered positions.
navigating to a source character offset, with a documented line/column conversion convention;
resolving a rendered point or selection back to a source range, returning an approximate block range when exact mapping is unavailable;
capturing/restoring a reading anchor: source location plus intra-block/viewport displacement, rather than only a percentage scroll offset.
Exact mapping for every syntax need not ship first. Block-level accuracy with an explicit precision/result status would already be useful. Treat source positions as tied to a document identity/version, and document behavior before snapshots/layout are ready.
Acceptance criteria
A source position inside a paragraph, list or code block can navigate the preview; it need not be a heading.
Rendered text containing emphasis/links does not silently equate plain-text offsets with Markdown source offsets.
Repeated headings, CRLF and CJK/surrogate-pair text follow a documented offset convention.
Anchors survive width/font/theme changes within the same source, without restoring onto a different document.
For edited source, remapping may be supplied by the host; deleted or unavailable targets have an explicit fallback.
This request covers navigation/anchor primitives. Preserving state during a refresh is a separate concern.
Thanks for the detailed report — implemented in #8 (released in 0.1.0-alpha.24).
What it adds on MarkdownView (block-level precision, as you suggested):
ScrollToSourceOffset(offset) → SourceNavigationResult. It scrolls to the deepest block containing the offset, so a list item or a paragraph inside a quote works, not only headings; a code block is targeted as a whole. When nothing moves, the status says why: NoDocument, OutOfRange or LayoutPending.
GetSourceRange(TextPoint) / GetSelectionSourceRange() map a rendered point or the selection back to the source range of the covering blocks. TextPoint.Offset stays a plain-text offset inside a segment and is never treated as a source offset.
CaptureAnchor() / RestoreAnchor(anchor) keep the character at the top of the outer ScrollViewer plus its pixel displacement. Anchors are tied to MarkdownView.DocumentId, so they are not restored onto a different document. If the anchored block was deleted, the view goes to the next block (Approximate).
Offset convention (documented, tested with CRLF, CJK and surrogate pairs): UTF-16 code units, i.e. string indexes; \r\n counts as 2. SourceText.ToLocation/ToOffset convert to/from 0-based line and column.
Limits: precision is block-level (no line/character inside a block); anchors need an ancestor ScrollViewer. The anchor test covers a width change; font and theme changes go through the same re-layout path but are not tested separately.
The edit case is covered by RefreshMarkdown (see #5). Feedback on the API shape is welcome — this is a pre-release, so it can still change.
Activity
239573049 commentedon Oct 21, 2026
Thanks for the detailed report — implemented in #8 (released in
0.1.0-alpha.24).What it adds on
MarkdownView(block-level precision, as you suggested):ScrollToSourceOffset(offset)→SourceNavigationResult. It scrolls to the deepest block containing the offset, so a list item or a paragraph inside a quote works, not only headings; a code block is targeted as a whole. When nothing moves, the status says why:NoDocument,OutOfRangeorLayoutPending.GetSourceRange(TextPoint)/GetSelectionSourceRange()map a rendered point or the selection back to the source range of the covering blocks.TextPoint.Offsetstays a plain-text offset inside a segment and is never treated as a source offset.CaptureAnchor()/RestoreAnchor(anchor)keep the character at the top of the outerScrollViewerplus its pixel displacement. Anchors are tied toMarkdownView.DocumentId, so they are not restored onto a different document. If the anchored block was deleted, the view goes to the next block (Approximate).stringindexes;\r\ncounts as 2.SourceText.ToLocation/ToOffsetconvert to/from 0-based line and column.Limits: precision is block-level (no line/character inside a block); anchors need an ancestor
ScrollViewer. The anchor test covers a width change; font and theme changes go through the same re-layout path but are not tested separately.The edit case is covered by
RefreshMarkdown(see #5). Feedback on the API shape is welcome — this is a pre-release, so it can still change.感谢详细的反馈,已在 #8 中实现(已随
0.1.0-alpha.24发布)。MarkdownView新增(块级精度):ScrollToSourceOffset(offset):滚动到包含该偏移的最深的块,列表项、引用里的段落都可以,不限标题;代码块按整块定位。没有滚动时用状态说明原因(NoDocument/OutOfRange/LayoutPending)。GetSourceRange(TextPoint)/GetSelectionSourceRange():把渲染位置或选区反查为所覆盖块的源码区间。TextPoint.Offset仍是片段纯文本偏移,不会被当成源码偏移。CaptureAnchor()/RestoreAnchor(anchor):记录外层ScrollViewer可视区顶部的字符及其像素位移,绑定MarkdownView.DocumentId,不会恢复到另一个文档;锚点所在块被删除时落到下一个块(Approximate)。string下标),\r\n占 2。SourceText.ToLocation/ToOffset做 0 起始的行列换算。局限:只有块级精度;锚点需要外层有
ScrollViewer。锚点测试覆盖了宽度变化,字体 / 主题变化走同一条重排路径,但没有单独测试。编辑后的映射由
RefreshMarkdown处理(见 #5)。欢迎对 API 形态提意见——目前是预发布版本,仍可调整。Merge pull request #8 from AtomUICommunity/release/0.1.0-alpha.24