Skip to main content

sim_codec_doc/markup/
model.rs

1use std::collections::BTreeMap;
2use std::fmt;
3
4use sim_kernel::Expr;
5
6/// A semantic markup document independent of its concrete source backend.
7#[derive(Clone, Debug, PartialEq)]
8pub struct MarkupDoc {
9    /// Optional document title.
10    pub title: Option<String>,
11    /// Semantic block sequence.
12    pub blocks: Vec<MarkupBlock>,
13    /// Open document attributes carried as ordinary SIM expressions.
14    pub attrs: BTreeMap<String, Expr>,
15    /// Original source text, when a backend can preserve it.
16    pub source: Option<SourceDoc>,
17}
18
19/// A concrete source document preserved alongside the semantic IR.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct SourceDoc {
22    /// Backend that produced the source text.
23    pub backend: BackendId,
24    /// Verbatim source text.
25    pub text: String,
26}
27
28/// A backend identifier such as `markdown`, `typst`, `asciidoc`, or `latex`.
29#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
30pub struct BackendId(
31    /// Backend id text.
32    pub String,
33);
34
35impl BackendId {
36    /// Create a backend id from stable id text.
37    pub fn new(id: impl Into<String>) -> Self {
38        Self(id.into())
39    }
40
41    /// Borrow the backend id text.
42    pub fn as_str(&self) -> &str {
43        &self.0
44    }
45}
46
47impl fmt::Display for BackendId {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        f.write_str(&self.0)
50    }
51}
52
53/// A source span in byte offsets.
54#[derive(Clone, Debug, PartialEq, Eq)]
55pub struct Span {
56    /// Inclusive start byte offset.
57    pub start: usize,
58    /// Exclusive end byte offset.
59    pub end: usize,
60    /// Whether the semantic node still matches the original source span.
61    pub state: SpanState,
62}
63
64/// Source-span freshness after reversible document edits.
65#[derive(Clone, Debug, PartialEq, Eq)]
66pub enum SpanState {
67    /// The span still points at untouched source bytes.
68    Preserved,
69    /// The span's source bytes must be regenerated.
70    Dirty,
71}
72
73/// Markup math source with a notation label.
74#[derive(Clone, Debug, PartialEq, Eq)]
75pub struct MathSource {
76    /// Math notation, such as `tex`, `typst`, `asciimath`, or `unknown`.
77    pub notation: String,
78    /// Source text for the math expression.
79    pub text: String,
80}
81
82/// A semantic block in a markup document.
83#[derive(Clone, Debug, PartialEq)]
84pub enum MarkupBlock {
85    /// A heading with inline text.
86    Heading {
87        /// Heading level, normally 1 through 6.
88        level: u8,
89        /// Inline heading content.
90        text: Vec<Inline>,
91        /// Optional stable heading id.
92        id: Option<String>,
93        /// Optional source span.
94        span: Option<Span>,
95    },
96    /// A paragraph of inline content.
97    Paragraph {
98        /// Inline paragraph content.
99        content: Vec<Inline>,
100        /// Optional source span.
101        span: Option<Span>,
102    },
103    /// A fenced or literal code block.
104    CodeBlock {
105        /// Optional language tag.
106        lang: Option<String>,
107        /// Code text.
108        code: String,
109        /// Optional source span.
110        span: Option<Span>,
111    },
112    /// A display math block.
113    MathBlock {
114        /// Math source.
115        source: MathSource,
116        /// Optional source span.
117        span: Option<Span>,
118    },
119    /// A block quote.
120    Quote {
121        /// Quoted blocks.
122        blocks: Vec<MarkupBlock>,
123        /// Optional source span.
124        span: Option<Span>,
125    },
126    /// An ordered or unordered list.
127    List {
128        /// Whether the list is ordered.
129        ordered: bool,
130        /// List items, each item containing block content.
131        items: Vec<Vec<MarkupBlock>>,
132        /// Optional source span.
133        span: Option<Span>,
134    },
135    /// A simple table.
136    Table {
137        /// Header cells.
138        header: Vec<Vec<Inline>>,
139        /// Row cells.
140        rows: Vec<Vec<Vec<Inline>>>,
141        /// Optional source span.
142        span: Option<Span>,
143    },
144    /// A figure with source and caption.
145    Figure {
146        /// Image/media source.
147        src: String,
148        /// Figure caption.
149        caption: Vec<Inline>,
150        /// Optional source span.
151        span: Option<Span>,
152    },
153    /// Backend-specific raw text.
154    Raw {
155        /// Backend that owns the raw text.
156        backend: BackendId,
157        /// Raw source text.
158        text: String,
159        /// Optional source span.
160        span: Option<Span>,
161    },
162}
163
164/// Inline content in a markup document.
165#[derive(Clone, Debug, PartialEq, Eq)]
166pub enum Inline {
167    /// Plain text.
168    Text(String),
169    /// Emphasized inline content.
170    Emph(Vec<Inline>),
171    /// Strong inline content.
172    Strong(Vec<Inline>),
173    /// Inline code.
174    Code(String),
175    /// Link with label and target.
176    Link {
177        /// Link label.
178        label: Vec<Inline>,
179        /// Link target.
180        target: String,
181    },
182    /// Inline math.
183    Math(MathSource),
184    /// Backend-specific raw inline text.
185    Raw {
186        /// Backend that owns the raw text.
187        backend: BackendId,
188        /// Raw source text.
189        text: String,
190    },
191}