krz/orgo

Lightning fast org-mode static site generator. fast go org-mode static-site-generator

Commit f274b51656

f274b516569365d352ed75206f6678f862cfd7f7

parent: ce3f2623d5

Unregistered key

cmc <hello@cleberg.net> · 2026-08-23 05:05 UTC
committer: <noreply@github.com>

Point scope references at the org-support guide (#29)

Ten comments cited `README §"v1 scope"` or `README §OUT`. README.md has no
such section — that content now lives in `docs/guide/05-org-support.org`, which
README links to from the docs table. The citations now name that file, and the
"v1" phrasing goes with them, since nothing defines a v1 scope line any more.

Two claims in `parser.rs`'s header were stale as of #27 and #28: unknown block
types do not "keep their content verbatim as example blocks" — they render as
special blocks holding parsed org — and `#+TBLFM:` is no longer described as
out of scope.

Adding the section back to README instead would put the supported/unsupported
list in two places, which is how these drifted apart to begin with.

Layout: unified · split

fixtures/outofscope.org +3 −2
@@ -1,8 +1,9 @@
11#+TITLE: Out of Scope
22#+INCLUDE: "other.org"
33
4Every construct here is on the README's explicit OUT list. The contract is not that we
5handle them — it is that they degrade predictably and never crash the build.
4Every construct here is on the explicit "Not supported" list in the org-support guide.
5The contract is not that we handle them — it is that they degrade predictably and never
6crash the build.
67
78* Babel
89
src/audit.rs +8 −7
@@ -1,9 +1,9 @@
11//! Corpus audit (spec §5, Phase 0): measure which org constructs a real corpus actually
2//! uses, and classify each against the v1 IN/OUT line.
2//! uses, and classify each against the supported/unsupported line.
33//!
4//! This exists because the v1 scope was, on the README's own admission, *recommended*
5//! rather than measured — a guess about which slice of org matters. A guess about a
6//! corpus is a hypothesis, and this is the experiment. It answers two questions:
4//! This exists because the scope was recommended rather than measured — a guess about
5//! which slice of org matters. A guess about a corpus is a hypothesis, and this is the
6//! experiment. It answers two questions:
77//!
88//! 1. **Coverage** — of the constructs this corpus uses, which do we handle? A construct
99//! that is common here and out of scope is a scope bug, not a corpus quirk.
@@ -24,12 +24,13 @@ use anyhow::{Context, Result};
2424use camino::{Utf8Path, Utf8PathBuf};
2525use walkdir::WalkDir;
2626
27/// Where a construct sits relative to the v1 scope line (README §"v1 scope").
27/// Where a construct sits relative to the supported set, which
28/// `docs/guide/05-org-support.org` defines.
2829#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
2930pub enum Scope {
30 /// v1 handles this.
31 /// orgo handles this.
3132 In,
32 /// v1 deliberately excludes this; it degrades predictably.
33 /// orgo deliberately excludes this; it degrades predictably.
3334 Out,
3435}
3536
src/main.rs +2 −2
@@ -90,8 +90,8 @@ enum Command {
9090 /// Output directory to remove.
9191 output: Utf8PathBuf,
9292 },
93 /// Audit a corpus: report which org constructs it uses and how they land against
94 /// the v1 scope line. Reports names, counts and locations — never document text.
93 /// Audit a corpus: report which org constructs it uses and how each one lands.
94 /// Reports names, counts and locations — never document text.
9595 Audit {
9696 /// Source directory (or single `.org` file) to audit.
9797 input: Utf8PathBuf,
src/parser.rs +16 −13
@@ -9,16 +9,19 @@
99//! PARSE is a pure function of a single file's bytes (spec §2.1): it never depends on
1010//! another file, which is what makes content-hash caching sound.
1111//!
12//! Scope is the v1 IN list (README §"v1 scope"): headings with nesting, TODO keywords,
13//! priorities, tags and property drawers; paragraphs; plain lists (unordered, ordered,
14//! description) with checkboxes and nesting; tables; source/example/quote/center/export
15//! blocks; footnotes; `#+` keywords; inline markup, links, timestamps; images with
16//! `#+CAPTION`/`#+ATTR_HTML`.
12//! Scope is `docs/guide/05-org-support.org` §Supported: headings with nesting, TODO
13//! keywords, priorities, tags and property drawers; paragraphs; plain lists (unordered,
14//! ordered, description) with checkboxes and nesting; tables; footnotes; `#+` keywords;
15//! source, example, quote, center and export blocks; inline markup, links, timestamps;
16//! images with `#+CAPTION`/`#+ATTR_HTML`.
1717//!
18//! Out-of-scope constructs are parsed-and-ignored, never fatal: babel `:results` and
19//! `#+TBLFM:` are inert keywords, unknown block types keep their content verbatim as
20//! example blocks, generic drawers are captured and dropped at render, and LaTeX,
21//! macros and radio targets survive as literal text.
18//! A block name with no dedicated handling is a special block: a div carrying the name,
19//! holding parsed org, which is what org's exporter emits for it.
20//!
21//! Out-of-scope constructs are parsed-and-ignored, never fatal: babel `:results` is an
22//! inert keyword, generic drawers are captured and dropped at render, and LaTeX, macros
23//! and radio targets survive as literal text. `#+TBLFM:` is inert for the same reason
24//! org's exporter leaves it alone: it does not recalculate on export either.
2225
2326use camino::Utf8Path;
2427use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
@@ -408,7 +411,7 @@ fn parse_elements(lines: &[&str], base: usize, diags: &mut Vec<Diagnostic>) -> V
408411 }
409412 if let Some((key, value)) = keyword_kv(line) {
410413 if key.eq_ignore_ascii_case("INCLUDE") {
411 // Never expanded (README §OUT). Expanding it means resolving paths,
414 // Never expanded (§"Not supported"). Expanding it means resolving paths,
412415 // recursion and `:lines`/`:only-contents`; dropping it silently means a
413416 // page missing content nobody was told about. Saying so is the honest
414417 // middle, and `--strict` turns it into a failure.
@@ -422,7 +425,7 @@ fn parse_elements(lines: &[&str], base: usize, diags: &mut Vec<Diagnostic>) -> V
422425 });
423426 }
424427 if key.eq_ignore_ascii_case("RESULTS") {
425 // Babel is never executed (README §OUT), so a checked-in `#+RESULTS:`
428 // Babel is never executed (§"Not supported"), so a checked-in `#+RESULTS:`
426429 // block is output from someone else's Emacs session at some other time.
427430 // Emitting it would put unverifiable content on the page dressed as
428431 // real content, so the block it labels is dropped.
@@ -617,7 +620,7 @@ fn unescape_block_line(line: &str) -> String {
617620
618621/// `:NAME:` … `:END:` at block level. A PROPERTIES drawer directly under a heading is
619622/// consumed by [`parse_section_body`]; anything reaching here is a generic drawer,
620/// which the renderer drops (README §OUT).
623/// which the renderer drops (§"Not supported").
621624fn parse_drawer(
622625 lines: &[&str],
623626 start: usize,
@@ -1353,7 +1356,7 @@ fn try_timestamp(chars: &[char], i: usize) -> Option<(Object, usize)> {
13531356
13541357/// One bracketed stamp → `(start, same-day end, has_time, index past the bracket)`.
13551358/// Day names (`Mon`) and repeater/warning cookies (`+1w`, `-2d`) are recognized and
1356/// discarded — they carry no export meaning (README §OUT: agenda semantics).
1359/// discarded — they carry no export meaning (§"Not supported": agenda semantics).
13571360fn parse_stamp(
13581361 chars: &[char],
13591362 i: usize,
src/render.rs +8 −7
@@ -8,12 +8,13 @@
88//! and its cost must be cache-skippable (spec §4.2). Emit CSS classes, not inline
99//! styles, so themes live in the stylesheet (spec §3.2).
1010//!
11//! Renders the v1 IN set: headings (always anchored, with TODO keyword, priority and
12//! tags), paragraphs, plain lists (unordered/ordered/description, nested, with
13//! checkboxes), tables, source blocks (syntect-highlighted), example/quote/center
14//! blocks, HTML export blocks, horizontal rules, images and captioned figures,
15//! footnotes, timestamps, and inline markup. Out-of-scope elements (generic drawers,
16//! comments, stray keywords, non-HTML export blocks) render to nothing.
11//! Renders the supported set (`docs/guide/05-org-support.org`): headings (always
12//! anchored, with TODO keyword, priority and tags), paragraphs, plain lists
13//! (unordered/ordered/description, nested, with checkboxes), tables, source blocks
14//! (syntect-highlighted), example/quote/center blocks, HTML export blocks, horizontal
15//! rules, images and captioned figures, footnotes, timestamps, and inline markup.
16//! Out-of-scope elements (generic drawers, comments, stray keywords, non-HTML export
17//! blocks) render to nothing.
1718
1819use std::collections::HashMap;
1920use std::sync::OnceLock;
@@ -463,7 +464,7 @@ impl Renderer<'_> {
463464 out.push_str("</div>\n");
464465 }
465466 // An `html` export block is verbatim output by definition; every other
466 // backend is out of scope and drops (README §OUT).
467 // backend is out of scope and drops (§"Not supported").
467468 Element::ExportBlock { backend, raw } => {
468469 if backend.eq_ignore_ascii_case("html") {
469470 out.push_str(raw);
tests/constructs.rs +6 −6
@@ -1,10 +1,10 @@
1//! Golden-file coverage of the v1 scope line (README §"v1 scope").
1//! Golden-file coverage of the scope line `docs/guide/05-org-support.org` draws.
22//!
33//! Two halves, and the second is the point:
44//!
5//! - **IN** — every construct the v1 scope claims gets an element-tree snapshot (parser
5//! - **IN** — every construct the guide claims gets an element-tree snapshot (parser
66//! correctness) and a rendered-HTML snapshot (renderer correctness).
7//! - **OUT** — every construct the v1 scope explicitly excludes gets an assertion that it
7//! - **OUT** — every construct the guide explicitly excludes gets an assertion that it
88//! *degrades predictably*: parsed and ignored, content preserved where that is the
99//! honest fallback, never a crash and never a half-rendered artifact.
1010//!
@@ -34,7 +34,7 @@ fn render_fixture(name: &str) -> String {
3434}
3535
3636// ---------------------------------------------------------------------------
37// IN: the constructs v1 promises to handle
37// IN: the constructs the guide promises to handle
3838// ---------------------------------------------------------------------------
3939
4040#[test]
@@ -180,7 +180,7 @@ fn unknown_source_language_falls_back_to_plain_code() {
180180}
181181
182182// ---------------------------------------------------------------------------
183// OUT: the constructs v1 explicitly excludes must degrade, not explode
183// OUT: the constructs the guide explicitly excludes must degrade, not explode
184184// ---------------------------------------------------------------------------
185185
186186/// The whole OUT fixture parses and renders. This is the crash gate.
@@ -222,7 +222,7 @@ fn table_formulas_are_inert() {
222222 );
223223}
224224
225/// LaTeX, macros and radio targets have no v1 semantics, so they survive as the literal
225/// LaTeX, macros and radio targets have no orgo semantics, so they survive as the literal
226226/// text the author typed — lossless, and obviously unhandled to a reader.
227227#[test]
228228fn latex_macros_and_radio_targets_stay_literal() {
tests/snapshots/constructs__out_of_scope_html.snap +1 −1
@@ -2,7 +2,7 @@
22source: tests/constructs.rs
33expression: "render_fixture(\"outofscope.org\")"
44---
5<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>
5<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>
66<h2 id="babel">Babel</h2>
77<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">&quot;</span>the block renders; :results is never executed<span class="punctuation definition string end shell">&quot;</span></span></span></span></code></pre>
88<h2 id="table-formulas">Table formulas</h2>