retex-engine 0.9.0

The TeX typesetting engine for retex
Documentation
//! Displaying boxes, glue, equivalents and the semantic nest (*TeX: The
//! Program* §173–§198, §218–§219, §252; e-TeX `show_save_groups`).

use crate::Engine;
use crate::cmd::Cmd;
use crate::eqtb::{Loc, MU_GLUE_OFFSET, NUM_FAMILIES, Val};
use crate::glue::Glue;
use crate::nodes::NodeRef;

impl Engine {
    /// Prints a glue component with its order (`print_glue`). LuaTeX's
    /// orders are `fi`, `fil`, `fill`, `filll`.
    pub fn print_glue(&mut self, d: i32, order: u8, s: Option<&str>) {
        self.printer.print_scaled(d);
        if order > 4 {
            self.printer.print_str("foul");
        } else if order > 0 {
            self.printer.print_str("fi");
            for _ in 1..order {
                self.printer.print_char('l' as u32);
            }
        } else if let Some(s) = s {
            self.printer.print_str(s);
        }
    }

    /// Prints a glue specification (`print_spec`).
    pub fn print_spec(&mut self, g: &Glue, s: Option<&str>) {
        self.printer.print_scaled(g.width);
        if let Some(s) = s {
            self.printer.print_str(s);
        }
        if g.stretch != 0 {
            self.printer.print_str(" plus ");
            self.print_glue(g.stretch, g.stretch_order, s);
        }
        if g.shrink != 0 {
            self.printer.print_str(" minus ");
            self.print_glue(g.shrink, g.shrink_order, s);
        }
    }

    /// Displays an equivalent for tracing (`show_eqtb`).
    pub fn show_eqtb(&mut self, loc: Loc) {
        match loc {
            Loc::Cs(cs) => {
                self.sprint_cs(cs);
                self.printer.print_char('=' as u32);
                let m = self.eqtb.meaning(cs).clone();
                if m.cmd.is_call() && m.mac.as_ref().is_some_and(|mac| mac.protected) {
                    self.print_esc_str("protected");
                    if m.cmd == Cmd::Call {
                        self.printer.print_char(' ' as u32);
                    }
                }
                self.print_cmd_chr(m.cmd, m.chr);
                if m.cmd >= Cmd::Call
                    && let Some(mac) = m.mac
                {
                    self.printer.print_char(':' as u32);
                    let t = mac.tokens.clone();
                    self.show_token_list(&t, 0, t.len(), 32);
                }
            }
            Loc::IntPar(i) => {
                self.print_cmd_chr(Cmd::AssignInt, i);
                self.printer.print_char('=' as u32);
                self.printer.print_int(self.eqtb.int(i).into());
            }
            Loc::Count(n) => {
                self.print_esc_str("count");
                self.printer.print_int(n.into());
                self.printer.print_char('=' as u32);
                self.printer
                    .print_int(self.eqtb.count[n as usize].val.into());
            }
            Loc::Attr(n) => {
                self.print_esc_str("attribute");
                self.printer.print_int(n.into());
                self.printer.print_char('=' as u32);
                self.printer
                    .print_int(self.eqtb.attr[n as usize].val.into());
            }
            Loc::DimenPar(i) => {
                self.print_cmd_chr(Cmd::AssignDimen, i);
                self.printer.print_char('=' as u32);
                self.printer.print_scaled(self.eqtb.dimen(i));
                self.printer.print_str("pt");
            }
            Loc::Dimen(n) => {
                self.print_esc_str("dimen");
                self.printer.print_int(n.into());
                self.printer.print_char('=' as u32);
                self.printer.print_scaled(self.eqtb.dimen[n as usize].val);
                self.printer.print_str("pt");
            }
            Loc::GluePar(i) => {
                let mu = i >= MU_GLUE_OFFSET;
                if mu {
                    self.print_cmd_chr(Cmd::AssignMuGlue, i - MU_GLUE_OFFSET);
                } else {
                    self.print_cmd_chr(Cmd::AssignGlue, i);
                }
                self.printer.print_char('=' as u32);
                let g = self.eqtb.glue(i);
                self.print_spec(&g, Some(if mu { "mu" } else { "pt" }));
            }
            Loc::Skip(n) | Loc::MuSkip(n) => {
                let mu = matches!(loc, Loc::MuSkip(_));
                self.print_esc_str(if mu { "muskip" } else { "skip" });
                self.printer.print_int(n.into());
                self.printer.print_char('=' as u32);
                let g = if mu {
                    self.eqtb.muskip[n as usize].val
                } else {
                    self.eqtb.skip[n as usize].val
                };
                self.print_spec(&g, Some(if mu { "mu" } else { "pt" }));
            }
            Loc::ToksPar(_) | Loc::Toks(_) => {
                if let Loc::Toks(n) = loc {
                    self.print_esc_str("toks");
                    self.printer.print_int(n.into());
                } else if let Loc::ToksPar(i) = loc {
                    self.print_cmd_chr(Cmd::AssignToks, i);
                }
                self.printer.print_char('=' as u32);
                if let Val::Toks(t) = self.eqtb.get(loc) {
                    self.show_token_list(&t, 0, t.len(), 32);
                }
            }
            Loc::Box(n) => {
                self.print_esc_str("box");
                self.printer.print_int(n.into());
                self.printer.print_char('=' as u32);
                let b = self.eqtb.boxes[n as usize].val;
                if b.is_null() {
                    self.printer.print_str("void");
                } else {
                    self.depth_threshold = 0;
                    self.breadth_max = 1;
                    self.show_box1(b);
                }
            }
            Loc::CurFont => {
                self.printer.print_str("current font");
                self.printer.print_char('=' as u32);
                let cs = self.font_id_cs(self.eqtb.cur_font.val);
                let name = self.hash.name(cs).to_vec();
                self.print_esc_bytes(&name);
            }
            Loc::MathFont(i) => {
                let size = i / NUM_FAMILIES as u32;
                let fam = i % NUM_FAMILIES as u32;
                self.print_cmd_chr(Cmd::DefFamily, size);
                self.printer.print_int(fam.into());
                self.printer.print_char('=' as u32);
                let cs = self.font_id_cs(self.eqtb.math_font[i as usize].val);
                let name = self.hash.name(cs).to_vec();
                self.print_esc_bytes(&name);
            }
            Loc::ParShape => {
                self.print_esc_str("parshape");
                self.printer.print_char('=' as u32);
                let n = self.eqtb.par_shape.val.as_ref().map_or(0, |s| s.len() / 2);
                self.printer.print_int(n as i64);
            }
            Loc::EtexShape(i) => {
                self.print_cmd_chr(Cmd::SetEtexShape, i);
                self.printer.print_char('=' as u32);
                let n = self.eqtb.etex_shape[i as usize]
                    .val
                    .as_ref()
                    .map_or(0, |s| s.len());
                self.printer.print_int(n as i64);
            }
            Loc::Cat { table, ch } => {
                self.print_esc_str("catcode");
                self.printer.print_int(ch.into());
                self.printer.print_char('=' as u32);
                self.printer
                    .print_int(self.eqtb.catcode_in(table, ch).into());
            }
            Loc::Code(kind, ch) => {
                use crate::eqtb::CodeKind as K;
                let name = match kind {
                    K::Lc => "lccode",
                    K::Uc => "uccode",
                    K::Sf => "sfcode",
                    K::Math => "mathcode",
                    K::Del => "delcode",
                };
                self.print_esc_str(name);
                self.printer.print_int(ch.into());
                self.printer.print_char('=' as u32);
                self.printer.print_int(self.eqtb.code_table(kind).get(ch));
            }
            Loc::MathParam(i) => {
                self.print_cmd_chr(Cmd::SetMathParam, i / 8);
                self.printer.print_int((i % 8).into());
                self.printer.print_char('=' as u32);
                if let Val::Int(v) = self.eqtb.get(loc) {
                    self.printer.print_scaled(v as i32);
                    self.printer.print_str("pt");
                }
            }
            Loc::LocalCount(i) => {
                self.print_esc_str(if i == 0 {
                    "nolocalwhatsits"
                } else {
                    "nolocaldirs"
                });
                self.printer.print_char('=' as u32);
                self.printer
                    .print_int(self.eqtb.local_count[i as usize].val.into());
            }
            Loc::DirPar(i) => {
                // Observed: `{changing \textdir TLT}`.
                const NAMES: [&str; 6] = [
                    "pagedir", "bodydir", "pardir", "textdir", "mathdir", "linedir",
                ];
                self.print_esc_str(NAMES.get(i as usize).copied().unwrap_or("textdir"));
                self.printer.print_char(' ' as u32);
                let d = self.eqtb.dir_par[i as usize].val;
                self.printer
                    .print_str(crate::showbox::DIR_NAMES[d.rem_euclid(4) as usize]);
            }
            Loc::LocalBox(i) => {
                self.print_esc_str(if i == 0 {
                    "localleftbox"
                } else {
                    "localrightbox"
                });
                self.printer.print_char('=' as u32);
                let b = self.eqtb.local_box[i as usize].val;
                if b.is_null() {
                    self.printer.print_str("void");
                } else {
                    self.depth_threshold = 0;
                    self.breadth_max = 1;
                    self.show_box1(b);
                }
            }
        }
    }

    /// Displays a box (`show_box`), honouring `\showboxdepth` and
    /// `\showboxbreadth`.
    pub fn show_box(&mut self, p: NodeRef) {
        self.show_box_param_limits();
        self.show_node_list(p);
        self.printer.print_ln();
    }

    /// Displays a node list with the current limits (`show_node_list`).
    pub fn show_box1(&mut self, p: NodeRef) {
        self.show_node_list(p);
    }

    /// Shows the active groups (e-TeX `show_save_groups`, simplified).
    pub fn show_save_groups(&mut self) {
        self.printer.print_nl("");
        self.printer.print_ln();
        let lvl = self.save.cur_level;
        let mut l = lvl;
        // Walk the boundaries from the innermost outwards.
        let mut boundary = self.save.cur_boundary;
        let mut group = self.save.cur_group;
        while group != crate::save::GroupCode::BottomLevel {
            l -= 1;
            self.printer.print_nl("### ");
            let saved_group = self.save.cur_group;
            let saved_level = self.save.cur_level;
            let saved_boundary = self.save.cur_boundary;
            self.save.cur_group = group;
            self.save.cur_level = l;
            self.save.cur_boundary = boundary;
            self.print_group(true);
            self.save.cur_group = saved_group;
            self.save.cur_level = saved_level;
            self.save.cur_boundary = saved_boundary;
            match self.save.stack.get(boundary) {
                Some(crate::save::SaveEntry::Boundary {
                    group: g,
                    boundary: b,
                    ..
                }) => {
                    group = *g;
                    boundary = *b;
                }
                _ => break,
            }
        }
    }
}