Skip to content

Map bare autolinks from their end, not their start column - #11

Merged
hebertialmeida merged 1 commit into
masterfrom
fix/bare-autolink-line-offsets
Sep 10, 2026
Merged

hebertialmeida merged 1 commit into
masterfrom
fix/bare-autolink-line-offsets

Conversation

@hebertialmeida

Copy link
Copy Markdown
Owner

Follow-up to #10, reported against 1.4.0 from a wrapped paragraph holding two links: the second URL was highlighted from partway through — e.com in the link colour, https://www.exampl as plain text.

cmark's GFM autolink extension gets a bare URL's start wrong in two different ways, and its end right in both. Both are now handled by measuring back from the end, which needs no knowledge of where the line began.

1. Wrong by the length of the preceding lines

The start is reported as an offset into the paragraph's content buffer, not a column on its line. On a single-line paragraph the two coincide to within one character — which is exactly why the +1 adjustment in #10 looked correct. After a soft line break the error grows by every byte of the preceding lines:

link actual column reported error
https://one.example.com 3 12 +9 ("line one" + newline)
https://two.example.com 31 40 +9

So highlighting painted a suffix of each URL and left the front as plain text.

2. Missing entirely

A URL that opens a paragraph is reported at column 0. position(in:) is all-or-nothing by design — it is used by every node type — so it mapped neither end, and the URL went unhighlighted anywhere. This was called out as a known gap in the 1.4.0 notes.

Bare autolinks are now adjusted before that check, and the end is mapped from its own line and column when position(in:) declined to.

The fix

The start is derived by walking back the length of the URL as written in the source. The source length matters, and is pinned by a test: GFM expands www.example.com to http://www.example.com, so measuring the destination would walk back too far.

The two text nodes around the link were wrong for the same reason and are repaired with it — the autolink's own child takes the link's whole position, and the run in front of it stops where the link starts. Where the URL opens the line, cmark emits a leading text node with an empty literal whose span cannot be clamped without inverting it; that one is left alone and documented, since it carries no text and every consumer reads the literal rather than the span.

Tests

Tests/MarkdownSyntaxTests/BareAutolinkLineOffsetTests.swift — 14 cases: the reported two-links-on-a-wrapped-line shape, a URL alone on a later line, two bare links after a soft break, a link on line 5, www. expansion, both text-node repairs, paragraph-opening URLs (document-initial and later), and four that pin the single-line and inline/angle-link behaviour that was already right.

CI=true swift test130 passed, 1 skipped.

🤖 Generated with Claude Code

cmark's GFM autolink extension gets a bare URL's start wrong in two ways,
and its end right in both. Both are now handled by measuring back from the
end, which needs no knowledge of where the line began.

**Wrong by the length of the preceding lines.** The start is reported as an
offset into the paragraph's *content buffer*, not a column on its line. On a
single-line paragraph the two coincide to within one character, which is why
the `+1` adjustment looked right. After a soft line break the error grows:

    "line one\na https://one.example.com and https://two.example.com b"
                 ↑ column 3, reported as 12      ↑ column 31, reported as 40

Highlighting therefore painted a *suffix* of each URL — "ne.example.com" —
and left the front as plain text. Reported against 1.4.0 from a wrapped
paragraph holding two links.

**Missing entirely.** A URL that opens a paragraph is reported at column 0,
so `position(in:)` — all-or-nothing by design — mapped neither end, and the
URL went unhighlighted anywhere. Bare autolinks are now adjusted before that
check, and the end is mapped from its own line and column when needed.

The start is derived by walking back the length of the URL *as written in
the source*. The source length matters: GFM expands `www.example.com` to
`http://www.example.com`, so measuring the destination would walk back too
far.

The two text nodes around the link were wrong for the same reason and are
repaired with it — the autolink's own child takes the link's whole position,
and the run in front of it stops where the link starts. Where the URL opens
the line, cmark emits a leading text node with an *empty literal* whose span
cannot be clamped without inverting it; that one is left alone and documented.

`CI=true swift test`: 130 passed, 1 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (a4e066e) to head (f75dd87).
⚠️ Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master       #11   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines          133       133           
=========================================
  Hits           133       133           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@hebertialmeida
hebertialmeida merged commit a7f47b2 into master Sep 10, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant