summaryrefslogtreecommitdiff
path: root/src/eval/template.rs
blob: 4fc6985a1be3d6ccb7bfd136e63f545272897f26 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
use std::convert::TryFrom;
use std::fmt::{self, Debug, Display, Formatter};
use std::mem;
use std::ops::{Add, AddAssign};
use std::rc::Rc;

use super::{State, Str};
use crate::diag::StrResult;
use crate::geom::{Align, Dir, Gen, GenAxis, Length, Linear, Sides, Size};
use crate::layout::{
    Decoration, LayoutNode, LayoutTree, PadNode, PageRun, ParChild, ParNode, StackChild,
    StackNode,
};
use crate::util::EcoString;

/// A template value: `[*Hi* there]`.
#[derive(Default, Clone)]
pub struct Template(Rc<Vec<TemplateNode>>);

/// One node in a template.
#[derive(Clone)]
enum TemplateNode {
    /// A word space.
    Space(Vec<Decoration>),
    /// A line break.
    Linebreak,
    /// A paragraph break.
    Parbreak,
    /// A page break.
    Pagebreak(bool),
    /// Plain text.
    Text(EcoString, Vec<Decoration>),
    /// Spacing.
    Spacing(GenAxis, Linear),
    /// An inline node builder.
    Inline(Rc<dyn Fn(&State) -> LayoutNode>, Vec<Decoration>),
    /// An block node builder.
    Block(Rc<dyn Fn(&State) -> LayoutNode>),
    /// Save the current state.
    Save,
    /// Restore the last saved state.
    Restore,
    /// A function that can modify the current state.
    Modify(Rc<dyn Fn(&mut State)>),
}

impl Template {
    /// Create a new, empty template.
    pub fn new() -> Self {
        Self(Rc::new(vec![]))
    }

    /// Create a template from a builder for an inline-level node.
    pub fn from_inline<F, T>(f: F) -> Self
    where
        F: Fn(&State) -> T + 'static,
        T: Into<LayoutNode>,
    {
        let node = TemplateNode::Inline(Rc::new(move |s| f(s).into()), vec![]);
        Self(Rc::new(vec![node]))
    }

    /// Create a template from a builder for a block-level node.
    pub fn from_block<F, T>(f: F) -> Self
    where
        F: Fn(&State) -> T + 'static,
        T: Into<LayoutNode>,
    {
        let node = TemplateNode::Block(Rc::new(move |s| f(s).into()));
        Self(Rc::new(vec![node]))
    }

    /// Add a word space to the template.
    pub fn space(&mut self) {
        self.make_mut().push(TemplateNode::Space(vec![]));
    }

    /// Add a line break to the template.
    pub fn linebreak(&mut self) {
        self.make_mut().push(TemplateNode::Linebreak);
    }

    /// Add a paragraph break to the template.
    pub fn parbreak(&mut self) {
        self.make_mut().push(TemplateNode::Parbreak);
    }

    /// Add a page break to the template.
    pub fn pagebreak(&mut self, keep: bool) {
        self.make_mut().push(TemplateNode::Pagebreak(keep));
    }

    /// Add text to the template.
    pub fn text(&mut self, text: impl Into<EcoString>) {
        self.make_mut().push(TemplateNode::Text(text.into(), vec![]));
    }

    /// Add text, but in monospace.
    pub fn monospace(&mut self, text: impl Into<EcoString>) {
        self.save();
        self.modify(|state| state.font_mut().monospace = true);
        self.text(text);
        self.restore();
    }

    /// Add spacing along an axis.
    pub fn spacing(&mut self, axis: GenAxis, spacing: Linear) {
        self.make_mut().push(TemplateNode::Spacing(axis, spacing));
    }

    /// Add a decoration to all contained nodes.
    pub fn decorate(&mut self, deco: Decoration) {
        for node in self.make_mut() {
            let decos = match node {
                TemplateNode::Space(decos) => decos,
                TemplateNode::Text(_, decos) => decos,
                TemplateNode::Inline(_, decos) => decos,
                _ => continue,
            };
            decos.push(deco.clone());
        }
    }

    /// Register a restorable snapshot.
    pub fn save(&mut self) {
        self.make_mut().push(TemplateNode::Save);
    }

    /// Ensure that later nodes are untouched by state modifications made since
    /// the last snapshot.
    pub fn restore(&mut self) {
        self.make_mut().push(TemplateNode::Restore);
    }

    /// Modify the state.
    pub fn modify<F>(&mut self, f: F)
    where
        F: Fn(&mut State) + 'static,
    {
        self.make_mut().push(TemplateNode::Modify(Rc::new(f)));
    }

    /// Build the stack node resulting from instantiating the template in the
    /// given state.
    pub fn to_stack(&self, state: &State) -> StackNode {
        let mut builder = Builder::new(state, false);
        builder.template(self);
        builder.build_stack()
    }

    /// Build the layout tree resulting from instantiating the template in the
    /// given state.
    pub fn to_tree(&self, state: &State) -> LayoutTree {
        let mut builder = Builder::new(state, true);
        builder.template(self);
        builder.build_tree()
    }

    /// Repeat this template `n` times.
    pub fn repeat(&self, n: i64) -> StrResult<Self> {
        let count = usize::try_from(n)
            .ok()
            .and_then(|n| self.0.len().checked_mul(n))
            .ok_or_else(|| format!("cannot repeat this template {} times", n))?;

        Ok(Self(Rc::new(
            self.0.iter().cloned().cycle().take(count).collect(),
        )))
    }

    /// Return a mutable reference to the inner vector.
    fn make_mut(&mut self) -> &mut Vec<TemplateNode> {
        Rc::make_mut(&mut self.0)
    }
}

impl Debug for Template {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.pad("Template { .. }")
    }
}

impl Display for Template {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.pad("<template>")
    }
}

impl PartialEq for Template {
    fn eq(&self, other: &Self) -> bool {
        Rc::ptr_eq(&self.0, &other.0)
    }
}

impl Add for Template {
    type Output = Self;

    fn add(mut self, rhs: Self) -> Self::Output {
        self += rhs;
        self
    }
}

impl AddAssign for Template {
    fn add_assign(&mut self, rhs: Template) {
        let sink = Rc::make_mut(&mut self.0);
        match Rc::try_unwrap(rhs.0) {
            Ok(source) => sink.extend(source),
            Err(rc) => sink.extend(rc.iter().cloned()),
        }
    }
}

impl Add<Str> for Template {
    type Output = Self;

    fn add(mut self, rhs: Str) -> Self::Output {
        Rc::make_mut(&mut self.0).push(TemplateNode::Text(rhs.into(), vec![]));
        self
    }
}

impl Add<Template> for Str {
    type Output = Template;

    fn add(self, mut rhs: Template) -> Self::Output {
        Rc::make_mut(&mut rhs.0).insert(0, TemplateNode::Text(self.into(), vec![]));
        rhs
    }
}

/// Transforms from template to layout representation.
struct Builder {
    /// The active state.
    state: State,
    /// Snapshots of the state.
    snapshots: Vec<State>,
    /// The tree of finished page runs.
    tree: LayoutTree,
    /// When we are building the top-level layout trees, this contains metrics
    /// of the page. While building a stack, this is `None`.
    page: Option<PageBuilder>,
    /// The currently built stack of paragraphs.
    stack: StackBuilder,
}

impl Builder {
    /// Create a new builder with a base state.
    fn new(state: &State, pages: bool) -> Self {
        Self {
            state: state.clone(),
            snapshots: vec![],
            tree: LayoutTree { runs: vec![] },
            page: pages.then(|| PageBuilder::new(state, true)),
            stack: StackBuilder::new(state),
        }
    }

    /// Build a template.
    fn template(&mut self, template: &Template) {
        for node in template.0.iter() {
            self.node(node);
        }
    }

    /// Build a template node.
    fn node(&mut self, node: &TemplateNode) {
        match node {
            TemplateNode::Save => self.snapshots.push(self.state.clone()),
            TemplateNode::Restore => {
                let state = self.snapshots.pop().unwrap();
                let newpage = state.page != self.state.page;
                self.state = state;
                if newpage {
                    self.pagebreak(true, false);
                }
            }
            TemplateNode::Space(decos) => self.space(decos),
            TemplateNode::Linebreak => self.linebreak(),
            TemplateNode::Parbreak => self.parbreak(),
            TemplateNode::Pagebreak(keep) => self.pagebreak(*keep, true),
            TemplateNode::Text(text, decos) => self.text(text, decos),
            TemplateNode::Spacing(axis, amount) => self.spacing(*axis, *amount),
            TemplateNode::Inline(f, decos) => self.inline(f(&self.state), decos),
            TemplateNode::Block(f) => self.block(f(&self.state)),
            TemplateNode::Modify(f) => f(&mut self.state),
        }
    }

    /// Push a word space into the active paragraph.
    fn space(&mut self, decos: &[Decoration]) {
        self.stack.par.push_soft(self.make_text_node(' ', decos.to_vec()));
    }

    /// Apply a forced line break.
    fn linebreak(&mut self) {
        self.stack.par.push_hard(self.make_text_node('\n', vec![]));
    }

    /// Apply a forced paragraph break.
    fn parbreak(&mut self) {
        let amount = self.state.par_spacing();
        self.stack.finish_par(&self.state);
        self.stack.push_soft(StackChild::Spacing(amount.into()));
    }

    /// Apply a forced page break.
    fn pagebreak(&mut self, keep: bool, hard: bool) {
        if let Some(builder) = &mut self.page {
            let page = mem::replace(builder, PageBuilder::new(&self.state, hard));
            let stack = mem::replace(&mut self.stack, StackBuilder::new(&self.state));
            self.tree.runs.extend(page.build(stack.build(), keep));
        }
    }

    /// Push text into the active paragraph.
    fn text(&mut self, text: impl Into<EcoString>, decos: &[Decoration]) {
        self.stack.par.push(self.make_text_node(text, decos.to_vec()));
    }

    /// Push an inline node into the active paragraph.
    fn inline(&mut self, node: impl Into<LayoutNode>, decos: &[Decoration]) {
        let align = self.state.aligns.inline;
        self.stack.par.push(ParChild::Any(node.into(), align, decos.to_vec()));
    }

    /// Push a block node into the active stack, finishing the active paragraph.
    fn block(&mut self, node: impl Into<LayoutNode>) {
        self.parbreak();
        let aligns = self.state.aligns;
        self.stack.push(StackChild::Any(node.into(), aligns));
        self.parbreak();
    }

    /// Push spacing into the active paragraph or stack depending on the `axis`.
    fn spacing(&mut self, axis: GenAxis, amount: Linear) {
        match axis {
            GenAxis::Block => {
                self.stack.finish_par(&self.state);
                self.stack.push_hard(StackChild::Spacing(amount));
            }
            GenAxis::Inline => {
                self.stack.par.push_hard(ParChild::Spacing(amount));
            }
        }
    }

    /// Finish building and return the created stack.
    fn build_stack(self) -> StackNode {
        assert!(self.page.is_none());
        self.stack.build()
    }

    /// Finish building and return the created layout tree.
    fn build_tree(mut self) -> LayoutTree {
        assert!(self.page.is_some());
        self.pagebreak(true, false);
        self.tree
    }

    /// Construct a text node with the given text and settings from the active
    /// state.
    fn make_text_node(
        &self,
        text: impl Into<EcoString>,
        decos: Vec<Decoration>,
    ) -> ParChild {
        ParChild::Text(
            text.into(),
            self.state.aligns.inline,
            Rc::clone(&self.state.font),
            decos,
        )
    }
}

struct PageBuilder {
    size: Size,
    padding: Sides<Linear>,
    hard: bool,
}

impl PageBuilder {
    fn new(state: &State, hard: bool) -> Self {
        Self {
            size: state.page.size,
            padding: state.page.margins(),
            hard,
        }
    }

    fn build(self, child: StackNode, keep: bool) -> Option<PageRun> {
        let Self { size, padding, hard } = self;
        (!child.children.is_empty() || (keep && hard)).then(|| PageRun {
            size,
            child: PadNode { padding, child: child.into() }.into(),
        })
    }
}

struct StackBuilder {
    dirs: Gen<Dir>,
    children: Vec<StackChild>,
    last: Last<StackChild>,
    par: ParBuilder,
}

impl StackBuilder {
    fn new(state: &State) -> Self {
        Self {
            dirs: state.dirs,
            children: vec![],
            last: Last::None,
            par: ParBuilder::new(state),
        }
    }

    fn push(&mut self, child: StackChild) {
        self.children.extend(self.last.any());
        self.children.push(child);
    }

    fn push_soft(&mut self, child: StackChild) {
        self.last.soft(child);
    }

    fn push_hard(&mut self, child: StackChild) {
        self.last.hard();
        self.children.push(child);
    }

    fn finish_par(&mut self, state: &State) {
        let par = mem::replace(&mut self.par, ParBuilder::new(state));
        if let Some(par) = par.build() {
            self.push(par);
        }
    }

    fn build(self) -> StackNode {
        let Self { dirs, mut children, par, mut last } = self;
        if let Some(par) = par.build() {
            children.extend(last.any());
            children.push(par);
        }
        StackNode { dirs, children }
    }
}

struct ParBuilder {
    aligns: Gen<Align>,
    dir: Dir,
    line_spacing: Length,
    children: Vec<ParChild>,
    last: Last<ParChild>,
}

impl ParBuilder {
    fn new(state: &State) -> Self {
        Self {
            aligns: state.aligns,
            dir: state.dirs.inline,
            line_spacing: state.line_spacing(),
            children: vec![],
            last: Last::None,
        }
    }

    fn push(&mut self, child: ParChild) {
        if let Some(soft) = self.last.any() {
            self.push_inner(soft);
        }
        self.push_inner(child);
    }

    fn push_soft(&mut self, child: ParChild) {
        self.last.soft(child);
    }

    fn push_hard(&mut self, child: ParChild) {
        self.last.hard();
        self.push_inner(child);
    }

    fn push_inner(&mut self, child: ParChild) {
        if let ParChild::Text(curr_text, curr_align, curr_props, curr_decos) = &child {
            if let Some(ParChild::Text(prev_text, prev_align, prev_props, prev_decos)) =
                self.children.last_mut()
            {
                if prev_align == curr_align
                    && Rc::ptr_eq(prev_props, curr_props)
                    && curr_decos == prev_decos
                {
                    prev_text.push_str(&curr_text);
                    return;
                }
            }
        }

        self.children.push(child);
    }

    fn build(self) -> Option<StackChild> {
        let Self { aligns, dir, line_spacing, children, .. } = self;
        (!children.is_empty()).then(|| {
            let node = ParNode { dir, line_spacing, children };
            StackChild::Any(node.into(), aligns)
        })
    }
}

/// Finite state machine for spacing coalescing.
enum Last<N> {
    None,
    Any,
    Soft(N),
}

impl<N> Last<N> {
    fn any(&mut self) -> Option<N> {
        match mem::replace(self, Self::Any) {
            Self::Soft(soft) => Some(soft),
            _ => None,
        }
    }

    fn soft(&mut self, soft: N) {
        if let Self::Any = self {
            *self = Self::Soft(soft);
        }
    }

    fn hard(&mut self) {
        *self = Self::None;
    }
}