From b8cb2d07e4c694ae10512b6823fd14059da2429c Mon Sep 17 00:00:00 2001 From: mtgmonkey Date: Wed, 9 Jul 2025 21:01:49 -0400 Subject: [PATCH] some type of intermediate --- Cargo.lock | 79 +++++++++++++++-- Cargo.toml | 2 + src/enums.rs | 230 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 103 ++++++++++++++++++---- src/parsers.rs | 167 +++++++++++++++++++++++++++++++++++ 5 files changed, 558 insertions(+), 23 deletions(-) create mode 100644 src/enums.rs create mode 100644 src/parsers.rs diff --git a/Cargo.lock b/Cargo.lock index 3b16745..bd7a6ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,12 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.10.1" @@ -1107,6 +1113,15 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1138,6 +1153,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1599,6 +1624,15 @@ dependencies = [ "libc", ] +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1924,7 +1958,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.11", ] [[package]] @@ -1941,6 +1985,19 @@ dependencies = [ "winapi", ] +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.13", + "smallvec", + "windows-targets 0.52.6", +] + [[package]] name = "paste" version = "1.0.15" @@ -2236,8 +2293,10 @@ name = "rust_term" version = "0.1.0" dependencies = [ "bpaf", + "heapless", "iced", "nix", + "nom", ] [[package]] @@ -2508,6 +2567,12 @@ dependencies = [ "bitflags 2.9.1", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2873,7 +2938,7 @@ checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" dependencies = [ "futures", "js-sys", - "parking_lot", + "parking_lot 0.11.2", "pin-utils", "wasm-bindgen", "wasm-bindgen-futures", @@ -3021,7 +3086,7 @@ dependencies = [ "js-sys", "log", "naga", - "parking_lot", + "parking_lot 0.12.4", "profiling", "raw-window-handle", "smallvec", @@ -3049,7 +3114,7 @@ dependencies = [ "log", "naga", "once_cell", - "parking_lot", + "parking_lot 0.12.4", "profiling", "raw-window-handle", "rustc-hash 1.1.0", @@ -3091,7 +3156,7 @@ dependencies = [ "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", - "parking_lot", + "parking_lot 0.12.4", "profiling", "range-alloc", "raw-window-handle", @@ -3602,9 +3667,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "yazi" diff --git a/Cargo.toml b/Cargo.toml index a4df731..9734f94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ edition = "2024" nix = { version = "0.30.1", features = ["term", "process", "fs"], default-features = false } iced = { version = "0.13.1", features = ["advanced", "smol", "wgpu"], default-features = false } bpaf = { version = "0.9.20", features = ["derive"], default-features = false } +heapless = "0.8.0" +nom = "8.0.0" [lints.clippy] cargo = "deny" diff --git a/src/enums.rs b/src/enums.rs new file mode 100644 index 0000000..23aaa79 --- /dev/null +++ b/src/enums.rs @@ -0,0 +1,230 @@ +//! docs are more or less from [this document](https://real-world-systems.com/docs/ANSIcode.html) +//! this source file `enums.rs` is licensed under the Mozilla Public License 2.0 (MPL2.0) +//! as parts of it are from [this](https://gitlab.com/davidbittner/ansi-parser) excellent crate + +#![allow(dead_code)] + +use crate::parsers::parse_escape; +use heapless::Vec; +use std::fmt; + +#[derive(Debug, PartialEq, Clone)] +pub enum Token<'a> { + Text(&'a str), + C0(C0), + EscapeSequence(EscapeSequence), +} + +impl<'a> fmt::Display for Token<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Token::*; + match self { + Text(txt) => write!(f, "{}", txt), + C0(c0) => write!(f, "{:?}", c0), + EscapeSequence(escape_sequence) => write!(f, "{:?}", escape_sequence), + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub enum C0 { + NUL, + SOH, + STX, + ETX, + EOT, + ENQ, + ACK, + BEL, + BS, + HT, + LF, + VT, + FF, + CR, + SO, + SI, + DLE, + DC1, + DC2, + DC3, + DC4, + NAK, + SYN, + ETB, + CAN, + EM, + SUB, + ESC, + FS, + GS, + RS, + US, + SP, + DEL, +} + +// TODO figure out defaults +#[derive(Debug, PartialEq, Clone)] +pub enum EscapeSequence { + ICH(u32), // [#@ def 1 Insert CHaracter + CUU(u32), // [#A def 1 CUrsor Up + CUD(u32), // [#B def 1 CUrsor Down + CUF(u32), // [#C def 1 CUrsor Forward + CUB(u32), // [#D def 1 CUrsor Backward + CNL(u32), // [#E def 1 Cursor to Next Line + CPL(u32), // [#F def 1 Cursor to Previous Line + CHA(u32), // [#G def 1 Cursor Horizontal position Absolute + CUP(u32, u32), // [#;#H def 1;1 CUrsor Position + CHT(u32), // [#I def 1 Cursor Horizontal Tabulation + ED(u32), // [#J def 0 Erase in Display + EL(u32), // [#K def 0 Erase in Line + IL(u32), // [#L def 0 Insert Line, current line moves down + DL(u32), // [#M def 0 Delete Line, lines below current move up + EF(u32), // [#N def 0 Erase in Field + EA(u32), // [#O def 0 Erase in qualified Area + DCH(u32), // [#P def 1 Delete CHaracter + SEM(u32), // [#Q def 0 Set Editing extent Mode + CPR, // [R Cursor Position Report + SU(u32), // [#S def 1 Scroll Up + SD(u32), // [#T def 1 Scroll Down + NP(u32), // [#U def 1 Next Page + PP(u32), // [#V def 1 Previous Page + CTC(u32), // [#W def 0 Cursor Tabulation Control + ECH(u32), // [#X def 1 Erase CHaracter + CVT(u32), // [#Y def 1 Cursor Vertical Tab + CBT(u32), // [#Z def 1 Cursor Back Tab + HPA(u32), // [#` def 0 Horizontal Position Absolute + HPR(u32), // [#a def 0 Horizontal Position Relative + REP(u32), // [#b def 1 REPeat previous displayable character + DA, // [c Device Attributes + VPA(u32), // [#d def 0 Vertical Position Absolute + VPR(u32), // [#e def 0 Vertical Position Relative + HVP(u32, u32), // [#;#f def 0;0 Horizontal and Vertical Position + TBC(u32), // [#g def 0 TaBulation Clear + SM(u32), // [#h def 0 Set Mode + MC(u32), // [#i def 0 Media Copy + PageFormatSelect(u32), // [#j def 0 + RM, // [l Reset Mode + SGR(Vec), // [#;#;#;#;#l def 0 Set Graphics Rendition + DSR(u32), // [#n def 0 Device Status Report + DAQ(u32), // [#o def 0 Define Area Qualification starting at current position + DECLL, // [q UNIMPLEMENTED many params + DECSTBM(u32, u32), // [#;#r def 1;1 top and bottom margins + DECSTRM(u32, u32), // [#;#s def 1;1 left and right margins + DECSLPP(u32), // [#t def 66 physical lines per page + DECSHTS, // [u UNIMPLEMENTED many params + DECSVTS, // [v UNIMPLEMENTED many params + DECSHORP(u32), // [#w def 0 set horizontal pitch on LAxxx printers + DECREQTPARM, // [x UNIMPLEMENTED many params + DECTST(u32, u32), // [#;#y def 2;1 invoke confidence test + DECVERP(u32), // [#z def 0 set vertical -pitch on LA100 + DECTTC(u32), // [#| def 0 transmit termination character + DECPRO, // [} UNIMPLEMENTED many params + DECKEYS(u32), // [#~ def 0 sent by special function keys + DELETE, // [DELETE always ignored + SL(u32), // [# @ def 1 Scroll Left + SR(u32), // [# A def 1 Scroll Right + GSM(u32, u32), // [#;# B def 100;100 Graphic Size Modification + GSS(u32), // [# C def 720 Graphic Size Selection + FNT(u32, u32), // [#;# D def 0;1 FoNT selection + TSS(u32), // [# E def 720 Thin Space Specification + JFY(u32), // [# F def 0 JustiFY + SPI(u32, u32), // [#;# G def 720;720 SPacing Increment + QUAD(u32), // [# H def 0 do QUADding on current line of text +} + +pub trait AnsiParser { + fn ansi_parse(&self) -> AnsiParseIterator<'_>; +} + +impl AnsiParser for str { + fn ansi_parse(&self) -> AnsiParseIterator<'_> { + AnsiParseIterator { dat: &self } + } +} + +impl AnsiParser for String { + fn ansi_parse(&self) -> AnsiParseIterator<'_> { + AnsiParseIterator { dat: self } + } +} + +#[derive(Debug)] +pub struct AnsiParseIterator<'a> { + dat: &'a str, +} + +impl<'a> Iterator for AnsiParseIterator<'a> { + type Item = Token<'a>; + + fn next(&mut self) -> Option { + if self.dat.is_empty() { + return None; + } + + let (head, tail) = head(self.dat); + let dat = self.dat; + self.dat = tail; + + // bound checked + use C0::*; + Some(match head { + "\u{00}" => Token::C0(NUL), + "\u{01}" => Token::C0(SOH), + "\u{02}" => Token::C0(STX), + "\u{03}" => Token::C0(ETX), + "\u{04}" => Token::C0(EOT), + "\u{05}" => Token::C0(ENQ), + "\u{06}" => Token::C0(ACK), + "\u{07}" => Token::C0(BEL), + "\u{08}" => Token::C0(BS), + "\u{09}" => Token::C0(HT), + "\u{0a}" => Token::C0(LF), + "\u{0b}" => Token::C0(VT), + "\u{0c}" => Token::C0(FF), + "\u{0d}" => Token::C0(CR), + "\u{0e}" => Token::C0(SO), + "\u{0f}" => Token::C0(SI), + "\u{10}" => Token::C0(DLE), + "\u{11}" => Token::C0(DC1), + "\u{12}" => Token::C0(DC2), + "\u{13}" => Token::C0(DC3), + "\u{14}" => Token::C0(DC4), + "\u{15}" => Token::C0(NAK), + "\u{16}" => Token::C0(SYN), + "\u{17}" => Token::C0(ETB), + "\u{18}" => Token::C0(CAN), + "\u{19}" => Token::C0(EM), + "\u{1a}" => Token::C0(SUB), + "\u{1b}" => { + if let Ok(ret) = parse_escape(dat) { + println!("^[ successfully parsed"); + self.dat = ret.0.clone(); + Token::EscapeSequence(ret.1) + } else { + println!("^[ unimplemented"); + Token::C0(ESC) + } + } + "\u{1c}" => Token::C0(FS), + "\u{1d}" => Token::C0(GS), + "\u{1e}" => Token::C0(RS), + "\u{1f}" => Token::C0(US), + "\u{20}" => Token::C0(SP), + "\u{7F}" => Token::C0(DEL), + txt => Token::Text(txt), + }) + } +} + +fn head(string: &str) -> (&str, &str) { + for i in 1..5 { + let r = string.get(0..i); + match r { + Some(val) => return (val, &string[i..]), + None => (), + } + } + panic!(); +} diff --git a/src/lib.rs b/src/lib.rs index 1bf6e93..8620e1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,9 @@ reason = "" )] +use crate::enums::*; +use crate::parsers::*; + use bpaf::Bpaf; use iced::widget::{column, rich_text, row, scrollable, span, text}; @@ -32,9 +35,18 @@ use std::os::unix::io::{AsFd as _, OwnedFd}; use std::process::Command; use std::{error, fmt, thread, time as core_time}; +pub mod enums; +pub mod parsers; + /// whether to enable verbose logging; see `Flags::verbose` static mut VERBOSE: bool = false; +/// whether to enable debug logging; see `Flags::debug` +static mut DEBUG: bool = false; + +/// whether to enable vomit logging; see `Flags::vomit` +static mut VOMIT: bool = false; + /// shell path; see `Flags::shell` static mut SHELL: Option = None; @@ -106,13 +118,25 @@ pub struct Flags { #[bpaf(short('S'), long)] shell: Option, - /// whether to debug log + /// no logging, NOOP; log level 0 #[bpaf(short, long)] + quiet: bool, + + /// whether to error log; log level 1 + #[bpaf(short('v'), long)] verbose: bool, - /// whether to display version: TODO + /// whether to debug log; log level 2 + #[bpaf(long)] + debug: bool, + + /// whether to vomit log; log level 3 + #[bpaf(long)] + vomit: bool, + + /// whether to display version, NOOP; TODO #[expect(dead_code, reason = "TODO")] - #[bpaf(short, long)] + #[bpaf(short('V'), long)] version: bool, } @@ -126,7 +150,7 @@ pub struct Flags { /// .subscription(Model::subscription) /// .run() /// ``` -pub struct Model { +pub struct Model<'a> { /// location of cursor in user input line cursor_index: usize, /// fd of pty @@ -139,9 +163,12 @@ pub struct Model { screen_buffer_index: usize, /// path to shell shell: String, + + screen: Vec<&'a str>, + cursor: (usize, usize), } -impl Model { +impl Model<'_> { /// applies needed side effects when taking an input char #[expect( clippy::arithmetic_side_effects, @@ -238,7 +265,7 @@ impl Model { print_err(&error); } } - Err(error) => print_err(&error), + Err(error) => print_vomit(&error.to_string()), } return iced::Task::none(); } @@ -252,15 +279,21 @@ impl Model { reason = "all is bound checked" )] fn update_screen_buffer(&mut self, vec: &[u8]) -> Result<(), Error> { - let offset = self.screen_buffer_index; - for (i, chr) in vec.iter().enumerate() { - let index = i + offset; - if index < self.screen_buffer.len() { - self.screen_buffer[index] = *chr; - } else { - return Err(Error::IndexOutOfBounds); + for chr in String::from_utf8_lossy(vec).ansi_parse() { + match chr { + Token::Text(txt) => { + print_debug(&(String::from("[CHR]") + txt)); + if self.screen_buffer_index < self.screen_buffer.len() { + self.screen_buffer[self.screen_buffer_index] = + *txt.as_bytes().get(0).unwrap_or(&b'_'); + self.screen_buffer_index += 1; + } + } + Token::C0(c0) => print_debug(&(String::from("[C0]") + &format!("{:?}", c0))), + Token::EscapeSequence(seq) => { + print_debug(&(String::from("[SEQ]") + &format!("{:?}", seq))) + } } - self.screen_buffer_index += 1; } return Ok(()); } @@ -324,7 +357,7 @@ impl Model { } } -impl Default for Model { +impl Default for Model<'_> { #[inline] #[expect(clippy::undocumented_unsafe_blocks, reason = "clippy be trippin")] fn default() -> Self { @@ -339,6 +372,8 @@ impl Default for Model { || return String::from("/home/mtgmonkey/.nix-profile/bin/dash"), |shell| return shell, ), + screen: vec![], + cursor: (1, 1), }; me.fd = spawn_pty_with_shell(&me.shell).ok(); let mut nored = true; @@ -361,9 +396,15 @@ impl Default for Model { #[inline] #[expect(clippy::undocumented_unsafe_blocks, reason = "clippy be trippin")] pub unsafe fn init(flags: Flags) { + unsafe { + DEBUG = flags.debug; + } unsafe { VERBOSE = flags.verbose; } + unsafe { + VOMIT = flags.vomit; + } unsafe { SHELL = flags.shell; } @@ -448,8 +489,38 @@ fn set_nonblock(fd: &OwnedFd) -> Result<(), Error> { clippy be buggin" )] fn print_err(error: &Error) { - /// SAFETY the only time VERBOSE is written to should be `init()` + /// SAFETY the only time `VERBOSE` is written to should be `init()` if unsafe { VERBOSE } { println!("[ERROR] {error}"); } } + +/// if `VOMIT` is `true`, logs vomit +#[inline] +#[expect( + clippy::print_stdout, + clippy::undocumented_unsafe_blocks, + reason = "toggleable with VERBOSE option\n + clippy be buggin" +)] +fn print_vomit(vomit: &str) { + /// SAFETY the only time `VOMIT` is written to should be `init()` + if unsafe { VOMIT } { + println!("[VOMIT] {:?}", vomit); + } +} + +/// if `DEBUG` is `true`, logs errors +#[inline] +#[expect( + clippy::print_stdout, + clippy::undocumented_unsafe_blocks, + reason = "toggleable with VERBOSE option\n + clippy be buggin" +)] +fn print_debug(debug: &str) { + /// SAFETY the only time `DEBUG` is written to should be `init()` + if unsafe { DEBUG } { + println!("[DEBUG] {:?}", debug); + } +} diff --git a/src/parsers.rs b/src/parsers.rs new file mode 100644 index 0000000..447b12a --- /dev/null +++ b/src/parsers.rs @@ -0,0 +1,167 @@ +//! docs are more or less from [this document](https://real-world-systems.com/docs/ANSIcode.html) +//! this source file `parsers.rs` is licensed under the Mozilla Public License 2.0 (MPL2.0) +//! as parts of it are from [this](https://gitlab.com/davidbittner/ansi-parser) excellent crate + +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::character::complete::{digit0, digit1}; +use nom::combinator::{map, map_res, opt, value}; +use nom::sequence::{delimited, preceded}; +use nom::{IResult, Parser}; + +use crate::enums::{C0, EscapeSequence}; + +macro_rules! tag_parser { + ($sig:ident, $tag:expr, $ret:expr) => { + fn $sig(input: &str) -> IResult<&str, EscapeSequence> { + value($ret, tag($tag)).parse(input) + } + }; +} + +macro_rules! one_ctlseq { + ($sig:ident, $tag:expr, $def:expr, $ret:expr) => { + fn $sig(input: &str) -> IResult<&str, EscapeSequence> { + map( + delimited(tag("["), |a| parse_maybe_int(a, $def), tag($tag)), + |am| $ret(am), + ) + .parse(input) + } + }; +} + +macro_rules! two_ctlseq { + ($sig:ident, $tag:expr, $def0:expr, $def1:expr, $ret:expr) => { + fn $sig(input: &str) -> IResult<&str, EscapeSequence> { + map( + ( + tag("["), + |a| parse_maybe_int(a, $def0), + opt(tag(";")), + |b| parse_maybe_int(b, $def1), + tag($tag), + ), + |(_, a, _, b, _)| $ret(a, b), + ) + .parse(input) + } + }; +} + +fn parse_u32(input: &str) -> IResult<&str, u32> { + map_res(digit1, |s: &str| s.parse::()).parse(input) +} + +fn parse_maybe_int(input: &str, default: u32) -> IResult<&str, u32> { + map(digit0, |s: &str| s.parse::().unwrap_or(default)).parse(input) +} + +fn combined(input: &str) -> IResult<&str, EscapeSequence> { + alt(( + alt(( + ICH, CUU, CUD, CUF, CUB, CNL, CPL, CHA, CUP, CHT, ED, EL, IL, DL, EF, EA, DCH, SEM, + CPR, SU, SD, + )), + alt(( + NP, + PP, + CTC, + ECH, + CVT, + CBT, + HPA, + HPR, + REP, + DA, + VPA, + VPR, + HVP, + TBC, + SM, + MC, + PageFormatSelect, + RM, + /*SGR,*/ DSR, + DAQ, + )), + alt(( + DECLL, DECSTBM, DECSTRM, DECSLPP, /*DECSHTS,*/ /*DECSVTS,*/ DECSHORP, + /*DECREQTPARM,*/ DECTST, DECVERP, DECTTC, /*DECPRO,*/ DECKEYS, + /*DELETE,*/ SL, SR, GSM, GSS, FNT, TSS, JFY, + )), + SPI, + QUAD, + )) + .parse(input) +} + +pub fn parse_escape(input: &str) -> IResult<&str, EscapeSequence> { + preceded(tag("\u{1b}"), combined).parse(input) +} + +one_ctlseq!(ICH, "@", 1, EscapeSequence::ICH); +one_ctlseq!(CUU, "A", 1, EscapeSequence::CUU); +one_ctlseq!(CUD, "B", 1, EscapeSequence::CUD); +one_ctlseq!(CUF, "C", 1, EscapeSequence::CUF); +one_ctlseq!(CUB, "D", 1, EscapeSequence::CUB); +one_ctlseq!(CNL, "E", 1, EscapeSequence::CNL); +one_ctlseq!(CPL, "F", 1, EscapeSequence::CPL); +one_ctlseq!(CHA, "G", 1, EscapeSequence::CHA); +two_ctlseq!(CUP, "H", 1, 1, EscapeSequence::CUP); +one_ctlseq!(CHT, "I", 1, EscapeSequence::CHT); +one_ctlseq!(ED, "J", 0, EscapeSequence::ED); +one_ctlseq!(EL, "K", 0, EscapeSequence::EL); +one_ctlseq!(IL, "L", 0, EscapeSequence::IL); +one_ctlseq!(DL, "M", 0, EscapeSequence::DL); +one_ctlseq!(EF, "N", 0, EscapeSequence::EF); +one_ctlseq!(EA, "O", 0, EscapeSequence::EA); +one_ctlseq!(DCH, "P", 1, EscapeSequence::DCH); +one_ctlseq!(SEM, "Q", 0, EscapeSequence::SEM); +tag_parser!(CPR, "[R", EscapeSequence::CPR); +one_ctlseq!(SU, "S", 1, EscapeSequence::SU); +one_ctlseq!(SD, "T", 1, EscapeSequence::SD); +one_ctlseq!(NP, "U", 1, EscapeSequence::NP); +one_ctlseq!(PP, "V", 1, EscapeSequence::PP); +one_ctlseq!(CTC, "W", 0, EscapeSequence::CTC); +one_ctlseq!(ECH, "X", 1, EscapeSequence::ECH); +one_ctlseq!(CVT, "Y", 1, EscapeSequence::CVT); +one_ctlseq!(CBT, "Z", 1, EscapeSequence::CBT); +one_ctlseq!(HPA, "`", 0, EscapeSequence::HPA); +one_ctlseq!(HPR, "a", 0, EscapeSequence::HPR); +one_ctlseq!(REP, "b", 1, EscapeSequence::REP); +tag_parser!(DA, "[c", EscapeSequence::DA); +one_ctlseq!(VPA, "d", 0, EscapeSequence::VPA); +one_ctlseq!(VPR, "e", 0, EscapeSequence::VPR); +two_ctlseq!(HVP, "f", 1, 1, EscapeSequence::HVP); +one_ctlseq!(TBC, "g", 0, EscapeSequence::TBC); +one_ctlseq!(SM, "h", 0, EscapeSequence::SM); +one_ctlseq!(MC, "i", 0, EscapeSequence::MC); +one_ctlseq!(PageFormatSelect, "j", 0, EscapeSequence::PageFormatSelect); +tag_parser!(RM, "[l", EscapeSequence::RM); +// TODO SGR takes 5 parameters +one_ctlseq!(DSR, "n", 0, EscapeSequence::DSR); +one_ctlseq!(DAQ, "o", 0, EscapeSequence::DAQ); +tag_parser!(DECLL, "[q", EscapeSequence::DECLL); +two_ctlseq!(DECSTBM, "r", 1, 1, EscapeSequence::DECSTBM); +two_ctlseq!(DECSTRM, "s", 1, 1, EscapeSequence::DECSTRM); +one_ctlseq!(DECSLPP, "t", 66, EscapeSequence::DECSLPP); +// TODO DECSHTS takes many parameters +// TODO DECSVTS takes many parameters +one_ctlseq!(DECSHORP, "w", 0, EscapeSequence::DECSHORP); +// TODO DECREQTPARM takes many parameters +two_ctlseq!(DECTST, "y", 2, 1, EscapeSequence::DECTST); +one_ctlseq!(DECVERP, "z", 0, EscapeSequence::DECVERP); +one_ctlseq!(DECTTC, "!", 0, EscapeSequence::DECTTC); +// TODO DECPRO takes many parameters +one_ctlseq!(DECKEYS, "~", 0, EscapeSequence::DECKEYS); +// TODO DELETE needs reading into, what hex code do I need +one_ctlseq!(SL, " @", 1, EscapeSequence::SL); +one_ctlseq!(SR, " A", 1, EscapeSequence::SR); +two_ctlseq!(GSM, " B", 100, 100, EscapeSequence::GSM); +one_ctlseq!(GSS, " C", 720, EscapeSequence::GSS); +two_ctlseq!(FNT, " D", 0, 1, EscapeSequence::FNT); +one_ctlseq!(TSS, " E", 720, EscapeSequence::TSS); +one_ctlseq!(JFY, " F", 0, EscapeSequence::JFY); +two_ctlseq!(SPI, " G", 720, 720, EscapeSequence::SPI); +one_ctlseq!(QUAD, " H", 0, EscapeSequence::QUAD);