some type of intermediate

This commit is contained in:
mtgmonkey 2025-07-09 21:01:49 -04:00
parent 803230738e
commit b8cb2d07e4
5 changed files with 558 additions and 23 deletions

79
Cargo.lock generated
View file

@ -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"

View file

@ -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"

230
src/enums.rs Normal file
View file

@ -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<u8, 5>), // [#;#;#;#;#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<Self::Item> {
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!();
}

View file

@ -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<String> = None;
@ -106,13 +118,25 @@ pub struct Flags {
#[bpaf(short('S'), long)]
shell: Option<String>,
/// 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);
}
}

167
src/parsers.rs Normal file
View file

@ -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::<u32>()).parse(input)
}
fn parse_maybe_int(input: &str, default: u32) -> IResult<&str, u32> {
map(digit0, |s: &str| s.parse::<u32>().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);