krz/orgo
Lightning fast org-mode static site generator.
clone: git clone https://gitbay.org/krz/orgo.git
82639e554bcf4a7465c41e6c47effceb501d91d3
signed_unknown_key
author: Christian Cleberg <hello@cleberg.net> · 2026-08-23T03:43:51Z
committer: <noreply@github.com>
fixtures/audit-entities.org | 3 +++ fixtures/audit-nonentities.org | 7 +++++++ src/audit.rs | 44 ++++++++++++++++++++++++++++++++++++------ tests/constructs.rs | 31 +++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 6 deletions(-) new file mode 100644 @@ -0,0 +1,3 @@ +#+TITLE: Real entities + +The angle \alpha is small, and the arrow \rarr points right. new file mode 100644 @@ -0,0 +1,7 @@ +#+TITLE: Backslashes that are not entities + +Windows paths and namespaced identifiers are not org entities: +=C:\Users\youruser\Downloads= and Tumblr\API\Client. + +Names shown inside verbatim or code are displayed, not rendered, so they are +not a use of the feature either: =\alpha=, ~\rarr~, =20\deg=. @@ -506,21 +506,53 @@ fn latex_inline(line: &str) -> bool { dollars >= 2 && line.contains("$\\") } -/// A `\name` entity reference such as `\alpha`, excluding LaTeX environment commands. +/// A `\name` entity reference such as `\alpha`. +/// +/// Only names org knows count, matching [`crate::parser`]'s rule for rendering one: a +/// Windows path (`C:\Users\me`) and a namespaced identifier (`Tumblr\API\Client`) are not +/// entity references. +/// +/// Verbatim and code spans are skipped: `=\alpha=` shows the name rather than rendering +/// the character, so it is not a use of the feature. fn entity_ref(line: &str) -> bool { - for (i, c) in line.char_indices() { - if c != '\\' { + let line = without_literal_spans(line); + let chars: Vec<char> = line.chars().collect(); + for (i, c) in chars.iter().enumerate() { + if *c != '\\' { continue; } - let rest = &line[i + 1..]; - let name: String = rest.chars().take_while(|c| c.is_ascii_alphabetic()).collect(); - if name.len() >= 3 && !matches!(name.as_str(), "begin" | "end") { + let name: String = chars[i + 1..].iter().take_while(|c| c.is_ascii_alphabetic()).collect(); + if crate::entities::lookup(&name).is_some() { return true; } } false } +/// Blank out `=verbatim=` and `~code~` spans. Deliberately looser than the parser's +/// border rules — the audit measures prevalence, and erring toward blanking keeps it +/// from overstating a gap. +fn without_literal_spans(line: &str) -> String { + let mut out = String::with_capacity(line.len()); + let mut open: Option<char> = None; + for c in line.chars() { + match open { + Some(marker) => { + out.push(' '); + if c == marker { + open = None; + } + } + None if c == '=' || c == '~' => { + open = Some(c); + out.push(' '); + } + None => out.push(c), + } + } + out +} + /// A plausible `*bold*`-style emphasis pair: two markers on one line with non-space /// content between them. Approximate by design — the audit measures prevalence, and the /// parser owns the exact pre/post-character rules. @@ -769,3 +769,34 @@ fn footnote_links_and_section_are_labelled() { "each back-link says where it goes:\n{html}" ); } + +// --------------------------------------------------------------------------- +// Audit: the entity check counts only what org would actually render +// --------------------------------------------------------------------------- + +fn audit_fixture(name: &str) -> String { + let path = Utf8PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("fixtures").join(name); + let audit = orgo::audit::audit(&path).expect("audit fixture"); + orgo::audit::report(&audit) +} + +/// `\Users` and `\API` are not names org knows, and an entity inside verbatim is shown +/// rather than rendered. Counting either overstates what the corpus needs. +#[test] +fn audit_ignores_backslashes_that_are_not_entities() { + let report = audit_fixture("audit-nonentities.org"); + assert!( + !report.contains("entity (\\name)"), + "a Windows path or a verbatim-quoted name was counted as an entity:\n{report}" + ); +} + +/// A bare entity outside verbatim still counts. +#[test] +fn audit_counts_real_entities() { + let report = audit_fixture("audit-entities.org"); + assert!( + report.contains("entity (\\name)"), + "a real entity was not counted:\n{report}" + ); +}