Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_text/src/doc/djot/mod.rs

3.7 KiB, 1 run

created by r1870400018:14702, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Djot -- a reader for the post-Markdown markup language, producing the neutral
2//! [document tree](crate::doc).
3//!
4//! Djot is what a prose author reaches for when Markdown cannot name the thing they mean: a box around
5//! an aside, a class on a span, an id on a heading. Its blocks and inlines carry [attributes](crate::doc::Attrs),
6//! and a `:::` fence draws a [division](crate::doc::Block::Div) that Markdown has no way to write. What
7//! this reader produces belongs to [`crate::doc`] and knows nothing of Djot, which is what lets a site
8//! author the same page in either Djot or Markdown and reach the one tree.
9//!
10//! # Where Djot parts from Markdown
11//!
12//! The two front-ends agree on most of the tree, and part on a few points a reader of both should hold
13//! in mind:
14//!
15//! - The emphasis markers are swapped and single. A single `_` is ordinary emphasis and a single `*`
16//! is strong, where Markdown reads a single marker as ordinary and doubles it for strong. So `_it_`
17//! is italic here and `*it*` is bold, and neither is doubled.
18//! - Emphasis may fall inside a word, since Djot judges a marker by the whitespace against it and keeps
19//! no intraword exception.
20//! - There is no two-space hard break and no indented code block. A break the author asked for is a
21//! backslash at the end of the line, and code is fenced.
22//! - A `:::` fence draws a division, and a `{...}` group names attributes, on a division, on a span, or
23//! on its own line to attach to the block below. These are the constructs Djot exists for.
24//!
25//! # A soft line break says a space
26//!
27//! A single newline within a paragraph is a soft break, and it contributes a space rather than a
28//! newline. Where an author's editor wrapped a line is not where the author asked for a break, and
29//! preserving it would freeze prose at the width it was typed at instead of reflowing to the width it
30//! is read at. [`Inline::Break`](crate::doc::Inline::Break) is only ever a break the author did ask
31//! for -- here, a backslash at the end of a line.
32//!
33//! # The dialect
34//!
35//! The subset read is what prose actually uses: headings, paragraphs, block quotations, fenced code,
36//! ordered and unordered lists (nested), thematic breaks, pipe tables, divisions, standalone
37//! attributes lines, and the inline run of ordinary and strong emphasis, verbatim spans, links
38//! (inline and by reference), images, attributed spans and hard breaks.
39//!
40//! # Not yet read
41//!
42//! The following Djot constructs are not yet read, and their syntax survives as the literal text it is
43//! written in rather than being interpreted: footnotes, inline and display maths (`$` and `$$`),
44//! definition lists, superscript and subscript (`^` and `~`), inline symbols (`:name:`), smart
45//! punctuation, raw inline and raw blocks, comments (`{% %}`), line blocks, and task lists. A document
46//! that uses them is read without error; the marks simply stand as characters.
47//!
48//! # Usage
49//!
50//! ```ignore
51//! use oxedyne_fe2o3_text::doc::djot;
52//!
53//! let tree = res!(djot::parse("# A heading\n\nA paragraph with *strength*.\n"));
54//! ```
55
56pub mod block;
57pub mod inline;
58
59use crate::doc::Doc;
60
61use oxedyne_fe2o3_core::prelude::*;
62
63/// Reads Djot text and produces its document tree.
64///
65/// Parsing does not fail on badly formed markup: Djot has no syntax errors, only text that means less
66/// than the author hoped. An unclosed fence runs to the end, an unmatched bracket is literal text, and
67/// a stray asterisk is an asterisk. The outcome is an error only when the input breaks a limit the
68/// reader holds against a hostile document, such as nesting past [`block::DEPTH_LIMIT`].
69pub fn parse(src: &str) -> Outcome<Doc> {
70 let blocks = res!(block::parse(src));
71 Ok(Doc { blocks })
72}