aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Duerr <contact@christianduerr.com>2020-08-05 00:36:41 +0000
committerGitHub <noreply@github.com>2020-08-05 00:36:41 +0000
commit4f44023dab081f7da74fee14bc53b10ee8f96a1e (patch)
tree4f85102557f2fd55b35cbec026dda172a8427907
parent0310be12d3007e32be614c5df94653d29fcc1a8b (diff)
downloadr-alacritty-vte-4f44023dab081f7da74fee14bc53b10ee8f96a1e.tar.gz
r-alacritty-vte-4f44023dab081f7da74fee14bc53b10ee8f96a1e.tar.bz2
r-alacritty-vte-4f44023dab081f7da74fee14bc53b10ee8f96a1e.zip
Add CSI subparameter support
This adds support for CSI subparameters like `\x1b[38:2:255:0:255m`, which allows the combination of truecolor SGR commands together with other SGR parameters like bold text, without any ambiguity. This implements subparameters by storing them in a list together with all other parameters and having a separate slice to indicate which parameter is a subparameter and how long the subparameter list is. This allows for static memory allocation and good performance while still having the option for dynamic sizing of the parameters. Since the subparameters are now also counted as parameters, the number of allowed parameters has been increased from `16` to `32`. Since the existing structures combine the handling of parameters for CSI and DCS escape sequences, it is now also possible for DCS parameters to have subparameters, even though that is currently never used. Considering that DCS is rarely supported by terminal emulators, handling these separately would likely just cause unnecessary issues. The performance should also be better by using this existing subparam structure rather than having two separate structures for DCS and CSI parameters. The only API provided for accessing the list of parameters is using an iterator, this is intentional to make the internal structure clear and allow for easy optimizations downstream. Since it makes little sense to access parameters out of order, this limitation should not have any negative effects on performance. The main drawback is that direct access to the first parameter while ignoring all other subparameters is less efficient, since it requires indexing a slice after iterating to the element. However while this is often useful, it's mostly done for the first few parameters which significantly reduces the overhead to a negligible amount. At the same time this forces people to support subparameters or at least consider their existence, which should make it more difficult to implement things improperly downstream. Fixes #22.
-rw-r--r--examples/parselog.rs12
-rw-r--r--src/lib.rs128
-rw-r--r--src/params.rs142
-rw-r--r--src/table.rs12
4 files changed, 225 insertions, 69 deletions
diff --git a/examples/parselog.rs b/examples/parselog.rs
index c310182..dfd0aee 100644
--- a/examples/parselog.rs
+++ b/examples/parselog.rs
@@ -1,12 +1,12 @@
//! Parse input from stdin and log actions on stdout
use std::io::{self, Read};
-use vte;
+use vte::{Params, Parser, Perform};
/// A type implementing Perform that just logs actions
struct Log;
-impl vte::Perform for Log {
+impl Perform for Log {
fn print(&mut self, c: char) {
println!("[print] {:?}", c);
}
@@ -15,7 +15,7 @@ impl vte::Perform for Log {
println!("[execute] {:02x}", byte);
}
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
println!(
"[hook] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
params, intermediates, ignore, c
@@ -34,9 +34,9 @@ impl vte::Perform for Log {
println!("[osc_dispatch] params={:?} bell_terminated={}", params, bell_terminated);
}
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
println!(
- "[csi_dispatch] params={:?}, intermediates={:?}, ignore={:?}, char={:?}",
+ "[csi_dispatch] params={:#?}, intermediates={:?}, ignore={:?}, char={:?}",
params, intermediates, ignore, c
);
}
@@ -53,7 +53,7 @@ fn main() {
let input = io::stdin();
let mut handle = input.lock();
- let mut statemachine = vte::Parser::new();
+ let mut statemachine = Parser::new();
let mut performer = Log;
let mut buf = [0; 2048];
diff --git a/src/lib.rs b/src/lib.rs
index a99638a..749dd21 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -41,14 +41,17 @@ use arrayvec::ArrayVec;
use utf8parse as utf8;
mod definitions;
+mod params;
mod table;
+pub use params::{Params, ParamsIter};
+
use definitions::{unpack, Action, State};
const MAX_INTERMEDIATES: usize = 2;
+const MAX_OSC_PARAMS: usize = 16;
#[cfg(any(feature = "no_std", test))]
const MAX_OSC_RAW: usize = 1024;
-const MAX_PARAMS: usize = 16;
struct VtUtf8Receiver<'a, P: Perform>(&'a mut P, &'a mut State);
@@ -72,14 +75,13 @@ pub struct Parser {
state: State,
intermediates: [u8; MAX_INTERMEDIATES],
intermediate_idx: usize,
- params: [i64; MAX_PARAMS],
+ params: Params,
param: i64,
- num_params: usize,
#[cfg(feature = "no_std")]
osc_raw: ArrayVec<[u8; MAX_OSC_RAW]>,
#[cfg(not(feature = "no_std"))]
osc_raw: Vec<u8>,
- osc_params: [(usize, usize); MAX_PARAMS],
+ osc_params: [(usize, usize); MAX_OSC_PARAMS],
osc_num_params: usize,
ignoring: bool,
utf8_parser: utf8::Parser,
@@ -92,8 +94,8 @@ impl Parser {
}
#[inline]
- fn params(&self) -> &[i64] {
- &self.params[..self.num_params]
+ fn params(&self) -> &Params {
+ &self.params
}
#[inline]
@@ -198,7 +200,7 @@ impl Parser {
/// The aliasing is needed here for multiple slices into self.osc_raw
#[inline]
fn osc_dispatch<P: Perform>(&self, performer: &mut P, byte: u8) {
- let mut slices: [MaybeUninit<&[u8]>; MAX_PARAMS] =
+ let mut slices: [MaybeUninit<&[u8]>; MAX_OSC_PARAMS] =
unsafe { MaybeUninit::uninit().assume_init() };
for (i, slice) in slices.iter_mut().enumerate().take(self.osc_num_params) {
@@ -219,11 +221,10 @@ impl Parser {
Action::Print => performer.print(byte as char),
Action::Execute => performer.execute(byte),
Action::Hook => {
- if self.num_params == MAX_PARAMS {
+ if self.params.is_full() {
self.ignoring = true;
} else {
- self.params[self.num_params] = self.param;
- self.num_params += 1;
+ self.params.push(self.param);
}
performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char);
@@ -247,8 +248,8 @@ impl Parser {
if byte == b';' {
let param_idx = self.osc_num_params;
match param_idx {
- // Only process up to MAX_PARAMS
- MAX_PARAMS => return,
+ // Only process up to MAX_OSC_PARAMS
+ MAX_OSC_PARAMS => return,
// First param is special - 0 to current byte index
0 => {
@@ -274,7 +275,7 @@ impl Parser {
match param_idx {
// Finish last parameter if not already maxed
- MAX_PARAMS => (),
+ MAX_OSC_PARAMS => (),
// First param is special - 0 to current byte index
0 => {
@@ -294,11 +295,10 @@ impl Parser {
},
Action::Unhook => performer.unhook(),
Action::CsiDispatch => {
- if self.num_params == MAX_PARAMS {
+ if self.params.is_full() {
self.ignoring = true;
} else {
- self.params[self.num_params] = self.param;
- self.num_params += 1;
+ self.params.push(self.param);
}
performer.csi_dispatch(
@@ -320,18 +320,17 @@ impl Parser {
}
},
Action::Param => {
- // Completed a param
- let idx = self.num_params;
-
- if idx == MAX_PARAMS {
+ if self.params.is_full() {
self.ignoring = true;
return;
}
if byte == b';' {
- self.params[idx] = self.param;
+ self.params.push(self.param);
+ self.param = 0;
+ } else if byte == b':' {
+ self.params.extend(self.param);
self.param = 0;
- self.num_params += 1;
} else {
// Continue collecting bytes into param
self.param = self.param.saturating_mul(10);
@@ -342,8 +341,9 @@ impl Parser {
// Reset everything on ESC/CSI/DCS entry
self.intermediate_idx = 0;
self.ignoring = false;
- self.num_params = 0;
self.param = 0;
+
+ self.params.clear();
},
Action::BeginUtf8 => self.process_utf8(performer, byte),
Action::Ignore => (),
@@ -378,7 +378,7 @@ pub trait Perform {
///
/// The `ignore` flag indicates that more than two intermediates arrived and
/// subsequent characters were ignored.
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, action: char);
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, action: char);
/// Pass bytes as part of a device control string to the handle chosen in `hook`. C0 controls
/// will also be passed to the handler.
@@ -398,7 +398,7 @@ pub trait Perform {
/// The `ignore` flag indicates that either more than two intermediates arrived
/// or the number of parameters exceeded the maximum supported length,
/// and subsequent characters were ignored.
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, action: char);
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, action: char);
/// The final character of an escape sequence has arrived.
///
@@ -439,7 +439,7 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn hook(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn put(&mut self, _: u8) {}
@@ -452,7 +452,7 @@ mod tests {
self.params = params.iter().map(|p| p.to_vec()).collect();
}
- fn csi_dispatch(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {}
}
@@ -461,7 +461,7 @@ mod tests {
struct CsiDispatcher {
dispatched_csi: bool,
ignore: bool,
- params: Vec<i64>,
+ params: Vec<Vec<i64>>,
intermediates: Vec<u8>,
}
@@ -470,7 +470,7 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn hook(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn put(&mut self, _: u8) {}
@@ -478,11 +478,11 @@ mod tests {
fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {}
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, _: char) {
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, _: char) {
self.intermediates = intermediates.to_vec();
- self.params = params.to_vec();
- self.ignore = ignore;
self.dispatched_csi = true;
+ self.params = params.iter().map(|subparam| subparam.to_vec()).collect();
+ self.ignore = ignore;
}
fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {}
@@ -503,9 +503,9 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
self.intermediates = intermediates.to_vec();
- self.params = params.to_vec();
+ self.params = params.iter().map(|x| x.to_vec()).flatten().collect();
self.ignore = ignore;
self.c = Some(c);
self.dispatched_dcs = true;
@@ -521,7 +521,7 @@ mod tests {
fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {}
- fn csi_dispatch(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn esc_dispatch(&mut self, _: &[u8], _: bool, _: u8) {}
}
@@ -539,7 +539,7 @@ mod tests {
fn execute(&mut self, _: u8) {}
- fn hook(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn hook(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn put(&mut self, _: u8) {}
@@ -547,7 +547,7 @@ mod tests {
fn osc_dispatch(&mut self, _: &[&[u8]], _: bool) {}
- fn csi_dispatch(&mut self, _: &[i64], _: &[u8], _: bool, _: char) {}
+ fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, _: char) {}
fn esc_dispatch(&mut self, intermediates: &[u8], ignore: bool, byte: u8) {
self.intermediates = intermediates.to_vec();
@@ -588,17 +588,18 @@ mod tests {
#[test]
fn parse_osc_max_params() {
- static INPUT: &[u8] = b"\x1b];;;;;;;;;;;;;;;;;\x1b";
+ let params = std::iter::repeat(";").take(params::MAX_PARAMS + 1).collect::<String>();
+ let input = format!("\x1b]{}\x1b", &params[..]).into_bytes();
let mut dispatcher = OscDispatcher::default();
let mut parser = Parser::new();
- for byte in INPUT {
- parser.advance(&mut dispatcher, *byte);
+ for byte in input {
+ parser.advance(&mut dispatcher, byte);
}
// Check that flag is set and thus osc_dispatch assertions ran.
assert!(dispatcher.dispatched_osc);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), MAX_OSC_PARAMS);
for param in dispatcher.params.iter() {
assert_eq!(param.len(), 0);
}
@@ -606,18 +607,19 @@ mod tests {
#[test]
fn parse_dcs_max_params() {
- static INPUT: &[u8] = b"\x1bP1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;1;p\x1b";
+ let params = std::iter::repeat("1;").take(params::MAX_PARAMS + 1).collect::<String>();
+ let input = format!("\x1bP{}p", &params[..]).into_bytes();
let mut dispatcher = DcsDispatcher::default();
let mut parser = Parser::new();
- for byte in INPUT {
- parser.advance(&mut dispatcher, *byte);
+ for byte in input {
+ parser.advance(&mut dispatcher, byte);
}
// Check that flag is set and thus osc_dispatch assertions ran.
assert!(dispatcher.ignore);
assert!(dispatcher.dispatched_dcs);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), params::MAX_PARAMS);
for param in dispatcher.params.iter() {
assert_eq!(*param, 1);
}
@@ -656,7 +658,7 @@ mod tests {
// This will build a list of repeating '1;'s
// The length is MAX_PARAMS - 1 because the last semicolon is interpreted
// as an implicit zero, making the total number of parameters MAX_PARAMS
- let params = std::iter::repeat("1;").take(MAX_PARAMS - 1).collect::<String>();
+ let params = std::iter::repeat("1;").take(params::MAX_PARAMS - 1).collect::<String>();
let input = format!("\x1b[{}p", &params[..]).into_bytes();
let mut dispatcher = CsiDispatcher::default();
@@ -668,7 +670,7 @@ mod tests {
// Check that flag is set and thus csi_dispatch assertions ran.
assert!(dispatcher.dispatched_csi);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), params::MAX_PARAMS);
assert!(!dispatcher.ignore);
}
@@ -677,7 +679,7 @@ mod tests {
// This will build a list of repeating '1;'s
// The length is MAX_PARAMS because the last semicolon is interpreted
// as an implicit zero, making the total number of parameters MAX_PARAMS + 1
- let params = std::iter::repeat("1;").take(MAX_PARAMS).collect::<String>();
+ let params = std::iter::repeat("1;").take(params::MAX_PARAMS).collect::<String>();
let input = format!("\x1b[{}p", &params[..]).into_bytes();
let mut dispatcher = CsiDispatcher::default();
@@ -689,7 +691,7 @@ mod tests {
// Check that flag is set and thus csi_dispatch assertions ran.
assert!(dispatcher.dispatched_csi);
- assert_eq!(dispatcher.params.len(), MAX_PARAMS);
+ assert_eq!(dispatcher.params.len(), params::MAX_PARAMS);
assert!(dispatcher.ignore);
}
@@ -702,7 +704,7 @@ mod tests {
parser.advance(&mut dispatcher, *byte);
}
- assert_eq!(dispatcher.params, &[4, 0]);
+ assert_eq!(dispatcher.params, &[[4], [0]]);
}
#[test]
@@ -716,7 +718,7 @@ mod tests {
}
// Check that flag is set and thus osc_dispatch assertions ran.
- assert_eq!(dispatcher.params, &[0, 4]);
+ assert_eq!(dispatcher.params, &[[0], [4]]);
}
#[test]
@@ -730,7 +732,7 @@ mod tests {
parser.advance(&mut dispatcher, *byte);
}
- assert_eq!(dispatcher.params, &[i64::MAX as i64]);
+ assert_eq!(dispatcher.params, &[[i64::MAX as i64]]);
}
#[test]
@@ -746,7 +748,23 @@ mod tests {
assert!(dispatcher.dispatched_csi);
assert!(!dispatcher.ignore);
assert_eq!(dispatcher.intermediates, &[b'?']);
- assert_eq!(dispatcher.params, &[1049]);
+ assert_eq!(dispatcher.params, &[[1049]]);
+ }
+
+ #[test]
+ fn csi_subparameters() {
+ static INPUT: &[u8] = b"\x1b[38:2:255:0:255;1m";
+ let mut dispatcher = CsiDispatcher::default();
+ let mut parser = Parser::new();
+
+ for byte in INPUT {
+ parser.advance(&mut dispatcher, *byte);
+ }
+
+ assert!(dispatcher.dispatched_csi);
+ assert!(!dispatcher.ignore);
+ assert_eq!(dispatcher.intermediates, &[]);
+ assert_eq!(dispatcher.params, &[vec![38, 2, 255, 0, 255], vec![1]]);
}
#[test]
@@ -887,7 +905,7 @@ mod bench {
black_box(byte);
}
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn hook(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
black_box((params, intermediates, ignore, c));
}
@@ -901,7 +919,7 @@ mod bench {
black_box((params, bell_terminated));
}
- fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) {
+ fn csi_dispatch(&mut self, params: &Params, intermediates: &[u8], ignore: bool, c: char) {
black_box((params, intermediates, ignore, c));
}
diff --git a/src/params.rs b/src/params.rs
new file mode 100644
index 0000000..efa24cb
--- /dev/null
+++ b/src/params.rs
@@ -0,0 +1,142 @@
+//! Fixed size parameters list with optional subparameters.
+
+use core::fmt::{self, Debug, Formatter};
+
+pub(crate) const MAX_PARAMS: usize = 32;
+
+#[derive(Default)]
+pub struct Params {
+ /// Number of subparameters for each parameter.
+ ///
+ /// For each entry in the `params` slice, this stores the length of the param as number of
+ /// subparams at the same index as the param in the `params` slice.
+ ///
+ /// At the subparam positions the length will always be `0`.
+ subparams: [u8; MAX_PARAMS],
+
+ /// All parameters and subparameters.
+ params: [i64; MAX_PARAMS],
+
+ /// Number of suparameters in the current parameter.
+ current_subparams: u8,
+
+ /// Total number of parameters and subparameters.
+ len: usize,
+}
+
+impl Params {
+ /// Returns the number of parameters.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.len
+ }
+
+ /// Returns `true` if there are no parameters present.
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.len == 0
+ }
+
+ /// Returns an iterator over all parameters and subparameters.
+ #[inline]
+ pub fn iter(&self) -> ParamsIter<'_> {
+ ParamsIter::new(self)
+ }
+
+ /// Returns `true` if there is no more space for additional parameters.
+ #[inline]
+ pub(crate) fn is_full(&self) -> bool {
+ self.len == MAX_PARAMS
+ }
+
+ /// Clear all parameters.
+ #[inline]
+ pub(crate) fn clear(&mut self) {
+ self.current_subparams = 0;
+ self.len = 0;
+ }
+
+ /// Add an additional parameter.
+ #[inline]
+ pub(crate) fn push(&mut self, item: i64) {
+ self.subparams[self.len - self.current_subparams as usize] = self.current_subparams + 1;
+ self.params[self.len] = item;
+ self.current_subparams = 0;
+ self.len += 1;
+ }
+
+ /// Add an additional subparameter to the current parameter.
+ #[inline]
+ pub(crate) fn extend(&mut self, item: i64) {
+ self.params[self.len] = item;
+ self.current_subparams += 1;
+ self.len += 1;
+ }
+}
+
+impl<'a> IntoIterator for &'a Params {
+ type IntoIter = ParamsIter<'a>;
+ type Item = &'a [i64];
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+/// Immutable subparameter iterator.
+pub struct ParamsIter<'a> {
+ params: &'a Params,
+ index: usize,
+}
+
+impl<'a> ParamsIter<'a> {
+ fn new(params: &'a Params) -> Self {
+ Self { params, index: 0 }
+ }
+}
+
+impl<'a> Iterator for ParamsIter<'a> {
+ type Item = &'a [i64];
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.index >= self.params.len() {
+ return None;
+ }
+
+ // Get all subparameters for the current parameter.
+ let num_subparams = self.params.subparams[self.index];
+ let param = &self.params.params[self.index..self.index + num_subparams as usize];
+
+ // Jump to the next parameter.
+ self.index += num_subparams as usize;
+
+ Some(param)
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let remaining = self.params.len() - self.index;
+ (remaining, Some(remaining))
+ }
+}
+
+impl Debug for Params {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "[")?;
+
+ for (i, param) in self.iter().enumerate() {
+ if i != 0 {
+ write!(f, ";")?;
+ }
+
+ for (i, subparam) in param.iter().enumerate() {
+ if i != 0 {
+ write!(f, ":")?;
+ }
+
+ subparam.fmt(f)?;
+ }
+ }
+
+ write!(f, "]")
+ }
+}
diff --git a/src/table.rs b/src/table.rs
index c19e4ab..f2c0105 100644
--- a/src/table.rs
+++ b/src/table.rs
@@ -64,9 +64,8 @@ generate_state_changes!(state_changes, {
0x1c..=0x1f => (Anywhere, Execute),
0x7f => (Anywhere, Ignore),
0x20..=0x2f => (CsiIntermediate, Collect),
- 0x3a => (CsiIgnore, None),
0x30..=0x39 => (CsiParam, Param),
- 0x3b => (CsiParam, Param),
+ 0x3a..=0x3b => (CsiParam, Param),
0x3c..=0x3f => (CsiParam, Collect),
0x40..=0x7e => (Ground, CsiDispatch),
},
@@ -85,9 +84,8 @@ generate_state_changes!(state_changes, {
0x19 => (Anywhere, Execute),
0x1c..=0x1f => (Anywhere, Execute),
0x30..=0x39 => (Anywhere, Param),
- 0x3b => (Anywhere, Param),
+ 0x3a..=0x3b => (Anywhere, Param),
0x7f => (Anywhere, Ignore),
- 0x3a => (CsiIgnore, None),
0x3c..=0x3f => (CsiIgnore, None),
0x20..=0x2f => (CsiIntermediate, Collect),
0x40..=0x7e => (Ground, CsiDispatch),
@@ -108,10 +106,9 @@ generate_state_changes!(state_changes, {
0x19 => (Anywhere, Ignore),
0x1c..=0x1f => (Anywhere, Ignore),
0x7f => (Anywhere, Ignore),
- 0x3a => (DcsIgnore, None),
0x20..=0x2f => (DcsIntermediate, Collect),
0x30..=0x39 => (DcsParam, Param),
- 0x3b => (DcsParam, Param),
+ 0x3a..=0x3b => (DcsParam, Param),
0x3c..=0x3f => (DcsParam, Collect),
0x40..=0x7e => (DcsPassthrough, None),
},
@@ -139,9 +136,8 @@ generate_state_changes!(state_changes, {
0x19 => (Anywhere, Ignore),
0x1c..=0x1f => (Anywhere, Ignore),
0x30..=0x39 => (Anywhere, Param),
- 0x3b => (Anywhere, Param),
+ 0x3a..=0x3b => (Anywhere, Param),
0x7f => (Anywhere, Ignore),
- 0x3a => (DcsIgnore, None),
0x3c..=0x3f => (DcsIgnore, None),
0x20..=0x2f => (DcsIntermediate, Collect),
0x40..=0x7e => (DcsPassthrough, None),