1use crate::commands::primitives::{PrimitiveCommands, PrimitiveIdentifier, PRIMITIVES};
3use crate::commands::{PrimitiveCommand, TeXCommand};
4use crate::engine::fontsystem::Font;
5use crate::engine::mouth::Mouth;
6use crate::engine::state::{GroupType, State, StateChange, StateChangeTracker, StateStack};
7use crate::engine::utils::outputs::Outputs;
8use crate::engine::{EngineAux, EngineTypes};
9use crate::tex::catcodes::{CategoryCode, CategoryCodeScheme};
10use crate::tex::characters::Character;
11use crate::tex::characters::CharacterMap;
12use crate::tex::nodes::boxes::TeXBox;
13use crate::tex::numerics::{MuSkip, Skip};
14use crate::tex::tokens::control_sequences::CSNameMap;
15use crate::tex::tokens::control_sequences::{CSHandler, CSName};
16use crate::tex::tokens::token_lists::TokenList;
17use crate::utils::HMap;
18
19#[derive(Clone)]
21pub struct DefaultState<ET: EngineTypes> {
22 stack: StateStack<ET>,
23 primitives: PrimitiveCommands<ET>,
24 catcodes: CategoryCodeScheme<ET::Char>,
25 sfcodes: <ET::Char as Character>::CharMap<u16>,
26 lccodes: <ET::Char as Character>::CharMap<ET::Char>,
27 uccodes: <ET::Char as Character>::CharMap<ET::Char>,
28 mathcodes: <ET::Char as Character>::CharMap<u32>,
29 delcodes: <ET::Char as Character>::CharMap<ET::Int>,
30 primitive_ints: HMap<PrimitiveIdentifier, ET::Int>,
31 primitive_dims: HMap<PrimitiveIdentifier, ET::Dim>,
32 primitive_skips: HMap<PrimitiveIdentifier, Skip<ET::Dim>>,
33 primitive_muskips: HMap<PrimitiveIdentifier, MuSkip<ET::MuDim>>,
34 primitive_toks: HMap<PrimitiveIdentifier, TokenList<ET::Token>>,
35 int_register: Vec<ET::Int>,
36 dim_register: Vec<ET::Dim>,
37 skip_register: Vec<Skip<ET::Dim>>,
38 muskip_register: Vec<MuSkip<ET::MuDim>>,
39 toks_register: Vec<TokenList<ET::Token>>,
40 box_register: Vec<Option<TeXBox<ET>>>,
41 pub commands: <ET::CSName as CSName<ET::Char>>::Map<TeXCommand<ET>>, ac_commands: <ET::Char as Character>::CharMap<Option<TeXCommand<ET>>>,
43 endline_char: Option<ET::Char>,
44 escape_char: Option<ET::Char>,
45 newline_char: Option<ET::Char>,
46 current_font: ET::Font,
47 textfonts: [ET::Font; 16],
48 scriptfonts: [ET::Font; 16],
49 scriptscriptfonts: [ET::Font; 16],
50 empty_list: TokenList<ET::Token>,
51 parshape: Vec<(ET::Dim, ET::Dim)>,
52}
53impl<ET: EngineTypes> DefaultState<ET> {
54 fn tracing_assigns(&self) -> bool {
55 matches!(self.primitive_ints.get(&PRIMITIVES.tracingassigns), Some(v) if *v > ET::Int::default())
56 }
57 fn tracing_restores(&self) -> bool {
58 matches!(self.primitive_ints.get(&PRIMITIVES.tracingrestores), Some(v) if *v > ET::Int::default())
59 }
60 pub fn set_command_direct(&mut self, name: ET::CSName, cmd: Option<TeXCommand<ET>>) {
61 match cmd {
62 None => self.commands.remove(&name),
63 Some(c) => self.commands.insert(name, c),
64 };
65 }
66}
67
68impl<ET: EngineTypes> StateChangeTracker<ET> for DefaultState<ET> {
69 fn stack(&mut self) -> &mut StateStack<ET> {
70 &mut self.stack
71 }
72}
73
74impl<ET: EngineTypes> State<ET> for DefaultState<ET> {
75 fn new(nullfont: ET::Font, aux: &mut EngineAux<ET>) -> Self {
76 let mem = &aux.memory;
77 let mut lccodes: <ET::Char as Character>::CharMap<ET::Char> = CharacterMap::default();
78 let mut uccodes: <ET::Char as Character>::CharMap<ET::Char> = CharacterMap::default();
79 let mut mathcodes: <ET::Char as Character>::CharMap<u32> = CharacterMap::default();
80 for i in 97..123 {
81 *uccodes.get_mut(i.into()) = (i - 32).into();
82 *lccodes.get_mut((i - 32).into()) = i.into();
83 *mathcodes.get_mut(ET::Char::from(i - 32)) =
84 (i as u32 - 32) + (16 * 16) + (7 * 16 * 16 * 16);
85 *mathcodes.get_mut(ET::Char::from(i)) = (i as u32) + (16 * 16) + (7 * 16 * 16 * 16);
86 }
87 for i in 48..58 {
88 *mathcodes.get_mut(ET::Char::from(i)) = (i as u32) + (7 * 16 * 16 * 16);
89 }
90 let mathfonts = array_init::array_init(|_| nullfont.clone());
91 Self {
92 stack: StateStack::default(),
93 primitives: PrimitiveCommands::default(),
94 catcodes: ET::Char::starting_catcode_scheme(),
95 sfcodes: CharacterMap::default(),
96 delcodes: CharacterMap::default(),
97 lccodes,
98 uccodes,
99 mathcodes,
100 current_font: nullfont,
101 primitive_ints: HMap::default(),
102 primitive_dims: HMap::default(),
103 primitive_skips: HMap::default(),
104 primitive_muskips: HMap::default(),
105 primitive_toks: HMap::default(),
106 int_register: Vec::new(),
107 dim_register: Vec::new(),
108 skip_register: Vec::new(),
109 muskip_register: Vec::new(),
110 toks_register: Vec::new(),
111 box_register: Vec::new(),
112 commands: <ET::CSName as CSName<ET::Char>>::Map::default(),
113 ac_commands: <ET::Char as Character>::CharMap::default(),
114 endline_char: Some(ET::Char::from(b'\r')),
115 escape_char: Some(ET::Char::from(b'\\')),
116 newline_char: None,
117 empty_list: mem.empty_list(),
118 parshape: Vec::new(),
119 textfonts: mathfonts.clone(),
120 scriptfonts: mathfonts.clone(),
121 scriptscriptfonts: mathfonts,
122 }
123 }
124
125 fn register_primitive(
126 &mut self,
127 aux: &mut EngineAux<ET>,
128 name: &'static str,
129 cmd: PrimitiveCommand<ET>,
130 ) {
131 let id = self.primitives.register(name, cmd.clone());
132 let name = aux.memory.cs_interner_mut().cs_from_str(name);
133 self.commands
134 .insert(name, TeXCommand::Primitive { name: id, cmd });
135 }
136 fn primitives(&self) -> &PrimitiveCommands<ET> {
137 &self.primitives
138 }
139
140 fn get_group_type(&self) -> Option<GroupType> {
141 self.stack.stack.last().map(|lvl| lvl.group_type)
142 }
143
144 fn get_group_level(&self) -> usize {
145 self.stack.stack.len()
146 }
147
148 fn aftergroup(&mut self, token: ET::Token) {
149 match self.stack.stack.last_mut() {
150 None => (),
151 Some(lvl) => lvl.aftergroup.push(token),
152 }
153 }
154
155 fn push(&mut self, aux: &mut EngineAux<ET>, group_type: GroupType, line_number: usize) {
156 self.stack.push(group_type);
157 let tracing = matches!(self.primitive_ints.get(&PRIMITIVES.tracinggroups), Some(v) if *v > ET::Int::default());
158 if tracing {
159 aux.outputs.write_neg1(format_args!(
160 "{{entering {} group (level {}) at line {}}}",
161 group_type,
162 self.stack.stack.len(),
163 line_number
164 ))
165 }
166 }
167
168 fn pop(&mut self, aux: &mut EngineAux<ET>, mouth: &mut ET::Mouth) {
169 let len = self.stack.stack.len();
170 assert!(len > 0);
171 let traceg = matches!(self.primitive_ints.get(&PRIMITIVES.tracinggroups), Some(v) if *v > ET::Int::default());
172 let trace = self.tracing_restores();
173
174 let (gt, ag, ch) = self.stack.pop();
175
176 if traceg {
177 aux.outputs.write_neg1(format_args!(
178 "{{leaving {} group (level {}) at line {}}}",
179 gt,
180 len,
181 mouth.line_number()
182 ))
183 }
184 ch.close(|c| {
185 match c {
186 StateChange::Catcode { char, old } => {
188 if trace {
189 aux.outputs.write_neg1(format_args!(
190 "{{restoring {}catcode{}={}}}",
191 <ET::Char as Character>::display_opt(self.escape_char),
192 char.into(),
193 old
194 ));
195 }
196 *self.catcodes.get_mut(char) = old;
197 }
198 StateChange::CurrentFont(font) => {
199 if trace {
200 aux.outputs.write_neg1(format_args!(
201 "{{restoring current font ={}{}}}",
202 <ET::Char as Character>::display_opt(self.escape_char),
203 aux.memory.cs_interner().resolve(font.name())
204 ));
205 }
206 self.current_font = font;
207 }
208 StateChange::TextFont { idx, old: font } => {
209 if trace {
210 aux.outputs.write_neg1(format_args!(
211 "{{restoring textfont{} ={}{}}}",
212 idx,
213 <ET::Char as Character>::display_opt(self.escape_char),
214 aux.memory.cs_interner().resolve(font.name())
215 ));
216 }
217 self.textfonts[idx as usize] = font;
218 }
219 StateChange::ScriptFont { idx, old: font } => {
220 if trace {
221 aux.outputs.write_neg1(format_args!(
222 "{{restoring scriptfont{} ={}{}}}",
223 idx,
224 <ET::Char as Character>::display_opt(self.escape_char),
225 aux.memory.cs_interner().resolve(font.name())
226 ));
227 }
228 self.scriptfonts[idx as usize] = font;
229 }
230 StateChange::ScriptScriptFont { idx, old: font } => {
231 if trace {
232 aux.outputs.write_neg1(format_args!(
233 "{{restoring scriptscriptfont{} ={}{}}}",
234 idx,
235 <ET::Char as Character>::display_opt(self.escape_char),
236 aux.memory.cs_interner().resolve(font.name())
237 ));
238 }
239 self.scriptscriptfonts[idx as usize] = font;
240 }
241 StateChange::ParShape { old } => {
242 if trace {
243 aux.outputs.write_neg1(format_args!("{{TODO parshape}}"));
244 }
245 self.parshape = old;
246 }
247 StateChange::SfCode { char, old } => {
248 if trace {
249 aux.outputs.write_neg1(format_args!(
250 "{{restoring {}sfcode{}={}}}",
251 <ET::Char as Character>::display_opt(self.escape_char),
252 char.into(),
253 old
254 ));
255 }
256 *self.sfcodes.get_mut(char) = old;
257 }
258 StateChange::DelCode { char, old } => {
259 if trace {
260 aux.outputs.write_neg1(format_args!(
261 "{{restoring {}delcode{}={}}}",
262 <ET::Char as Character>::display_opt(self.escape_char),
263 char.into(),
264 old
265 ));
266 }
267 *self.delcodes.get_mut(char) = old;
268 }
269 StateChange::LcCode { char, old } => {
270 if trace {
271 aux.outputs.write_neg1(format_args!(
272 "{{restoring {}lccode{}={}}}",
273 <ET::Char as Character>::display_opt(self.escape_char),
274 char.into(),
275 old
276 ));
277 }
278 *self.lccodes.get_mut(char) = old;
279 }
280 StateChange::UcCode { char, old } => {
281 if trace {
282 aux.outputs.write_neg1(format_args!(
283 "{{restoring {}uccode{}={}}}",
284 <ET::Char as Character>::display_opt(self.escape_char),
285 char.into(),
286 old
287 ));
288 }
289 *self.uccodes.get_mut(char) = old;
290 }
291 StateChange::MathCode { char, old } => {
292 if trace {
293 aux.outputs.write_neg1(format_args!(
294 "{{restoring {}mathcode{}=\"{:X}}}",
295 <ET::Char as Character>::display_opt(self.escape_char),
296 char.into(),
297 old
298 ));
299 }
300 *self.mathcodes.get_mut(char) = old;
301 }
302 StateChange::EndlineChar { old } => {
303 if trace {
304 aux.outputs.write_neg1(format_args!(
305 "{{restoring {}endlinechar={}}}",
306 <ET::Char as Character>::display_opt(self.escape_char),
307 match old {
308 None => -1,
309 Some(c) => c.into() as i64,
310 }
311 ));
312 }
313 self.endline_char = old;
314 }
315 StateChange::EscapeChar { old } => {
316 if trace {
317 aux.outputs.write_neg1(format_args!(
318 "{{restoring {}escapechar={}}}",
319 <ET::Char as Character>::display_opt(self.escape_char),
320 match old {
321 None => -1,
322 Some(c) => c.into() as i64,
323 }
324 ));
325 }
326 self.escape_char = old;
327 }
328 StateChange::NewlineChar { old } => {
329 if trace {
330 aux.outputs.write_neg1(format_args!(
331 "{{restoring {}newlinechar={}}}",
332 <ET::Char as Character>::display_opt(self.escape_char),
333 match old {
334 None => -1,
335 Some(c) => c.into() as i64,
336 }
337 ));
338 }
339 self.newline_char = old;
340 }
341 StateChange::IntRegister { idx, old } => {
342 if trace {
343 aux.outputs.write_neg1(format_args!(
344 "{{restoring {}count{}={}}}",
345 <ET::Char as Character>::display_opt(self.escape_char),
346 idx,
347 old
348 ));
349 }
350 self.int_register[idx] = old;
351 }
352 StateChange::DimRegister { idx, old } => {
353 if trace {
354 aux.outputs.write_neg1(format_args!(
355 "{{restoring {}dimen{}={}}}",
356 <ET::Char as Character>::display_opt(self.escape_char),
357 idx,
358 old
359 ));
360 }
361 self.dim_register[idx] = old;
362 }
363 StateChange::SkipRegister { idx, old } => {
364 if trace {
365 aux.outputs.write_neg1(format_args!(
366 "{{restoring {}skip{}={}}}",
367 <ET::Char as Character>::display_opt(self.escape_char),
368 idx,
369 old
370 ));
371 }
372 self.skip_register[idx] = old;
373 }
374 StateChange::MuSkipRegister { idx, old } => {
375 if trace {
376 aux.outputs.write_neg1(format_args!(
377 "{{restoring {}muskip{}={}}}",
378 <ET::Char as Character>::display_opt(self.escape_char),
379 idx,
380 old
381 ));
382 }
383 self.muskip_register[idx] = old;
384 }
385 StateChange::ToksRegister { idx, old } => {
386 if trace {
387 aux.outputs.write_neg1(format_args!(
388 "{{restoring {}toks{}={}}}",
389 <ET::Char as Character>::display_opt(self.escape_char),
390 idx,
391 old.display(
392 aux.memory.cs_interner(),
393 &self.catcodes,
394 self.escape_char,
395 false
396 )
397 ));
398 }
399 self.toks_register[idx] = old;
400 }
401 StateChange::BoxRegister { idx, old } => {
402 if trace {
403 aux.outputs.write_neg1("TODO trace box restore")
404 }
405 self.box_register[idx] = old;
406 }
407 StateChange::PrimitiveInt { name, old } => {
408 if trace {
409 aux.outputs.write_neg1(format_args!(
410 "{{restoring {}{}={}}}",
411 <ET::Char as Character>::display_opt(self.escape_char),
412 name.display(self.escape_char),
413 old
414 ));
415 }
416 self.primitive_ints.insert(name, old);
417 }
418 StateChange::PrimitiveDim { name, old } => {
419 if trace {
420 aux.outputs.write_neg1(format_args!(
421 "{{restoring {}{}={}}}",
422 <ET::Char as Character>::display_opt(self.escape_char),
423 name.display(self.escape_char),
424 old
425 ));
426 }
427 self.primitive_dims.insert(name, old);
428 }
429 StateChange::PrimitiveSkip { name, old } => {
430 if trace {
431 aux.outputs.write_neg1(format_args!(
432 "{{restoring {}{}={}}}",
433 <ET::Char as Character>::display_opt(self.escape_char),
434 name.display(self.escape_char),
435 old
436 ));
437 }
438 self.primitive_skips.insert(name, old);
439 }
440 StateChange::PrimitiveMuSkip { name, old } => {
441 if trace {
442 aux.outputs.write_neg1(format_args!(
443 "{{restoring {}{}={}}}",
444 <ET::Char as Character>::display_opt(self.escape_char),
445 name.display(self.escape_char),
446 old
447 ));
448 }
449 self.primitive_muskips.insert(name, old);
450 }
451 StateChange::PrimitiveToks { name, old } => {
452 if trace {
453 aux.outputs.write_neg1(format_args!(
454 "{{restoring {}{}={}}}",
455 <ET::Char as Character>::display_opt(self.escape_char),
456 name.display(self.escape_char),
457 old.display(
458 aux.memory.cs_interner(),
459 &self.catcodes,
460 self.escape_char,
461 false
462 )
463 ));
464 }
465 self.primitive_toks.insert(name, old);
466 }
467 StateChange::Command { name, old } => {
468 if trace {
478 match old {
479 None => aux.outputs.write_neg1(format_args!(
480 "{{restoring {}{}={}undefined}}",
481 <ET::Char as Character>::display_opt(self.escape_char),
482 aux.memory.cs_interner().resolve(&name),
483 <ET::Char as Character>::display_opt(self.escape_char)
484 )),
485 Some(ref c) => aux.outputs.write_neg1(format_args!(
486 "{{restoring {}{}={}}}",
487 <ET::Char as Character>::display_opt(self.escape_char),
488 aux.memory.cs_interner().resolve(&name),
489 c.meaning(
490 aux.memory.cs_interner(),
491 &self.catcodes,
492 self.escape_char
493 )
494 )),
495 }
496 }
497 match old {
498 Some(o) => self.commands.insert(name, o),
499 None => self.commands.remove(&name),
500 };
501 }
502 StateChange::AcCommand { char, old } => {
503 if trace {
504 match old {
505 None => aux.outputs.write_neg1(format_args!(
506 "{{restoring {}{}={}undefined}}",
507 <ET::Char as Character>::display_opt(self.escape_char),
508 char.display(),
509 <ET::Char as Character>::display_opt(self.escape_char)
510 )),
511 Some(ref c) => aux.outputs.write_neg1(format_args!(
512 "{{restoring {}{}={}}}",
513 <ET::Char as Character>::display_opt(self.escape_char),
514 char.display(),
515 c.meaning(
516 aux.memory.cs_interner(),
517 &self.catcodes,
518 self.escape_char
519 )
520 )),
521 }
522 }
523 *self.ac_commands.get_mut(char) = old;
524 } }
532 });
533 if !ag.is_empty() {
534 mouth.push_vec(ag)
535 }
536 }
537
538 fn get_parshape(&self) -> &Vec<(ET::Dim, ET::Dim)> {
539 &self.parshape
540 }
541 fn take_parshape(&mut self) -> Vec<(ET::Dim, ET::Dim)> {
542 let sh = std::mem::take(&mut self.parshape);
543 self.stack
544 .add_change_locally(StateChange::ParShape { old: sh.clone() });
545 sh
546 }
547 fn set_parshape(
548 &mut self,
549 aux: &EngineAux<ET>,
550 parshape: Vec<(ET::Dim, ET::Dim)>,
551 globally: bool,
552 ) {
553 self.change_field(globally, |s, g| {
554 if s.tracing_assigns() {
555 aux.outputs.write_neg1(format_args!(
556 "{{{}changing parshape}}",
557 if g { "globally " } else { "" }
558 ));
559 }
560 let old = std::mem::replace(&mut s.parshape, parshape);
561 StateChange::ParShape { old }
562 })
563 }
564
565 fn get_current_font(&self) -> &ET::Font {
566 &self.current_font
567 }
568 fn set_current_font(&mut self, aux: &mut EngineAux<ET>, fnt: ET::Font, globally: bool) {
569 self.change_field(globally, |s, g| {
570 if s.tracing_assigns() {
571 aux.outputs.write_neg1(format_args!(
572 "{{{}changing current font={}{}}}",
573 if g { "globally " } else { "" },
574 <ET::Char as Character>::display_opt(s.escape_char),
575 aux.memory.cs_interner().resolve(s.current_font.name())
576 ));
577 aux.outputs.write_neg1(format_args!(
578 "{{into current font={}{}}}",
579 <ET::Char as Character>::display_opt(s.escape_char),
580 aux.memory.cs_interner().resolve(fnt.name())
581 ));
582 }
583 let old = std::mem::replace(&mut s.current_font, fnt);
584 StateChange::CurrentFont(old)
585 })
586 }
587
588 fn get_textfont(&self, i: u8) -> &<ET as EngineTypes>::Font {
589 &self.textfonts[i as usize]
590 }
591 fn set_textfont(
592 &mut self,
593 aux: &mut EngineAux<ET>,
594 idx: u8,
595 fnt: <ET as EngineTypes>::Font,
596 globally: bool,
597 ) {
598 self.change_field(globally, |s, g| {
599 if s.tracing_assigns() {
600 aux.outputs.write_neg1(format_args!(
601 "{{{}changing textfont{}={}{}}}",
602 if g { "globally " } else { "" },
603 idx,
604 <ET::Char as Character>::display_opt(s.escape_char),
605 aux.memory
606 .cs_interner()
607 .resolve(s.textfonts[idx as usize].name())
608 ));
609 aux.outputs.write_neg1(format_args!(
610 "{{into textfont{}={}{}}}",
611 idx,
612 <ET::Char as Character>::display_opt(s.escape_char),
613 aux.memory.cs_interner().resolve(fnt.name())
614 ));
615 }
616 let old = std::mem::replace(&mut s.textfonts[idx as usize], fnt);
617 StateChange::TextFont { idx, old }
618 })
619 }
620
621 fn get_scriptfont(&self, i: u8) -> &<ET as EngineTypes>::Font {
622 &self.scriptfonts[i as usize]
623 }
624 fn set_scriptfont(
625 &mut self,
626 aux: &mut EngineAux<ET>,
627 idx: u8,
628 fnt: <ET as EngineTypes>::Font,
629 globally: bool,
630 ) {
631 self.change_field(globally, |s, g| {
632 if s.tracing_assigns() {
633 aux.outputs.write_neg1(format_args!(
634 "{{{}changing scriptfont{}={}{}}}",
635 if g { "globally " } else { "" },
636 idx,
637 <ET::Char as Character>::display_opt(s.escape_char),
638 aux.memory
639 .cs_interner()
640 .resolve(s.scriptfonts[idx as usize].name())
641 ));
642 aux.outputs.write_neg1(format_args!(
643 "{{into scriptfont{}={}{}}}",
644 idx,
645 <ET::Char as Character>::display_opt(s.escape_char),
646 aux.memory.cs_interner().resolve(fnt.name())
647 ));
648 }
649 let old = std::mem::replace(&mut s.scriptfonts[idx as usize], fnt);
650 StateChange::ScriptFont { idx, old }
651 })
652 }
653
654 fn get_scriptscriptfont(&self, i: u8) -> &<ET as EngineTypes>::Font {
655 &self.scriptscriptfonts[i as usize]
656 }
657 fn set_scriptscriptfont(
658 &mut self,
659 aux: &mut EngineAux<ET>,
660 idx: u8,
661 fnt: <ET as EngineTypes>::Font,
662 globally: bool,
663 ) {
664 self.change_field(globally, |s, g| {
665 if s.tracing_assigns() {
666 aux.outputs.write_neg1(format_args!(
667 "{{{}changing scriptscriptfont{}={}{}}}",
668 if g { "globally " } else { "" },
669 idx,
670 <ET::Char as Character>::display_opt(s.escape_char),
671 aux.memory
672 .cs_interner()
673 .resolve(s.scriptscriptfonts[idx as usize].name())
674 ));
675 aux.outputs.write_neg1(format_args!(
676 "{{into scriptscriptfont{}={}{}}}",
677 idx,
678 <ET::Char as Character>::display_opt(s.escape_char),
679 aux.memory.cs_interner().resolve(fnt.name())
680 ));
681 }
682 let old = std::mem::replace(&mut s.scriptscriptfonts[idx as usize], fnt);
683 StateChange::ScriptScriptFont { idx, old }
684 })
685 }
686
687 fn get_catcode_scheme(&self) -> &CategoryCodeScheme<ET::Char> {
688 &self.catcodes
689 }
690 fn set_catcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, cc: CategoryCode, globally: bool) {
691 self.change_field(globally, |s, g| {
692 if s.tracing_assigns() {
693 let num = c.into();
694 let cc: u8 = cc.into();
695 aux.outputs.write_neg1(format_args!(
696 "{{{} {}catcode{}={}}}",
697 if g {
698 "globally changing"
699 } else {
700 "reassigning"
701 },
702 <ET::Char as Character>::display_opt(s.escape_char),
703 num,
704 cc
705 ));
706 }
707 let old = std::mem::replace(s.catcodes.get_mut(c), cc);
708 StateChange::Catcode { char: c, old }
709 })
710 }
711
712 fn get_sfcode(&self, c: ET::Char) -> u16 {
713 *self.sfcodes.get(c)
714 }
715 fn set_sfcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: u16, globally: bool) {
716 self.change_field(globally, |s, g| {
717 let old = std::mem::replace(s.sfcodes.get_mut(c), val);
718 if s.tracing_assigns() {
719 let num = c.into();
720 aux.outputs.write_neg1(format_args!(
721 "{{{}changing {}sfcode{}={}}}",
722 if g { "globally " } else { "" },
723 <ET::Char as Character>::display_opt(s.escape_char),
724 num,
725 old
726 ));
727 aux.outputs.write_neg1(format_args!(
728 "{{into {}sfcode{}={}}}",
729 <ET::Char as Character>::display_opt(s.escape_char),
730 num,
731 val
732 ));
733 }
734 StateChange::SfCode { char: c, old }
735 })
736 }
737
738 fn get_delcode(&self, c: ET::Char) -> ET::Int {
739 *self.delcodes.get(c)
740 }
741 fn set_delcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: ET::Int, globally: bool) {
742 self.change_field(globally, |s, g| {
743 let old = std::mem::replace(s.delcodes.get_mut(c), val);
744 if s.tracing_assigns() {
745 let num = c.into();
746 aux.outputs.write_neg1(format_args!(
747 "{{{}changing {}delcode{}={}}}",
748 if g { "globally " } else { "" },
749 <ET::Char as Character>::display_opt(s.escape_char),
750 num,
751 old
752 ));
753 aux.outputs.write_neg1(format_args!(
754 "{{into {}delcode{}={}}}",
755 <ET::Char as Character>::display_opt(s.escape_char),
756 num,
757 val
758 ));
759 }
760 StateChange::DelCode { char: c, old }
761 })
762 }
763
764 fn get_lccode(&self, c: ET::Char) -> ET::Char {
765 *self.lccodes.get(c)
766 }
767 fn set_lccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: ET::Char, globally: bool) {
768 self.change_field(globally, |s, g| {
769 let old = std::mem::replace(s.lccodes.get_mut(c), val);
770 if s.tracing_assigns() {
771 let num = c.into();
772 aux.outputs.write_neg1(format_args!(
773 "{{{}changing {}lccode{}={}}}",
774 if g { "globally " } else { "" },
775 <ET::Char as Character>::display_opt(s.escape_char),
776 num,
777 old.into()
778 ));
779 aux.outputs.write_neg1(format_args!(
780 "{{into {}lccode{}={}}}",
781 <ET::Char as Character>::display_opt(s.escape_char),
782 num,
783 val.into()
784 ));
785 }
786 StateChange::LcCode { char: c, old }
787 })
788 }
789
790 fn get_uccode(&self, c: ET::Char) -> ET::Char {
791 *self.uccodes.get(c)
792 }
793 fn set_uccode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: ET::Char, globally: bool) {
794 self.change_field(globally, |s, g| {
795 let old = std::mem::replace(s.uccodes.get_mut(c), val);
796 if s.tracing_assigns() {
797 let num = c.into();
798 aux.outputs.write_neg1(format_args!(
799 "{{{}changing {}uccode{}={}}}",
800 if g { "globally " } else { "" },
801 <ET::Char as Character>::display_opt(s.escape_char),
802 num,
803 old.into()
804 ));
805 aux.outputs.write_neg1(format_args!(
806 "{{into {}uccode{}={}}}",
807 <ET::Char as Character>::display_opt(s.escape_char),
808 num,
809 val.into()
810 ));
811 }
812 StateChange::UcCode { char: c, old }
813 })
814 }
815
816 fn get_mathcode(&self, c: ET::Char) -> u32 {
817 *self.mathcodes.get(c)
818 }
819 fn set_mathcode(&mut self, aux: &EngineAux<ET>, c: ET::Char, val: u32, globally: bool) {
820 self.change_field(globally, |s, g| {
821 let old = std::mem::replace(s.mathcodes.get_mut(c), val);
822 if s.tracing_assigns() {
823 let num = c.into();
824 aux.outputs.write_neg1(format_args!(
825 "{{{}changing {}mathcode{}=\"{:X}}}",
826 if g { "globally " } else { "" },
827 <ET::Char as Character>::display_opt(s.escape_char),
828 num,
829 old
830 ));
831 aux.outputs.write_neg1(format_args!(
832 "{{into {}mathcode{}=\"{:X}}}",
833 <ET::Char as Character>::display_opt(s.escape_char),
834 num,
835 val
836 ));
837 }
838 StateChange::MathCode { char: c, old }
839 })
840 }
841
842 fn get_endline_char(&self) -> Option<ET::Char> {
843 self.endline_char
844 }
845 fn set_endline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool) {
846 self.change_field(globally, |s, g| {
847 if s.tracing_assigns() {
848 aux.outputs.write_neg1(format_args!(
849 "{{{}changing {}endlinechar={}}}",
850 if g { "globally " } else { "" },
851 <ET::Char as Character>::display_opt(s.escape_char),
852 match s.endline_char {
853 None => -1,
854 Some(c) => c.into() as i64,
855 }
856 ));
857 aux.outputs.write_neg1(format_args!(
858 "{{into {}endlinechar={}}}",
859 <ET::Char as Character>::display_opt(s.escape_char),
860 match c {
861 None => -1,
862 Some(c) => c.into() as i64,
863 }
864 ));
865 }
866 let old = std::mem::replace(&mut s.endline_char, c);
867 StateChange::EndlineChar { old }
868 })
869 }
870
871 fn get_escape_char(&self) -> Option<ET::Char> {
872 self.escape_char
873 }
874 fn set_escape_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool) {
875 self.change_field(globally, |s, g| {
876 if s.tracing_assigns() {
877 aux.outputs.write_neg1(format_args!(
878 "{{{}changing {}escapechar={}}}",
879 if g { "globally " } else { "" },
880 <ET::Char as Character>::display_opt(s.escape_char),
881 match s.escape_char {
882 None => -1,
883 Some(c) => c.into() as i64,
884 }
885 ));
886 aux.outputs.write_neg1(format_args!(
887 "{{into {}escapechar={}}}",
888 <ET::Char as Character>::display_opt(s.escape_char),
889 match c {
890 None => -1,
891 Some(c) => c.into() as i64,
892 }
893 ));
894 }
895 let old = std::mem::replace(&mut s.escape_char, c);
896 StateChange::EscapeChar { old }
897 })
898 }
899
900 fn get_newline_char(&self) -> Option<ET::Char> {
901 self.newline_char
902 }
903 fn set_newline_char(&mut self, aux: &EngineAux<ET>, c: Option<ET::Char>, globally: bool) {
904 self.change_field(globally, |s, g| {
905 if s.tracing_assigns() {
906 aux.outputs.write_neg1(format_args!(
907 "{{{}changing {}newlinechar={}}}",
908 if g { "globally " } else { "" },
909 <ET::Char as Character>::display_opt(s.escape_char),
910 match s.newline_char {
911 None => -1,
912 Some(c) => c.into() as i64,
913 }
914 ));
915 aux.outputs.write_neg1(format_args!(
916 "{{into {}newlinechar={}}}",
917 <ET::Char as Character>::display_opt(s.escape_char),
918 match c {
919 None => -1,
920 Some(c) => c.into() as i64,
921 }
922 ));
923 }
924 let old = std::mem::replace(&mut s.newline_char, c);
925 StateChange::NewlineChar { old }
926 })
927 }
928
929 fn get_primitive_int(&self, name: PrimitiveIdentifier) -> ET::Int {
930 match self.primitive_ints.get(&name) {
931 Some(i) => *i,
932 _ => ET::Int::default(),
933 }
934 }
935 fn set_primitive_int(
936 &mut self,
937 aux: &EngineAux<ET>,
938 name: PrimitiveIdentifier,
939 v: ET::Int,
940 globally: bool,
941 ) {
942 self.change_field(globally, |s, g| {
943 let old = s.primitive_ints.insert(name, v).unwrap_or_default();
944 if s.tracing_assigns() {
945 aux.outputs.write_neg1(format_args!(
946 "{{{}changing {}={}}}",
947 if g { "globally " } else { "" },
948 name.display(s.escape_char),
949 old
950 ));
951 aux.outputs.write_neg1(format_args!(
952 "{{into {}={}}}",
953 name.display(s.escape_char),
954 v
955 ))
956 }
957 StateChange::PrimitiveInt { name, old }
958 });
959 }
960
961 fn get_int_register(&self, idx: usize) -> ET::Int {
962 match self.int_register.get(idx) {
963 Some(i) => *i,
964 _ => ET::Int::default(),
965 }
966 }
967 fn set_int_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Int, globally: bool) {
968 self.change_field(globally, |s, g| {
969 if s.int_register.len() <= idx {
970 s.int_register.resize(idx + 1, ET::Int::default());
971 }
972 let old = std::mem::replace(&mut s.int_register[idx], v);
973 if s.tracing_assigns() {
974 aux.outputs.write_neg1(format_args!(
975 "{{{}changing {}count{}={}}}",
976 if g { "globally " } else { "" },
977 ET::Char::display_opt(s.escape_char),
978 idx,
979 old
980 ));
981 aux.outputs.write_neg1(format_args!(
982 "{{into {}count{}={}}}",
983 ET::Char::display_opt(s.escape_char),
984 idx,
985 v
986 ))
987 }
988 StateChange::IntRegister { idx, old }
989 });
990 }
991
992 fn get_dim_register(&self, idx: usize) -> ET::Dim {
993 match self.dim_register.get(idx) {
994 Some(i) => *i,
995 _ => ET::Dim::default(),
996 }
997 }
998 fn set_dim_register(&mut self, aux: &EngineAux<ET>, idx: usize, v: ET::Dim, globally: bool) {
999 self.change_field(globally, |s, g| {
1000 if s.dim_register.len() <= idx {
1001 s.dim_register.resize(idx + 1, ET::Dim::default());
1002 }
1003 let old = std::mem::replace(&mut s.dim_register[idx], v);
1004 if s.tracing_assigns() {
1005 aux.outputs.write_neg1(format_args!(
1006 "{{{}changing {}dimen{}={}}}",
1007 if g { "globally " } else { "" },
1008 ET::Char::display_opt(s.escape_char),
1009 idx,
1010 old
1011 ));
1012 aux.outputs.write_neg1(format_args!(
1013 "{{into {}dimen{}={}}}",
1014 ET::Char::display_opt(s.escape_char),
1015 idx,
1016 v
1017 ));
1018 }
1019 StateChange::DimRegister { idx, old }
1020 });
1021 }
1022
1023 fn get_skip_register(&self, idx: usize) -> Skip<ET::Dim> {
1024 match self.skip_register.get(idx) {
1025 Some(i) => *i,
1026 _ => Skip::default(),
1027 }
1028 }
1029 fn set_skip_register(
1030 &mut self,
1031 aux: &EngineAux<ET>,
1032 idx: usize,
1033 v: Skip<ET::Dim>,
1034 globally: bool,
1035 ) {
1036 self.change_field(globally, |s, g| {
1037 if s.skip_register.len() <= idx {
1038 s.skip_register.resize(idx + 1, Skip::default());
1039 }
1040 let old = std::mem::replace(&mut s.skip_register[idx], v);
1041 if s.tracing_assigns() {
1042 aux.outputs.write_neg1(format_args!(
1043 "{{{}changing {}skip{}={}}}",
1044 if g { "globally " } else { "" },
1045 ET::Char::display_opt(s.escape_char),
1046 idx,
1047 old
1048 ));
1049 aux.outputs.write_neg1(format_args!(
1050 "{{into {}skip{}={}}}",
1051 ET::Char::display_opt(s.escape_char),
1052 idx,
1053 v
1054 ))
1055 }
1056 StateChange::SkipRegister { idx, old }
1057 });
1058 }
1059
1060 fn get_muskip_register(&self, idx: usize) -> MuSkip<ET::MuDim> {
1061 match self.muskip_register.get(idx) {
1062 Some(i) => *i,
1063 _ => MuSkip::default(),
1064 }
1065 }
1066 fn set_muskip_register(
1067 &mut self,
1068 aux: &EngineAux<ET>,
1069 idx: usize,
1070 v: MuSkip<ET::MuDim>,
1071 globally: bool,
1072 ) {
1073 self.change_field(globally, |s, g| {
1074 if s.muskip_register.len() <= idx {
1075 s.muskip_register.resize(idx + 1, MuSkip::default());
1076 }
1077 let old = std::mem::replace(&mut s.muskip_register[idx], v);
1078 if s.tracing_assigns() {
1079 aux.outputs.write_neg1(format_args!(
1080 "{{{}changing {}muskip{}={}}}",
1081 if g { "globally " } else { "" },
1082 ET::Char::display_opt(s.escape_char),
1083 idx,
1084 old
1085 ));
1086 aux.outputs.write_neg1(format_args!(
1087 "{{into {}muskip{}={}}}",
1088 ET::Char::display_opt(s.escape_char),
1089 idx,
1090 v
1091 ))
1092 }
1093 StateChange::MuSkipRegister { idx, old }
1094 });
1095 }
1096
1097 fn get_box_register(&self, idx: usize) -> Option<&TeXBox<ET>> {
1098 match self.box_register.get(idx) {
1099 None => None,
1100 Some(i) => i.as_ref(),
1101 }
1102 }
1103 fn get_box_register_mut(&mut self, idx: usize) -> Option<&mut TeXBox<ET>> {
1104 match self.box_register.get_mut(idx) {
1105 None => None,
1106 Some(i) => i.as_mut(),
1107 }
1108 }
1109 fn take_box_register(&mut self, idx: usize) -> Option<TeXBox<ET>> {
1110 match self.box_register.get_mut(idx) {
1111 None => None,
1112 Some(i) => std::mem::take(i),
1113 }
1114 }
1115 fn set_box_register(
1116 &mut self,
1117 aux: &EngineAux<ET>,
1118 idx: usize,
1119 v: Option<TeXBox<ET>>,
1120 globally: bool,
1121 ) {
1122 self.change_field(globally, |s, _| {
1123 if s.box_register.len() <= idx {
1124 s.box_register.resize(idx + 1, None);
1125 }
1126 let old = std::mem::replace(&mut s.box_register[idx], v);
1127 if s.tracing_assigns() {
1128 aux.outputs
1129 .write_neg1(format_args!("{{TODO: trace box register change {}}}", idx));
1130 }
1131 StateChange::BoxRegister { idx, old }
1132 });
1133 }
1134
1135 fn get_toks_register(&self, idx: usize) -> &TokenList<ET::Token> {
1136 match self.toks_register.get(idx) {
1137 Some(i) => i,
1138 _ => &self.empty_list,
1139 }
1140 }
1141 fn set_toks_register(
1142 &mut self,
1143 aux: &EngineAux<ET>,
1144 idx: usize,
1145 v: TokenList<ET::Token>,
1146 globally: bool,
1147 ) {
1148 self.change_field(globally, |s, g| {
1149 if s.toks_register.len() <= idx {
1150 s.toks_register.resize(idx + 1, s.empty_list.clone());
1151 }
1152 let trace = s.tracing_assigns();
1153 if trace {
1154 aux.outputs.write_neg1(format_args!(
1155 "{{{}changing {}toks{}={}}}",
1156 if g { "globally " } else { "" },
1157 ET::Char::display_opt(s.escape_char),
1158 idx,
1159 s.toks_register[idx].display(
1160 aux.memory.cs_interner(),
1161 &s.catcodes,
1162 s.escape_char,
1163 false
1164 )
1165 ));
1166 }
1167 let old = std::mem::replace(&mut s.toks_register[idx], v);
1168 if s.tracing_assigns() {
1169 aux.outputs.write_neg1(format_args!(
1170 "{{into {}toks{}={}}}",
1171 ET::Char::display_opt(s.escape_char),
1172 idx,
1173 s.toks_register[idx].display(
1174 aux.memory.cs_interner(),
1175 &s.catcodes,
1176 s.escape_char,
1177 false
1178 )
1179 ))
1180 }
1181 StateChange::ToksRegister { idx, old }
1182 });
1183 }
1184
1185 fn get_primitive_tokens(&self, name: PrimitiveIdentifier) -> &TokenList<ET::Token> {
1186 match self.primitive_toks.get(&name) {
1187 Some(i) => i,
1188 _ => &self.empty_list,
1189 }
1190 }
1191 fn set_primitive_tokens(
1192 &mut self,
1193 aux: &EngineAux<ET>,
1194 name: PrimitiveIdentifier,
1195 v: TokenList<ET::Token>,
1196 globally: bool,
1197 ) {
1198 self.change_field(globally, |s, g| {
1199 let old = s
1200 .primitive_toks
1201 .insert(name, v)
1202 .unwrap_or(s.empty_list.clone());
1203 if s.tracing_assigns() {
1204 aux.outputs.write_neg1(format_args!(
1205 "{{{}changing {}={}}}",
1206 if g { "globally " } else { "" },
1207 name.display(s.escape_char),
1208 old.display(aux.memory.cs_interner(), &s.catcodes, s.escape_char, false)
1209 ));
1210 aux.outputs.write_neg1(format_args!(
1211 "{{into {}={}}}",
1212 name.display(s.escape_char),
1213 s.primitive_toks.get(&name).unwrap().display(
1214 aux.memory.cs_interner(),
1215 &s.catcodes,
1216 s.escape_char,
1217 false
1218 )
1219 ))
1220 }
1221 StateChange::PrimitiveToks { name, old }
1222 });
1223 }
1224
1225 fn get_primitive_dim(&self, name: PrimitiveIdentifier) -> ET::Dim {
1226 match self.primitive_dims.get(&name) {
1227 Some(i) => *i,
1228 _ => ET::Dim::default(),
1229 }
1230 }
1231 fn set_primitive_dim(
1232 &mut self,
1233 aux: &EngineAux<ET>,
1234 name: PrimitiveIdentifier,
1235 v: ET::Dim,
1236 globally: bool,
1237 ) {
1238 self.change_field(globally, |s, g| {
1239 let old = s.primitive_dims.insert(name, v).unwrap_or_default();
1240 if s.tracing_assigns() {
1241 aux.outputs.write_neg1(format_args!(
1242 "{{{}changing {}={}}}",
1243 if g { "globally " } else { "" },
1244 name.display(s.escape_char),
1245 old
1246 ));
1247 aux.outputs.write_neg1(format_args!(
1248 "{{into {}={}}}",
1249 name.display(s.escape_char),
1250 v
1251 ))
1252 }
1253 StateChange::PrimitiveDim { name, old }
1254 });
1255 }
1256
1257 fn get_primitive_skip(&self, name: PrimitiveIdentifier) -> Skip<ET::Dim> {
1258 match self.primitive_skips.get(&name) {
1259 Some(i) => *i,
1260 _ => Skip::default(),
1261 }
1262 }
1263 fn set_primitive_skip(
1264 &mut self,
1265 aux: &EngineAux<ET>,
1266 name: PrimitiveIdentifier,
1267 v: Skip<ET::Dim>,
1268 globally: bool,
1269 ) {
1270 self.change_field(globally, |s, g| {
1271 let old = s.primitive_skips.insert(name, v).unwrap_or_default();
1272 if s.tracing_assigns() {
1273 aux.outputs.write_neg1(format_args!(
1274 "{{{}changing {}={}}}",
1275 if g { "globally " } else { "" },
1276 name.display(s.escape_char),
1277 old
1278 ));
1279 aux.outputs.write_neg1(format_args!(
1280 "{{into {}={}}}",
1281 name.display(s.escape_char),
1282 v
1283 ))
1284 }
1285 StateChange::PrimitiveSkip { name, old }
1286 });
1287 }
1288
1289 fn get_primitive_muskip(&self, name: PrimitiveIdentifier) -> MuSkip<ET::MuDim> {
1290 match self.primitive_muskips.get(&name) {
1291 Some(i) => *i,
1292 _ => MuSkip::default(),
1293 }
1294 }
1295 fn set_primitive_muskip(
1296 &mut self,
1297 aux: &EngineAux<ET>,
1298 name: PrimitiveIdentifier,
1299 v: MuSkip<ET::MuDim>,
1300 globally: bool,
1301 ) {
1302 self.change_field(globally, |s, g| {
1303 let old = s.primitive_muskips.insert(name, v).unwrap_or_default();
1304 if s.tracing_assigns() {
1305 aux.outputs.write_neg1(format_args!(
1306 "{{{}changing {}={}}}",
1307 if g { "globally " } else { "" },
1308 name.display(s.escape_char),
1309 old
1310 ));
1311 aux.outputs.write_neg1(format_args!(
1312 "{{into {}={}}}",
1313 name.display(s.escape_char),
1314 v
1315 ))
1316 }
1317 StateChange::PrimitiveMuSkip { name, old }
1318 });
1319 }
1320
1321 fn get_command(&self, name: &ET::CSName) -> Option<&TeXCommand<ET>> {
1322 self.commands.get(name)
1323 }
1324 fn set_command(
1325 &mut self,
1326 aux: &EngineAux<ET>,
1327 name: ET::CSName,
1328 cmd: Option<TeXCommand<ET>>,
1329 globally: bool,
1330 ) {
1331 self.change_field(globally, |s, g| {
1332 let old = match cmd {
1342 None => {
1343 let o = s.commands.remove(&name);
1344 if s.tracing_assigns() {
1345 match o {
1346 None => aux.outputs.write_neg1(format_args!(
1347 "{{{}changing {}{}={}undefined}}",
1348 if g { "globally " } else { "" },
1349 ET::Char::display_opt(s.escape_char),
1350 aux.memory.cs_interner().resolve(&name),
1351 ET::Char::display_opt(s.escape_char)
1352 )),
1353 Some(ref c) => aux.outputs.write_neg1(format_args!(
1354 "{{{}changing {}{}={}}}",
1355 if g { "globally " } else { "" },
1356 ET::Char::display_opt(s.escape_char),
1357 aux.memory.cs_interner().resolve(&name),
1358 c.meaning(aux.memory.cs_interner(), &s.catcodes, s.escape_char)
1359 )),
1360 }
1361 aux.outputs.write_neg1(format_args!(
1362 "{{into {}{}={}undefined}}",
1363 ET::Char::display_opt(s.escape_char),
1364 aux.memory.cs_interner().resolve(&name),
1365 ET::Char::display_opt(s.escape_char)
1366 ));
1367 }
1368 o
1369 }
1370 Some(cmd) => {
1371 if s.tracing_assigns() {
1372 match s.commands.get(&name) {
1373 None => aux.outputs.write_neg1(format_args!(
1374 "{{{}changing {}{}={}undefined}}",
1375 if g { "globally " } else { "" },
1376 ET::Char::display_opt(s.escape_char),
1377 aux.memory.cs_interner().resolve(&name),
1378 ET::Char::display_opt(s.escape_char)
1379 )),
1380 Some(c) => aux.outputs.write_neg1(format_args!(
1381 "{{{}changing {}{}={}}}",
1382 if g { "globally " } else { "" },
1383 ET::Char::display_opt(s.escape_char),
1384 aux.memory.cs_interner().resolve(&name),
1385 c.meaning(aux.memory.cs_interner(), &s.catcodes, s.escape_char)
1386 )),
1387 }
1388 aux.outputs.write_neg1(format_args!(
1389 "{{into {}{}={}}}",
1390 ET::Char::display_opt(s.escape_char),
1391 aux.memory.cs_interner().resolve(&name),
1392 cmd.meaning(aux.memory.cs_interner(), &s.catcodes, s.escape_char)
1393 ));
1394 }
1395 s.commands.insert(name.clone(), cmd)
1396 }
1397 };
1398 StateChange::Command { name, old }
1399 });
1400 }
1401
1402 fn get_ac_command(&self, c: ET::Char) -> Option<&TeXCommand<ET>> {
1403 self.ac_commands.get(c).as_ref()
1404 }
1405 fn set_ac_command(
1406 &mut self,
1407 _aux: &EngineAux<ET>,
1408 c: ET::Char,
1409 cmd: Option<TeXCommand<ET>>,
1410 globally: bool,
1411 ) {
1412 self.change_field(globally, |s, _| {
1413 let old = std::mem::replace(s.ac_commands.get_mut(c), cmd);
1414 StateChange::AcCommand { char: c, old }
1415 });
1416 }
1417}