boxworks_knuthplass/
debug.rs

1use std::{cell::RefCell, rc::Rc};
2
3use boxworks::ds;
4
5pub struct FeasibleBreakpoint {
6    pub elem_index: usize,
7    pub badness: i32,
8    pub penalty: i32,
9    pub demerits: i32,
10    pub previous_node_index: usize,
11}
12
13pub struct NewActiveNode {
14    pub node_index: usize,
15    pub line_number: usize,
16    pub fitness_class: u8,
17    pub hyphenated: bool,
18    pub total_demerits: i32,
19    pub previous_node_index: usize,
20}
21
22pub trait Logger {
23    fn log_attempt(&mut self, attempt_number: u8);
24    fn log_feasible_breakpoint(&mut self, list: &[ds::Horizontal], fb: FeasibleBreakpoint);
25    fn log_new_active_node(&mut self, an: NewActiveNode);
26}
27
28pub struct TexLogger {
29    writer: Rc<RefCell<dyn std::fmt::Write>>,
30    next_elem_to_write: usize,
31    pending_fbs: Vec<(FeasibleBreakpoint, String)>,
32}
33
34impl TexLogger {
35    pub fn new(writer: Rc<RefCell<dyn std::fmt::Write>>) -> Self {
36        Self {
37            writer,
38            next_elem_to_write: 0,
39            pending_fbs: Default::default(),
40        }
41    }
42}
43
44impl Logger for TexLogger {
45    fn log_attempt(&mut self, attempt_number: u8) {
46        self.next_elem_to_write = 0;
47        // TeX.2021.863
48        let _ = writeln!(
49            self.writer.borrow_mut(),
50            "@{}",
51            match attempt_number {
52                1 => "firstpass",
53                2 => "secondpass",
54                _ => "emergencypass",
55            }
56        );
57    }
58    fn log_feasible_breakpoint(&mut self, list: &[ds::Horizontal], fb: FeasibleBreakpoint) {
59        if self.next_elem_to_write <= fb.elem_index {
60            let upper = if fb.elem_index >= list.len() {
61                list.len() - 1
62            } else {
63                fb.elem_index
64            };
65            _ = boxworks::ds::short_display_hlist(
66                &mut *self.writer.borrow_mut(),
67                &list[self.next_elem_to_write..=upper],
68            );
69            _ = writeln!(&mut *self.writer.borrow_mut());
70        }
71        self.next_elem_to_write = fb.elem_index + 1;
72        // Rather than writing the breakpoints now, we store them and write them out
73        // when the active node is printed. We do this because the ordering of breakpoints
74        // is different in our implementation versus TeXs. We want the logging to be the
75        // same, we so need to print the breakpoints in the same order TeX does.
76        // TeX.2021.856
77        let line = format!(
78            "@{} via @@{} b={} p={} d={}",
79            match list.get(fb.elem_index) {
80                None => r"\par",
81                Some(elem) => {
82                    use ds::Horizontal::*;
83                    match elem {
84                        Discretionary(discretionary) => {
85                            self.next_elem_to_write += discretionary.replace_count as usize;
86                            r"\discretionary"
87                        }
88                        _ => "",
89                    }
90                }
91            },
92            fb.previous_node_index,
93            fb.badness,
94            fb.penalty,
95            fb.demerits,
96        );
97        self.pending_fbs.push((fb, line));
98    }
99    fn log_new_active_node(&mut self, an: NewActiveNode) {
100        self.pending_fbs.sort_by_key(|a| a.0.previous_node_index);
101        for (_, line) in &self.pending_fbs {
102            _ = writeln!(self.writer.borrow_mut(), "{}", line);
103        }
104        self.pending_fbs.clear();
105        // TeX.2021.846
106        let _ = writeln!(
107            self.writer.borrow_mut(),
108            "@@{}: line {}.{}{} t={} -> @@{}",
109            an.node_index,
110            an.line_number,
111            an.fitness_class,
112            if an.hyphenated { "-" } else { "" },
113            an.total_demerits,
114            an.previous_node_index,
115        );
116    }
117}