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