1pub mod ascii;
42pub mod capture;
43pub mod event;
44pub mod iter;
45pub mod signature;
46
47use smallvec::SmallVec;
48
49use ascii::AsciiControl;
50use event::{CSI, DCS, Esc, EscInvalid, SS2, SS3, VTEvent, VTIntermediate};
51
52const ESC: u8 = AsciiControl::Esc as _;
53const BEL: u8 = AsciiControl::Bel as _;
54const DEL: u8 = AsciiControl::Del as _;
55const CAN: u8 = AsciiControl::Can as _;
56const SUB: u8 = AsciiControl::Sub as _;
57const CSI: u8 = b'[';
58const OSC: u8 = b']';
59const SS2: u8 = b'N';
60const SS3: u8 = b'O';
61const DCS: u8 = b'P';
62const APC: u8 = b'_';
63const PM: u8 = b'^';
64const SOS: u8 = b'X';
65const ST_FINAL: u8 = b'\\';
66
67use crate::event::{Param, ParamBuf, Params};
68
69#[allow(private_bounds)]
71pub trait VTEventCallback: VTEventCallbackMaybeAbortable<()> {
72 fn event(&mut self, event: VTEvent<'_>) -> ();
73}
74
75impl<T: FnMut(VTEvent<'_>)> VTEventCallback for T {
76 #[inline(always)]
77 fn event(&mut self, event: VTEvent<'_>) {
78 self(event)
79 }
80}
81
82impl<T: VTEventCallback> VTEventCallbackMaybeAbortable<()> for T {
83 #[inline(always)]
84 fn event(&mut self, event: VTEvent<'_>) {
85 VTEventCallback::event(self, event)
86 }
87}
88
89#[allow(private_bounds)]
92pub trait VTEventCallbackAbortable: VTEventCallbackMaybeAbortable<bool> {
93 fn event(&mut self, event: VTEvent<'_>) -> bool;
94}
95
96impl<T: VTEventCallbackAbortable> VTEventCallbackMaybeAbortable<bool> for T {
97 #[inline(always)]
98 fn event(&mut self, event: VTEvent<'_>) -> bool {
99 VTEventCallbackAbortable::event(self, event)
100 }
101}
102
103impl<T: FnMut(VTEvent<'_>) -> bool> VTEventCallbackAbortable for T {
104 #[inline(always)]
105 fn event(&mut self, event: VTEvent<'_>) -> bool {
106 self(event)
107 }
108}
109
110trait VTEventCallbackMaybeAbortable<R: MaybeAbortable> {
111 fn event<'e>(&mut self, event: VTEvent<'e>) -> R;
112}
113
114enum VTAction<'a> {
116 None,
119 Event(VTEvent<'a>),
122 End(VTEnd),
124 Buffer(VTEmit),
127 Hold(VTEmit),
130 Cancel(VTEmit),
132}
133
134#[derive(Debug, Copy, Clone, PartialEq, Eq)]
135enum VTEmit {
136 Ground,
138 Dcs,
140 Osc,
142}
143
144#[derive(Debug, Copy, Clone, PartialEq, Eq)]
145enum VTEnd {
146 Dcs,
148 Osc { used_bel: bool },
150}
151
152macro_rules! def_pattern {
153 ($name:ident => $pattern:pat) => {
154 #[inline]
155 #[allow(unused)]
156 const fn $name(b: u8) -> bool {
157 matches!(b, $pattern)
158 }
159
160 #[allow(unused)]
161 macro_rules! $name {
162 () => {
163 $pattern
164 };
165 }
166 };
167}
168
169def_pattern!(is_c0 => 0x00..=0x08 | 0x0b..=0x0c | 0x0e..=0x1f);
170def_pattern!(is_any_c0 => 0x00..=0x1f);
171def_pattern!(is_printable => b' '..=b'~');
172def_pattern!(is_intermediate => b' '..=b'/');
173def_pattern!(is_final => 0x40..=0x7e);
174def_pattern!(is_priv => b'<' | b'=' | b'>' | b'?');
175def_pattern!(is_priv_no_q => b'<' | b'=' | b'>');
176def_pattern!(is_digit => b'0'..=b'9');
177
178macro_rules! byte_predicate {
179 (|$p:ident| $body:block) => {{
180 let mut out: [bool; 256] = [false; 256];
181 let mut i = 0;
182 while i < 256 {
183 let $p: u8 = i as u8;
184 out[i] = $body;
185 i += 1;
186 }
187 out
188 }};
189}
190
191const ENDS_CSI: [bool; 256] =
192 byte_predicate!(|b| { is_final(b) || b == ESC || b == CAN || b == SUB });
193
194const ENDS_GROUND: [bool; 256] = byte_predicate!(|b| { is_c0(b) || b == DEL });
195
196#[derive(Debug, Copy, Clone, PartialEq, Eq)]
197enum State {
198 Ground,
199 Escape,
200 EscInt,
201 EscSs2,
202 EscSs3,
203 CsiEntry,
204 CsiParam,
205 CsiInt,
206 CsiIgnore,
207 DcsEntry,
208 DcsParam,
209 DcsInt,
210 DcsIgnore,
211 DcsIgnoreEsc,
212 DcsPassthrough,
213 DcsEsc,
214 OscString,
215 OscEsc,
216 SosPmApcString,
217 SpaEsc,
218}
219
220pub const VT_PARSER_INTEREST_NONE: u8 = 0;
222pub const VT_PARSER_INTEREST_CSI: u8 = 1 << 0;
224pub const VT_PARSER_INTEREST_DCS: u8 = 1 << 1;
226pub const VT_PARSER_INTEREST_OSC: u8 = 1 << 2;
228pub const VT_PARSER_INTEREST_ESCAPE_RECOVERY: u8 = 1 << 4;
230pub const VT_PARSER_INTEREST_OTHER: u8 = 1 << 5;
232
233pub const VT_PARSER_INTEREST_ALL: u8 = VT_PARSER_INTEREST_CSI
235 | VT_PARSER_INTEREST_DCS
236 | VT_PARSER_INTEREST_OSC
237 | VT_PARSER_INTEREST_ESCAPE_RECOVERY
238 | VT_PARSER_INTEREST_OTHER;
239
240pub const VT_PARSER_INTEREST_DEFAULT: u8 = VT_PARSER_INTEREST_CSI
242 | VT_PARSER_INTEREST_DCS
243 | VT_PARSER_INTEREST_OSC
244 | VT_PARSER_INTEREST_OTHER;
245
246#[must_use]
247trait MaybeAbortable {
248 fn abort(self) -> bool;
249}
250
251impl MaybeAbortable for bool {
252 #[inline(always)]
253 fn abort(self) -> bool {
254 !self
255 }
256}
257
258impl MaybeAbortable for () {
259 #[inline(always)]
260 fn abort(self) -> bool {
261 false
262 }
263}
264
265pub struct VTPushParser<const INTEREST: u8 = VT_PARSER_INTEREST_DEFAULT> {
270 st: State,
271
272 ints: VTIntermediate,
274 params: Params,
275 cur_param: Param,
276 priv_prefix: Option<u8>,
277 held_byte: Option<u8>,
278}
279
280impl Default for VTPushParser {
281 fn default() -> Self {
282 Self::new()
283 }
284}
285
286impl VTPushParser {
287 pub const fn new() -> Self {
288 VTPushParser::new_with()
289 }
290
291 pub fn decode_buffer<'a>(input: &'a [u8], mut cb: impl for<'b> FnMut(VTEvent<'b>)) {
293 let mut parser = VTPushParser::new();
294 parser.feed_with(input, &mut cb);
295 }
296
297 pub const fn new_with_interest<const INTEREST: u8>() -> VTPushParser<INTEREST> {
298 VTPushParser::new_with()
299 }
300}
301
302macro_rules! invalid {
304 ($self:ident .priv_prefix, $self_:ident .ints, $b:expr) => {
305 if let Some(p) = $self.priv_prefix {
306 if $self.ints.len() == 0 {
307 VTEvent::EscInvalid(EscInvalid::Two(p, $b))
308 } else if $self.ints.len() == 1 {
309 VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $b))
310 } else {
311 VTEvent::EscInvalid(EscInvalid::Four(
312 p,
313 $self.ints.data[0],
314 $self.ints.data[1],
315 $b,
316 ))
317 }
318 } else {
319 if $self.ints.len() == 0 {
320 VTEvent::EscInvalid(EscInvalid::One($b))
321 } else if $self.ints.len() == 1 {
322 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $b))
323 } else {
324 VTEvent::EscInvalid(EscInvalid::Three(
325 $self.ints.data[0],
326 $self.ints.data[1],
327 $b,
328 ))
329 }
330 }
331 };
332 ($self:ident .priv_prefix, $self_:ident .ints) => {
333 if let Some(p) = $self.priv_prefix {
334 if $self.ints.len() == 0 {
335 VTEvent::EscInvalid(EscInvalid::One(p))
336 } else if $self.ints.len() == 1 {
337 VTEvent::EscInvalid(EscInvalid::Two(p, $self.ints.data[0]))
338 } else {
339 VTEvent::EscInvalid(EscInvalid::Three(p, $self.ints.data[0], $self.ints.data[1]))
340 }
341 } else {
342 if $self.ints.len() == 0 {
343 VTEvent::C0(0x1b)
345 } else if $self.ints.len() == 1 {
346 VTEvent::EscInvalid(EscInvalid::One($self.ints.data[0]))
347 } else {
348 VTEvent::EscInvalid(EscInvalid::Two($self.ints.data[0], $self.ints.data[1]))
349 }
350 }
351 };
352 ($a:expr) => {
353 VTEvent::EscInvalid(EscInvalid::One($a))
354 };
355 ($a:expr, $b:expr) => {
356 VTEvent::EscInvalid(EscInvalid::Two($a, $b))
357 };
358}
359
360impl<const INTEREST: u8> VTPushParser<INTEREST> {
361 const fn new_with() -> Self {
362 Self {
363 st: State::Ground,
364 ints: VTIntermediate::empty(),
365 params: SmallVec::new_const(),
366 cur_param: SmallVec::new_const(),
367 priv_prefix: None,
368 held_byte: None,
369 }
370 }
371
372 #[inline]
416 pub fn feed_with<F: VTEventCallback>(&mut self, input: &[u8], mut cb: F) {
417 self.feed_with_internal(input, &mut cb);
418 }
419
420 #[inline]
433 pub fn feed_with_abortable<F: VTEventCallbackAbortable>(
434 &mut self,
435 input: &[u8],
436 mut cb: F,
437 ) -> usize {
438 self.feed_with_internal(input, &mut cb)
439 }
440
441 #[inline(always)]
442 fn feed_with_internal<R: MaybeAbortable, F: VTEventCallbackMaybeAbortable<R>>(
443 &mut self,
444 input: &[u8],
445 cb: &mut F,
446 ) -> usize {
447 if input.is_empty() {
448 return 0;
449 }
450
451 #[derive(Debug)]
452 struct FeedState {
453 buffer_idx: usize,
454 current_emit: Option<VTEmit>,
455 hold: bool,
456 }
457
458 let mut state = FeedState {
459 buffer_idx: 0,
460 current_emit: None,
461 hold: self.held_byte.is_some(),
462 };
463
464 let mut held_byte = self.held_byte.take();
465 let mut i = 0;
466
467 while i < input.len() {
468 if self.st == State::Ground {
470 let start = i;
471 loop {
472 if i >= input.len() {
473 cb.event(VTEvent::Raw(&input[start..]));
474 return input.len();
475 }
476 if ENDS_GROUND[input[i] as usize] {
477 break;
478 }
479 i += 1;
480 }
481
482 if start != i && cb.event(VTEvent::Raw(&input[start..i])).abort() {
483 return i;
484 }
485
486 if input[i] == ESC {
487 self.clear_hdr_collectors();
488 self.st = State::Escape;
489 i += 1;
490 continue;
491 }
492 }
493
494 if self.st == State::CsiIgnore {
496 loop {
497 if i >= input.len() {
498 return input.len();
499 }
500 if ENDS_CSI[input[i] as usize] {
501 break;
502 }
503 i += 1;
504 }
505
506 if input[i] == ESC {
507 self.st = State::Escape;
508 } else {
509 self.st = State::Ground;
510 }
511 i += 1;
512 continue;
513 }
514
515 let action = self.push_with(input[i]);
516
517 match action {
518 VTAction::None => {
519 if let Some(emit) = state.current_emit {
520 let range = state.buffer_idx..(i - state.hold as usize);
522 if !range.is_empty()
523 && match emit {
524 VTEmit::Ground => cb.event(VTEvent::Raw(&input[range])),
525 VTEmit::Dcs => cb.event(VTEvent::DcsData(&input[range])),
526 VTEmit::Osc => cb.event(VTEvent::OscData(&input[range])),
527 }
528 .abort()
529 {
530 if state.hold {
531 self.held_byte = Some(0x1b);
532 }
533 return i + 1;
534 }
535 if state.hold {
536 held_byte = Some(0x1b);
537 }
538 state.current_emit = None;
539 }
540 }
541 VTAction::Event(e) => {
542 if cb.event(e).abort() {
543 return i + 1;
544 }
545 }
546 VTAction::End(VTEnd::Dcs) => {
547 state.current_emit = None;
548 let hold = std::mem::take(&mut state.hold);
549 let range = state.buffer_idx..(i.saturating_sub(hold as usize));
550 if cb.event(VTEvent::DcsEnd(&input[range])).abort() {
551 return i + 1;
552 }
553 held_byte = None;
554 }
555 VTAction::End(VTEnd::Osc { used_bel }) => {
556 state.current_emit = None;
557 let hold = std::mem::take(&mut state.hold);
558 let range = state.buffer_idx..(i.saturating_sub(hold as usize));
559 if cb
560 .event(VTEvent::OscEnd {
561 data: &input[range],
562 used_bel,
563 })
564 .abort()
565 {
566 return i + 1;
567 }
568 held_byte = None;
569 }
570 VTAction::Buffer(emit) | VTAction::Hold(emit) => {
571 if state.current_emit.is_none()
572 && let Some(h) = held_byte.take()
573 && match emit {
574 VTEmit::Ground => cb.event(VTEvent::Raw(&[h])),
575 VTEmit::Dcs => cb.event(VTEvent::DcsData(&[h])),
576 VTEmit::Osc => cb.event(VTEvent::OscData(&[h])),
577 }
578 .abort()
579 {
580 if matches!(action, VTAction::Hold(_)) {
581 self.held_byte = Some(0x1b);
582 return 1;
583 }
584 return 0;
585 }
586
587 debug_assert!(state.current_emit.is_none() || state.current_emit == Some(emit));
588
589 state.hold = matches!(action, VTAction::Hold(_));
590 if state.current_emit.is_none() {
591 state.buffer_idx = i;
592 state.current_emit = Some(emit);
593 }
594 }
595 VTAction::Cancel(emit) => {
596 state.current_emit = None;
597 state.hold = false;
598 if match emit {
599 VTEmit::Ground => unreachable!(),
600 VTEmit::Dcs => cb.event(VTEvent::DcsCancel),
601 VTEmit::Osc => cb.event(VTEvent::OscCancel),
602 }
603 .abort()
604 {
605 return i + 1;
606 }
607 }
608 };
609 i += 1;
610 }
611
612 if state.hold {
614 self.held_byte = Some(0x1b);
615 }
616
617 if let Some(emit) = state.current_emit.take() {
618 let range = &input[state.buffer_idx..input.len() - state.hold as usize];
619 if !range.is_empty() {
620 match emit {
621 VTEmit::Ground => cb.event(VTEvent::Raw(range)),
622 VTEmit::Dcs => cb.event(VTEvent::DcsData(range)),
623 VTEmit::Osc => cb.event(VTEvent::OscData(range)),
624 };
625 }
626 };
627
628 input.len()
630 }
631
632 pub fn is_ground(&self) -> bool {
634 self.st == State::Ground
635 }
636
637 pub fn idle(&mut self) -> Option<VTEvent<'static>> {
641 match self.st {
642 State::Escape => {
643 self.st = State::Ground;
644 Some(VTEvent::C0(ESC))
645 }
646 State::EscInt => {
647 self.st = State::Ground;
648 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
649 None
650 } else {
651 Some(invalid!(self.priv_prefix, self.ints))
652 }
653 }
654 State::EscSs2 | State::EscSs3 => {
655 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
656 self.st = State::Ground;
657 None
658 } else {
659 let c = match self.st {
660 State::EscSs2 => SS2,
661 State::EscSs3 => SS3,
662 _ => unreachable!(),
663 };
664 self.st = State::Ground;
665 Some(invalid!(c))
666 }
667 }
668 _ => None,
669 }
670 }
671
672 pub fn finish<F: FnMut(VTEvent)>(&mut self, mut cb: F) {
685 let held = self.held_byte.take();
686
687 if let Some(b) = held {
689 let buf = [b];
690 let event = match self.st {
691 State::OscString | State::OscEsc => VTEvent::OscData(&buf),
692 State::DcsPassthrough | State::DcsEsc => VTEvent::DcsData(&buf),
693 _ => VTEvent::Raw(&buf),
694 };
695 cb(event);
696 }
697
698 let event = match self.st {
700 State::OscString | State::OscEsc => Some(VTEvent::OscCancel),
701 State::DcsPassthrough | State::DcsEsc => Some(VTEvent::DcsCancel),
702 State::Escape => Some(VTEvent::C0(ESC)),
703 State::EscInt => {
704 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
705 None
706 } else {
707 Some(invalid!(self.priv_prefix, self.ints))
708 }
709 }
710 State::EscSs2 | State::EscSs3 => {
711 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
712 None
713 } else {
714 let c = match self.st {
715 State::EscSs2 => SS2,
716 State::EscSs3 => SS3,
717 _ => unreachable!(),
718 };
719 Some(invalid!(c))
720 }
721 }
722 _ => None,
723 };
724
725 self.reset_collectors();
726 self.st = State::Ground;
727
728 if let Some(event) = event {
729 cb(event);
730 }
731 }
732
733 fn clear_hdr_collectors(&mut self) {
738 self.ints.clear();
739 self.params.clear();
740 self.cur_param.clear();
741 self.priv_prefix = None;
742 }
743
744 fn reset_collectors(&mut self) {
745 self.clear_hdr_collectors();
746 }
747
748 fn next_param(&mut self) {
749 self.params.push(std::mem::take(&mut self.cur_param));
750 }
751
752 fn finish_params_if_any(&mut self) {
753 if !self.cur_param.is_empty() || !self.params.is_empty() {
754 self.next_param();
755 }
756 }
757
758 fn emit_csi(&mut self, final_byte: u8) -> VTAction<'_> {
759 self.finish_params_if_any();
760
761 let privp = self.priv_prefix.take();
762 VTAction::Event(VTEvent::Csi(CSI {
763 private: privp,
764 params: ParamBuf {
765 params: &self.params,
766 },
767 intermediates: self.ints,
768 final_byte,
769 }))
770 }
771
772 fn dcs_start(&mut self, final_byte: u8) -> VTAction<'_> {
773 self.finish_params_if_any();
774
775 let privp = self.priv_prefix.take();
776 VTAction::Event(VTEvent::DcsStart(DCS {
777 private: privp,
778 params: ParamBuf {
779 params: &self.params,
780 },
781 intermediates: self.ints,
782 final_byte,
783 }))
784 }
785
786 fn push_with(&mut self, b: u8) -> VTAction<'_> {
791 use State::*;
792
793 macro_rules! state_machine {
794 (def $c:ident $(
795 $state:ident => { $( $p:pat
796 $(if $( $if_ident:ident $( ($call_ident:ident) )? $( == $literal:literal)? $(||)? )+)?
797 => $block:expr $(,)? )* }
798 )*) => {
799 #[allow(unused, clippy::redundant_pattern)]
800 match self.st {
801 $(
802 $state => {
803 match b {
804 $(
805 $c @ state_machine!(pattern $p $(if $( ($if_ident ($($call_ident)?) ($($literal)?)) )+ )?) => $block,
806 )*
807 }
808 }
809 )*
810 }
811 };
812 (pattern $pat:pat if $( ($if_ident:ident ($($call_ident:ident)?) ($($literal:literal)?)) )+ ) => {
813 $(
814 state_machine!(pattern_if ($if_ident) ($($call_ident)?) ($($literal)?) )
815 )|+
816 };
817 (pattern $pat:pat) => {
818 $pat
819 };
820 (pattern_if ($any:ident) ($call_ident:ident) ()) => {
821 $any!()
822 };
823 (pattern_if ($if_ident:ident) () ($literal:literal)) => {
824 $literal
825 };
826 }
827
828 state_machine! {
835 def c
836 Ground => {
837 ESC => {
838 self.clear_hdr_collectors();
839 self.st = State::Escape;
840 VTAction::None
841 }
842 DEL => VTAction::Event(VTEvent::C0(DEL)),
843 c if is_c0(c) => VTAction::Event(VTEvent::C0(c)),
844 _ => VTAction::Buffer(VTEmit::Ground),
845 }
846 Escape => {
847 CAN | SUB => {
848 self.st = Ground;
849 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
850 VTAction::None
851 } else {
852 VTAction::Event(invalid!(b))
853 }
854 }
855 DEL => {
858 self.st = Ground;
859 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
860 VTAction::None
861 } else {
862 VTAction::Event(invalid!(b))
863 }
864 }
865 c if is_intermediate(c) => {
866 if self.ints.push(c) {
867 self.st = EscInt;
868 } else {
869 self.st = Ground;
870 }
871 VTAction::None
872 }
873 b'?' => {
875 self.priv_prefix = Some(b);
876 self.st = EscInt;
877 VTAction::None
878 }
879 c if is_priv_no_q(c) => {
881 self.st = Ground;
882 VTAction::Event(VTEvent::Esc(Esc {
883 intermediates: VTIntermediate::empty(),
884 private: None,
885 final_byte: b,
886 }))
887 }
888 CSI => {
889 if INTEREST & VT_PARSER_INTEREST_CSI == 0 {
890 self.st = CsiIgnore;
891 } else {
892 self.st = CsiEntry;
893 }
894 VTAction::None
895 }
896 DCS => {
897 if INTEREST & VT_PARSER_INTEREST_DCS == 0 {
898 self.st = DcsIgnore;
899 } else {
900 self.st = DcsEntry;
901 }
902 VTAction::None
903 }
904 OSC => {
905 self.st = OscString;
906 VTAction::Event(VTEvent::OscStart)
907 }
908 SS2 => {
909 self.st = EscSs2;
910 VTAction::None
911 }
912 SS3 => {
913 self.st = EscSs3;
914 VTAction::None
915 }
916 SOS | PM | APC => {
917 self.st = State::SosPmApcString;
918 VTAction::None
919 }
920 c if is_final(c) || is_digit(c) => {
921 self.st = Ground;
922 VTAction::Event(VTEvent::Esc(Esc {
923 intermediates: self.ints,
924 private: self.priv_prefix.take(),
925 final_byte: c,
926 }))
927 }
928 ESC => {
929 VTAction::Event(VTEvent::C0(ESC))
931 }
932 _ => {
933 self.st = Ground;
934 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
935 VTAction::None
936 } else {
937 VTAction::Event(invalid!(b))
938 }
939 }
940 }
941 EscInt => {
942 CAN | SUB => {
943 self.st = Ground;
944 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
945 VTAction::None
946 } else {
947 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
948 }
949 }
950 DEL => {
953 self.st = Ground;
954 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
955 VTAction::None
956 } else {
957 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
958 }
959 }
960 c if is_intermediate(c) => {
961 if !self.ints.push(c) {
962 self.st = Ground;
963 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
964 VTAction::None
965 } else {
966 VTAction::Event(invalid!(self.priv_prefix, self.ints, b))
967 }
968 } else {
969 VTAction::None
970 }
971 }
972 c if is_final(c) || is_digit(c) => {
973 self.st = Ground;
974 VTAction::Event(VTEvent::Esc(Esc {
975 intermediates: self.ints,
976 private: self.priv_prefix.take(),
977 final_byte: c,
978 }))
979 }
980 ESC => {
983 self.st = Escape;
984 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
985 VTAction::None
986 } else {
987 VTAction::Event(invalid!(self.priv_prefix, self.ints))
988 }
989 }
990 _ => {
991 self.st = Ground;
992 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
993 VTAction::None
994 } else {
995 VTAction::Event(invalid!(self.priv_prefix, self.ints, c))
996 }
997 }
998 }
999 EscSs2 => {
1000 CAN | SUB => {
1001 self.st = Ground;
1002 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1003 VTAction::None
1004 } else {
1005 VTAction::Event(invalid!(SS2, b))
1006 }
1007 }
1008 ESC => {
1011 self.st = Escape;
1012 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1013 VTAction::None
1014 } else {
1015 VTAction::Event(invalid!(SS2))
1016 }
1017 }
1018 _ => {
1019 self.st = Ground;
1020 VTAction::Event(VTEvent::Ss2(SS2 { char: c }))
1021 }
1022 }
1023 EscSs3 => {
1024 CAN | SUB => {
1025 self.st = Ground;
1026 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1027 VTAction::None
1028 } else {
1029 VTAction::Event(invalid!(SS3, b))
1030 }
1031 }
1032 ESC => {
1035 self.st = Escape;
1036 if INTEREST & VT_PARSER_INTEREST_ESCAPE_RECOVERY == 0 {
1037 VTAction::None
1038 } else {
1039 VTAction::Event(invalid!(SS3))
1040 }
1041 }
1042 _ => {
1043 self.st = Ground;
1044 VTAction::Event(VTEvent::Ss3(SS3 { char: c }))
1045 }
1046 }
1047 CsiEntry => {
1048 CAN | SUB => {
1049 self.st = Ground;
1050 VTAction::None
1051 }
1052 DEL => VTAction::None,
1053 ESC => {
1054 self.st = Escape;
1055 VTAction::None
1056 }
1057 c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1059 c if is_priv(c) => {
1060 self.priv_prefix = Some(c);
1061 self.st = CsiParam;
1062 VTAction::None
1063 }
1064 c if is_digit(c) => {
1065 self.cur_param.push(c);
1066 self.st = CsiParam;
1067 VTAction::None
1068 }
1069 b';' => {
1070 self.next_param();
1071 self.st = CsiParam;
1072 VTAction::None
1073 }
1074 b':' => {
1075 self.cur_param.push(b':');
1076 self.st = CsiParam;
1077 VTAction::None
1078 }
1079 c if is_intermediate(c) => {
1080 if self.ints.push(c) {
1081 self.st = CsiInt;
1082 } else {
1083 self.st = Ground;
1084 }
1085 VTAction::None
1086 }
1087 c if is_final(c) => {
1088 self.st = Ground;
1089 self.emit_csi(c)
1090 }
1091 _ => {
1092 self.st = CsiIgnore;
1093 VTAction::None
1094 }
1095 }
1096 CsiParam => {
1097 CAN | SUB => {
1098 self.st = Ground;
1099 VTAction::None
1100 }
1101 DEL => VTAction::None,
1102 ESC => {
1103 self.st = Escape;
1104 VTAction::None
1105 }
1106 c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1108 c if is_digit(c) => {
1109 self.cur_param.push(c);
1110 VTAction::None
1111 }
1112 b';' => {
1113 self.next_param();
1114 VTAction::None
1115 }
1116 b':' => {
1117 self.cur_param.push(b':');
1118 VTAction::None
1119 }
1120 c if is_intermediate(c) => {
1121 if self.ints.push(c) {
1122 self.st = CsiInt;
1123 } else {
1124 self.st = Ground;
1125 }
1126 VTAction::None
1127 }
1128 c if is_final(c) => {
1129 self.st = Ground;
1130 self.emit_csi(c)
1131 }
1132 _ => {
1133 self.st = CsiIgnore;
1134 VTAction::None
1135 }
1136 }
1137 CsiInt => {
1138 CAN | SUB => {
1139 self.st = Ground;
1140 VTAction::None
1141 }
1142 DEL => VTAction::None,
1143 ESC => {
1144 self.st = Escape;
1145 VTAction::None
1146 }
1147 c if is_any_c0(c) => VTAction::Event(VTEvent::C0(c)),
1149 c if is_intermediate(c) => {
1150 if self.ints.push(c) {
1151 self.st = CsiInt;
1152 } else {
1153 self.st = Ground;
1154 }
1155 VTAction::None
1156 }
1157 c if is_final(c) => {
1158 self.st = Ground;
1159 self.emit_csi(c)
1160 }
1161 _ => {
1162 self.st = CsiIgnore;
1163 VTAction::None
1164 }
1165 }
1166 CsiIgnore => {
1167 CAN | SUB => {
1168 self.st = Ground;
1169 VTAction::None
1170 }
1171 DEL => VTAction::None,
1172 ESC => {
1173 self.st = Escape;
1174 VTAction::None
1175 }
1176 c if is_final(c) => {
1177 self.st = Ground;
1178 VTAction::None
1179 }
1180 _ => VTAction::None,
1181 }
1182 DcsEntry => {
1183 CAN | SUB => {
1184 self.st = Ground;
1185 VTAction::None
1186 }
1187 DEL => VTAction::None,
1188 ESC => {
1189 self.st = Escape;
1190 VTAction::None
1191 }
1192 c if is_priv(c) => {
1193 self.priv_prefix = Some(c);
1194 self.st = DcsParam;
1195 VTAction::None
1196 }
1197 c if is_digit(c) => {
1198 self.cur_param.push(c);
1199 self.st = DcsParam;
1200 VTAction::None
1201 }
1202 b';' => {
1203 self.next_param();
1204 self.st = DcsParam;
1205 VTAction::None
1206 }
1207 b':' => {
1208 self.st = DcsIgnore;
1209 VTAction::None
1210 }
1211 c if is_intermediate(c) => {
1212 if self.ints.push(c) {
1213 self.st = DcsInt;
1214 } else {
1215 self.st = Ground;
1216 }
1217 VTAction::None
1218 }
1219 c if is_final(c) => {
1220 self.st = DcsPassthrough;
1221 self.dcs_start(c)
1222 }
1223 _ => {
1224 self.st = DcsIgnore;
1225 VTAction::None
1226 }
1227 }
1228 DcsParam => {
1229 CAN | SUB => {
1230 self.st = Ground;
1231 VTAction::None
1232 }
1233 DEL => VTAction::None,
1234 ESC => {
1235 self.st = Escape;
1236 VTAction::None
1237 }
1238 c if is_digit(c) => {
1239 self.cur_param.push(c);
1240 VTAction::None
1241 }
1242 b';' => {
1243 self.next_param();
1244 VTAction::None
1245 }
1246 b':' => {
1247 self.st = DcsIgnore;
1248 VTAction::None
1249 }
1250 c if is_intermediate(c) => {
1251 if self.ints.push(c) {
1252 self.st = DcsInt;
1253 } else {
1254 self.st = Ground;
1255 }
1256 self.st = DcsInt;
1257 VTAction::None
1258 }
1259 c if is_final(c) => {
1260 self.st = DcsPassthrough;
1261 self.dcs_start(c)
1262 }
1263 _ => {
1264 self.st = DcsIgnore;
1265 VTAction::None
1266 }
1267 }
1268 DcsInt => {
1269 CAN | SUB => {
1270 self.st = Ground;
1271 VTAction::None
1272 }
1273 DEL => VTAction::None,
1274 ESC => {
1275 self.st = Escape;
1276 VTAction::None
1277 }
1278 c if is_intermediate(c) => {
1279 if self.ints.push(c) {
1280 self.st = DcsInt;
1281 } else {
1282 self.st = Ground;
1283 }
1284 VTAction::None
1285 }
1286 c if is_final(c) || is_digit(c) || c == b':' || c == b';' => {
1287 self.st = DcsPassthrough;
1288 self.dcs_start(c)
1289 }
1290 _ => {
1291 self.st = DcsIgnore;
1292 VTAction::None
1293 }
1294 }
1295 DcsIgnore => {
1296 CAN | SUB => {
1297 self.st = Ground;
1298 VTAction::None
1299 }
1300 DEL => VTAction::None,
1301 ESC => {
1302 self.st = DcsIgnoreEsc;
1303 VTAction::None
1304 }
1305 _ => VTAction::None,
1306 }
1307 DcsIgnoreEsc => {
1308 CAN | SUB => {
1309 self.st = Ground;
1310 VTAction::None
1311 }
1312 ST_FINAL => {
1313 self.st = Ground;
1314 VTAction::None
1315 }
1316 DEL => VTAction::None,
1317 ESC => VTAction::None,
1318 _ => {
1319 self.st = DcsIgnore;
1320 VTAction::None
1321 }
1322 }
1323 DcsPassthrough => {
1324 CAN | SUB => {
1325 self.st = Ground;
1326 VTAction::Cancel(VTEmit::Dcs)
1327 }
1328 DEL => VTAction::None,
1329 ESC => {
1330 self.st = DcsEsc;
1331 VTAction::Hold(VTEmit::Dcs)
1332 }
1333 _ => VTAction::Buffer(VTEmit::Dcs),
1334 }
1335 DcsEsc => {
1336 ST_FINAL => {
1337 self.st = Ground;
1338 VTAction::End(VTEnd::Dcs)
1339 }
1340 DEL => VTAction::None,
1341 ESC => {
1342 VTAction::Hold(VTEmit::Dcs)
1344 }
1345 _ => {
1346 self.st = DcsPassthrough;
1348 VTAction::Buffer(VTEmit::Dcs)
1349 }
1350 }
1351 OscString => {
1352 CAN | SUB => {
1353 self.st = Ground;
1354 VTAction::Cancel(VTEmit::Osc)
1355 }
1356 DEL => VTAction::None,
1357 BEL => {
1358 self.st = Ground;
1359 VTAction::End(VTEnd::Osc { used_bel: true })
1360 }
1361 ESC => {
1362 self.st = OscEsc;
1363 VTAction::Hold(VTEmit::Osc)
1364 }
1365 p if is_printable(p) => VTAction::Buffer(VTEmit::Osc),
1366 _ => VTAction::None, }
1368 OscEsc => {
1369 ST_FINAL => {
1370 self.st = Ground;
1371 VTAction::End(VTEnd::Osc { used_bel: false })
1372 } ESC => VTAction::Hold(VTEmit::Osc),
1374 DEL => VTAction::None,
1375 _ => {
1376 self.st = OscString;
1377 VTAction::Buffer(VTEmit::Osc)
1378 }
1379 }
1380 SosPmApcString => {
1381 CAN | SUB => {
1382 self.st = Ground;
1383 VTAction::None
1384 }
1385 DEL => VTAction::None,
1386 ESC => {
1387 self.st = SpaEsc;
1388 VTAction::None
1389 }
1390 _ => VTAction::None,
1391 }
1392 SpaEsc => {
1393 ST_FINAL => {
1394 self.st = Ground;
1395 VTAction::None
1396 }
1397 DEL => VTAction::None,
1398 ESC => {
1399 VTAction::None
1401 }
1402 _ => {
1403 self.st = State::SosPmApcString;
1404 VTAction::None
1405 }
1406 }
1407 }
1408 }
1409}
1410
1411#[cfg(test)]
1412mod tests {
1413 use crate::event::VTOwnedEvent;
1414
1415 use super::*;
1416 use pretty_assertions::assert_eq;
1417
1418 #[test]
1419 fn test_edge_cases() {
1420 let mut result = String::new();
1422 VTPushParser::decode_buffer(&[], |e| result.push_str(&format!("{e:?}\n")));
1423 assert_eq!(result.trim(), "");
1424
1425 let mut result = String::new();
1427 VTPushParser::decode_buffer(b"\x1b", |e| result.push_str(&format!("{e:?}\n")));
1428 assert_eq!(result.trim(), "");
1429
1430 let mut result = String::new();
1432 VTPushParser::decode_buffer(b"\x1b[", |e| result.push_str(&format!("{e:?}\n")));
1433 assert_eq!(result.trim(), "");
1434
1435 let mut result = String::new();
1437 VTPushParser::decode_buffer(b"\x1bP", |e| result.push_str(&format!("{e:?}\n")));
1438 assert_eq!(result.trim(), "");
1439
1440 let mut result = String::new();
1442 VTPushParser::decode_buffer(b"\x1b]", |e| result.push_str(&format!("{e:?}\n")));
1443 assert_eq!(result.trim(), "OscStart");
1444 }
1445
1446 #[test]
1447 fn test_streaming_behavior() {
1448 let mut parser = VTPushParser::new(); let mut result = String::new();
1451 let mut callback = |vt_input: VTEvent<'_>| {
1452 result.push_str(&format!("{vt_input:?}\n"));
1453 };
1454
1455 parser.feed_with(b"\x1bP1;2;3 |", &mut callback);
1457 parser.feed_with(b"data", &mut callback);
1458 parser.feed_with(b" more", &mut callback);
1459 parser.feed_with(b"\x1b\\", &mut callback);
1460
1461 assert_eq!(
1462 result.trim(),
1463 "DcsStart('1', '2', '3', ' ', |)\nDcsData('data')\nDcsData(' more')\nDcsEnd('')"
1464 );
1465 }
1466
1467 #[test]
1468 fn test_dcs_payload_passthrough() {
1469 let dcs_cases: &[(&[u8], &str)] = &[
1477 (b"\x1bPq\x1b[38:2:12:34:56m\x1b\\", "<ESC>[38:2:12:34:56m"),
1479 (b"\x1bPq\x1b[48:2:0:0:0m;xyz\x1b\\", "<ESC>[48:2:0:0:0m;xyz"),
1481 (
1483 b"\x1bP1$r\x1b[38:2:10:20:30;58:2::200:100:0m\x1b\\",
1484 "<ESC>[38:2:10:20:30;58:2::200:100:0m",
1485 ),
1486 (
1488 b"\x1bPqABC\x1b\x1bDEF\x1bXG\x1b\\",
1489 "ABC<ESC><ESC>DEF<ESC>XG",
1490 ),
1491 (b"\x1bPqDATA\x07MORE\x1b\\", "DATA<BEL>MORE"),
1493 (b"\x1bP!|\x1b[38:5:208m\x1b\\", "<ESC>[38:5:208m"),
1495 (b"\x1bP>|Hello world\x1b\\", "Hello world"),
1497 (
1499 b"\x1bPq\x1b[38:2:1:2:3m\x1b[48:5:17m\x1b\\",
1500 "<ESC>[38:2:1:2:3m<ESC>[48:5:17m",
1501 ),
1502 (
1504 b"\x1bPq\x1b[58:2::000:007:042m\x1b\\",
1505 "<ESC>[58:2::000:007:042m",
1506 ),
1507 ];
1508
1509 for (input, expected_body) in dcs_cases {
1510 let events = collect_owned_events(input);
1511
1512 let mut actual_body = Vec::new();
1514 for event in &events {
1515 match event {
1516 VTOwnedEvent::DcsData(data) | VTOwnedEvent::DcsEnd(data) => {
1517 actual_body.extend(data);
1518 }
1519 _ => {}
1520 }
1521 }
1522
1523 let actual_body = String::from_utf8(actual_body).unwrap();
1524 let actual_body = actual_body
1525 .replace("\x1b", "<ESC>")
1526 .replace("\x07", "<BEL>");
1527
1528 assert_eq!(
1529 actual_body, *expected_body,
1530 "DCS payload mismatch for input {:?}. Full events: {:#?}",
1531 input, events
1532 );
1533 }
1534 }
1535
1536 fn collect_events(input: &[u8]) -> Vec<String> {
1537 let mut out = Vec::new();
1538 let mut p = VTPushParser::new();
1539 p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1540 out
1541 }
1542
1543 fn collect_owned_events(input: &[u8]) -> Vec<VTOwnedEvent> {
1544 let mut out = Vec::new();
1545 let mut p = VTPushParser::new();
1546 p.feed_with(input, &mut |ev: VTEvent| out.push(ev.to_owned()));
1547 out
1548 }
1549
1550 #[test]
1551 fn dcs_esc_esc_del() {
1552 let ev = collect_events(b"\x1bP1;2;3|\x1b\x1b\x7fdata\x1b\\");
1553 eprintln!("{ev:?}");
1554 }
1555
1556 #[test]
1557 fn osc_bel() {
1558 let mut parser = VTPushParser::new();
1559 let mut output = String::new();
1560 for b in b"\x1b]X\x07" {
1561 parser.feed_with(&[*b], &mut |ev: VTEvent| {
1562 output.push_str(&format!("{ev:?}\n"));
1563 });
1564 }
1565 assert_eq!(output.trim(), "OscStart\nOscData('X')\nOscEnd('')");
1566 }
1567
1568 #[test]
1569 fn dcs_header_with_colon_is_ignored_case1() {
1570 let ev = collect_events(b"\x1bP1:2qHELLO\x1b\\");
1572 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1574 }
1575
1576 #[test]
1577 fn dcs_header_with_colon_is_ignored_case2() {
1578 let ev = collect_events(b"\x1bP:1qDATA\x1b\\");
1580 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1581 }
1582
1583 #[test]
1584 fn dcs_header_with_colon_is_ignored_case3() {
1585 let ev = collect_events(b"\x1bP12:34!qPAYLOAD\x1b\\");
1587 assert!(ev.iter().all(|e| !e.starts_with("DcsStart")), "{ev:#?}");
1588 }
1589
1590 #[test]
1591 fn osc_aborted_by_can_mid_body() {
1592 let mut s = Vec::new();
1594 s.extend_from_slice(b"\x1b]0;Title");
1595 s.push(CAN);
1596 s.extend_from_slice(b"more\x07");
1597
1598 let ev = collect_debug(&s);
1599
1600 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1605 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1606 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1607 }
1608
1609 #[test]
1610 fn osc_aborted_by_sub_before_terminator() {
1611 let mut s = Vec::new();
1612 s.extend_from_slice(b"\x1b]52;c;YWJjZA==");
1613 s.push(SUB); s.extend_from_slice(b"\x1b\\"); let ev = collect_debug(&s);
1617 assert!(ev.iter().any(|e| e.starts_with("OscStart")), "{ev:#?}");
1621 assert!(!ev.iter().any(|e| e.starts_with("OscData")), "{ev:#?}");
1622 assert!(!ev.iter().any(|e| e.starts_with("OscEnd")), "{ev:#?}");
1623 }
1624
1625 fn collect_debug(input: &[u8]) -> Vec<String> {
1627 let mut out = Vec::new();
1628 let mut p = VTPushParser::new();
1629 p.feed_with(input, |ev: VTEvent| out.push(format!("{ev:?}")));
1630 out
1631 }
1632
1633 #[test]
1634 fn dcs_aborted_by_can_before_body() {
1635 let mut s = Vec::new();
1637 s.extend_from_slice(b"\x1bPq"); s.push(CAN);
1639 s.extend_from_slice(b"IGNORED\x1b\\"); let ev = collect_debug(&s);
1642
1643 assert_eq!(ev.len(), 4, "{ev:#?}");
1644 assert_eq!(ev[0], "DcsStart('', q)");
1645 assert_eq!(ev[1], "DcsCancel");
1646 assert_eq!(ev[2], "Raw('IGNORED')");
1647 assert_eq!(ev[3], "Esc('', \\)");
1648 }
1649
1650 #[test]
1651 fn dcs_aborted_by_can_mid_body() {
1652 let mut s = Vec::new();
1654 s.extend_from_slice(b"\x1bPqABC");
1655 s.push(CAN);
1656 s.extend_from_slice(b"MORE\x1b\\"); let ev = collect_debug(&s);
1659
1660 assert_eq!(ev.len(), 4, "{ev:#?}");
1661 assert_eq!(ev[0], "DcsStart('', q)");
1662 assert_eq!(ev[1], "DcsCancel");
1663 assert_eq!(ev[2], "Raw('MORE')");
1664 assert_eq!(ev[3], "Esc('', \\)");
1665 }
1666
1667 #[test]
1670 fn spa_aborted_by_can_is_ignored() {
1671 let mut s = Vec::new();
1673 s.extend_from_slice(b"\x1b_hello");
1674 s.push(CAN);
1675 s.extend_from_slice(b"world\x1b\\");
1676
1677 let ev = collect_debug(&s);
1678 assert_eq!(ev.len(), 2, "{ev:#?}");
1679 assert_eq!(ev[0], "Raw('world')");
1680 assert_eq!(ev[1], "Esc('', \\)");
1681 }
1682
1683 #[test]
1684 fn spa_sub_aborts_too() {
1685 let mut s = Vec::new();
1686 s.extend_from_slice(b"\x1bXhello");
1687 s.push(SUB);
1688 s.extend_from_slice(b"world\x1b\\");
1689 let ev = collect_debug(&s);
1690 assert_eq!(ev.len(), 2, "{ev:#?}");
1691 assert_eq!(ev[0], "Raw('world')");
1692 assert_eq!(ev[1], "Esc('', \\)");
1693 }
1694
1695 #[test]
1698 fn can_in_ground_is_c0() {
1699 let mut s = Vec::new();
1700 s.extend_from_slice(b"abc");
1701 s.push(CAN);
1702 s.extend_from_slice(b"def");
1703 let ev = collect_debug(&s);
1704 assert_eq!(ev.len(), 3, "{ev:#?}");
1706 assert_eq!(ev[0], "Raw('abc')");
1707 assert_eq!(ev[1], "C0(18)");
1708 assert_eq!(ev[2], "Raw('def')");
1709 }
1710
1711 #[test]
1714 fn three_byte_sequences_capturable() {
1715 let mut bytes = vec![];
1716 for i in 0..=0xFFFFFF_u32 {
1717 bytes.clear();
1718 let test_bytes = i.to_le_bytes();
1719 let test_bytes = &test_bytes[..3];
1720 if test_bytes.iter().any(|b| b == &0) {
1721 continue;
1722 }
1723 if test_bytes[0] == 0x1b && matches!(test_bytes[1], CSI | DCS | OSC | APC | PM | SOS) {
1724 continue;
1725 }
1726 if test_bytes[1] == 0x1b && matches!(test_bytes[2], CSI | DCS | OSC | APC | PM | SOS) {
1727 continue;
1728 }
1729
1730 let mut parser = VTPushParser::<VT_PARSER_INTEREST_ALL>::new_with();
1731 parser.feed_with(test_bytes, |event: VTEvent| {
1732 let mut chunk = [0_u8; 3];
1733 let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1734 panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1735 });
1736 bytes.extend_from_slice(&chunk[..b]);
1737 });
1738 if let Some(event) = parser.idle() {
1739 let mut chunk = [0_u8; 3];
1740 let b = event.encode(&mut chunk).unwrap_or_else(|_| {
1741 panic!("Failed to encode event {test_bytes:02X?} -> {event:?}")
1742 });
1743 bytes.extend_from_slice(&chunk[..b]);
1744 }
1745
1746 if bytes.len() != 3 || bytes != test_bytes {
1747 eprintln!("Failed to parse:");
1748 parser.feed_with(test_bytes, |event: VTEvent| {
1749 eprintln!("{event:?}");
1750 });
1751 if let Some(event) = parser.idle() {
1752 eprintln!("{event:?}");
1753 }
1754 assert_eq!(bytes, test_bytes, "{test_bytes:02X?} -> {bytes:02X?}");
1755 }
1756 }
1757 }
1758
1759 #[test]
1762 fn finish_after_esc_emits_c0_esc() {
1763 let mut parser = VTPushParser::new();
1764 let mut out = Vec::new();
1765 parser.feed_with(b"abc\x1b", |ev: VTEvent| out.push(format!("{ev:?}")));
1766 assert_eq!(out, ["Raw('abc')"]);
1767
1768 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1769 assert_eq!(out, ["Raw('abc')", "C0(1b)"]);
1770 assert!(parser.is_ground());
1771 }
1772
1773 #[test]
1774 fn finish_mid_dcs_cancels() {
1775 let mut parser = VTPushParser::new();
1776 let mut out = Vec::new();
1777 parser.feed_with(b"\x1bPqdata\x1b", |ev: VTEvent| out.push(format!("{ev:?}")));
1778 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1779 assert_eq!(
1780 out,
1781 [
1782 "DcsStart('', q)",
1783 "DcsData('data')",
1784 "DcsData('<ESC>')",
1785 "DcsCancel"
1786 ]
1787 );
1788 assert!(parser.is_ground());
1789 }
1790
1791 #[test]
1792 fn finish_mid_osc_cancels() {
1793 let mut parser = VTPushParser::new();
1794 let mut out = Vec::new();
1795 parser.feed_with(b"\x1b]0;title\x1b", |ev: VTEvent| {
1796 out.push(format!("{ev:?}"))
1797 });
1798 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1799 assert_eq!(
1800 out,
1801 [
1802 "OscStart",
1803 "OscData('0;title')",
1804 "OscData('<ESC>')",
1805 "OscCancel"
1806 ]
1807 );
1808 assert!(parser.is_ground());
1809 }
1810
1811 #[test]
1812 fn finish_in_csi_header_drops_params() {
1813 let mut parser = VTPushParser::new();
1814 let mut out = Vec::new();
1815 parser.feed_with(b"\x1b[1;2;3", |ev: VTEvent| out.push(format!("{ev:?}")));
1816 parser.finish(|ev: VTEvent| out.push(format!("{ev:?}")));
1817 assert!(out.is_empty(), "{out:#?}");
1818 assert!(parser.is_ground());
1819
1820 parser.feed_with(b"\x1b[0m", |ev: VTEvent| out.push(format!("{ev:?}")));
1822 assert_eq!(out, ["Csi('0', '', 'm')"], "{out:#?}");
1823 }
1824
1825 #[test]
1826 fn finish_is_idempotent() {
1827 let mut parser = VTPushParser::new();
1828 let mut count = 0;
1829 parser.feed_with(b"\x1b]0;x\x1b", |_ev: VTEvent| {});
1830 parser.finish(|_ev: VTEvent| count += 1);
1831 parser.finish(|_ev: VTEvent| count += 1);
1832 assert_eq!(count, 2);
1834 }
1835}