Skip to content

Don't autolink URLs used as markdown link text#3319

Merged
theletterf merged 4 commits into
mainfrom
double-external-icons
May 21, 2026
Merged

Don't autolink URLs used as markdown link text#3319
theletterf merged 4 commits into
mainfrom
double-external-icons

Conversation

@shainaraskas
Copy link
Copy Markdown
Member

@shainaraskas shainaraskas commented May 13, 2026

Why

When a URL is used as both the visible text and the target of a markdown link — e.g. https://gist.github.com — the page renders two external-link icons. The custom bare-URL autolinker runs before the standard link parser, so it matches the URL inside the [...] text and produces a nested . The CSS rule that paints the external icon then fires on both anchors. Closes #3317.

Chose to do this through a code fix rather than fixing instances because sometimes the URL link text is the most accurate + making it a bare link is busywork + the problem is likely to recur. the most expedient fix is in the code

What

HtmxLinkInlineRenderer now walks the parent chain of each LinkInline it's about to write; if any ancestor is also a LinkInline, it skips the wrapping and just renders the children. The outer link keeps its target="_blank" + external icon; the inner autolinked URL becomes plain text inside it. One anchor, one icon, valid HTML.

Adds two regression tests covering the exact issue case and a URL embedded mid-text inside link text.

bad good
image image

Made with Cursor

@shainaraskas shainaraskas requested a review from a team as a code owner May 13, 2026 21:52
@shainaraskas shainaraskas requested a review from technige May 13, 2026 21:52
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR prevents nested anchor markup by having HtmxLinkInlineRenderer detect when a LinkInline is inside another LinkInline and, when detected, render the link's children directly instead of emitting an outer . Regression tests were added: one for a URL used as the complete link text, one for a URL appearing inside surrounding link text, and one ensuring an image inside a link still renders the outer anchor and the image. All tests assert no diagnostics.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: preventing autolinked URLs from being nested when used as markdown link text.
Description check ✅ Passed The description clearly explains the issue, the solution approach, and includes supporting visual comparisons of the before/after behavior.
Linked Issues check ✅ Passed The PR fully addresses #3317 by detecting nested LinkInline elements and preventing inner anchor generation, with regression tests covering both the exact issue and embedded URL scenarios.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the nested anchor issue: the renderer detects nesting and three focused regression tests validate the fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch double-external-icons

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

The parser-level check only caught the case where the autolink URL
appeared immediately after `[`. Once any literal text intervened, the
LinkDelimiterInline walk-back missed it. Detecting nested links in the
renderer instead works for every parse shape.

Co-authored-by: Cursor <cursoragent@cursor.com>
@shainaraskas shainaraskas temporarily deployed to integration-tests May 20, 2026 16:10 — with GitHub Actions Inactive
Replace fragile exact-string NotContain with NotMatchRegex to catch
nested <a> regardless of attribute ordering. Add ImageInsideLinkTests
to confirm the IsImage branch is unaffected by the IsNestedInsideLink
guard.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
@theletterf theletterf temporarily deployed to integration-tests May 21, 2026 07:23 — with GitHub Actions Inactive
Copy link
Copy Markdown
Member

@theletterf theletterf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

@theletterf theletterf enabled auto-merge (squash) May 21, 2026 07:23
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tests/Elastic.Markdown.Tests/Inline/AutoLinkTests.cs (1)

255-279: ⚡ Quick win

Rename new test methods to Method_Scenario_Expected.

The newly added test method names don’t follow the required test naming convention. Please rename them to the Method_Scenario_Expected format for consistency with the project standard.

As per coding guidelines, "Test method naming convention: Method_Scenario_Expected."

Also applies to: 288-297

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Elastic.Markdown.Tests/Inline/AutoLinkTests.cs` around lines 255 - 279,
Rename the test methods to follow the Method_Scenario_Expected convention:
change DoesNotCreateNestedAnchor to a name like
RenderLink_WhenLinkContainsUrl_NoNestedAnchor (or similar
Method_Scenario_Expected wording) and change DoesNotAutolinkUrlInsideLinkText to
a name like RenderLink_WhenLinkTextContainsUrl_NoAutolink; also rename the
HasNoErrors methods in both classes to something like
Collector_HasNoDiagnostics_ZeroCount to match the same convention. Update the
method identifiers (e.g., DoesNotCreateNestedAnchor,
DoesNotAutolinkUrlInsideLinkText, and both HasNoErrors) to the new
Method_Scenario_Expected names throughout the file and any references.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@tests/Elastic.Markdown.Tests/Inline/AutoLinkTests.cs`:
- Around line 255-279: Rename the test methods to follow the
Method_Scenario_Expected convention: change DoesNotCreateNestedAnchor to a name
like RenderLink_WhenLinkContainsUrl_NoNestedAnchor (or similar
Method_Scenario_Expected wording) and change DoesNotAutolinkUrlInsideLinkText to
a name like RenderLink_WhenLinkTextContainsUrl_NoAutolink; also rename the
HasNoErrors methods in both classes to something like
Collector_HasNoDiagnostics_ZeroCount to match the same convention. Update the
method identifiers (e.g., DoesNotCreateNestedAnchor,
DoesNotAutolinkUrlInsideLinkText, and both HasNoErrors) to the new
Method_Scenario_Expected names throughout the file and any references.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 27f445c5-2bf3-4ab8-8f3f-f44285c37baa

📥 Commits

Reviewing files that changed from the base of the PR and between 0f783e4 and a345016.

📒 Files selected for processing (1)
  • tests/Elastic.Markdown.Tests/Inline/AutoLinkTests.cs

@theletterf theletterf merged commit ee19a02 into main May 21, 2026
24 checks passed
@theletterf theletterf deleted the double-external-icons branch May 21, 2026 07:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using a URL as text on a markdown link (external) results in two "external" icons

2 participants