1use crate::Engine;
10use crate::input::{Level, LineSource, TokKind};
11use crate::print::Selector;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
15pub enum Interaction {
16 Batch = 0,
18 NonStop = 1,
20 Scroll = 2,
22 ErrorStop = 3,
24}
25
26impl Interaction {
27 pub fn from_i32(n: i32) -> Interaction {
29 match n {
30 0 => Interaction::Batch,
31 1 => Interaction::NonStop,
32 2 => Interaction::Scroll,
33 _ => Interaction::ErrorStop,
34 }
35 }
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
40pub enum History {
41 Spotless,
43 WarningIssued,
45 ErrorMessageIssued,
47 FatalErrorStop,
49}
50
51#[derive(Debug, Clone, Copy)]
53pub struct JumpOut;
54
55#[derive(Debug)]
57pub struct ErrorState {
58 pub interaction: Interaction,
60 pub history: History,
62 pub error_count: i32,
64 pub help: Vec<&'static str>,
66 pub help_owned: Vec<String>,
68 pub use_err_help: bool,
70 pub deletions_allowed: bool,
72 pub set_box_allowed: bool,
74 pub no_new_control_sequence: bool,
76 pub old_setting: Option<Selector>,
78 pub file_line_error: bool,
80 pub halt_on_error: bool,
82 pub interrupt: bool,
84}
85
86impl Default for ErrorState {
87 fn default() -> Self {
88 ErrorState {
89 interaction: Interaction::ErrorStop,
90 history: History::FatalErrorStop,
91 error_count: 0,
92 help: Vec::new(),
93 help_owned: Vec::new(),
94 use_err_help: false,
95 deletions_allowed: true,
96 set_box_allowed: true,
97 no_new_control_sequence: true,
98 old_setting: None,
99 file_line_error: false,
100 halt_on_error: false,
101 interrupt: false,
102 }
103 }
104}
105
106impl Engine {
107 pub fn print_err(&mut self, msg: &str) {
109 self.printer.begin_line();
110 if !(self.err.file_line_error && self.print_file_line()) {
111 self.printer.print_str("! ");
112 }
113 if self.diagnostics.is_some() {
114 self.printer.capture = Some(Vec::new());
115 }
116 self.printer.print_str(msg);
117 }
118
119 pub fn diag_capture_start(&mut self) {
121 if self.diagnostics.is_some() {
122 self.printer.capture = Some(Vec::new());
123 }
124 }
125
126 pub fn source_location(&self) -> Option<(String, u32, String, usize)> {
129 let elc = self
130 .eqtb
131 .int(crate::primitives_gen::assign_int::ENDLINECHAR);
132 self.input.stack.iter().rev().find_map(|l| match l {
133 crate::input::Level::Text(t) if t.is_input_file => {
134 let mut chars: &[u32] = &t.buf;
135 if let Some((&last, rest)) = chars.split_last()
136 && i64::from(last) == i64::from(elc)
137 {
138 chars = rest;
139 }
140 let text: String = chars.iter().filter_map(|&c| char::from_u32(c)).collect();
141 Some((
142 String::from_utf8_lossy(&t.name).into_owned(),
143 t.line.max(0) as u32,
144 text,
145 t.loc.min(chars.len()),
146 ))
147 }
148 _ => None,
149 })
150 }
151
152 pub fn diag_capture_warning(&mut self, line: Option<u32>) {
155 let Some(text) = self.printer.capture.take() else {
156 return;
157 };
158 let message = String::from_utf8_lossy(&text).trim().replace('\n', " ");
159 let mut d = crate::diag::Diagnostic::new(crate::diag::Severity::Warning, message);
160 if let Some((file, cur, _, _)) = self.source_location() {
161 d.file = Some(file);
162 d.line = Some(line.unwrap_or(cur));
163 }
164 if let Some(list) = self.diagnostics.as_mut() {
165 list.push(d);
166 }
167 }
168
169 fn record_error(&mut self) {
171 let Some(text) = self.printer.capture.take() else {
172 return;
173 };
174 let mut message = String::from_utf8_lossy(&text).trim().replace('\n', " ");
175 if message == "Undefined control sequence" && self.cur_cs != crate::hash::NO_CS {
176 let name = self.print_to_string(|e| e.print_cs(e.cur_cs));
177 message = format!(
178 "Undefined control sequence {}",
179 String::from_utf8_lossy(&name).trim_end()
180 );
181 }
182 let mut d = crate::diag::Diagnostic::new(crate::diag::Severity::Error, message);
183 d.facts = self.error_facts(&d.message);
184 if let Some((file, line, source, column)) = self.source_location() {
185 d.file = Some(file);
186 d.line = Some(line);
187 d.source = Some(source);
188 d.column = Some(column);
189 }
190 d.help = if self.err.help_owned.is_empty() {
191 self.err.help.iter().map(|s| (*s).to_owned()).collect()
192 } else {
193 self.err.help_owned.clone()
194 };
195 if let Some(list) = self.diagnostics.as_mut() {
196 list.push(d);
197 }
198 }
199
200 fn error_facts(&mut self, message: &str) -> Vec<(String, String)> {
203 let mut facts = Vec::new();
204 if message.starts_with("Undefined control sequence") && self.cur_cs != crate::hash::NO_CS {
205 let name = self.hash.name(self.cur_cs).to_vec();
206 let similar = self.similar_defined_names(&name);
207 if !similar.is_empty() {
208 facts.push(("similar".to_owned(), similar.join(" ")));
209 }
210 }
211 if message.starts_with("Missing $ inserted") {
212 let tok = if self.cur_cmd == crate::cmd::Cmd::ParEnd {
213 "\\par".to_owned()
215 } else if self.cur_cs != crate::hash::NO_CS {
216 let cs = self.cur_cs;
217 String::from_utf8_lossy(&self.print_to_string(|e| e.print_cs(cs)))
218 .trim_end()
219 .to_owned()
220 } else {
221 char::from_u32(self.cur_chr)
222 .map(String::from)
223 .unwrap_or_default()
224 };
225 facts.push(("token".to_owned(), tok));
226 if self.mode.abs_kind() == crate::maincontrol::ModeKind::Math {
227 let line = self.cur_list().mode_line.unsigned_abs();
228 facts.push(("math_line".to_owned(), line.to_string()));
229 }
230 }
231 facts
232 }
233
234 fn similar_defined_names(&self, name: &[u8]) -> Vec<String> {
238 let internal = |n: &[u8]| n.iter().any(|&b| matches!(b, b'@' | b'_' | b':'));
239 let max = if name.len() < 4 { 1 } else { 2 };
240 let mut found: Vec<(usize, Vec<u8>)> = self
241 .hash
242 .iter_named()
243 .filter(|(n, _)| {
244 n.len() > 1
245 && n.iter()
246 .all(|b| !b.is_ascii_whitespace() && !b.is_ascii_control())
247 })
248 .filter(|(n, _)| !internal(n) || internal(name))
249 .filter(|(_, id)| self.eqtb.meaning(*id).cmd != crate::cmd::Cmd::UndefinedCs)
250 .filter_map(|(n, _)| {
251 crate::diag::edit_distance_within(name, n, max).map(|d| (d, n.to_vec()))
252 })
253 .filter(|(d, _)| *d > 0)
254 .collect();
255 found.sort();
256 found.dedup();
257 found
258 .into_iter()
259 .take(3)
260 .map(|(_, n)| format!("\\{}", String::from_utf8_lossy(&n)))
261 .collect()
262 }
263
264 pub fn print_err_bytes(&mut self, msg: &[u8]) {
266 self.print_err("");
267 self.printer.print_bytes(msg);
268 }
269
270 fn print_file_line(&mut self) -> bool {
273 let found = self.input.stack.iter().rev().find_map(|l| match l {
274 Level::Text(t) if t.is_input_file => Some((t.name.clone(), t.line)),
275 _ => None,
276 });
277 match found {
278 Some((name, line)) => {
279 self.printer.print_bytes(&name);
280 self.printer.print_char(':' as u32);
281 self.printer.print_int(line.into());
282 self.printer.print_str(": ");
283 true
284 }
285 None => false,
286 }
287 }
288
289 pub fn help(&mut self, lines: &[&'static str]) {
291 self.err.help.clear();
292 self.err.help.extend_from_slice(lines);
293 self.err.help_owned.clear();
294 }
295
296 pub fn help_owned(&mut self, lines: Vec<String>) {
298 self.err.help.clear();
299 self.err.help_owned = lines;
300 }
301
302 pub fn error(&mut self) {
305 self.record_error();
306 if self.err.history < History::ErrorMessageIssued {
307 self.err.history = History::ErrorMessageIssued;
308 }
309 self.printer.print_char('.' as u32);
310 self.show_context();
311 if self.err.halt_on_error {
312 self.halt_on_error_stop();
313 }
314 if self.err.interaction == Interaction::ErrorStop {
315 self.terminal_error_stop();
318 }
319 self.err.error_count += 1;
320 if self.err.error_count == 100 {
321 self.printer
322 .print_nl("(That makes 100 errors; please try again.)");
323 self.err.history = History::FatalErrorStop;
324 self.jump_out();
325 }
326 let sel = self.printer.selector;
328 if self.err.interaction > Interaction::Batch {
329 self.printer.selector = match sel {
330 Selector::TermAndLog => Selector::LogOnly,
331 Selector::TermOnly => Selector::NoPrint,
332 s => s,
333 };
334 }
335 if self.err.use_err_help {
336 self.printer.print_ln();
337 self.give_err_help();
338 } else {
339 let lines: Vec<String> = if self.err.help_owned.is_empty() {
340 self.err.help.iter().map(|s| s.to_string()).collect()
341 } else {
342 self.err.help_owned.clone()
343 };
344 let empty = lines.is_empty();
345 for l in lines {
346 self.printer.print_nl(&l);
347 }
348 if empty {
351 self.printer.begin_line();
352 } else {
353 self.printer.print_ln();
354 }
355 }
356 if self.err.use_err_help {
357 self.printer.print_ln();
358 }
359 self.err.help.clear();
360 self.err.help_owned.clear();
361 if self.err.interaction > Interaction::Batch {
362 self.printer.selector = sel;
363 }
364 self.printer.print_ln();
365 }
366
367 pub fn term_input(&mut self) -> Vec<u32> {
370 self.prompted = true;
371 if self.printer.term_quiet {
372 let prompt = std::mem::take(&mut self.printer.quiet_line);
373 self.show_before_prompt(&String::from_utf8_lossy(&prompt));
374 }
375 self.printer.update_terminal();
376 let mut line = String::new();
377 match std::io::stdin().read_line(&mut line) {
378 Ok(0) | Err(_) => self.fatal_error("End of file on the terminal!"),
379 Ok(_) => {}
380 }
381 let chars = crate::input::decode_line(line.trim_end_matches(['\n', '\r']).as_bytes());
382 self.printer.term_offset = 0;
383 let old = self.printer.selector;
384 self.printer.selector = match old {
385 Selector::TermAndLog => Selector::LogOnly,
386 Selector::TermOnly => Selector::NoPrint,
387 s => s,
388 };
389 for &c in &chars {
390 self.printer.print_char(c);
391 }
392 self.printer.print_ln();
393 self.printer.selector = old;
394 chars
395 }
396
397 pub fn prompt_input(&mut self, prompt: &str) -> Vec<u32> {
399 self.printer.print_str(prompt);
400 self.term_input()
401 }
402
403 fn show_before_prompt(&mut self, prompt: &str) -> bool {
406 let Some(mut hook) = self.prompt_hook.take() else {
407 return true;
408 };
409 let list = self.diagnostics.as_deref().unwrap_or(&[]);
410 let from = self.diagnostics_shown.min(list.len());
411 let shown = hook(&list[from..], prompt);
412 self.diagnostics_shown = list.len();
413 self.prompt_hook = Some(hook);
414 shown
415 }
416
417 fn terminal_error_stop(&mut self) {
421 let quiet = self.printer.term_quiet;
422 if quiet && self.err.interaction == Interaction::ErrorStop {
423 if !self.show_before_prompt("") {
424 return;
425 }
426 self.printer.term_quiet = false;
427 self.printer.quiet_line.clear();
428 }
429 self.terminal_error_dialog();
430 self.printer.term_quiet = quiet;
431 }
432
433 fn terminal_error_dialog(&mut self) {
434 loop {
435 if self.err.interaction != Interaction::ErrorStop {
436 return;
437 }
438 self.clear_for_error_prompt();
439 let line = self.prompt_input("? ");
440 let Some(&c0) = line.first() else {
441 return;
442 };
443 let c = char::from_u32(c0).unwrap_or(' ').to_ascii_uppercase();
444 match c {
445 '0'..='9' if self.err.deletions_allowed => {
446 let mut n = c as u32 - '0' as u32;
447 if let Some(&d) = line.get(1)
448 && (0x30..=0x39).contains(&d)
449 {
450 n = n * 10 + (d - 0x30);
451 }
452 let (s1, s2, s3, s4) = (
453 self.cur_tok,
454 self.cur_cmd,
455 self.cur_chr,
456 self.input.align_state,
457 );
458 self.input.align_state = 1_000_000;
459 for _ in 0..n {
460 self.get_token();
461 }
462 self.cur_tok = s1;
463 self.cur_cmd = s2;
464 self.cur_chr = s3;
465 self.input.align_state = s4;
466 self.help(&[
467 "I have just deleted some text, as you asked.",
468 "You can now delete more, or insert, or whatever.",
469 ]);
470 self.show_context();
471 continue;
472 }
473 'E' => {
474 let file = self.input.stack.iter().rev().find_map(|l| match l {
475 Level::Text(t) if t.is_input_file => Some((t.name.clone(), t.line)),
476 _ => None,
477 });
478 if let Some((name, line)) = file {
479 self.printer.print_nl("You want to edit file ");
480 self.printer.print_bytes(&name);
481 self.printer.print_str(" at line ");
482 self.printer.print_int(line.into());
483 self.edit_request = Some((name, line));
484 self.err.interaction = Interaction::Scroll;
485 self.jump_out();
486 }
487 }
488 'H' => {
489 if self.err.use_err_help {
490 self.give_err_help();
491 self.err.use_err_help = false;
492 } else {
493 let lines: Vec<String> = if !self.err.help_owned.is_empty() {
494 self.err.help_owned.clone()
495 } else if !self.err.help.is_empty() {
496 self.err.help.iter().map(|s| s.to_string()).collect()
497 } else {
498 vec![
499 "Sorry, I don't know how to help in this situation.".into(),
500 "Maybe you should try asking a human?".into(),
501 ]
502 };
503 for l in lines {
504 self.printer.print_str(&l);
505 self.printer.print_ln();
506 }
507 }
508 self.help(&[
509 "Sorry, I already gave what help I could...",
510 "Maybe you should try asking a human?",
511 "An error might have occurred before I noticed any problems.",
512 "``If all else fails, read the instructions.''",
513 ]);
514 continue;
515 }
516 'I' => {
517 let text: Vec<u32> = if line.len() > 1 {
519 let mut v = vec![' ' as u32];
520 v.extend_from_slice(&line[1..]);
521 v
522 } else {
523 self.prompt_input("insert>")
524 };
525 self.begin_file_reading(crate::input::LineSource::Terminal, Vec::new(), false);
526 if let Some(Level::Text(t)) = self.input.stack.last_mut() {
527 t.buf = text;
528 t.loc = 0;
529 t.state = crate::input::LexState::MidLine;
530 }
531 return;
532 }
533 'Q' | 'R' | 'S' => {
534 self.err.error_count = 0;
535 self.printer.print_str("OK, entering ");
536 match c {
537 'Q' => {
538 self.err.interaction = Interaction::Batch;
539 self.print_esc_str("batchmode");
540 self.printer.selector = match self.printer.selector {
541 Selector::TermAndLog => Selector::LogOnly,
542 _ => Selector::NoPrint,
543 };
544 }
545 'R' => {
546 self.err.interaction = Interaction::NonStop;
547 self.print_esc_str("nonstopmode");
548 }
549 _ => {
550 self.err.interaction = Interaction::Scroll;
551 self.print_esc_str("scrollmode");
552 }
553 }
554 self.printer.print_str("...");
555 self.printer.print_ln();
556 self.printer.update_terminal();
557 return;
558 }
559 'X' => {
560 self.err.interaction = Interaction::Scroll;
561 self.jump_out();
562 }
563 _ => {}
564 }
565 self.printer
567 .print_str("Type <return> to proceed, S to scroll future error messages,");
568 self.printer
569 .print_nl("R to run without stopping, Q to run quietly,");
570 self.printer.print_nl("I to insert something, ");
571 if self
572 .input
573 .stack
574 .iter()
575 .any(|l| matches!(l, Level::Text(t) if t.is_input_file))
576 {
577 self.printer.print_str("E to edit your file,");
578 }
579 if self.err.deletions_allowed {
580 self.printer
581 .print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
582 }
583 self.printer.print_nl("H for help, X to quit.");
584 }
585 }
586
587 fn clear_for_error_prompt(&mut self) {
590 while self.input.stack.len() > 1 {
591 match self.input.stack.last() {
592 Some(Level::Text(t))
593 if matches!(t.source, LineSource::Terminal) && t.loc >= t.buf.len() =>
594 {
595 self.end_file_reading();
596 }
597 _ => break,
598 }
599 }
600 self.printer.print_ln();
601 }
602
603 pub fn give_err_help(&mut self) {
605 let list = self
606 .eqtb
607 .toks_param(crate::primitives_gen::assign_toks::ERRHELP)
608 .clone();
609 self.token_show_list(&list, 0, list.len(), 10_000_000);
610 }
611
612 pub fn fatal_error(&mut self, s: &str) -> ! {
614 self.normalize_selector();
615 self.print_err("Emergency stop");
616 self.help_owned(vec![s.to_owned()]);
617 self.succumb();
618 }
619
620 pub fn luatex_fatal(&mut self, category: &str, msg: &str) -> ! {
624 self.normalize_selector();
625 self.printer.print_nl("! error: (");
626 self.printer.print_str(category);
627 self.printer.print_str("): ");
628 self.printer.print_str(msg);
629 let pdf = self.eqtb.int(crate::primitives_gen::assign_int::OUTPUTMODE) > 0;
630 self.printer.print_nl(if pdf {
631 "! ==> Fatal error occurred, bad output PDF file produced!"
632 } else {
633 "! ==> Fatal error occurred, bad output DVI file produced!"
634 });
635 self.printer.print_ln();
636 self.err.history = History::FatalErrorStop;
637 for n in 0..128u32 {
638 if self.printer.write_open(n) {
639 self.printer.close_write(n);
640 }
641 }
642 if self.total_pages == 0 {
643 self.printer.print_str("No pages of output.");
644 self.printer.print_ln();
645 }
646 if self.printer.log_opened() {
647 self.printer.close_log();
648 }
649 self.printer.selector = Selector::TermOnly;
650 self.printer.flush();
651 std::panic::resume_unwind(Box::new(JumpOut));
652 }
653
654 fn halt_on_error_stop(&mut self) -> ! {
658 let pdf = self.eqtb.int(crate::primitives_gen::assign_int::OUTPUTMODE) > 0;
659 self.printer.print_nl(if pdf {
660 "! ==> Fatal error occurred, bad output PDF file produced!"
661 } else {
662 "! ==> Fatal error occurred, bad output DVI file produced!"
663 });
664 self.printer.print_ln();
665 self.err.history = History::FatalErrorStop;
666 for n in 0..128u32 {
667 if self.printer.write_open(n) {
668 self.printer.close_write(n);
669 }
670 }
671 if self.total_pages == 0 {
672 self.printer.print_str("No pages of output.");
673 self.printer.print_ln();
674 }
675 if self.printer.log_opened() {
677 let sel = self.printer.selector;
678 self.printer.selector = Selector::LogOnly;
679 self.printer.print_ln();
680 self.printer.selector = sel;
681 }
682 self.finish_log();
683 std::panic::resume_unwind(Box::new(JumpOut));
684 }
685
686 pub fn overflow(&mut self, s: &str, n: i64) -> ! {
688 self.normalize_selector();
689 self.print_err("TeX capacity exceeded, sorry [");
690 self.printer.print_str(s);
691 self.printer.print_char('=' as u32);
692 self.printer.print_int(n);
693 self.printer.print_char(']' as u32);
694 self.help(&[
695 "If you really absolutely need more capacity,",
696 "you can ask a wizard to enlarge me.",
697 ]);
698 self.succumb();
699 }
700
701 pub fn confusion(&mut self, s: &str) -> ! {
703 self.normalize_selector();
704 if self.err.history < History::ErrorMessageIssued {
705 self.print_err("This can't happen (");
706 self.printer.print_str(s);
707 self.printer.print_char(')' as u32);
708 self.help(&["I'm broken. Please show this to someone who can fix"]);
709 } else {
710 self.print_err("I can't go on meeting you like this");
711 self.help(&[
712 "One of your faux pas seems to have wounded me deeply...",
713 "in fact, I'm barely conscious. Please fix it and try again.",
714 ]);
715 }
716 self.succumb();
717 }
718
719 fn succumb(&mut self) -> ! {
720 if self.err.interaction == Interaction::ErrorStop {
721 self.err.interaction = Interaction::Scroll;
722 }
723 if self.printer.log_opened() {
724 self.error();
725 }
726 self.err.history = History::FatalErrorStop;
727 self.jump_out();
728 }
729
730 pub fn jump_out(&mut self) -> ! {
732 if self.prompt_hook.is_some() {
733 self.printer.term_quiet = true;
735 }
736 self.close_files_and_terminate();
737 std::panic::resume_unwind(Box::new(JumpOut));
738 }
739
740 pub fn normalize_selector(&mut self) {
742 self.printer.selector = if self.printer.log_opened() {
743 Selector::TermAndLog
744 } else {
745 Selector::TermOnly
746 };
747 if self.job_name.is_none() {
748 self.open_log_file();
749 }
750 if self.err.interaction == Interaction::Batch {
751 self.printer.selector = match self.printer.selector {
752 Selector::TermAndLog => Selector::LogOnly,
753 _ => Selector::NoPrint,
754 };
755 }
756 }
757
758 pub fn check_interrupt(&mut self) {}
760
761 pub fn back_error(&mut self) {
763 self.err.interrupt = false;
764 self.back_input();
765 self.err.interrupt = true;
766 self.error();
767 }
768
769 pub fn ins_error(&mut self) {
771 self.err.interrupt = false;
772 self.back_input_inserted();
773 self.err.interrupt = true;
774 self.error();
775 }
776
777 pub fn luatex_warning(&mut self, category: &str, msg: &str) {
781 self.printer.luatex_warning(category, msg);
782 if self.err.history == History::Spotless {
783 self.err.history = History::WarningIssued;
784 }
785 }
786
787 pub fn begin_diagnostic(&mut self) {
790 self.err.old_setting = Some(self.printer.selector);
791 let online = self
792 .eqtb
793 .int(crate::primitives_gen::assign_int::TRACINGONLINE);
794 if online <= 0 && self.printer.selector == Selector::TermAndLog {
795 self.printer.selector = Selector::LogOnly;
796 if self.err.history == History::Spotless {
797 self.err.history = History::WarningIssued;
798 }
799 }
800 }
801
802 pub fn end_diagnostic(&mut self, blank_line: bool) {
804 self.printer.print_nl("");
805 if blank_line {
806 self.printer.print_ln();
807 }
808 if let Some(s) = self.err.old_setting.take() {
809 self.printer.selector = s;
810 }
811 }
812
813 pub fn show_context(&mut self) {
815 let error_context_lines = self
816 .eqtb
817 .int(crate::primitives_gen::assign_int::ERRORCONTEXTLINES);
818 let input_ptr = self.input.stack.len().saturating_sub(1);
819 let mut base_ptr = input_ptr;
820 let mut nn: i32 = -1;
821 loop {
822 let (is_text, bottom) = match &self.input.stack.get(base_ptr) {
823 Some(Level::Text(t)) => (
824 true,
825 t.is_input_file || matches!(t.source, LineSource::File { .. }) || base_ptr == 0,
826 ),
827 Some(Level::Tokens(_)) => (false, false),
828 None => (true, true),
829 };
830 let bottom_line = is_text && bottom;
831 if base_ptr == input_ptr || bottom_line || nn < error_context_lines {
832 let show = match self.input.stack.get(base_ptr) {
835 Some(Level::Tokens(t)) => {
836 base_ptr == input_ptr
837 || !matches!(t.kind, TokKind::BackedUp | TokKind::BackedUpChar)
838 || t.loc < t.list.len()
839 }
840 _ => true,
841 };
842 if show {
843 self.printer.term_just_wrapped = false;
844 self.display_context(base_ptr);
845 nn += 1;
846 if self.printer.term_just_wrapped
850 && !(bottom_line || base_ptr == 0)
851 && matches!(
852 self.printer.selector,
853 crate::print::Selector::TermAndLog | crate::print::Selector::TermOnly
854 )
855 {
856 self.printer.term_raw(b"\n");
857 }
858 }
859 } else if nn == error_context_lines {
860 self.printer.print_nl("...");
861 nn += 1;
862 }
863 if bottom_line || base_ptr == 0 {
864 break;
865 }
866 base_ptr -= 1;
867 }
868 }
869
870 fn display_context(&mut self, base_ptr: usize) {
871 let old_setting = self.printer.selector;
872 self.printer.tally = 0;
873 let level = self.input.stack[base_ptr].clone();
874 let l: usize;
875 match &level {
876 Level::Text(t) => {
877 match &t.source {
878 LineSource::Terminal => {
879 if base_ptr == 0 {
880 self.printer.print_nl("<*>");
881 } else {
882 self.printer.print_nl("<insert> ");
883 }
884 }
885 LineSource::Read(n) => {
886 self.printer.print_nl("<read ");
887 if *n == 16 {
888 self.printer.print_char('*' as u32);
889 } else {
890 self.printer.print_int(i64::from(*n));
891 }
892 self.printer.print_char('>' as u32);
893 }
894 _ => {
895 self.printer.print_nl("l.");
896 self.printer.print_int(t.line.into());
897 }
898 }
899 self.printer.print_char(' ' as u32);
900 l = self.begin_pseudoprint();
902 let end_line_char = self
903 .eqtb
904 .int(crate::primitives_gen::assign_int::ENDLINECHAR);
905 let mut j = t.buf.len();
906 if j > 0 && t.buf[j - 1] as i32 == end_line_char {
907 j -= 1;
908 }
909 for i in 0..j {
910 if i == t.loc {
911 self.set_trick_count();
912 }
913 let c = t.buf[i];
914 if crate::utf8x::is_raw(c) {
915 let n = match c & 0xFF {
919 0xC2..=0xDF => 2,
920 0xE0..=0xEF => 3,
921 0xF0..=0xF4 => 4,
922 _ => 0,
923 };
924 let mut bytes = Vec::new();
925 for &e in t.buf.get(i..j).unwrap_or_default() {
926 if bytes.len() >= n {
927 break;
928 }
929 crate::utf8x::push_entry(&mut bytes, e);
930 }
931 bytes.truncate(n);
932 self.printer.pseudo_unit(&bytes);
933 } else {
934 self.printer.print_char(c);
935 }
936 }
937 }
938 Level::Tokens(t) => {
939 match t.kind {
940 TokKind::Parameter => self.printer.print_nl("<argument> "),
941 TokKind::UTemplate | TokKind::VTemplate => self.printer.print_nl("<template> "),
942 TokKind::BackedUp | TokKind::BackedUpChar => {
943 if t.loc >= t.list.len() {
944 self.printer.print_nl("<recently read> ")
945 } else {
946 self.printer.print_nl("<to be read again> ")
947 }
948 }
949 TokKind::Inserted => self.printer.print_nl("<inserted text> "),
950 TokKind::Macro => {
951 self.printer.print_ln();
952 self.print_cs(t.name);
953 }
954 TokKind::OutputText => self.printer.print_nl("<output> "),
955 TokKind::EveryParText => self.printer.print_nl("<everypar> "),
956 TokKind::EveryMathText => self.printer.print_nl("<everymath> "),
957 TokKind::EveryDisplayText => self.printer.print_nl("<everydisplay> "),
958 TokKind::EveryHboxText => self.printer.print_nl("<everyhbox> "),
959 TokKind::EveryVboxText => self.printer.print_nl("<everyvbox> "),
960 TokKind::EveryJobText => self.printer.print_nl("<everyjob> "),
961 TokKind::EveryCrText => self.printer.print_nl("<everycr> "),
962 TokKind::MarkText => self.printer.print_nl("<mark> "),
963 TokKind::EveryEofText => self.printer.print_nl("<everyeof> "),
964 TokKind::WriteText => self.printer.print_nl("<write> "),
965 TokKind::LocalText => self.printer.print_nl("<local> "),
966 }
967 l = self.begin_pseudoprint();
968 self.show_token_list_ctx(&t.list, t.loc, 100_000);
971 }
972 }
973 self.printer.selector = old_setting;
974 self.print_two_lines(l);
975 }
976
977 fn begin_pseudoprint(&mut self) -> usize {
978 let l = self.printer.tally;
979 self.printer.tally = 0;
980 self.printer.selector = Selector::Pseudo;
981 self.printer.trick_count = 1_000_000;
982 self.printer.trick_buf.clear();
983 self.printer
984 .trick_buf
985 .resize(self.printer.error_line, vec![b' ']);
986 l
987 }
988
989 pub fn set_trick_count(&mut self) {
991 let p = &mut self.printer;
992 p.first_count = p.tally;
993 p.trick_count = p.tally + 1 + p.error_line - p.half_error_line;
994 if p.trick_count < p.error_line {
995 p.trick_count = p.error_line;
996 }
997 }
998
999 fn print_two_lines(&mut self, l: usize) {
1000 if self.printer.trick_count == 1_000_000 {
1001 self.set_trick_count();
1002 }
1003 let p = &self.printer;
1004 let (tally, first_count, trick_count, half, error_line) = (
1005 p.tally,
1006 p.first_count,
1007 p.trick_count,
1008 p.half_error_line,
1009 p.error_line,
1010 );
1011 let m = if tally < trick_count {
1012 tally - first_count
1013 } else {
1014 trick_count - first_count
1015 };
1016 let (start, n) = if l + first_count <= half {
1017 (0, l + first_count)
1018 } else {
1019 self.printer.print_plain("...");
1020 (l + first_count - half + 3, half)
1021 };
1022 let buf = self.printer.trick_buf.clone();
1023 let mut bytes = Vec::new();
1024 for q in start..first_count {
1025 bytes.extend_from_slice(&buf[q % error_line]);
1026 }
1027 self.print_raw_bytes(&bytes);
1028 self.printer.print_ln();
1029 for _ in 0..n {
1030 self.printer.print_char(' ' as u32);
1031 }
1032 let p_end = if m + n <= error_line {
1033 first_count + m
1034 } else {
1035 first_count + (error_line - n - 3)
1036 };
1037 bytes.clear();
1038 for q in first_count..p_end {
1039 bytes.extend_from_slice(&buf[q % error_line]);
1040 }
1041 self.print_raw_bytes(&bytes);
1042 if m + n > error_line {
1043 self.printer.print_plain("...");
1044 }
1045 }
1046
1047 fn print_raw_bytes(&mut self, bytes: &[u8]) {
1050 let mut i = 0;
1051 while i < bytes.len() {
1052 let (c, n) = crate::utf8x::decode(bytes, i);
1053 if n == 1 && c >= 0x80 {
1054 self.printer.print_raw_byte(c as u8);
1055 } else {
1056 self.printer.print_char(c);
1057 }
1058 i += n;
1059 }
1060 }
1061}