Skip to content

feature request: render footnotes ([^1]) #126

Description

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions