GAPS.md
65 lines · 4709 bytes
Conformance gaps
OrgSwift is measured against the org-conformance corpus, whose
goldens come from orgo (validated against Emacs ox-html). The renderer is run in
orgo-compatible mode — OrgRenderOptions(metadataHeader: false, headingLevelOffset: 1) —
so that only real rendering differences remain.
Of the 13 corpus cases, 12 match orgo exactly — every case except outofscope, which is
scope: out in the corpus (deliberately unsupported constructs like #+INCLUDE, babel, and
LaTeX; orgo may also differ there, so it is documentation, not a target). The per-case state
is recorded in the expectations map in Tests/OrgSwiftTests/ConformanceTests.swift: a case
that regresses, or outofscope starting to match, fails the test and forces the map to be
updated — that is how this list stays honest.
Two behaviors were made configurable during extraction rather than left hardcoded, because they are presentation policy, not parser capability:
metadataHeader— whether#+TITLE/#+AUTHOR/#+DATErender as a leading<div class="org-metadata">. Apps that want a visible README title keep the default (true); orgo carries the title in the page template, so conformance runs withfalse.headingLevelOffset— added to a heading's star count. Default0renders*as<h1>; orgo uses1(*→<h2>, leaving<h1>for the title).
Closed
timestamps— active/inactive timestamps, times, same-day and multi-day ranges now render as<time class="timestamp" datetime>elements matching orgo; day names and repeater/warning cookies are dropped, non-timestamps (3 < 4,[not a stamp]) stay literal.footnote— references ([fn:1]), inline footnotes ([fn:label:text]), and reference-style definitions now render as<sup>anchors plus a<section class="footnotes">at the end, numbered in first-reference order. Footnote references are collected from paragraphs and headings; a reference inside a list item or table cell is not yet collected (it degrades to literal text).minimal/headings— property drawers (:PROPERTIES:…:END:) are dropped rather than rendered as a<dl>, and heading trailing:tag1:tag2:are split off the title and rendered as<span class="tag">. TODO/DONE keywords and priority cookies are still emitted as plain title text (orgo wraps them in styled spans); that is a separate, cosmetic difference the skeleton does not distinguish.core— barehttp(s)://URLs in running text are autolinked (trailing sentence punctuation left outside the link), and task checkboxes render as<code>[ ]</code>/<code>[X]</code>/<code>[-]</code>the way org's HTML exporter emits them, rather than as<input type="checkbox">.images—[[file:…]]links have thefile:prefix stripped and relative imagesrc/linkhrefare allowed (a scheme-less path cannot carry ajavascript:payload). A standalone image line becomes<p><img></p>; an affiliated#+CAPTION/#+ATTR_HTMLpromotes it to a<figure>, with a numbered<figcaption>when a caption is present.#+ATTR_HTML:key valuepairs (quoted values honored) become<img>attributes, and a described image link ([[file:x.png][text]]) renders as a link, not an inline image.elements— any unhandled#+KEYWORD:line (#+FILETAGS,#+TBLFM, …) is consumed as document metadata instead of leaking into the body, and[[id:target]]links render as in-page fragment links (<a href="#target">).lists— the list renderer is now recursive: unordered/ordered nesting to any depth, description lists (term :: definition→<dl>), and multi-paragraph items (an item's paragraphs, separated by blank lines, wrap in<p>). A latent link-parser bug that let a no-description link swallow a later link's][was fixed in passing.listblocks— a list item's body is a block sequence, not a run of paragraphs, so an item holds a source block, an example, a quote, or a table alongside its prose and its sublist, in source order. Paragraphs wrap in<p>only when the item has more than one, matching orgo; blocks follow bare.tblfm—^superscript renders (N^2→N<sup>2</sup>,x^{group}supported).blocks— example blocks are a bare<pre>;#+BEGIN_EXPORT htmlpasses through verbatim while other export backends are dropped; verse renders as<p class="verse">with<br>; a#+BEGIN_<name>special block becomes<div class="<name>">with org content; and a source block inside a quote stays nested in the<blockquote>.