Skip to main content

retex_engine/
display.rs

1//! Displaying boxes, glue, equivalents and the semantic nest (*TeX: The
2//! Program* §173–§198, §218–§219, §252; e-TeX `show_save_groups`).
3
4use crate::Engine;
5use crate::cmd::Cmd;
6use crate::eqtb::{Loc, MU_GLUE_OFFSET, NUM_FAMILIES, Val};
7use crate::glue::Glue;
8use crate::nodes::NodeRef;
9
10impl Engine {
11    /// Prints a glue component with its order (`print_glue`). LuaTeX's
12    /// orders are `fi`, `fil`, `fill`, `filll`.
13    pub fn print_glue(&mut self, d: i32, order: u8, s: Option<&str>) {
14        self.printer.print_scaled(d);
15        if order > 4 {
16            self.printer.print_str("foul");
17        } else if order > 0 {
18            self.printer.print_str("fi");
19            for _ in 1..order {
20                self.printer.print_char('l' as u32);
21            }
22        } else if let Some(s) = s {
23            self.printer.print_str(s);
24        }
25    }
26
27    /// Prints a glue specification (`print_spec`).
28    pub fn print_spec(&mut self, g: &Glue, s: Option<&str>) {
29        self.printer.print_scaled(g.width);
30        if let Some(s) = s {
31            self.printer.print_str(s);
32        }
33        if g.stretch != 0 {
34            self.printer.print_str(" plus ");
35            self.print_glue(g.stretch, g.stretch_order, s);
36        }
37        if g.shrink != 0 {
38            self.printer.print_str(" minus ");
39            self.print_glue(g.shrink, g.shrink_order, s);
40        }
41    }
42
43    /// Displays an equivalent for tracing (`show_eqtb`).
44    pub fn show_eqtb(&mut self, loc: Loc) {
45        match loc {
46            Loc::Cs(cs) => {
47                self.sprint_cs(cs);
48                self.printer.print_char('=' as u32);
49                let m = self.eqtb.meaning(cs).clone();
50                if m.cmd.is_call() && m.mac.as_ref().is_some_and(|mac| mac.protected) {
51                    self.print_esc_str("protected");
52                    if m.cmd == Cmd::Call {
53                        self.printer.print_char(' ' as u32);
54                    }
55                }
56                self.print_cmd_chr(m.cmd, m.chr);
57                if m.cmd >= Cmd::Call
58                    && let Some(mac) = m.mac
59                {
60                    self.printer.print_char(':' as u32);
61                    let t = mac.tokens.clone();
62                    self.show_token_list(&t, 0, t.len(), 32);
63                }
64            }
65            Loc::IntPar(i) => {
66                self.print_cmd_chr(Cmd::AssignInt, i);
67                self.printer.print_char('=' as u32);
68                self.printer.print_int(self.eqtb.int(i).into());
69            }
70            Loc::Count(n) => {
71                self.print_esc_str("count");
72                self.printer.print_int(n.into());
73                self.printer.print_char('=' as u32);
74                self.printer
75                    .print_int(self.eqtb.count[n as usize].val.into());
76            }
77            Loc::Attr(n) => {
78                self.print_esc_str("attribute");
79                self.printer.print_int(n.into());
80                self.printer.print_char('=' as u32);
81                self.printer
82                    .print_int(self.eqtb.attr[n as usize].val.into());
83            }
84            Loc::DimenPar(i) => {
85                self.print_cmd_chr(Cmd::AssignDimen, i);
86                self.printer.print_char('=' as u32);
87                self.printer.print_scaled(self.eqtb.dimen(i));
88                self.printer.print_str("pt");
89            }
90            Loc::Dimen(n) => {
91                self.print_esc_str("dimen");
92                self.printer.print_int(n.into());
93                self.printer.print_char('=' as u32);
94                self.printer.print_scaled(self.eqtb.dimen[n as usize].val);
95                self.printer.print_str("pt");
96            }
97            Loc::GluePar(i) => {
98                let mu = i >= MU_GLUE_OFFSET;
99                if mu {
100                    self.print_cmd_chr(Cmd::AssignMuGlue, i - MU_GLUE_OFFSET);
101                } else {
102                    self.print_cmd_chr(Cmd::AssignGlue, i);
103                }
104                self.printer.print_char('=' as u32);
105                let g = self.eqtb.glue(i);
106                self.print_spec(&g, Some(if mu { "mu" } else { "pt" }));
107            }
108            Loc::Skip(n) | Loc::MuSkip(n) => {
109                let mu = matches!(loc, Loc::MuSkip(_));
110                self.print_esc_str(if mu { "muskip" } else { "skip" });
111                self.printer.print_int(n.into());
112                self.printer.print_char('=' as u32);
113                let g = if mu {
114                    self.eqtb.muskip[n as usize].val
115                } else {
116                    self.eqtb.skip[n as usize].val
117                };
118                self.print_spec(&g, Some(if mu { "mu" } else { "pt" }));
119            }
120            Loc::ToksPar(_) | Loc::Toks(_) => {
121                if let Loc::Toks(n) = loc {
122                    self.print_esc_str("toks");
123                    self.printer.print_int(n.into());
124                } else if let Loc::ToksPar(i) = loc {
125                    self.print_cmd_chr(Cmd::AssignToks, i);
126                }
127                self.printer.print_char('=' as u32);
128                if let Val::Toks(t) = self.eqtb.get(loc) {
129                    self.show_token_list(&t, 0, t.len(), 32);
130                }
131            }
132            Loc::Box(n) => {
133                self.print_esc_str("box");
134                self.printer.print_int(n.into());
135                self.printer.print_char('=' as u32);
136                let b = self.eqtb.boxes[n as usize].val;
137                if b.is_null() {
138                    self.printer.print_str("void");
139                } else {
140                    self.depth_threshold = 0;
141                    self.breadth_max = 1;
142                    self.show_box1(b);
143                }
144            }
145            Loc::CurFont => {
146                self.printer.print_str("current font");
147                self.printer.print_char('=' as u32);
148                let cs = self.font_id_cs(self.eqtb.cur_font.val);
149                let name = self.hash.name(cs).to_vec();
150                self.print_esc_bytes(&name);
151            }
152            Loc::MathFont(i) => {
153                let size = i / NUM_FAMILIES as u32;
154                let fam = i % NUM_FAMILIES as u32;
155                self.print_cmd_chr(Cmd::DefFamily, size);
156                self.printer.print_int(fam.into());
157                self.printer.print_char('=' as u32);
158                let cs = self.font_id_cs(self.eqtb.math_font[i as usize].val);
159                let name = self.hash.name(cs).to_vec();
160                self.print_esc_bytes(&name);
161            }
162            Loc::ParShape => {
163                self.print_esc_str("parshape");
164                self.printer.print_char('=' as u32);
165                let n = self.eqtb.par_shape.val.as_ref().map_or(0, |s| s.len() / 2);
166                self.printer.print_int(n as i64);
167            }
168            Loc::EtexShape(i) => {
169                self.print_cmd_chr(Cmd::SetEtexShape, i);
170                self.printer.print_char('=' as u32);
171                let n = self.eqtb.etex_shape[i as usize]
172                    .val
173                    .as_ref()
174                    .map_or(0, |s| s.len());
175                self.printer.print_int(n as i64);
176            }
177            Loc::Cat { table, ch } => {
178                self.print_esc_str("catcode");
179                self.printer.print_int(ch.into());
180                self.printer.print_char('=' as u32);
181                self.printer
182                    .print_int(self.eqtb.catcode_in(table, ch).into());
183            }
184            Loc::Code(kind, ch) => {
185                use crate::eqtb::CodeKind as K;
186                let name = match kind {
187                    K::Lc => "lccode",
188                    K::Uc => "uccode",
189                    K::Sf => "sfcode",
190                    K::Math => "mathcode",
191                    K::Del => "delcode",
192                };
193                self.print_esc_str(name);
194                self.printer.print_int(ch.into());
195                self.printer.print_char('=' as u32);
196                self.printer.print_int(self.eqtb.code_table(kind).get(ch));
197            }
198            Loc::MathParam(i) => {
199                self.print_cmd_chr(Cmd::SetMathParam, i / 8);
200                self.printer.print_int((i % 8).into());
201                self.printer.print_char('=' as u32);
202                if let Val::Int(v) = self.eqtb.get(loc) {
203                    self.printer.print_scaled(v as i32);
204                    self.printer.print_str("pt");
205                }
206            }
207            Loc::LocalCount(i) => {
208                self.print_esc_str(if i == 0 {
209                    "nolocalwhatsits"
210                } else {
211                    "nolocaldirs"
212                });
213                self.printer.print_char('=' as u32);
214                self.printer
215                    .print_int(self.eqtb.local_count[i as usize].val.into());
216            }
217            Loc::DirPar(i) => {
218                // Observed: `{changing \textdir TLT}`.
219                const NAMES: [&str; 6] = [
220                    "pagedir", "bodydir", "pardir", "textdir", "mathdir", "linedir",
221                ];
222                self.print_esc_str(NAMES.get(i as usize).copied().unwrap_or("textdir"));
223                self.printer.print_char(' ' as u32);
224                let d = self.eqtb.dir_par[i as usize].val;
225                self.printer
226                    .print_str(crate::showbox::DIR_NAMES[d.rem_euclid(4) as usize]);
227            }
228            Loc::LocalBox(i) => {
229                self.print_esc_str(if i == 0 {
230                    "localleftbox"
231                } else {
232                    "localrightbox"
233                });
234                self.printer.print_char('=' as u32);
235                let b = self.eqtb.local_box[i as usize].val;
236                if b.is_null() {
237                    self.printer.print_str("void");
238                } else {
239                    self.depth_threshold = 0;
240                    self.breadth_max = 1;
241                    self.show_box1(b);
242                }
243            }
244        }
245    }
246
247    /// Displays a box (`show_box`), honouring `\showboxdepth` and
248    /// `\showboxbreadth`.
249    pub fn show_box(&mut self, p: NodeRef) {
250        self.show_box_param_limits();
251        self.show_node_list(p);
252        self.printer.print_ln();
253    }
254
255    /// Displays a node list with the current limits (`show_node_list`).
256    pub fn show_box1(&mut self, p: NodeRef) {
257        self.show_node_list(p);
258    }
259
260    /// Shows the active groups (e-TeX `show_save_groups`, simplified).
261    pub fn show_save_groups(&mut self) {
262        self.printer.print_nl("");
263        self.printer.print_ln();
264        let lvl = self.save.cur_level;
265        let mut l = lvl;
266        // Walk the boundaries from the innermost outwards.
267        let mut boundary = self.save.cur_boundary;
268        let mut group = self.save.cur_group;
269        while group != crate::save::GroupCode::BottomLevel {
270            l -= 1;
271            self.printer.print_nl("### ");
272            let saved_group = self.save.cur_group;
273            let saved_level = self.save.cur_level;
274            let saved_boundary = self.save.cur_boundary;
275            self.save.cur_group = group;
276            self.save.cur_level = l;
277            self.save.cur_boundary = boundary;
278            self.print_group(true);
279            self.save.cur_group = saved_group;
280            self.save.cur_level = saved_level;
281            self.save.cur_boundary = saved_boundary;
282            match self.save.stack.get(boundary) {
283                Some(crate::save::SaveEntry::Boundary {
284                    group: g,
285                    boundary: b,
286                    ..
287                }) => {
288                    group = *g;
289                    boundary = *b;
290                }
291                _ => break,
292            }
293        }
294    }
295}