Commit ce3f2623d5
Unregistered key
Layout: unified · split
docs/guide/09-auditing.org +5 −9
| @@ -26,26 +26,22 @@ IN heading 1148 176 blog/2018-11-28-aes-encr | ||
| 26 | 26 | IN verbatim 1048 130 blog/2018-11-28-aes-encryption.org:79 |
| 27 | 27 | ... |
| 28 | 28 | IN table formula (#+TBLFM:) 4 1 blog/2024-08-11-org-mode-features.org:191 |
| 29 | OUT unmodelled block type 1 1 blog/2026-03-03-auditing-aws-s3.org:50 | |
| 29 | IN special block 1 1 blog/2026-03-03-auditing-aws-s3.org:50 | |
| 30 | 30 | |
| 31 | coverage: 9001 in-scope use(s) (100.0%), 1 out-of-scope (0.0%) | |
| 31 | coverage: 9002 in-scope use(s) (100.0%), 0 out-of-scope (0.0%) | |
| 32 | 32 | |
| 33 | 33 | KEYWORDS |
| 34 | 34 | SLUG 180 180 blog/2018-11-28-aes-encryption.org:4 |
| 35 | 35 | TITLE 180 180 blog/2018-11-28-aes-encryption.org:2 |
| 36 | 36 | ... |
| 37 | ||
| 38 | BLOCK TYPES | |
| 39 | SRC 941 123 blog/2018-11-28-cpp-compiler.org:17 | |
| 40 | QUOTE 115 48 blog/2018-11-28-aes-encryption.org:21 | |
| 41 | ??? NOTE 1 1 blog/2026-03-03-auditing-aws-s3.org:50 | |
| 42 | 37 | #+END_EXAMPLE |
| 43 | 38 | |
| 44 | 39 | - =IN= is supported; =OUT= is excluded by design and degrades as described in |
| 45 | 40 | [[file:05-org-support.org][Org support]]. |
| 46 | 41 | - The *coverage* line is the number to look at first. |
| 47 | - =???= marks a name orgo does not recognise at all — here =#+begin_note=, which one | |
| 48 | post uses once. That is the blind-spot signal: not "known unsupported", but unknown. | |
| 42 | - =???= marks a name orgo does not recognise at all. That is the blind-spot signal — | |
| 43 | not "known unsupported", but unknown — and this corpus has none. Block names never | |
| 44 | carry it: an unrecognised one is still a special block, and still renders. | |
| 49 | 45 | |
| 50 | 46 | Four censuses follow the construct table: every distinct =#+KEYWORD:=, block type, |
| 51 | 47 | drawer name and link scheme in the corpus. A =???= in any of them is worth a look. |
src/audit.rs +9 −17
| @@ -83,12 +83,6 @@ const KNOWN_KEYWORDS: &[&str] = &[ | ||
| 83 | 83 | // Leaving them out reported the corpus's most-used keyword as unrecognized. |
| 84 | 84 | "SLUG", "DRAFT", "TEMPLATE", |
| 85 | 85 | ]; |
| 86 | /// Blocks with dedicated handling. Any *other* name renders as a special block — a div | |
| 87 | /// with that name holding parsed org — so an unlisted block is a note about what a corpus | |
| 88 | /// contains rather than a construct that will be lost. | |
| 89 | const KNOWN_BLOCKS: &[&str] = &[ | |
| 90 | "SRC", "QUOTE", "EXAMPLE", "CENTER", "EXPORT", "VERSE", "COMMENT", | |
| 91 | ]; | |
| 92 | 86 | const KNOWN_DRAWERS: &[&str] = &["PROPERTIES", "LOGBOOK", "END"]; |
| 93 | 87 | /// Keyword names conventional enough to be worth flagging when they lead a heading. |
| 94 | 88 | /// A custom sequence is only *real* if some `#+TODO:` declares it, which the census |
| @@ -107,7 +101,11 @@ impl Audit { | ||
| 107 | 101 | pub fn is_known(kind: Census, name: &str) -> bool { |
| 108 | 102 | let known = match kind { |
| 109 | 103 | Census::Keyword => KNOWN_KEYWORDS, |
| 110 | Census::Block => KNOWN_BLOCKS, | |
| 104 | // Every block name renders, and renders as org renders it: the names in | |
| 105 | // `block_construct` through dedicated handling, every other name as a special | |
| 106 | // block — a div carrying the name, holding parsed org, which is exactly what | |
| 107 | // org's exporter emits. No block name is a blind spot. | |
| 108 | Census::Block => return true, | |
| 111 | 109 | Census::Drawer => KNOWN_DRAWERS, |
| 112 | 110 | Census::Scheme => KNOWN_SCHEMES, |
| 113 | 111 | }; |
| @@ -198,7 +196,7 @@ impl Audit { | ||
| 198 | 196 | if let Some(rest) = trimmed.to_ascii_uppercase().strip_prefix("#+BEGIN_") { |
| 199 | 197 | let kind = rest.split_whitespace().next().unwrap_or("").to_string(); |
| 200 | 198 | self.count_census(Census::Block, &kind, &at); |
| 201 | self.count(scope_of_block(&kind), block_construct(&kind), &at); | |
| 199 | self.count(Scope::In, block_construct(&kind), &at); | |
| 202 | 200 | if trimmed.to_ascii_uppercase().contains(":RESULTS") { |
| 203 | 201 | self.count(Scope::Out, "babel header args (:results)", &at); |
| 204 | 202 | } |
| @@ -440,14 +438,6 @@ fn is_drawer(trimmed: &str) -> bool { | ||
| 440 | 438 | && t.len() > 2 |
| 441 | 439 | } |
| 442 | 440 | |
| 443 | fn scope_of_block(kind: &str) -> Scope { | |
| 444 | if KNOWN_BLOCKS.iter().any(|k| k.eq_ignore_ascii_case(kind)) { | |
| 445 | Scope::In | |
| 446 | } else { | |
| 447 | Scope::Out | |
| 448 | } | |
| 449 | } | |
| 450 | ||
| 451 | 441 | fn block_construct(kind: &str) -> &'static str { |
| 452 | 442 | match kind.to_ascii_uppercase().as_str() { |
| 453 | 443 | "SRC" => "source block", |
| @@ -455,7 +445,9 @@ fn block_construct(kind: &str) -> &'static str { | ||
| 455 | 445 | "EXAMPLE" => "example block", |
| 456 | 446 | "CENTER" => "center block", |
| 457 | 447 | "EXPORT" => "export block", |
| 458 | _ => "unmodelled block type", | |
| 448 | "VERSE" => "verse block", | |
| 449 | "COMMENT" => "comment block", | |
| 450 | _ => "special block", | |
| 459 | 451 | } |
| 460 | 452 | } |
| 461 | 453 | |
tests/constructs.rs +21
| @@ -816,3 +816,24 @@ fn audit_counts_table_formulas_as_in_scope() { | ||
| 816 | 816 | "a #+TBLFM: table orgo renders exactly as Emacs does must not read as a gap:\n{line}" |
| 817 | 817 | ); |
| 818 | 818 | } |
| 819 | ||
| 820 | /// A block name with no dedicated handling is a special block, which org itself renders | |
| 821 | /// as a div carrying the name — `fixtures/blocks.org` holds `#+BEGIN_NOTE` and is in the | |
| 822 | /// Emacs oracle. So it is neither out of scope nor a `???` blind spot. | |
| 823 | #[test] | |
| 824 | fn audit_counts_special_blocks_as_in_scope() { | |
| 825 | let report = audit_fixture("blocks.org"); | |
| 826 | let line = report | |
| 827 | .lines() | |
| 828 | .find(|l| l.contains("special block")) | |
| 829 | .expect("the NOTE block is counted as a special block"); | |
| 830 | assert!( | |
| 831 | line.starts_with("IN "), | |
| 832 | "a block orgo renders exactly as Emacs does must not read as a gap:\n{line}" | |
| 833 | ); | |
| 834 | let flagged: Vec<&str> = report.lines().filter(|l| l.starts_with("???")).collect(); | |
| 835 | assert!( | |
| 836 | flagged.is_empty(), | |
| 837 | "no block name is unrecognised — every one renders: {flagged:?}" | |
| 838 | ); | |
| 839 | } | |