mirror of
https://github.com/wez/wezterm.git
synced 2024-11-29 21:44:24 +03:00
vtparse: allow for CSI parameters to be : separated
This allows us to support the kitty style underline sequence, or the : separated form of the true color escape sequences. refs: https://github.com/wez/wezterm/issues/415
This commit is contained in:
parent
eba263c8dd
commit
b6a422a542
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -4005,7 +4005,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vtparse"
|
name = "vtparse"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
|
@ -30,7 +30,7 @@ terminfo = "0.7"
|
|||||||
unicode-segmentation = "1.7"
|
unicode-segmentation = "1.7"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
xi-unicode = "0.3"
|
xi-unicode = "0.3"
|
||||||
vtparse = { version="0.3", path="../vtparse" }
|
vtparse = { version="0.4", path="../vtparse" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
widgets = ["cassowary", "fnv"]
|
widgets = ["cassowary", "fnv"]
|
||||||
|
@ -6,6 +6,8 @@ use num_derive::*;
|
|||||||
use num_traits::{FromPrimitive, ToPrimitive};
|
use num_traits::{FromPrimitive, ToPrimitive};
|
||||||
use std::fmt::{Display, Error as FmtError, Formatter};
|
use std::fmt::{Display, Error as FmtError, Formatter};
|
||||||
|
|
||||||
|
pub use vtparse::CsiParam;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum CSI {
|
pub enum CSI {
|
||||||
/// SGR: Set Graphics Rendition.
|
/// SGR: Set Graphics Rendition.
|
||||||
@ -33,15 +35,15 @@ pub enum CSI {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Unspecified {
|
pub struct Unspecified {
|
||||||
params: Vec<i64>,
|
pub params: Vec<CsiParam>,
|
||||||
// TODO: can we just make intermediates a single u8?
|
// TODO: can we just make intermediates a single u8?
|
||||||
intermediates: Vec<u8>,
|
pub intermediates: Vec<u8>,
|
||||||
/// if true, more than two intermediates arrived and the
|
/// if true, more than two intermediates arrived and the
|
||||||
/// remaining data was ignored
|
/// remaining data was ignored
|
||||||
ignored_extra_intermediates: bool,
|
pub ignored_extra_intermediates: bool,
|
||||||
/// The final character in the CSI sequence; this typically
|
/// The final character in the CSI sequence; this typically
|
||||||
/// defines how to interpret the other parameters.
|
/// defines how to interpret the other parameters.
|
||||||
control: char,
|
pub control: char,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Unspecified {
|
impl Display for Unspecified {
|
||||||
@ -117,7 +119,7 @@ pub enum DeviceAttributeCodes {
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum DeviceAttribute {
|
pub enum DeviceAttribute {
|
||||||
Code(DeviceAttributeCodes),
|
Code(DeviceAttributeCodes),
|
||||||
Unspecified(u16),
|
Unspecified(CsiParam),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@ -131,7 +133,7 @@ impl DeviceAttributeFlags {
|
|||||||
for item in &self.attributes {
|
for item in &self.attributes {
|
||||||
match item {
|
match item {
|
||||||
DeviceAttribute::Code(c) => write!(f, ";{}", c.to_u16().ok_or_else(|| FmtError)?)?,
|
DeviceAttribute::Code(c) => write!(f, ";{}", c.to_u16().ok_or_else(|| FmtError)?)?,
|
||||||
DeviceAttribute::Unspecified(c) => write!(f, ";{}", *c)?,
|
DeviceAttribute::Unspecified(param) => write!(f, ";{}", param)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
write!(f, "c")?;
|
write!(f, "c")?;
|
||||||
@ -142,12 +144,15 @@ impl DeviceAttributeFlags {
|
|||||||
Self { attributes }
|
Self { attributes }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_params(params: &[i64]) -> Self {
|
fn from_params(params: &[CsiParam]) -> Self {
|
||||||
let mut attributes = Vec::new();
|
let mut attributes = Vec::new();
|
||||||
for p in params {
|
for i in params {
|
||||||
match FromPrimitive::from_i64(*p) {
|
match i {
|
||||||
Some(c) => attributes.push(DeviceAttribute::Code(c)),
|
CsiParam::Integer(p) => match FromPrimitive::from_i64(*p) {
|
||||||
None => attributes.push(DeviceAttribute::Unspecified(*p as u16)),
|
Some(c) => attributes.push(DeviceAttribute::Code(c)),
|
||||||
|
None => attributes.push(DeviceAttribute::Unspecified(i.clone())),
|
||||||
|
},
|
||||||
|
_ => attributes.push(DeviceAttribute::Unspecified(i.clone())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self { attributes }
|
Self { attributes }
|
||||||
@ -941,16 +946,16 @@ impl Display for Cursor {
|
|||||||
/// but in some we build out an enum. The trait helps to generalize
|
/// but in some we build out an enum. The trait helps to generalize
|
||||||
/// the parser code while keeping it relatively terse.
|
/// the parser code while keeping it relatively terse.
|
||||||
trait ParseParams: Sized {
|
trait ParseParams: Sized {
|
||||||
fn parse_params(params: &[i64]) -> Result<Self, ()>;
|
fn parse_params(params: &[CsiParam]) -> Result<Self, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an input parameter into a 1-based unsigned value
|
/// Parse an input parameter into a 1-based unsigned value
|
||||||
impl ParseParams for u32 {
|
impl ParseParams for u32 {
|
||||||
fn parse_params(params: &[i64]) -> Result<u32, ()> {
|
fn parse_params(params: &[CsiParam]) -> Result<u32, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
Ok(1)
|
Ok(1)
|
||||||
} else if params.len() == 1 {
|
} else if params.len() == 1 {
|
||||||
to_1b_u32(params[0])
|
to_1b_u32(¶ms[0])
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
@ -959,11 +964,11 @@ impl ParseParams for u32 {
|
|||||||
|
|
||||||
/// Parse an input parameter into a 1-based unsigned value
|
/// Parse an input parameter into a 1-based unsigned value
|
||||||
impl ParseParams for OneBased {
|
impl ParseParams for OneBased {
|
||||||
fn parse_params(params: &[i64]) -> Result<OneBased, ()> {
|
fn parse_params(params: &[CsiParam]) -> Result<OneBased, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
Ok(OneBased::new(1))
|
Ok(OneBased::new(1))
|
||||||
} else if params.len() == 1 {
|
} else if params.len() == 1 {
|
||||||
OneBased::from_esc_param(params[0])
|
OneBased::from_esc_param(¶ms[0])
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
@ -974,15 +979,15 @@ impl ParseParams for OneBased {
|
|||||||
/// This is typically used to build a struct comprised of
|
/// This is typically used to build a struct comprised of
|
||||||
/// the pair of values.
|
/// the pair of values.
|
||||||
impl ParseParams for (OneBased, OneBased) {
|
impl ParseParams for (OneBased, OneBased) {
|
||||||
fn parse_params(params: &[i64]) -> Result<(OneBased, OneBased), ()> {
|
fn parse_params(params: &[CsiParam]) -> Result<(OneBased, OneBased), ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
Ok((OneBased::new(1), OneBased::new(1)))
|
Ok((OneBased::new(1), OneBased::new(1)))
|
||||||
} else if params.len() == 1 {
|
} else if params.len() == 1 {
|
||||||
Ok((OneBased::from_esc_param(params[0])?, OneBased::new(1)))
|
Ok((OneBased::from_esc_param(¶ms[0])?, OneBased::new(1)))
|
||||||
} else if params.len() == 2 {
|
} else if params.len() == 2 {
|
||||||
Ok((
|
Ok((
|
||||||
OneBased::from_esc_param(params[0])?,
|
OneBased::from_esc_param(¶ms[0])?,
|
||||||
OneBased::from_esc_param(params[1])?,
|
OneBased::from_esc_param(¶ms[1])?,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
@ -1000,11 +1005,14 @@ trait ParamEnum: FromPrimitive {
|
|||||||
|
|
||||||
/// implement ParseParams for the enums that also implement ParamEnum.
|
/// implement ParseParams for the enums that also implement ParamEnum.
|
||||||
impl<T: ParamEnum> ParseParams for T {
|
impl<T: ParamEnum> ParseParams for T {
|
||||||
fn parse_params(params: &[i64]) -> Result<Self, ()> {
|
fn parse_params(params: &[CsiParam]) -> Result<Self, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
Ok(ParamEnum::default())
|
Ok(ParamEnum::default())
|
||||||
} else if params.len() == 1 {
|
} else if params.len() == 1 {
|
||||||
FromPrimitive::from_i64(params[0]).ok_or(())
|
match params[0] {
|
||||||
|
CsiParam::Integer(i) => FromPrimitive::from_i64(i).ok_or(()),
|
||||||
|
CsiParam::ColonList(_) => Err(()),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
@ -1257,7 +1265,7 @@ struct CSIParser<'a> {
|
|||||||
/// In a number of cases an empty params list is used to indicate
|
/// In a number of cases an empty params list is used to indicate
|
||||||
/// default values, especially for SGR, so we need to be careful not
|
/// default values, especially for SGR, so we need to be careful not
|
||||||
/// to update params to an empty slice.
|
/// to update params to an empty slice.
|
||||||
params: Option<&'a [i64]>,
|
params: Option<&'a [CsiParam]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CSI {
|
impl CSI {
|
||||||
@ -1268,7 +1276,7 @@ impl CSI {
|
|||||||
/// If no semantic meaning is known for a subsequence, the remainder
|
/// If no semantic meaning is known for a subsequence, the remainder
|
||||||
/// of the sequence is returned wrapped in a `CSI::Unspecified` container.
|
/// of the sequence is returned wrapped in a `CSI::Unspecified` container.
|
||||||
pub fn parse<'a>(
|
pub fn parse<'a>(
|
||||||
params: &'a [i64],
|
params: &'a [CsiParam],
|
||||||
intermediates: &'a [u8],
|
intermediates: &'a [u8],
|
||||||
ignored_extra_intermediates: bool,
|
ignored_extra_intermediates: bool,
|
||||||
control: char,
|
control: char,
|
||||||
@ -1283,11 +1291,16 @@ impl CSI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A little helper to convert i64 -> u8 if safe
|
/// A little helper to convert i64 -> u8 if safe
|
||||||
fn to_u8(v: i64) -> Result<u8, ()> {
|
fn to_u8(v: &CsiParam) -> Result<u8, ()> {
|
||||||
if v <= i64::from(u8::max_value()) {
|
match v {
|
||||||
Ok(v as u8)
|
CsiParam::ColonList(_) => Err(()),
|
||||||
} else {
|
CsiParam::Integer(v) => {
|
||||||
Err(())
|
if *v <= i64::from(u8::max_value()) {
|
||||||
|
Ok(*v as u8)
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1302,13 +1315,11 @@ fn to_u8(v: i64) -> Result<u8, ()> {
|
|||||||
/// otherwise outside that range, an error is propagated and
|
/// otherwise outside that range, an error is propagated and
|
||||||
/// that will typically case the sequence to be reported via
|
/// that will typically case the sequence to be reported via
|
||||||
/// the Unspecified placeholder.
|
/// the Unspecified placeholder.
|
||||||
fn to_1b_u32(v: i64) -> Result<u32, ()> {
|
fn to_1b_u32(v: &CsiParam) -> Result<u32, ()> {
|
||||||
if v == 0 {
|
match v {
|
||||||
Ok(1)
|
CsiParam::Integer(v) if *v == 0 => Ok(1),
|
||||||
} else if v > 0 && v <= i64::from(u32::max_value()) {
|
CsiParam::Integer(v) if *v > 0 && *v <= i64::from(u32::max_value()) => Ok(*v as u32),
|
||||||
Ok(v as u32)
|
_ => Err(()),
|
||||||
} else {
|
|
||||||
Err(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1338,7 +1349,7 @@ macro_rules! parse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CSIParser<'a> {
|
impl<'a> CSIParser<'a> {
|
||||||
fn parse_next(&mut self, params: &'a [i64]) -> Result<CSI, ()> {
|
fn parse_next(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
|
||||||
match (self.control, self.intermediates) {
|
match (self.control, self.intermediates) {
|
||||||
('@', &[]) => parse!(Edit, InsertCharacter, params),
|
('@', &[]) => parse!(Edit, InsertCharacter, params),
|
||||||
('`', &[]) => parse!(Cursor, CharacterPositionAbsolute, params),
|
('`', &[]) => parse!(Cursor, CharacterPositionAbsolute, params),
|
||||||
@ -1387,8 +1398,8 @@ impl<'a> CSIParser<'a> {
|
|||||||
('t', &[]) => self.window(params).map(CSI::Window),
|
('t', &[]) => self.window(params).map(CSI::Window),
|
||||||
('u', &[]) => noparams!(Cursor, RestoreCursor, params),
|
('u', &[]) => noparams!(Cursor, RestoreCursor, params),
|
||||||
('y', &[b'*']) => {
|
('y', &[b'*']) => {
|
||||||
fn p(params: &[i64], idx: usize) -> Result<i64, ()> {
|
fn p(params: &[CsiParam], idx: usize) -> Result<i64, ()> {
|
||||||
params.get(idx).cloned().ok_or(())
|
params.get(idx).and_then(CsiParam::as_integer).ok_or(())
|
||||||
}
|
}
|
||||||
let request_id = p(params, 0)?;
|
let request_id = p(params, 0)?;
|
||||||
let page_number = p(params, 1)?;
|
let page_number = p(params, 1)?;
|
||||||
@ -1445,7 +1456,7 @@ impl<'a> CSIParser<'a> {
|
|||||||
/// Take care to avoid setting params back to an empty slice
|
/// Take care to avoid setting params back to an empty slice
|
||||||
/// as this would trigger returning a default value and/or
|
/// as this would trigger returning a default value and/or
|
||||||
/// an unterminated parse loop.
|
/// an unterminated parse loop.
|
||||||
fn advance_by<T>(&mut self, n: usize, params: &'a [i64], result: T) -> T {
|
fn advance_by<T>(&mut self, n: usize, params: &'a [CsiParam], result: T) -> T {
|
||||||
let (_, next) = params.split_at(n);
|
let (_, next) = params.split_at(n);
|
||||||
if !next.is_empty() {
|
if !next.is_empty() {
|
||||||
self.params = Some(next);
|
self.params = Some(next);
|
||||||
@ -1453,11 +1464,11 @@ impl<'a> CSIParser<'a> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_style(&mut self, params: &'a [i64]) -> Result<CSI, ()> {
|
fn cursor_style(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
|
||||||
if params.len() != 1 {
|
if params.len() != 1 {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
match FromPrimitive::from_i64(params[0]) {
|
match FromPrimitive::from_i64(params[0].as_integer().unwrap()) {
|
||||||
None => Err(()),
|
None => Err(()),
|
||||||
Some(style) => {
|
Some(style) => {
|
||||||
Ok(self.advance_by(1, params, CSI::Cursor(Cursor::CursorStyle(style))))
|
Ok(self.advance_by(1, params, CSI::Cursor(Cursor::CursorStyle(style))))
|
||||||
@ -1466,17 +1477,17 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dsr(&mut self, params: &'a [i64]) -> Result<CSI, ()> {
|
fn dsr(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
|
||||||
if params == [5] {
|
if params == [CsiParam::Integer(5)] {
|
||||||
Ok(self.advance_by(1, params, CSI::Device(Box::new(Device::StatusReport))))
|
Ok(self.advance_by(1, params, CSI::Device(Box::new(Device::StatusReport))))
|
||||||
} else if params == [6] {
|
} else if params == [CsiParam::Integer(6)] {
|
||||||
Ok(self.advance_by(1, params, CSI::Cursor(Cursor::RequestActivePositionReport)))
|
Ok(self.advance_by(1, params, CSI::Cursor(Cursor::RequestActivePositionReport)))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decstbm(&mut self, params: &'a [i64]) -> Result<CSI, ()> {
|
fn decstbm(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
Ok(CSI::Cursor(Cursor::SetTopAndBottomMargins {
|
Ok(CSI::Cursor(Cursor::SetTopAndBottomMargins {
|
||||||
top: OneBased::new(1),
|
top: OneBased::new(1),
|
||||||
@ -1487,7 +1498,7 @@ impl<'a> CSIParser<'a> {
|
|||||||
1,
|
1,
|
||||||
params,
|
params,
|
||||||
CSI::Cursor(Cursor::SetTopAndBottomMargins {
|
CSI::Cursor(Cursor::SetTopAndBottomMargins {
|
||||||
top: OneBased::from_esc_param(params[0])?,
|
top: OneBased::from_esc_param(¶ms[0])?,
|
||||||
bottom: OneBased::new(u32::max_value()),
|
bottom: OneBased::new(u32::max_value()),
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@ -1496,8 +1507,8 @@ impl<'a> CSIParser<'a> {
|
|||||||
2,
|
2,
|
||||||
params,
|
params,
|
||||||
CSI::Cursor(Cursor::SetTopAndBottomMargins {
|
CSI::Cursor(Cursor::SetTopAndBottomMargins {
|
||||||
top: OneBased::from_esc_param(params[0])?,
|
top: OneBased::from_esc_param(¶ms[0])?,
|
||||||
bottom: OneBased::from_esc_param_with_big_default(params[1])?,
|
bottom: OneBased::from_esc_param_with_big_default(¶ms[1])?,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@ -1505,19 +1516,21 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xterm_key_modifier(&mut self, params: &'a [i64]) -> Result<CSI, ()> {
|
fn xterm_key_modifier(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
|
||||||
if params.len() == 2 {
|
if params.len() == 2 {
|
||||||
let resource = XtermKeyModifierResource::parse(params[0]).ok_or_else(|| ())?;
|
let resource = XtermKeyModifierResource::parse(params[0].as_integer().unwrap())
|
||||||
|
.ok_or_else(|| ())?;
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
2,
|
2,
|
||||||
params,
|
params,
|
||||||
CSI::Mode(Mode::XtermKeyMode {
|
CSI::Mode(Mode::XtermKeyMode {
|
||||||
resource,
|
resource,
|
||||||
value: Some(params[1]),
|
value: Some(params[1].as_integer().ok_or_else(|| ())?),
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
} else if params.len() == 1 {
|
} else if params.len() == 1 {
|
||||||
let resource = XtermKeyModifierResource::parse(params[0]).ok_or_else(|| ())?;
|
let resource = XtermKeyModifierResource::parse(params[0].as_integer().unwrap())
|
||||||
|
.ok_or_else(|| ())?;
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
1,
|
1,
|
||||||
params,
|
params,
|
||||||
@ -1531,7 +1544,7 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decslrm(&mut self, params: &'a [i64]) -> Result<CSI, ()> {
|
fn decslrm(&mut self, params: &'a [CsiParam]) -> Result<CSI, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
// with no params this is a request to save the cursor
|
// with no params this is a request to save the cursor
|
||||||
// and is technically in conflict with SetLeftAndRightMargins.
|
// and is technically in conflict with SetLeftAndRightMargins.
|
||||||
@ -1544,7 +1557,7 @@ impl<'a> CSIParser<'a> {
|
|||||||
1,
|
1,
|
||||||
params,
|
params,
|
||||||
CSI::Cursor(Cursor::SetLeftAndRightMargins {
|
CSI::Cursor(Cursor::SetLeftAndRightMargins {
|
||||||
left: OneBased::from_esc_param(params[0])?,
|
left: OneBased::from_esc_param(¶ms[0])?,
|
||||||
right: OneBased::new(u32::max_value()),
|
right: OneBased::new(u32::max_value()),
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
@ -1553,8 +1566,8 @@ impl<'a> CSIParser<'a> {
|
|||||||
2,
|
2,
|
||||||
params,
|
params,
|
||||||
CSI::Cursor(Cursor::SetLeftAndRightMargins {
|
CSI::Cursor(Cursor::SetLeftAndRightMargins {
|
||||||
left: OneBased::from_esc_param(params[0])?,
|
left: OneBased::from_esc_param(¶ms[0])?,
|
||||||
right: OneBased::from_esc_param(params[1])?,
|
right: OneBased::from_esc_param(¶ms[1])?,
|
||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@ -1562,52 +1575,52 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn req_primary_device_attributes(&mut self, params: &'a [i64]) -> Result<Device, ()> {
|
fn req_primary_device_attributes(&mut self, params: &'a [CsiParam]) -> Result<Device, ()> {
|
||||||
if params == [] {
|
if params == [] {
|
||||||
Ok(Device::RequestPrimaryDeviceAttributes)
|
Ok(Device::RequestPrimaryDeviceAttributes)
|
||||||
} else if params == [0] {
|
} else if params == [CsiParam::Integer(0)] {
|
||||||
Ok(self.advance_by(1, params, Device::RequestPrimaryDeviceAttributes))
|
Ok(self.advance_by(1, params, Device::RequestPrimaryDeviceAttributes))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn req_terminal_name_and_version(&mut self, params: &'a [i64]) -> Result<Device, ()> {
|
fn req_terminal_name_and_version(&mut self, params: &'a [CsiParam]) -> Result<Device, ()> {
|
||||||
if params == [] {
|
if params == [] {
|
||||||
Ok(Device::RequestTerminalNameAndVersion)
|
Ok(Device::RequestTerminalNameAndVersion)
|
||||||
} else if params == [0] {
|
} else if params == [CsiParam::Integer(0)] {
|
||||||
Ok(self.advance_by(1, params, Device::RequestTerminalNameAndVersion))
|
Ok(self.advance_by(1, params, Device::RequestTerminalNameAndVersion))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn req_secondary_device_attributes(&mut self, params: &'a [i64]) -> Result<Device, ()> {
|
fn req_secondary_device_attributes(&mut self, params: &'a [CsiParam]) -> Result<Device, ()> {
|
||||||
if params == [] {
|
if params == [] {
|
||||||
Ok(Device::RequestSecondaryDeviceAttributes)
|
Ok(Device::RequestSecondaryDeviceAttributes)
|
||||||
} else if params == [0] {
|
} else if params == [CsiParam::Integer(0)] {
|
||||||
Ok(self.advance_by(1, params, Device::RequestSecondaryDeviceAttributes))
|
Ok(self.advance_by(1, params, Device::RequestSecondaryDeviceAttributes))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn secondary_device_attributes(&mut self, params: &'a [i64]) -> Result<Device, ()> {
|
fn secondary_device_attributes(&mut self, params: &'a [CsiParam]) -> Result<Device, ()> {
|
||||||
if params == [1, 0] {
|
if params == [CsiParam::Integer(1), CsiParam::Integer(0)] {
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
2,
|
2,
|
||||||
params,
|
params,
|
||||||
Device::DeviceAttributes(DeviceAttributes::Vt101WithNoOptions),
|
Device::DeviceAttributes(DeviceAttributes::Vt101WithNoOptions),
|
||||||
))
|
))
|
||||||
} else if params == [6] {
|
} else if params == [CsiParam::Integer(6)] {
|
||||||
Ok(self.advance_by(1, params, Device::DeviceAttributes(DeviceAttributes::Vt102)))
|
Ok(self.advance_by(1, params, Device::DeviceAttributes(DeviceAttributes::Vt102)))
|
||||||
} else if params == [1, 2] {
|
} else if params == [CsiParam::Integer(1), CsiParam::Integer(2)] {
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
2,
|
2,
|
||||||
params,
|
params,
|
||||||
Device::DeviceAttributes(DeviceAttributes::Vt100WithAdvancedVideoOption),
|
Device::DeviceAttributes(DeviceAttributes::Vt100WithAdvancedVideoOption),
|
||||||
))
|
))
|
||||||
} else if !params.is_empty() && params[0] == 62 {
|
} else if !params.is_empty() && params[0] == CsiParam::Integer(62) {
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
params.len(),
|
params.len(),
|
||||||
params,
|
params,
|
||||||
@ -1615,7 +1628,7 @@ impl<'a> CSIParser<'a> {
|
|||||||
DeviceAttributeFlags::from_params(¶ms[1..]),
|
DeviceAttributeFlags::from_params(¶ms[1..]),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
} else if !params.is_empty() && params[0] == 63 {
|
} else if !params.is_empty() && params[0] == CsiParam::Integer(63) {
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
params.len(),
|
params.len(),
|
||||||
params,
|
params,
|
||||||
@ -1623,7 +1636,7 @@ impl<'a> CSIParser<'a> {
|
|||||||
DeviceAttributeFlags::from_params(¶ms[1..]),
|
DeviceAttributeFlags::from_params(¶ms[1..]),
|
||||||
)),
|
)),
|
||||||
))
|
))
|
||||||
} else if !params.is_empty() && params[0] == 64 {
|
} else if !params.is_empty() && params[0] == CsiParam::Integer(64) {
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
params.len(),
|
params.len(),
|
||||||
params,
|
params,
|
||||||
@ -1637,13 +1650,15 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse extended mouse reports known as SGR 1006 mode
|
/// Parse extended mouse reports known as SGR 1006 mode
|
||||||
fn mouse_sgr1006(&mut self, params: &'a [i64]) -> Result<MouseReport, ()> {
|
fn mouse_sgr1006(&mut self, params: &'a [CsiParam]) -> Result<MouseReport, ()> {
|
||||||
if params.len() != 3 {
|
if params.len() != 3 {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let p0 = params[0].as_integer().unwrap();
|
||||||
|
|
||||||
// 'M' encodes a press, 'm' a release.
|
// 'M' encodes a press, 'm' a release.
|
||||||
let button = match (self.control, params[0] & 0b110_0011) {
|
let button = match (self.control, p0 & 0b110_0011) {
|
||||||
('M', 0) => MouseButton::Button1Press,
|
('M', 0) => MouseButton::Button1Press,
|
||||||
('m', 0) => MouseButton::Button1Release,
|
('m', 0) => MouseButton::Button1Release,
|
||||||
('M', 1) => MouseButton::Button2Press,
|
('M', 1) => MouseButton::Button2Press,
|
||||||
@ -1672,30 +1687,36 @@ impl<'a> CSIParser<'a> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut modifiers = Modifiers::NONE;
|
let mut modifiers = Modifiers::NONE;
|
||||||
if params[0] & 4 != 0 {
|
if p0 & 4 != 0 {
|
||||||
modifiers |= Modifiers::SHIFT;
|
modifiers |= Modifiers::SHIFT;
|
||||||
}
|
}
|
||||||
if params[0] & 8 != 0 {
|
if p0 & 8 != 0 {
|
||||||
modifiers |= Modifiers::ALT;
|
modifiers |= Modifiers::ALT;
|
||||||
}
|
}
|
||||||
if params[0] & 16 != 0 {
|
if p0 & 16 != 0 {
|
||||||
modifiers |= Modifiers::CTRL;
|
modifiers |= Modifiers::CTRL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let p1 = params[1].as_integer().unwrap();
|
||||||
|
let p2 = params[2].as_integer().unwrap();
|
||||||
|
|
||||||
Ok(self.advance_by(
|
Ok(self.advance_by(
|
||||||
3,
|
3,
|
||||||
params,
|
params,
|
||||||
MouseReport::SGR1006 {
|
MouseReport::SGR1006 {
|
||||||
x: params[1] as u16,
|
x: p1 as u16,
|
||||||
y: params[2] as u16,
|
y: p2 as u16,
|
||||||
button,
|
button,
|
||||||
modifiers,
|
modifiers,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dec(&mut self, params: &'a [i64]) -> Result<DecPrivateMode, ()> {
|
fn dec(&mut self, params: &'a [CsiParam]) -> Result<DecPrivateMode, ()> {
|
||||||
let p0 = *params.get(0).ok_or_else(|| ())?;
|
let p0 = params
|
||||||
|
.get(0)
|
||||||
|
.and_then(CsiParam::as_integer)
|
||||||
|
.ok_or_else(|| ())?;
|
||||||
match FromPrimitive::from_i64(p0) {
|
match FromPrimitive::from_i64(p0) {
|
||||||
None => Ok(self.advance_by(
|
None => Ok(self.advance_by(
|
||||||
1,
|
1,
|
||||||
@ -1706,8 +1727,11 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn terminal_mode(&mut self, params: &'a [i64]) -> Result<TerminalMode, ()> {
|
fn terminal_mode(&mut self, params: &'a [CsiParam]) -> Result<TerminalMode, ()> {
|
||||||
let p0 = *params.get(0).ok_or_else(|| ())?;
|
let p0 = params
|
||||||
|
.get(0)
|
||||||
|
.and_then(CsiParam::as_integer)
|
||||||
|
.ok_or_else(|| ())?;
|
||||||
match FromPrimitive::from_i64(p0) {
|
match FromPrimitive::from_i64(p0) {
|
||||||
None => {
|
None => {
|
||||||
Ok(self.advance_by(1, params, TerminalMode::Unspecified(p0.to_u16().ok_or(())?)))
|
Ok(self.advance_by(1, params, TerminalMode::Unspecified(p0.to_u16().ok_or(())?)))
|
||||||
@ -1716,100 +1740,103 @@ impl<'a> CSIParser<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_sgr_color(&mut self, params: &'a [i64]) -> Result<ColorSpec, ()> {
|
fn parse_sgr_color(&mut self, params: &'a [CsiParam]) -> Result<ColorSpec, ()> {
|
||||||
if params.len() >= 5 && params[1] == 2 {
|
if params.len() >= 5 && params[1].as_integer() == Some(2) {
|
||||||
let red = to_u8(params[2])?;
|
let red = to_u8(¶ms[2])?;
|
||||||
let green = to_u8(params[3])?;
|
let green = to_u8(¶ms[3])?;
|
||||||
let blue = to_u8(params[4])?;
|
let blue = to_u8(¶ms[4])?;
|
||||||
let res = RgbColor::new(red, green, blue).into();
|
let res = RgbColor::new(red, green, blue).into();
|
||||||
Ok(self.advance_by(5, params, res))
|
Ok(self.advance_by(5, params, res))
|
||||||
} else if params.len() >= 3 && params[1] == 5 {
|
} else if params.len() >= 3 && params[1].as_integer() == Some(5) {
|
||||||
let idx = to_u8(params[2])?;
|
let idx = to_u8(¶ms[2])?;
|
||||||
Ok(self.advance_by(3, params, ColorSpec::PaletteIndex(idx)))
|
Ok(self.advance_by(3, params, ColorSpec::PaletteIndex(idx)))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window(&mut self, params: &'a [i64]) -> Result<Window, ()> {
|
fn window(&mut self, params: &'a [CsiParam]) -> Result<Window, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
Err(())
|
Err(())
|
||||||
} else {
|
} else {
|
||||||
let arg1 = params.get(1).cloned();
|
let arg1 = params.get(1).and_then(CsiParam::as_integer);
|
||||||
let arg2 = params.get(2).cloned();
|
let arg2 = params.get(2).and_then(CsiParam::as_integer);
|
||||||
match params[0] {
|
match params[0].as_integer() {
|
||||||
1 => Ok(Window::DeIconify),
|
None => Err(()),
|
||||||
2 => Ok(Window::Iconify),
|
Some(p) => match p {
|
||||||
3 => Ok(Window::MoveWindow {
|
1 => Ok(Window::DeIconify),
|
||||||
x: arg1.unwrap_or(0),
|
2 => Ok(Window::Iconify),
|
||||||
y: arg2.unwrap_or(0),
|
3 => Ok(Window::MoveWindow {
|
||||||
}),
|
x: arg1.unwrap_or(0),
|
||||||
4 => Ok(Window::ResizeWindowPixels {
|
y: arg2.unwrap_or(0),
|
||||||
height: arg1,
|
}),
|
||||||
width: arg2,
|
4 => Ok(Window::ResizeWindowPixels {
|
||||||
}),
|
|
||||||
5 => Ok(Window::RaiseWindow),
|
|
||||||
6 => match params.len() {
|
|
||||||
1 => Ok(Window::LowerWindow),
|
|
||||||
3 => Ok(Window::ReportCellSizePixelsResponse {
|
|
||||||
height: arg1,
|
height: arg1,
|
||||||
width: arg2,
|
width: arg2,
|
||||||
}),
|
}),
|
||||||
|
5 => Ok(Window::RaiseWindow),
|
||||||
|
6 => match params.len() {
|
||||||
|
1 => Ok(Window::LowerWindow),
|
||||||
|
3 => Ok(Window::ReportCellSizePixelsResponse {
|
||||||
|
height: arg1,
|
||||||
|
width: arg2,
|
||||||
|
}),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
|
7 => Ok(Window::RefreshWindow),
|
||||||
|
8 => Ok(Window::ResizeWindowCells {
|
||||||
|
height: arg1,
|
||||||
|
width: arg2,
|
||||||
|
}),
|
||||||
|
9 => match arg1 {
|
||||||
|
Some(0) => Ok(Window::RestoreMaximizedWindow),
|
||||||
|
Some(1) => Ok(Window::MaximizeWindow),
|
||||||
|
Some(2) => Ok(Window::MaximizeWindowVertically),
|
||||||
|
Some(3) => Ok(Window::MaximizeWindowHorizontally),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
|
10 => match arg1 {
|
||||||
|
Some(0) => Ok(Window::UndoFullScreenMode),
|
||||||
|
Some(1) => Ok(Window::ChangeToFullScreenMode),
|
||||||
|
Some(2) => Ok(Window::ToggleFullScreen),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
|
11 => Ok(Window::ReportWindowState),
|
||||||
|
13 => match arg1 {
|
||||||
|
None => Ok(Window::ReportWindowPosition),
|
||||||
|
Some(2) => Ok(Window::ReportTextAreaPosition),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
|
14 => match arg1 {
|
||||||
|
None => Ok(Window::ReportTextAreaSizePixels),
|
||||||
|
Some(2) => Ok(Window::ReportWindowSizePixels),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
|
15 => Ok(Window::ReportScreenSizePixels),
|
||||||
|
16 => Ok(Window::ReportCellSizePixels),
|
||||||
|
18 => Ok(Window::ReportTextAreaSizeCells),
|
||||||
|
19 => Ok(Window::ReportScreenSizeCells),
|
||||||
|
20 => Ok(Window::ReportIconLabel),
|
||||||
|
21 => Ok(Window::ReportWindowTitle),
|
||||||
|
22 => match arg1 {
|
||||||
|
Some(0) => Ok(Window::PushIconAndWindowTitle),
|
||||||
|
Some(1) => Ok(Window::PushIconTitle),
|
||||||
|
Some(2) => Ok(Window::PushWindowTitle),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
|
23 => match arg1 {
|
||||||
|
Some(0) => Ok(Window::PopIconAndWindowTitle),
|
||||||
|
Some(1) => Ok(Window::PopIconTitle),
|
||||||
|
Some(2) => Ok(Window::PopWindowTitle),
|
||||||
|
_ => Err(()),
|
||||||
|
},
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
},
|
},
|
||||||
7 => Ok(Window::RefreshWindow),
|
|
||||||
8 => Ok(Window::ResizeWindowCells {
|
|
||||||
height: arg1,
|
|
||||||
width: arg2,
|
|
||||||
}),
|
|
||||||
9 => match arg1 {
|
|
||||||
Some(0) => Ok(Window::RestoreMaximizedWindow),
|
|
||||||
Some(1) => Ok(Window::MaximizeWindow),
|
|
||||||
Some(2) => Ok(Window::MaximizeWindowVertically),
|
|
||||||
Some(3) => Ok(Window::MaximizeWindowHorizontally),
|
|
||||||
_ => Err(()),
|
|
||||||
},
|
|
||||||
10 => match arg1 {
|
|
||||||
Some(0) => Ok(Window::UndoFullScreenMode),
|
|
||||||
Some(1) => Ok(Window::ChangeToFullScreenMode),
|
|
||||||
Some(2) => Ok(Window::ToggleFullScreen),
|
|
||||||
_ => Err(()),
|
|
||||||
},
|
|
||||||
11 => Ok(Window::ReportWindowState),
|
|
||||||
13 => match arg1 {
|
|
||||||
None => Ok(Window::ReportWindowPosition),
|
|
||||||
Some(2) => Ok(Window::ReportTextAreaPosition),
|
|
||||||
_ => Err(()),
|
|
||||||
},
|
|
||||||
14 => match arg1 {
|
|
||||||
None => Ok(Window::ReportTextAreaSizePixels),
|
|
||||||
Some(2) => Ok(Window::ReportWindowSizePixels),
|
|
||||||
_ => Err(()),
|
|
||||||
},
|
|
||||||
15 => Ok(Window::ReportScreenSizePixels),
|
|
||||||
16 => Ok(Window::ReportCellSizePixels),
|
|
||||||
18 => Ok(Window::ReportTextAreaSizeCells),
|
|
||||||
19 => Ok(Window::ReportScreenSizeCells),
|
|
||||||
20 => Ok(Window::ReportIconLabel),
|
|
||||||
21 => Ok(Window::ReportWindowTitle),
|
|
||||||
22 => match arg1 {
|
|
||||||
Some(0) => Ok(Window::PushIconAndWindowTitle),
|
|
||||||
Some(1) => Ok(Window::PushIconTitle),
|
|
||||||
Some(2) => Ok(Window::PushWindowTitle),
|
|
||||||
_ => Err(()),
|
|
||||||
},
|
|
||||||
23 => match arg1 {
|
|
||||||
Some(0) => Ok(Window::PopIconAndWindowTitle),
|
|
||||||
Some(1) => Ok(Window::PopIconTitle),
|
|
||||||
Some(2) => Ok(Window::PopWindowTitle),
|
|
||||||
_ => Err(()),
|
|
||||||
},
|
|
||||||
_ => Err(()),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sgr(&mut self, params: &'a [i64]) -> Result<Sgr, ()> {
|
fn sgr(&mut self, params: &'a [CsiParam]) -> Result<Sgr, ()> {
|
||||||
if params.is_empty() {
|
if params.is_empty() {
|
||||||
// With no parameters, treat as equivalent to Reset.
|
// With no parameters, treat as equivalent to Reset.
|
||||||
Ok(Sgr::Reset)
|
Ok(Sgr::Reset)
|
||||||
@ -1821,97 +1848,130 @@ impl<'a> CSIParser<'a> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
match FromPrimitive::from_i64(params[0]) {
|
match params[0] {
|
||||||
None => Err(()),
|
CsiParam::Integer(i) => match FromPrimitive::from_i64(i) {
|
||||||
Some(sgr) => match sgr {
|
None => Err(()),
|
||||||
SgrCode::Reset => one!(Sgr::Reset),
|
Some(sgr) => match sgr {
|
||||||
SgrCode::IntensityBold => one!(Sgr::Intensity(Intensity::Bold)),
|
SgrCode::Reset => one!(Sgr::Reset),
|
||||||
SgrCode::IntensityDim => one!(Sgr::Intensity(Intensity::Half)),
|
SgrCode::IntensityBold => one!(Sgr::Intensity(Intensity::Bold)),
|
||||||
SgrCode::NormalIntensity => one!(Sgr::Intensity(Intensity::Normal)),
|
SgrCode::IntensityDim => one!(Sgr::Intensity(Intensity::Half)),
|
||||||
SgrCode::UnderlineOn => one!(Sgr::Underline(Underline::Single)),
|
SgrCode::NormalIntensity => one!(Sgr::Intensity(Intensity::Normal)),
|
||||||
SgrCode::UnderlineDouble => one!(Sgr::Underline(Underline::Double)),
|
SgrCode::UnderlineOn => one!(Sgr::Underline(Underline::Single)),
|
||||||
SgrCode::UnderlineCurly => one!(Sgr::Underline(Underline::Curly)),
|
SgrCode::UnderlineDouble => one!(Sgr::Underline(Underline::Double)),
|
||||||
SgrCode::UnderlineDotted => one!(Sgr::Underline(Underline::Dotted)),
|
SgrCode::UnderlineCurly => one!(Sgr::Underline(Underline::Curly)),
|
||||||
SgrCode::UnderlineDashed => one!(Sgr::Underline(Underline::Dashed)),
|
SgrCode::UnderlineDotted => one!(Sgr::Underline(Underline::Dotted)),
|
||||||
SgrCode::UnderlineOff => one!(Sgr::Underline(Underline::None)),
|
SgrCode::UnderlineDashed => one!(Sgr::Underline(Underline::Dashed)),
|
||||||
SgrCode::UnderlineColor => {
|
SgrCode::UnderlineOff => one!(Sgr::Underline(Underline::None)),
|
||||||
self.parse_sgr_color(params).map(Sgr::UnderlineColor)
|
SgrCode::UnderlineColor => {
|
||||||
}
|
self.parse_sgr_color(params).map(Sgr::UnderlineColor)
|
||||||
SgrCode::ResetUnderlineColor => one!(Sgr::UnderlineColor(ColorSpec::default())),
|
}
|
||||||
SgrCode::BlinkOn => one!(Sgr::Blink(Blink::Slow)),
|
SgrCode::ResetUnderlineColor => {
|
||||||
SgrCode::RapidBlinkOn => one!(Sgr::Blink(Blink::Rapid)),
|
one!(Sgr::UnderlineColor(ColorSpec::default()))
|
||||||
SgrCode::BlinkOff => one!(Sgr::Blink(Blink::None)),
|
}
|
||||||
SgrCode::ItalicOn => one!(Sgr::Italic(true)),
|
SgrCode::BlinkOn => one!(Sgr::Blink(Blink::Slow)),
|
||||||
SgrCode::ItalicOff => one!(Sgr::Italic(false)),
|
SgrCode::RapidBlinkOn => one!(Sgr::Blink(Blink::Rapid)),
|
||||||
SgrCode::ForegroundColor => self.parse_sgr_color(params).map(Sgr::Foreground),
|
SgrCode::BlinkOff => one!(Sgr::Blink(Blink::None)),
|
||||||
SgrCode::ForegroundBlack => one!(Sgr::Foreground(AnsiColor::Black.into())),
|
SgrCode::ItalicOn => one!(Sgr::Italic(true)),
|
||||||
SgrCode::ForegroundRed => one!(Sgr::Foreground(AnsiColor::Maroon.into())),
|
SgrCode::ItalicOff => one!(Sgr::Italic(false)),
|
||||||
SgrCode::ForegroundGreen => one!(Sgr::Foreground(AnsiColor::Green.into())),
|
SgrCode::ForegroundColor => {
|
||||||
SgrCode::ForegroundYellow => one!(Sgr::Foreground(AnsiColor::Olive.into())),
|
self.parse_sgr_color(params).map(Sgr::Foreground)
|
||||||
SgrCode::ForegroundBlue => one!(Sgr::Foreground(AnsiColor::Navy.into())),
|
}
|
||||||
SgrCode::ForegroundMagenta => one!(Sgr::Foreground(AnsiColor::Purple.into())),
|
SgrCode::ForegroundBlack => one!(Sgr::Foreground(AnsiColor::Black.into())),
|
||||||
SgrCode::ForegroundCyan => one!(Sgr::Foreground(AnsiColor::Teal.into())),
|
SgrCode::ForegroundRed => one!(Sgr::Foreground(AnsiColor::Maroon.into())),
|
||||||
SgrCode::ForegroundWhite => one!(Sgr::Foreground(AnsiColor::Silver.into())),
|
SgrCode::ForegroundGreen => one!(Sgr::Foreground(AnsiColor::Green.into())),
|
||||||
SgrCode::ForegroundDefault => one!(Sgr::Foreground(ColorSpec::Default)),
|
SgrCode::ForegroundYellow => one!(Sgr::Foreground(AnsiColor::Olive.into())),
|
||||||
SgrCode::ForegroundBrightBlack => one!(Sgr::Foreground(AnsiColor::Grey.into())),
|
SgrCode::ForegroundBlue => one!(Sgr::Foreground(AnsiColor::Navy.into())),
|
||||||
SgrCode::ForegroundBrightRed => one!(Sgr::Foreground(AnsiColor::Red.into())),
|
SgrCode::ForegroundMagenta => {
|
||||||
SgrCode::ForegroundBrightGreen => one!(Sgr::Foreground(AnsiColor::Lime.into())),
|
one!(Sgr::Foreground(AnsiColor::Purple.into()))
|
||||||
SgrCode::ForegroundBrightYellow => {
|
}
|
||||||
one!(Sgr::Foreground(AnsiColor::Yellow.into()))
|
SgrCode::ForegroundCyan => one!(Sgr::Foreground(AnsiColor::Teal.into())),
|
||||||
}
|
SgrCode::ForegroundWhite => one!(Sgr::Foreground(AnsiColor::Silver.into())),
|
||||||
SgrCode::ForegroundBrightBlue => one!(Sgr::Foreground(AnsiColor::Blue.into())),
|
SgrCode::ForegroundDefault => one!(Sgr::Foreground(ColorSpec::Default)),
|
||||||
SgrCode::ForegroundBrightMagenta => {
|
SgrCode::ForegroundBrightBlack => {
|
||||||
one!(Sgr::Foreground(AnsiColor::Fuschia.into()))
|
one!(Sgr::Foreground(AnsiColor::Grey.into()))
|
||||||
}
|
}
|
||||||
SgrCode::ForegroundBrightCyan => one!(Sgr::Foreground(AnsiColor::Aqua.into())),
|
SgrCode::ForegroundBrightRed => {
|
||||||
SgrCode::ForegroundBrightWhite => {
|
one!(Sgr::Foreground(AnsiColor::Red.into()))
|
||||||
one!(Sgr::Foreground(AnsiColor::White.into()))
|
}
|
||||||
}
|
SgrCode::ForegroundBrightGreen => {
|
||||||
|
one!(Sgr::Foreground(AnsiColor::Lime.into()))
|
||||||
|
}
|
||||||
|
SgrCode::ForegroundBrightYellow => {
|
||||||
|
one!(Sgr::Foreground(AnsiColor::Yellow.into()))
|
||||||
|
}
|
||||||
|
SgrCode::ForegroundBrightBlue => {
|
||||||
|
one!(Sgr::Foreground(AnsiColor::Blue.into()))
|
||||||
|
}
|
||||||
|
SgrCode::ForegroundBrightMagenta => {
|
||||||
|
one!(Sgr::Foreground(AnsiColor::Fuschia.into()))
|
||||||
|
}
|
||||||
|
SgrCode::ForegroundBrightCyan => {
|
||||||
|
one!(Sgr::Foreground(AnsiColor::Aqua.into()))
|
||||||
|
}
|
||||||
|
SgrCode::ForegroundBrightWhite => {
|
||||||
|
one!(Sgr::Foreground(AnsiColor::White.into()))
|
||||||
|
}
|
||||||
|
|
||||||
SgrCode::BackgroundColor => self.parse_sgr_color(params).map(Sgr::Background),
|
SgrCode::BackgroundColor => {
|
||||||
SgrCode::BackgroundBlack => one!(Sgr::Background(AnsiColor::Black.into())),
|
self.parse_sgr_color(params).map(Sgr::Background)
|
||||||
SgrCode::BackgroundRed => one!(Sgr::Background(AnsiColor::Maroon.into())),
|
}
|
||||||
SgrCode::BackgroundGreen => one!(Sgr::Background(AnsiColor::Green.into())),
|
SgrCode::BackgroundBlack => one!(Sgr::Background(AnsiColor::Black.into())),
|
||||||
SgrCode::BackgroundYellow => one!(Sgr::Background(AnsiColor::Olive.into())),
|
SgrCode::BackgroundRed => one!(Sgr::Background(AnsiColor::Maroon.into())),
|
||||||
SgrCode::BackgroundBlue => one!(Sgr::Background(AnsiColor::Navy.into())),
|
SgrCode::BackgroundGreen => one!(Sgr::Background(AnsiColor::Green.into())),
|
||||||
SgrCode::BackgroundMagenta => one!(Sgr::Background(AnsiColor::Purple.into())),
|
SgrCode::BackgroundYellow => one!(Sgr::Background(AnsiColor::Olive.into())),
|
||||||
SgrCode::BackgroundCyan => one!(Sgr::Background(AnsiColor::Teal.into())),
|
SgrCode::BackgroundBlue => one!(Sgr::Background(AnsiColor::Navy.into())),
|
||||||
SgrCode::BackgroundWhite => one!(Sgr::Background(AnsiColor::Silver.into())),
|
SgrCode::BackgroundMagenta => {
|
||||||
SgrCode::BackgroundDefault => one!(Sgr::Background(ColorSpec::Default)),
|
one!(Sgr::Background(AnsiColor::Purple.into()))
|
||||||
SgrCode::BackgroundBrightBlack => one!(Sgr::Background(AnsiColor::Grey.into())),
|
}
|
||||||
SgrCode::BackgroundBrightRed => one!(Sgr::Background(AnsiColor::Red.into())),
|
SgrCode::BackgroundCyan => one!(Sgr::Background(AnsiColor::Teal.into())),
|
||||||
SgrCode::BackgroundBrightGreen => one!(Sgr::Background(AnsiColor::Lime.into())),
|
SgrCode::BackgroundWhite => one!(Sgr::Background(AnsiColor::Silver.into())),
|
||||||
SgrCode::BackgroundBrightYellow => {
|
SgrCode::BackgroundDefault => one!(Sgr::Background(ColorSpec::Default)),
|
||||||
one!(Sgr::Background(AnsiColor::Yellow.into()))
|
SgrCode::BackgroundBrightBlack => {
|
||||||
}
|
one!(Sgr::Background(AnsiColor::Grey.into()))
|
||||||
SgrCode::BackgroundBrightBlue => one!(Sgr::Background(AnsiColor::Blue.into())),
|
}
|
||||||
SgrCode::BackgroundBrightMagenta => {
|
SgrCode::BackgroundBrightRed => {
|
||||||
one!(Sgr::Background(AnsiColor::Fuschia.into()))
|
one!(Sgr::Background(AnsiColor::Red.into()))
|
||||||
}
|
}
|
||||||
SgrCode::BackgroundBrightCyan => one!(Sgr::Background(AnsiColor::Aqua.into())),
|
SgrCode::BackgroundBrightGreen => {
|
||||||
SgrCode::BackgroundBrightWhite => {
|
one!(Sgr::Background(AnsiColor::Lime.into()))
|
||||||
one!(Sgr::Background(AnsiColor::White.into()))
|
}
|
||||||
}
|
SgrCode::BackgroundBrightYellow => {
|
||||||
|
one!(Sgr::Background(AnsiColor::Yellow.into()))
|
||||||
|
}
|
||||||
|
SgrCode::BackgroundBrightBlue => {
|
||||||
|
one!(Sgr::Background(AnsiColor::Blue.into()))
|
||||||
|
}
|
||||||
|
SgrCode::BackgroundBrightMagenta => {
|
||||||
|
one!(Sgr::Background(AnsiColor::Fuschia.into()))
|
||||||
|
}
|
||||||
|
SgrCode::BackgroundBrightCyan => {
|
||||||
|
one!(Sgr::Background(AnsiColor::Aqua.into()))
|
||||||
|
}
|
||||||
|
SgrCode::BackgroundBrightWhite => {
|
||||||
|
one!(Sgr::Background(AnsiColor::White.into()))
|
||||||
|
}
|
||||||
|
|
||||||
SgrCode::InverseOn => one!(Sgr::Inverse(true)),
|
SgrCode::InverseOn => one!(Sgr::Inverse(true)),
|
||||||
SgrCode::InverseOff => one!(Sgr::Inverse(false)),
|
SgrCode::InverseOff => one!(Sgr::Inverse(false)),
|
||||||
SgrCode::InvisibleOn => one!(Sgr::Invisible(true)),
|
SgrCode::InvisibleOn => one!(Sgr::Invisible(true)),
|
||||||
SgrCode::InvisibleOff => one!(Sgr::Invisible(false)),
|
SgrCode::InvisibleOff => one!(Sgr::Invisible(false)),
|
||||||
SgrCode::StrikeThroughOn => one!(Sgr::StrikeThrough(true)),
|
SgrCode::StrikeThroughOn => one!(Sgr::StrikeThrough(true)),
|
||||||
SgrCode::StrikeThroughOff => one!(Sgr::StrikeThrough(false)),
|
SgrCode::StrikeThroughOff => one!(Sgr::StrikeThrough(false)),
|
||||||
SgrCode::OverlineOn => one!(Sgr::Overline(true)),
|
SgrCode::OverlineOn => one!(Sgr::Overline(true)),
|
||||||
SgrCode::OverlineOff => one!(Sgr::Overline(false)),
|
SgrCode::OverlineOff => one!(Sgr::Overline(false)),
|
||||||
SgrCode::DefaultFont => one!(Sgr::Font(Font::Default)),
|
SgrCode::DefaultFont => one!(Sgr::Font(Font::Default)),
|
||||||
SgrCode::AltFont1 => one!(Sgr::Font(Font::Alternate(1))),
|
SgrCode::AltFont1 => one!(Sgr::Font(Font::Alternate(1))),
|
||||||
SgrCode::AltFont2 => one!(Sgr::Font(Font::Alternate(2))),
|
SgrCode::AltFont2 => one!(Sgr::Font(Font::Alternate(2))),
|
||||||
SgrCode::AltFont3 => one!(Sgr::Font(Font::Alternate(3))),
|
SgrCode::AltFont3 => one!(Sgr::Font(Font::Alternate(3))),
|
||||||
SgrCode::AltFont4 => one!(Sgr::Font(Font::Alternate(4))),
|
SgrCode::AltFont4 => one!(Sgr::Font(Font::Alternate(4))),
|
||||||
SgrCode::AltFont5 => one!(Sgr::Font(Font::Alternate(5))),
|
SgrCode::AltFont5 => one!(Sgr::Font(Font::Alternate(5))),
|
||||||
SgrCode::AltFont6 => one!(Sgr::Font(Font::Alternate(6))),
|
SgrCode::AltFont6 => one!(Sgr::Font(Font::Alternate(6))),
|
||||||
SgrCode::AltFont7 => one!(Sgr::Font(Font::Alternate(7))),
|
SgrCode::AltFont7 => one!(Sgr::Font(Font::Alternate(7))),
|
||||||
SgrCode::AltFont8 => one!(Sgr::Font(Font::Alternate(8))),
|
SgrCode::AltFont8 => one!(Sgr::Font(Font::Alternate(8))),
|
||||||
SgrCode::AltFont9 => one!(Sgr::Font(Font::Alternate(9))),
|
SgrCode::AltFont9 => one!(Sgr::Font(Font::Alternate(9))),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
CsiParam::ColonList(_) => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2028,14 +2088,22 @@ mod test {
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
fn parse(control: char, params: &[i64], expected: &str) -> Vec<CSI> {
|
fn parse(control: char, params: &[i64], expected: &str) -> Vec<CSI> {
|
||||||
let res = CSI::parse(params, &[], false, control).collect();
|
let params = params
|
||||||
|
.iter()
|
||||||
|
.map(|&i| CsiParam::Integer(i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let res = CSI::parse(¶ms, &[], false, control).collect();
|
||||||
assert_eq!(encode(&res), expected);
|
assert_eq!(encode(&res), expected);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_int(control: char, params: &[i64], intermediate: u8, expected: &str) -> Vec<CSI> {
|
fn parse_int(control: char, params: &[i64], intermediate: u8, expected: &str) -> Vec<CSI> {
|
||||||
|
let params = params
|
||||||
|
.iter()
|
||||||
|
.map(|&i| CsiParam::Integer(i))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
let intermediates = [intermediate];
|
let intermediates = [intermediate];
|
||||||
let res = CSI::parse(params, &intermediates, false, control).collect();
|
let res = CSI::parse(¶ms, &intermediates, false, control).collect();
|
||||||
assert_eq!(encode(&res), expected);
|
assert_eq!(encode(&res), expected);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -2072,7 +2140,7 @@ mod test {
|
|||||||
CSI::Sgr(Sgr::Intensity(Intensity::Bold)),
|
CSI::Sgr(Sgr::Intensity(Intensity::Bold)),
|
||||||
CSI::Sgr(Sgr::Italic(true)),
|
CSI::Sgr(Sgr::Italic(true)),
|
||||||
CSI::Unspecified(Box::new(Unspecified {
|
CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [1231231].to_vec(),
|
params: [CsiParam::Integer(1231231)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
@ -2084,7 +2152,7 @@ mod test {
|
|||||||
vec![
|
vec![
|
||||||
CSI::Sgr(Sgr::Intensity(Intensity::Bold)),
|
CSI::Sgr(Sgr::Intensity(Intensity::Bold)),
|
||||||
CSI::Unspecified(Box::new(Unspecified {
|
CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [1231231, 3].to_vec(),
|
params: [CsiParam::Integer(1231231), CsiParam::Integer(3)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
@ -2094,7 +2162,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse('m', &[1231231, 3], "\x1b[1231231;3m"),
|
parse('m', &[1231231, 3], "\x1b[1231231;3m"),
|
||||||
vec![CSI::Unspecified(Box::new(Unspecified {
|
vec![CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [1231231, 3].to_vec(),
|
params: [CsiParam::Integer(1231231), CsiParam::Integer(3)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
@ -2119,7 +2187,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse('m', &[58, 2], "\x1b[58;2m"),
|
parse('m', &[58, 2], "\x1b[58;2m"),
|
||||||
vec![CSI::Unspecified(Box::new(Unspecified {
|
vec![CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [58, 2].to_vec(),
|
params: [CsiParam::Integer(58), CsiParam::Integer(2)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
@ -2137,7 +2205,7 @@ mod test {
|
|||||||
vec![
|
vec![
|
||||||
CSI::Sgr(Sgr::UnderlineColor(ColorSpec::PaletteIndex(220))),
|
CSI::Sgr(Sgr::UnderlineColor(ColorSpec::PaletteIndex(220))),
|
||||||
CSI::Unspecified(Box::new(Unspecified {
|
CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [255, 255].to_vec(),
|
params: [CsiParam::Integer(255), CsiParam::Integer(255)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
@ -2151,7 +2219,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse('m', &[38, 2], "\x1b[38;2m"),
|
parse('m', &[38, 2], "\x1b[38;2m"),
|
||||||
vec![CSI::Unspecified(Box::new(Unspecified {
|
vec![CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [38, 2].to_vec(),
|
params: [CsiParam::Integer(38), CsiParam::Integer(2)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
@ -2169,7 +2237,7 @@ mod test {
|
|||||||
vec![
|
vec![
|
||||||
CSI::Sgr(Sgr::Foreground(ColorSpec::PaletteIndex(220))),
|
CSI::Sgr(Sgr::Foreground(ColorSpec::PaletteIndex(220))),
|
||||||
CSI::Unspecified(Box::new(Unspecified {
|
CSI::Unspecified(Box::new(Unspecified {
|
||||||
params: [255, 255].to_vec(),
|
params: [CsiParam::Integer(255), CsiParam::Integer(255)].to_vec(),
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_extra_intermediates: false,
|
ignored_extra_intermediates: false,
|
||||||
control: 'm',
|
control: 'm',
|
||||||
|
@ -18,6 +18,8 @@ pub use self::esc::Esc;
|
|||||||
pub use self::esc::EscCode;
|
pub use self::esc::EscCode;
|
||||||
pub use self::osc::OperatingSystemCommand;
|
pub use self::osc::OperatingSystemCommand;
|
||||||
|
|
||||||
|
use vtparse::CsiParam;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
/// Send a single printable character to the display
|
/// Send a single printable character to the display
|
||||||
@ -477,35 +479,35 @@ impl OneBased {
|
|||||||
|
|
||||||
/// Map a value from an escape sequence parameter.
|
/// Map a value from an escape sequence parameter.
|
||||||
/// 0 is equivalent to 1
|
/// 0 is equivalent to 1
|
||||||
pub fn from_esc_param(v: i64) -> Result<Self, ()> {
|
pub fn from_esc_param(v: &CsiParam) -> Result<Self, ()> {
|
||||||
if v == 0 {
|
match v {
|
||||||
Ok(Self {
|
CsiParam::Integer(v) if *v == 0 => Ok(Self {
|
||||||
value: num_traits::one(),
|
value: num_traits::one(),
|
||||||
})
|
}),
|
||||||
} else if v > 0 && v <= i64::from(u32::max_value()) {
|
CsiParam::Integer(v) if *v > 0 && *v <= i64::from(u32::max_value()) => {
|
||||||
Ok(Self { value: v as u32 })
|
Ok(Self { value: *v as u32 })
|
||||||
} else {
|
}
|
||||||
Err(())
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a value from an escape sequence parameter.
|
/// Map a value from an escape sequence parameter.
|
||||||
/// 0 is equivalent to max_value.
|
/// 0 is equivalent to max_value.
|
||||||
pub fn from_esc_param_with_big_default(v: i64) -> Result<Self, ()> {
|
pub fn from_esc_param_with_big_default(v: &CsiParam) -> Result<Self, ()> {
|
||||||
if v == 0 {
|
match v {
|
||||||
Ok(Self {
|
CsiParam::Integer(v) if *v == 0 => Ok(Self {
|
||||||
value: u32::max_value(),
|
value: u32::max_value(),
|
||||||
})
|
}),
|
||||||
} else if v > 0 && v <= i64::from(u32::max_value()) {
|
CsiParam::Integer(v) if *v > 0 && *v <= i64::from(u32::max_value()) => {
|
||||||
Ok(Self { value: v as u32 })
|
Ok(Self { value: *v as u32 })
|
||||||
} else {
|
}
|
||||||
Err(())
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a value from an optional escape sequence parameter
|
/// Map a value from an optional escape sequence parameter
|
||||||
pub fn from_optional_esc_param(o: Option<&i64>) -> Result<Self, ()> {
|
pub fn from_optional_esc_param(o: Option<&CsiParam>) -> Result<Self, ()> {
|
||||||
Self::from_esc_param(o.cloned().unwrap_or(1))
|
Self::from_esc_param(o.unwrap_or(&CsiParam::Integer(1)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the underlying value as a 0-based value
|
/// Return the underlying value as a 0-based value
|
||||||
|
@ -7,7 +7,7 @@ use log::error;
|
|||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use regex::bytes::Regex;
|
use regex::bytes::Regex;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use vtparse::{VTActor, VTParser};
|
use vtparse::{CsiParam, VTActor, VTParser};
|
||||||
|
|
||||||
struct SixelBuilder {
|
struct SixelBuilder {
|
||||||
sixel: Sixel,
|
sixel: Sixel,
|
||||||
@ -213,7 +213,7 @@ impl<'a, F: FnMut(Action)> VTActor for Performer<'a, F> {
|
|||||||
|
|
||||||
fn csi_dispatch(
|
fn csi_dispatch(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: &[i64],
|
params: &[CsiParam],
|
||||||
intermediates: &[u8],
|
intermediates: &[u8],
|
||||||
ignored_extra_intermediates: bool,
|
ignored_extra_intermediates: bool,
|
||||||
control: u8,
|
control: u8,
|
||||||
@ -482,12 +482,17 @@ mod test {
|
|||||||
fn fancy_underline() {
|
fn fancy_underline() {
|
||||||
let mut p = Parser::new();
|
let mut p = Parser::new();
|
||||||
|
|
||||||
// Kitty underline sequences use a `:` which is explicitly invalid
|
|
||||||
// and deleted by the dec/ansi vtparser
|
|
||||||
let actions = p.parse_as_vec(b"\x1b[4:0mb");
|
let actions = p.parse_as_vec(b"\x1b[4:0mb");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![
|
vec![
|
||||||
// NO: Action::CSI(CSI::Sgr(Sgr::Underline(Underline::None))),
|
Action::CSI(CSI::Unspecified(Box::new(
|
||||||
|
crate::escape::csi::Unspecified {
|
||||||
|
params: vec![CsiParam::ColonList(vec![Some(4), Some(0)])],
|
||||||
|
intermediates: vec![],
|
||||||
|
ignored_extra_intermediates: false,
|
||||||
|
control: 'm'
|
||||||
|
}
|
||||||
|
))),
|
||||||
Action::Print('b'),
|
Action::Print('b'),
|
||||||
],
|
],
|
||||||
actions
|
actions
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
||||||
name = "vtparse"
|
name = "vtparse"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/wez/wezterm"
|
repository = "https://github.com/wez/wezterm"
|
||||||
description = "Low level escape sequence parser"
|
description = "Low level escape sequence parser"
|
||||||
|
@ -151,7 +151,7 @@ pub trait VTActor {
|
|||||||
/// for more information on control functions.
|
/// for more information on control functions.
|
||||||
fn csi_dispatch(
|
fn csi_dispatch(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: &[i64],
|
params: &[CsiParam],
|
||||||
intermediates: &[u8],
|
intermediates: &[u8],
|
||||||
ignored_excess_intermediates: bool,
|
ignored_excess_intermediates: bool,
|
||||||
byte: u8,
|
byte: u8,
|
||||||
@ -187,7 +187,7 @@ pub enum VTAction {
|
|||||||
byte: u8,
|
byte: u8,
|
||||||
},
|
},
|
||||||
CsiDispatch {
|
CsiDispatch {
|
||||||
params: Vec<i64>,
|
params: Vec<CsiParam>,
|
||||||
intermediates: Vec<u8>,
|
intermediates: Vec<u8>,
|
||||||
ignored_excess_intermediates: bool,
|
ignored_excess_intermediates: bool,
|
||||||
byte: u8,
|
byte: u8,
|
||||||
@ -268,7 +268,7 @@ impl VTActor for CollectingVTActor {
|
|||||||
|
|
||||||
fn csi_dispatch(
|
fn csi_dispatch(
|
||||||
&mut self,
|
&mut self,
|
||||||
params: &[i64],
|
params: &[CsiParam],
|
||||||
intermediates: &[u8],
|
intermediates: &[u8],
|
||||||
ignored_excess_intermediates: bool,
|
ignored_excess_intermediates: bool,
|
||||||
byte: u8,
|
byte: u8,
|
||||||
@ -333,20 +333,84 @@ pub struct VTParser {
|
|||||||
|
|
||||||
osc: OscState,
|
osc: OscState,
|
||||||
|
|
||||||
params: [i64; MAX_PARAMS],
|
params: [CsiParam; MAX_PARAMS],
|
||||||
num_params: usize,
|
num_params: usize,
|
||||||
current_param: Option<i64>,
|
current_param: Option<CsiParam>,
|
||||||
params_full: bool,
|
params_full: bool,
|
||||||
|
|
||||||
utf8_parser: Utf8Parser,
|
utf8_parser: Utf8Parser,
|
||||||
utf8_return_state: State,
|
utf8_return_state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub enum CsiParam {
|
||||||
|
Integer(i64),
|
||||||
|
ColonList(Vec<Option<i64>>),
|
||||||
|
// TODO: add a None case here, but that requires more care
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CsiParam {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Integer(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_digit(target: &mut i64, digit: u8) {
|
||||||
|
*target = target
|
||||||
|
.saturating_mul(10)
|
||||||
|
.saturating_add((digit - b'0') as i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CsiParam {
|
||||||
|
fn add_digit(&mut self, digit: u8) {
|
||||||
|
match self {
|
||||||
|
Self::Integer(i) => add_digit(i, digit),
|
||||||
|
Self::ColonList(list) => {
|
||||||
|
if let Some(target) = list.last_mut().unwrap() {
|
||||||
|
add_digit(target, digit);
|
||||||
|
} else {
|
||||||
|
// Promote trailing None into a 0 value
|
||||||
|
let mut target = 0;
|
||||||
|
add_digit(&mut target, digit);
|
||||||
|
list.last_mut().unwrap().replace(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_integer(&self) -> Option<i64> {
|
||||||
|
match self {
|
||||||
|
Self::Integer(i) => Some(*i),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CsiParam {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
CsiParam::Integer(v) => {
|
||||||
|
write!(f, "{}", v)?;
|
||||||
|
}
|
||||||
|
CsiParam::ColonList(list) => {
|
||||||
|
for (idx, p) in list.iter().enumerate() {
|
||||||
|
if idx > 0 {
|
||||||
|
write!(f, ":")?;
|
||||||
|
}
|
||||||
|
if let Some(num) = p {
|
||||||
|
write!(f, "{}", num)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VTParser {
|
impl VTParser {
|
||||||
#[allow(clippy::new_without_default)]
|
#[allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let param_indices = [0usize; MAX_OSC];
|
let param_indices = [0usize; MAX_OSC];
|
||||||
let params = [0i64; MAX_PARAMS];
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
state: State::Ground,
|
state: State::Ground,
|
||||||
@ -363,7 +427,7 @@ impl VTParser {
|
|||||||
full: false,
|
full: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
params,
|
params: Default::default(),
|
||||||
num_params: 0,
|
num_params: 0,
|
||||||
params_full: false,
|
params_full: false,
|
||||||
current_param: None,
|
current_param: None,
|
||||||
@ -372,6 +436,20 @@ impl VTParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_integer_params(&self) -> [i64; MAX_PARAMS] {
|
||||||
|
let mut res = [0i64; MAX_PARAMS];
|
||||||
|
for (src, dest) in self.params[0..self.num_params]
|
||||||
|
.iter()
|
||||||
|
.zip(&mut res[0..self.num_params])
|
||||||
|
{
|
||||||
|
match src {
|
||||||
|
CsiParam::Integer(i) => *dest = *i,
|
||||||
|
bad => panic!("illegal parameter type: {:?}", bad),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
fn finish_param(&mut self) {
|
fn finish_param(&mut self) {
|
||||||
if let Some(val) = self.current_param.take() {
|
if let Some(val) = self.current_param.take() {
|
||||||
if self.num_params < MAX_PARAMS {
|
if self.num_params < MAX_PARAMS {
|
||||||
@ -411,24 +489,34 @@ impl VTParser {
|
|||||||
if self.num_params + 1 > MAX_OSC {
|
if self.num_params + 1 > MAX_OSC {
|
||||||
self.params_full = true;
|
self.params_full = true;
|
||||||
} else {
|
} else {
|
||||||
self.params[self.num_params] = self.current_param.take().unwrap_or(0);
|
// FIXME: the unwrap_or here is a lossy representation.
|
||||||
|
// Consider replacing this with a CsiParam::None variant
|
||||||
|
// to indicate that it wasn't present?
|
||||||
|
self.params[self.num_params] =
|
||||||
|
self.current_param.take().unwrap_or(CsiParam::Integer(0));
|
||||||
self.num_params += 1;
|
self.num_params += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else if param == b':' {
|
||||||
let current = self.current_param.take().unwrap_or(0);
|
let mut current = match self.current_param.take() {
|
||||||
|
None => vec![None],
|
||||||
|
Some(CsiParam::Integer(i)) => vec![Some(i)],
|
||||||
|
Some(CsiParam::ColonList(list)) => list,
|
||||||
|
};
|
||||||
|
|
||||||
self.current_param.replace(
|
current.push(None); // Start a new, empty parameter
|
||||||
current
|
|
||||||
.saturating_mul(10)
|
self.current_param.replace(CsiParam::ColonList(current));
|
||||||
.saturating_add((param - b'0') as i64),
|
} else {
|
||||||
);
|
let mut current = self.current_param.take().unwrap_or(CsiParam::Integer(0));
|
||||||
|
current.add_digit(param);
|
||||||
|
self.current_param.replace(current);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Action::Hook => {
|
Action::Hook => {
|
||||||
self.finish_param();
|
self.finish_param();
|
||||||
actor.dcs_hook(
|
actor.dcs_hook(
|
||||||
param,
|
param,
|
||||||
&self.params[0..self.num_params],
|
&self.as_integer_params()[0..self.num_params],
|
||||||
&self.intermediates[0..self.num_intermediates],
|
&self.intermediates[0..self.num_intermediates],
|
||||||
self.ignored_excess_intermediates,
|
self.ignored_excess_intermediates,
|
||||||
);
|
);
|
||||||
@ -437,7 +525,7 @@ impl VTParser {
|
|||||||
Action::EscDispatch => {
|
Action::EscDispatch => {
|
||||||
self.finish_param();
|
self.finish_param();
|
||||||
actor.esc_dispatch(
|
actor.esc_dispatch(
|
||||||
&self.params[0..self.num_params],
|
&self.as_integer_params()[0..self.num_params],
|
||||||
&self.intermediates[0..self.num_intermediates],
|
&self.intermediates[0..self.num_intermediates],
|
||||||
self.ignored_excess_intermediates,
|
self.ignored_excess_intermediates,
|
||||||
param,
|
param,
|
||||||
@ -599,7 +687,7 @@ mod test {
|
|||||||
VTAction::Print('o'),
|
VTAction::Print('o'),
|
||||||
VTAction::ExecuteC0orC1(0x07,),
|
VTAction::ExecuteC0orC1(0x07,),
|
||||||
VTAction::CsiDispatch {
|
VTAction::CsiDispatch {
|
||||||
params: vec![32],
|
params: vec![CsiParam::Integer(32)],
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'm',
|
byte: b'm',
|
||||||
@ -609,7 +697,7 @@ mod test {
|
|||||||
VTAction::Print('o',),
|
VTAction::Print('o',),
|
||||||
VTAction::Print('t',),
|
VTAction::Print('t',),
|
||||||
VTAction::CsiDispatch {
|
VTAction::CsiDispatch {
|
||||||
params: vec![0],
|
params: vec![CsiParam::Integer(0)],
|
||||||
intermediates: vec![],
|
intermediates: vec![],
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'm',
|
byte: b'm',
|
||||||
@ -712,7 +800,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_as_vec(b"\x1b[4m"),
|
parse_as_vec(b"\x1b[4m"),
|
||||||
vec![VTAction::CsiDispatch {
|
vec![VTAction::CsiDispatch {
|
||||||
params: vec![4],
|
params: vec![CsiParam::Integer(4)],
|
||||||
intermediates: b"".to_vec(),
|
intermediates: b"".to_vec(),
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'm'
|
byte: b'm'
|
||||||
@ -721,11 +809,33 @@ mod test {
|
|||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
// This is the kitty curly underline sequence.
|
// This is the kitty curly underline sequence.
|
||||||
// The : is explicitly set to be ignored by
|
|
||||||
// the state machine tables, so this whole sequence
|
|
||||||
// is discarded during parsing.
|
|
||||||
parse_as_vec(b"\x1b[4:3m"),
|
parse_as_vec(b"\x1b[4:3m"),
|
||||||
vec![]
|
vec![VTAction::CsiDispatch {
|
||||||
|
params: vec![CsiParam::ColonList(vec![Some(4), Some(3)])],
|
||||||
|
intermediates: b"".to_vec(),
|
||||||
|
ignored_excess_intermediates: false,
|
||||||
|
byte: b'm'
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_colon_rgb() {
|
||||||
|
assert_eq!(
|
||||||
|
parse_as_vec(b"\x1b[38:2::128:64:192m"),
|
||||||
|
vec![VTAction::CsiDispatch {
|
||||||
|
params: vec![CsiParam::ColonList(vec![
|
||||||
|
Some(38),
|
||||||
|
Some(2),
|
||||||
|
None,
|
||||||
|
Some(128),
|
||||||
|
Some(64),
|
||||||
|
Some(192)
|
||||||
|
])],
|
||||||
|
intermediates: b"".to_vec(),
|
||||||
|
ignored_excess_intermediates: false,
|
||||||
|
byte: b'm'
|
||||||
|
}]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -735,7 +845,7 @@ mod test {
|
|||||||
parse_as_vec(b"\x1b[;1m"),
|
parse_as_vec(b"\x1b[;1m"),
|
||||||
vec![VTAction::CsiDispatch {
|
vec![VTAction::CsiDispatch {
|
||||||
// The omitted parameter defaults to 0
|
// The omitted parameter defaults to 0
|
||||||
params: vec![0, 1],
|
params: vec![CsiParam::Integer(0), CsiParam::Integer(1)],
|
||||||
intermediates: b"".to_vec(),
|
intermediates: b"".to_vec(),
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'm'
|
byte: b'm'
|
||||||
@ -748,7 +858,10 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_as_vec(b"\x1b[0;1;2;3;4;5;6;7;8;9;0;1;2;3;4;51;6p"),
|
parse_as_vec(b"\x1b[0;1;2;3;4;5;6;7;8;9;0;1;2;3;4;51;6p"),
|
||||||
vec![VTAction::CsiDispatch {
|
vec![VTAction::CsiDispatch {
|
||||||
params: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 51],
|
params: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 51]
|
||||||
|
.iter()
|
||||||
|
.map(|&i| CsiParam::Integer(i))
|
||||||
|
.collect(),
|
||||||
intermediates: b"".to_vec(),
|
intermediates: b"".to_vec(),
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'p'
|
byte: b'p'
|
||||||
@ -761,7 +874,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_as_vec(b"\x1b[1 p"),
|
parse_as_vec(b"\x1b[1 p"),
|
||||||
vec![VTAction::CsiDispatch {
|
vec![VTAction::CsiDispatch {
|
||||||
params: vec![1],
|
params: vec![CsiParam::Integer(1)],
|
||||||
intermediates: b" ".to_vec(),
|
intermediates: b" ".to_vec(),
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'p'
|
byte: b'p'
|
||||||
@ -770,7 +883,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_as_vec(b"\x1b[1 !p"),
|
parse_as_vec(b"\x1b[1 !p"),
|
||||||
vec![VTAction::CsiDispatch {
|
vec![VTAction::CsiDispatch {
|
||||||
params: vec![1],
|
params: vec![CsiParam::Integer(1)],
|
||||||
intermediates: b" !".to_vec(),
|
intermediates: b" !".to_vec(),
|
||||||
ignored_excess_intermediates: false,
|
ignored_excess_intermediates: false,
|
||||||
byte: b'p'
|
byte: b'p'
|
||||||
@ -779,7 +892,7 @@ mod test {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_as_vec(b"\x1b[1 !#p"),
|
parse_as_vec(b"\x1b[1 !#p"),
|
||||||
vec![VTAction::CsiDispatch {
|
vec![VTAction::CsiDispatch {
|
||||||
params: vec![1],
|
params: vec![CsiParam::Integer(1)],
|
||||||
// Note that the `#` was discarded
|
// Note that the `#` was discarded
|
||||||
intermediates: b" !".to_vec(),
|
intermediates: b" !".to_vec(),
|
||||||
ignored_excess_intermediates: true,
|
ignored_excess_intermediates: true,
|
||||||
|
@ -194,10 +194,8 @@ define_function! {
|
|||||||
0x00..=0x17 => (Execute, CsiParam),
|
0x00..=0x17 => (Execute, CsiParam),
|
||||||
0x19 => (Execute, CsiParam),
|
0x19 => (Execute, CsiParam),
|
||||||
0x1c..=0x1f => (Execute, CsiParam),
|
0x1c..=0x1f => (Execute, CsiParam),
|
||||||
0x30..=0x39 => (Param, CsiParam),
|
0x30..=0x3b => (Param, CsiParam),
|
||||||
0x3b => (Param, CsiParam),
|
|
||||||
0x7f => (Ignore, CsiParam),
|
0x7f => (Ignore, CsiParam),
|
||||||
0x3a => (None, CsiIgnore),
|
|
||||||
0x3c..=0x3f => (None, CsiIgnore),
|
0x3c..=0x3f => (None, CsiIgnore),
|
||||||
0x20..=0x2f => (Collect, CsiIntermediate),
|
0x20..=0x2f => (Collect, CsiIntermediate),
|
||||||
0x40..=0x7e => (CsiDispatch, Ground),
|
0x40..=0x7e => (CsiDispatch, Ground),
|
||||||
|
Loading…
Reference in New Issue
Block a user