import Foundation /// Assigns footnote numbers in first-reference order and holds the notes they resolve to. /// /// Numbering is document-wide and order-of-use, so it belongs to the render pass rather than /// the parse: a renderer walks the tree, calls ``number(for:inline:)`` as it meets each /// reference, then reads ``notes`` to emit the section. It is output-format agnostic — /// the HTML renderer and the SwiftUI one share it. package struct OrgFootnotes { private var numbers: [String: Int] = [:] private var order: [String] = [] /// Reference-style definitions, gathered from the document's `[fn:x] …` lines. private var definitions: [String: [OrgObject]] = [:] /// Inline definitions, gathered from `[fn:x:text]` references as they are rendered. private var inlineDefinitions: [String: [OrgObject]] = [:] package var figureNumber = 0 package init(document: OrgDocument) { for element in document.elements { if case .footnoteDefinition(let label, let content) = element { definitions[label] = content } } } package mutating func number(for label: String, inline: [OrgObject]? = nil) -> Int { if let inline, inlineDefinitions[label] == nil { inlineDefinitions[label] = inline } if let existing = numbers[label] { return existing } let next = order.count + 1 numbers[label] = next order.append(label) return next } /// One numbered note, resolved to its content. package struct Note { package var number: Int package var label: String package var content: [OrgObject] /// An inline footnote's text sits directly in the item; a reference-style definition /// is a paragraph, matching org's exporter. package var isInline: Bool } /// The referenced notes, in numbering order. Empty when the document has none, which is /// the renderer's cue to omit the section entirely. package var notes: [Note] { order.map { label in Note( number: numbers[label] ?? 0, label: label, content: inlineDefinitions[label] ?? definitions[label] ?? [], isInline: inlineDefinitions[label] != nil ) } } }