summary
footnotes are on the README's "Not yet supported" list. unlike task lists (#118), the parser gives us nothing here: swift-markdown attaches only the table, strikethrough, and tasklist cmark-gfm extensions and has no footnote node types at all, so footnote syntax survives parsing as plain literal text. i'd like to contribute rendering support via a preprocessing pass, following the same approach the library already uses for LaTeX — and i'd love a design ack before sending the PR, since there's an architectural choice involved.
minimal markdown input
here is a footnote reference.[^1]
[^1]: and here is the definition.
(the kitchen-sink fixture already exercises this — [^streaming] around lines 229–231.)
current behavior
the reference renders inline as the literal text "here is a footnote reference.[^1]", and the definition renders as a regular paragraph "[^1]: and here is the definition." — readable, as the README promises, but far from the GitHub rendering.
proposed change
a FootnotePreProcessor alongside the existing LaTexPreProcessor (whose own doc comment describes preprocessing as "a less heavy-weight approach than forking commonmark-gfm and swift-markdown" — same reasoning applies here):
- collect single-line definitions (
[^id]: text) and remove them from the source;
- number references by order of first appearance (GFM semantics — labels like
[^note] render as numbers);
- replace each reference with a specially-marked inline construct that the inline layer renders as a real superscript (smaller font + baseline offset) — the same marker technique the LaTeX preprocessor already uses for inline math;
- append a footnotes section at the end of the document: thematic break + ordered list with the definition texts (inline formatting inside definitions is preserved, since the emitted text goes through the normal markdown parse);
- skip anything inside fenced code blocks or inline code spans, so code samples containing
[^1] aren't transformed.
GFM parity for the edges: references without a definition stay literal; definitions never referenced are dropped.
v1 scope / open questions
- display-only: no tap-to-scroll between reference and definition (the block-based architecture makes that a separate, bigger conversation — can be a follow-up).
- single-line definitions only; GFM's multi-line/indented continuations are out of scope for v1 and keep today's behavior.
- streaming: the preprocessor is a pure function of each snapshot, so numbering stays stable as text grows; a partially streamed [^ renders as literal text until the bracket closes (same class of jitter as other constructs).
- your architecture notes describe preprocessing as "not ideal" with in-parser math on the roadmap — if you'd rather not grow this stage, happy to discuss alternatives (a post-parse rewriter, or waiting on upstream swift-markdown support). could also gate it behind
MarkdownParseOption like latexMatchingRules if you want it opt-out.
validation plan
- unit tests for the preprocessor (pure string → string: numbering, missing definitions, unreferenced definitions, code-fence immunity, idempotence on already-processed text);
- a unit test for the superscript inline conversion;
- snapshot tests (iOS + macOS variants) for a document with references and the footnotes section;
- kitchen-sink fixture update once supported, plus the README support-list move.
if this sounds good, i'll send a focused PR.
summary
footnotes are on the README's "Not yet supported" list. unlike task lists (#118), the parser gives us nothing here: swift-markdown attaches only the
table,strikethrough, andtasklistcmark-gfm extensions and has no footnote node types at all, so footnote syntax survives parsing as plain literal text. i'd like to contribute rendering support via a preprocessing pass, following the same approach the library already uses for LaTeX — and i'd love a design ack before sending the PR, since there's an architectural choice involved.minimal markdown input
(the kitchen-sink fixture already exercises this —
[^streaming]around lines 229–231.)current behavior
the reference renders inline as the literal text "here is a footnote reference.[^1]", and the definition renders as a regular paragraph "[^1]: and here is the definition." — readable, as the README promises, but far from the GitHub rendering.
proposed change
a
FootnotePreProcessoralongside the existingLaTexPreProcessor(whose own doc comment describes preprocessing as "a less heavy-weight approach than forking commonmark-gfm and swift-markdown" — same reasoning applies here):[^id]: text) and remove them from the source;[^note]render as numbers);[^1]aren't transformed.GFM parity for the edges: references without a definition stay literal; definitions never referenced are dropped.
v1 scope / open questions
MarkdownParseOptionlikelatexMatchingRulesif you want it opt-out.validation plan
if this sounds good, i'll send a focused PR.