krz/orgo
Lightning fast org-mode static site generator.
clone: git clone https://gitbay.org/krz/orgo.git
f274b516569365d352ed75206f6678f862cfd7f7
signed_unknown_key
author: Christian Cleberg <hello@cleberg.net> · 2026-08-23T05:05:05Z
committer: <noreply@github.com>
fixtures/outofscope.org | 5 ++-- src/audit.rs | 15 +++++------ src/main.rs | 4 +-- src/parser.rs | 29 ++++++++++++---------- src/render.rs | 15 +++++------ tests/constructs.rs | 12 ++++----- tests/snapshots/constructs__out_of_scope_html.snap | 2 +- 7 files changed, 44 insertions(+), 38 deletions(-) @@ -1,8 +1,9 @@ #+TITLE: Out of Scope #+INCLUDE: "other.org" -Every construct here is on the README's explicit OUT list. The contract is not that we -handle them — it is that they degrade predictably and never crash the build. +Every construct here is on the explicit "Not supported" list in the org-support guide. +The contract is not that we handle them — it is that they degrade predictably and never +crash the build. * Babel @@ -1,9 +1,9 @@ //! Corpus audit (spec §5, Phase 0): measure which org constructs a real corpus actually -//! uses, and classify each against the v1 IN/OUT line. +//! uses, and classify each against the supported/unsupported line. //! -//! This exists because the v1 scope was, on the README's own admission, *recommended* -//! rather than measured — a guess about which slice of org matters. A guess about a -//! corpus is a hypothesis, and this is the experiment. It answers two questions: +//! This exists because the scope was recommended rather than measured — a guess about +//! which slice of org matters. A guess about a corpus is a hypothesis, and this is the +//! experiment. It answers two questions: //! //! 1. **Coverage** — of the constructs this corpus uses, which do we handle? A construct //! that is common here and out of scope is a scope bug, not a corpus quirk. @@ -24,12 +24,13 @@ use anyhow::{Context, Result}; use camino::{Utf8Path, Utf8PathBuf}; use walkdir::WalkDir; -/// Where a construct sits relative to the v1 scope line (README §"v1 scope"). +/// Where a construct sits relative to the supported set, which +/// `docs/guide/05-org-support.org` defines. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Scope { - /// v1 handles this. + /// orgo handles this. In, - /// v1 deliberately excludes this; it degrades predictably. + /// orgo deliberately excludes this; it degrades predictably. Out, } @@ -90,8 +90,8 @@ enum Command { /// Output directory to remove. output: Utf8PathBuf, }, - /// Audit a corpus: report which org constructs it uses and how they land against - /// the v1 scope line. Reports names, counts and locations — never document text. + /// Audit a corpus: report which org constructs it uses and how each one lands. + /// Reports names, counts and locations — never document text. Audit { /// Source directory (or single `.org` file) to audit. input: Utf8PathBuf, @@ -9,16 +9,19 @@ //! PARSE is a pure function of a single file's bytes (spec §2.1): it never depends on //! another file, which is what makes content-hash caching sound. //! -//! Scope is the v1 IN list (README §"v1 scope"): headings with nesting, TODO keywords, -//! priorities, tags and property drawers; paragraphs; plain lists (unordered, ordered, -//! description) with checkboxes and nesting; tables; source/example/quote/center/export -//! blocks; footnotes; `#+` keywords; inline markup, links, timestamps; images with -//! `#+CAPTION`/`#+ATTR_HTML`. +//! Scope is `docs/guide/05-org-support.org` §Supported: headings with nesting, TODO +//! keywords, priorities, tags and property drawers; paragraphs; plain lists (unordered, +//! ordered, description) with checkboxes and nesting; tables; footnotes; `#+` keywords; +//! source, example, quote, center and export blocks; inline markup, links, timestamps; +//! images with `#+CAPTION`/`#+ATTR_HTML`. //! -//! Out-of-scope constructs are parsed-and-ignored, never fatal: babel `:results` and -//! `#+TBLFM:` are inert keywords, unknown block types keep their content verbatim as -//! example blocks, generic drawers are captured and dropped at render, and LaTeX, -//! macros and radio targets survive as literal text. +//! A block name with no dedicated handling is a special block: a div carrying the name, +//! holding parsed org, which is what org's exporter emits for it. +//! +//! Out-of-scope constructs are parsed-and-ignored, never fatal: babel `:results` is an +//! inert keyword, generic drawers are captured and dropped at render, and LaTeX, macros +//! and radio targets survive as literal text. `#+TBLFM:` is inert for the same reason +//! org's exporter leaves it alone: it does not recalculate on export either. use camino::Utf8Path; use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; @@ -408,7 +411,7 @@ fn parse_elements(lines: &[&str], base: usize, diags: &mut Vec<Diagnostic>) -> V } if let Some((key, value)) = keyword_kv(line) { if key.eq_ignore_ascii_case("INCLUDE") { - // Never expanded (README §OUT). Expanding it means resolving paths, + // Never expanded (§"Not supported"). Expanding it means resolving paths, // recursion and `:lines`/`:only-contents`; dropping it silently means a // page missing content nobody was told about. Saying so is the honest // middle, and `--strict` turns it into a failure. @@ -422,7 +425,7 @@ fn parse_elements(lines: &[&str], base: usize, diags: &mut Vec<Diagnostic>) -> V }); } if key.eq_ignore_ascii_case("RESULTS") { - // Babel is never executed (README §OUT), so a checked-in `#+RESULTS:` + // Babel is never executed (§"Not supported"), so a checked-in `#+RESULTS:` // block is output from someone else's Emacs session at some other time. // Emitting it would put unverifiable content on the page dressed as // real content, so the block it labels is dropped. @@ -617,7 +620,7 @@ fn unescape_block_line(line: &str) -> String { /// `:NAME:` … `:END:` at block level. A PROPERTIES drawer directly under a heading is /// consumed by [`parse_section_body`]; anything reaching here is a generic drawer, -/// which the renderer drops (README §OUT). +/// which the renderer drops (§"Not supported"). fn parse_drawer( lines: &[&str], start: usize, @@ -1353,7 +1356,7 @@ fn try_timestamp(chars: &[char], i: usize) -> Option<(Object, usize)> { /// One bracketed stamp → `(start, same-day end, has_time, index past the bracket)`. /// Day names (`Mon`) and repeater/warning cookies (`+1w`, `-2d`) are recognized and -/// discarded — they carry no export meaning (README §OUT: agenda semantics). +/// discarded — they carry no export meaning (§"Not supported": agenda semantics). fn parse_stamp( chars: &[char], i: usize, @@ -8,12 +8,13 @@ //! and its cost must be cache-skippable (spec §4.2). Emit CSS classes, not inline //! styles, so themes live in the stylesheet (spec §3.2). //! -//! Renders the v1 IN set: headings (always anchored, with TODO keyword, priority and -//! tags), paragraphs, plain lists (unordered/ordered/description, nested, with -//! checkboxes), tables, source blocks (syntect-highlighted), example/quote/center -//! blocks, HTML export blocks, horizontal rules, images and captioned figures, -//! footnotes, timestamps, and inline markup. Out-of-scope elements (generic drawers, -//! comments, stray keywords, non-HTML export blocks) render to nothing. +//! Renders the supported set (`docs/guide/05-org-support.org`): headings (always +//! anchored, with TODO keyword, priority and tags), paragraphs, plain lists +//! (unordered/ordered/description, nested, with checkboxes), tables, source blocks +//! (syntect-highlighted), example/quote/center blocks, HTML export blocks, horizontal +//! rules, images and captioned figures, footnotes, timestamps, and inline markup. +//! Out-of-scope elements (generic drawers, comments, stray keywords, non-HTML export +//! blocks) render to nothing. use std::collections::HashMap; use std::sync::OnceLock; @@ -463,7 +464,7 @@ impl Renderer<'_> { out.push_str("</div>\n"); } // An `html` export block is verbatim output by definition; every other - // backend is out of scope and drops (README §OUT). + // backend is out of scope and drops (§"Not supported"). Element::ExportBlock { backend, raw } => { if backend.eq_ignore_ascii_case("html") { out.push_str(raw); @@ -1,10 +1,10 @@ -//! Golden-file coverage of the v1 scope line (README §"v1 scope"). +//! Golden-file coverage of the scope line `docs/guide/05-org-support.org` draws. //! //! Two halves, and the second is the point: //! -//! - **IN** — every construct the v1 scope claims gets an element-tree snapshot (parser +//! - **IN** — every construct the guide claims gets an element-tree snapshot (parser //! correctness) and a rendered-HTML snapshot (renderer correctness). -//! - **OUT** — every construct the v1 scope explicitly excludes gets an assertion that it +//! - **OUT** — every construct the guide explicitly excludes gets an assertion that it //! *degrades predictably*: parsed and ignored, content preserved where that is the //! honest fallback, never a crash and never a half-rendered artifact. //! @@ -34,7 +34,7 @@ fn render_fixture(name: &str) -> String { } // --------------------------------------------------------------------------- -// IN: the constructs v1 promises to handle +// IN: the constructs the guide promises to handle // --------------------------------------------------------------------------- #[test] @@ -180,7 +180,7 @@ fn unknown_source_language_falls_back_to_plain_code() { } // --------------------------------------------------------------------------- -// OUT: the constructs v1 explicitly excludes must degrade, not explode +// OUT: the constructs the guide explicitly excludes must degrade, not explode // --------------------------------------------------------------------------- /// The whole OUT fixture parses and renders. This is the crash gate. @@ -222,7 +222,7 @@ fn table_formulas_are_inert() { ); } -/// LaTeX, macros and radio targets have no v1 semantics, so they survive as the literal +/// LaTeX, macros and radio targets have no orgo semantics, so they survive as the literal /// text the author typed — lossless, and obviously unhandled to a reader. #[test] fn latex_macros_and_radio_targets_stay_literal() { @@ -2,7 +2,7 @@ source: tests/constructs.rs expression: "render_fixture(\"outofscope.org\")" --- -<p>Every construct here is on the README's explicit OUT list. The contract is not that we handle them — it is that they degrade predictably and never crash the build.</p> +<p>Every construct here is on the explicit "Not supported" list in the org-support guide. The contract is not that we handle them — it is that they degrade predictably and never crash the build.</p> <h2 id="babel">Babel</h2> <pre><code class="language-sh highlight"><span class="source shell bash"><span class="meta function-call shell"><span class="support function echo shell">echo</span></span><span class="meta function-call arguments shell"> <span class="string quoted double shell"><span class="punctuation definition string begin shell">"</span>the block renders; :results is never executed<span class="punctuation definition string end shell">"</span></span></span></span></code></pre> <h2 id="table-formulas">Table formulas</h2>