# gfm_autolinks

URL: /patitas/api/parsing/inline/gfm_autolinks/
Section: inline
Description: GFM extended autolink scanning for Patitas.

Implements the GFM "Autolinks (extension)" feature: recognizing bare URLs,
``www.`` links, and bare email addresses inside ordinary inline text. This is
gated on ``ParseConfig.autolinks_enabled`` and is entirely separate from the
CommonMark angle-bracket autolinks (``<https://...>``) handled in
``special.py``, which work without any plugin.

Reference: https://github.github.com/gfm/#autolinks-extension-

The public entry point is `scan_text_for_autolinks`(). It scans the *full*
inline text from a starting offset so that a URL/``www.`` body can include
characters that are otherwise inline-special (``& ~ $ _ * [ ! {``) — these are
ubiquitous in real URLs (query separators, ``~user`` paths, ``Foo_(bar)``
article slugs). The scan follows the GFM rule that a URL body extends until
whitespace or ``<`` (with one pragmatic exception below), then applies
trailing-punctuation / paren-balance / entity trimming. It returns the position
at which it stopped so the main inline tokenizer can resume there.

Precedence (GFM: code spans > autolinks > emphasis):
- A code-span backtick terminates a URL body, so ``http://a.com`code` `` keeps
  the higher-precedence code span rather than swallowing it. (CommonMark/GFM
  give code spans higher precedence than autolinks.)
- An email *local part* may contain ``_`` (the one emphasis delimiter that is a
  valid local-part character), e.g. ``a_b@example.com``. The inline tokenizer
  resolves such a bare email *before* treating the ``_`` as emphasis (via
  `try_email_autolink_at_delimiter`()), so the full local part is linked.
  ``*`` and ``~`` are not valid local-part characters, so they never need this.

Thread Safety:
    Pure functions over their arguments; no shared mutable state. Safe for
    concurrent use.

---

> For a complete page index, fetch /patitas/llms.txt.

Open LLM text
(/patitas/api/parsing/inline/gfm_autolinks/index.txt)

Share with AI

Ask Claude
(https://claude.ai/new?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Finline%2Fgfm_autolinks%2Findex.txt)

Ask ChatGPT
(https://chatgpt.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Finline%2Fgfm_autolinks%2Findex.txt)

Ask Gemini
(https://gemini.google.com/app?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Finline%2Fgfm_autolinks%2Findex.txt)

Ask Copilot
(https://copilot.microsoft.com/?q=Please%20help%20me%20understand%20this%20documentation%3A%20%2Fpatitas%2Fapi%2Fparsing%2Finline%2Fgfm_autolinks%2Findex.txt)

Module

#
`parsing.inline.gfm_autolinks`

GFM extended autolink scanning for Patitas.

Implements the GFM "Autolinks (extension)" feature: recognizing bare URLs,
`www.`links, and bare email addresses inside ordinary inline text. This is
gated on`ParseConfig.autolinks_enabled`and is entirely separate from the
CommonMark angle-bracket autolinks (`<https://...>`) handled in
`special.py`, which work without any plugin.

Reference: https://github.github.com/gfm/#autolinks-extension-

The public entry point is`scan_text_for_autolinks`(). It scans the full
inline text from a starting offset so that a URL/`www.`body can include
characters that are otherwise inline-special (`& ~ $ _ * [ ! {`) — these are
ubiquitous in real URLs (query separators,`~user` paths, `Foo_(bar)`
article slugs). The scan follows the GFM rule that a URL body extends until
whitespace or`<`(with one pragmatic exception below), then applies
trailing-punctuation / paren-balance / entity trimming. It returns the position
at which it stopped so the main inline tokenizer can resume there.

Precedence (GFM: code spans > autolinks > emphasis):

- A code-span backtick terminates a URL body, so`http://a.com`code` `keeps
the higher-precedence code span rather than swallowing it. (CommonMark/GFM
give code spans higher precedence than autolinks.)

- An email local part may contain`_`(the one emphasis delimiter that is a
valid local-part character), e.g.`a_b@example.com`. The inline tokenizer
resolves such a bare email before treating the`_`as emphasis (via
`try_email_autolink_at_delimiter`()), so the full local part is linked.
`*` and `~`are not valid local-part characters, so they never need this.

Thread Safety:

```
Pure functions over their arguments; no shared mutable state. Safe for
concurrent use.
```

8Functions

## Functions

`_is_left_boundary`

1

`bool`

▼

Return True if *prev_char* is a valid left boundary for an autolink.

A valid a…

`def _is_left_boundary(prev_char: str | None) -> bool`

Return True if prev_char is a valid left boundary for an autolink.

A valid autolink is recognized only when preceded by start-of-line/text
(`prev_char is None`), whitespace, or one of `* _ ~ (`.

##### Parameters

Name
Type
Description

`prev_char`
`str | None`

##### Returns

`bool`

`_is_url_terminator`

1

`bool`

▼

Return True if *c* ends a bare URL/www body.

GFM extends a URL until whitespac…

`def _is_url_terminator(c: str) -> bool`

Return True if c ends a bare URL/www body.

GFM extends a URL until whitespace or`<`. We additionally stop at a
backtick so that a code span (higher precedence in CommonMark/GFM) is not
swallowed by the URL.

##### Parameters

Name
Type
Description

`c`
`str`

##### Returns

`bool`

`_trim_trailing_punctuation`

1

`str`

▼

Trim GFM trailing punctuation from a URL/www match.

Rules (GFM autolinks exten…

`def _trim_trailing_punctuation(match: str) -> str`

Trim GFM trailing punctuation from a URL/www match.

Rules (GFM autolinks extension):

- Strip trailing`?!.,:*_~`characters.

- A trailing`)` is stripped while there are more `)` than `(`in the
remaining match.

- If the match ends in a semicolon that closes an entity-like reference
(`&...;`), strip the whole entity reference.

These rules are applied repeatedly until the match is stable.

##### Parameters

Name
Type
Description

`match`
`str`

##### Returns

`str`

`_scan_url`

3

`int`

▼

Scan a bare ``http://``/``https://`` URL body starting at *start*.

*start* poi…

`def _scan_url(text: str, start: int, scheme_len: int) -> int`

Scan a bare`http://`/`https://`URL body starting at start.

start points at the first character of the scheme. scheme_len is the
length of the`http://` or `https://`prefix. Returns the exclusive end
index of the raw match (before trailing-punctuation trimming), or start
if the authority is not a valid domain.

##### Parameters

Name
Type
Description

`text`
`str`

`start`
`int`

`scheme_len`
`int`

##### Returns

`int`

`_scan_www`

2

`int`

▼

Scan a ``www.`` link starting at *start* (the first ``w``).

Returns the exclus…

`def _scan_www(text: str, start: int) -> int`

Scan a`www.` link starting at start (the first `w`).

Returns the exclusive end index of the raw match, or start if the
authority is not a valid domain.

##### Parameters

Name
Type
Description

`text`
`str`

`start`
`int`

##### Returns

`int`

`_scan_email`

3

`tuple[int, int] | None`

▼

Scan a bare email around the ``@`` at *at_pos*.

Returns ``(start, end)`` of th…

`def _scan_email(text: str, at_pos: int, min_start: int) -> tuple[int, int] | None`

Scan a bare email around the`@`at at_pos.

Returns`(start, end)` of the matched email (exclusive end), or `None`
if there is no valid email. The local part is scanned backwards from the
`@`(but no earlier than min_start, the start of the current scan span,
so the local part never crosses an inline-special boundary); the domain
forwards.

##### Parameters

Name
Type
Description

`text`
`str`

`at_pos`
`int`

`min_start`
`int`

##### Returns

`tuple[int, int] | None`

`try_email_autolink_at_delimiter`

3

`tuple[int, int, Link (/patitas/api/nodes/#Link)] | …`

▼

Try to recognize a bare email whose local part spans an emphasis delimiter.

Th…

`def try_email_autolink_at_delimiter(text: str, delim_pos: int, location: SourceLocation) -> tuple[int, int, Link] | None`

Try to recognize a bare email whose local part spans an emphasis delimiter.

The main inline tokenizer dispatches an emphasis delimiter (`_`) before the
plain-text autolink scan runs, which would otherwise split an email local
part such as the`a_b` in `a_b@example.com`. GFM gives autolinks higher
precedence than emphasis, so this is called first at a`_`candidate: it
looks for an`@`reachable forward through valid local-part characters,
recovers the full local part backwards (honoring the GFM left-boundary
rule), and validates the domain. Only`_` needs this treatment: `*`and
`~`are not valid GFM email-local-part characters, so they can never sit
inside a linkable local part.

##### Parameters

Name
Type
Description

`text`
`str`

The full inline text being tokenized.

`delim_pos`
`int`

Index of the candidate`_`delimiter.

`location`
`SourceLocation (/patitas/api/location/#SourceLocation)`

Source location attached to the produced node.

##### Returns

`tuple[int, int, Link (/patitas/api/nodes/#Link)] | None`

`scan_text_for_autolinks`

4

`tuple[list[InlineToken],…`

▼

Scan *text* from *start* for GFM autolinks.

Consumes plain text and any bare U…

`def scan_text_for_autolinks(text: str, start: int, prev_char: str | None, location: SourceLocation) -> tuple[list[InlineToken], int]`

Scan text from start for GFM autolinks.

Consumes plain text and any bare URL /`www.`/ email autolinks, where a
URL body may contain characters that are otherwise inline-special (`& ~ $ _ * [ ! {`) — it extends until whitespace, `<`, or a backtick. Stops at
the first inline-special character that is not part of an autolink (or at
the end of text) so the main inline tokenizer can resume from there.

##### Parameters

Name
Type
Description

`text`
`str`

The full inline text being tokenized.

`start`
`int`

Offset into text at which to begin scanning.`text[start]`is guaranteed by the caller not to be an inline-special character.

`prev_char`
`str | None`

The character immediately preceding start, or`None`if start is the beginning of the inline text. Drives the GFM left-boundary rule.

`location`
`SourceLocation (/patitas/api/location/#SourceLocation)`

Source location attached to produced nodes.

##### Returns

`tuple[list[InlineToken], int]`
