diff options
Diffstat (limited to 'vendor/anes/src')
-rw-r--r-- | vendor/anes/src/lib.rs | 76 | ||||
-rw-r--r-- | vendor/anes/src/macros.rs | 437 | ||||
-rw-r--r-- | vendor/anes/src/parser.rs | 252 | ||||
-rw-r--r-- | vendor/anes/src/parser/engine.rs | 614 | ||||
-rw-r--r-- | vendor/anes/src/parser/parsers.rs | 239 | ||||
-rw-r--r-- | vendor/anes/src/parser/types.rs | 79 | ||||
-rw-r--r-- | vendor/anes/src/sequences.rs | 5 | ||||
-rw-r--r-- | vendor/anes/src/sequences/attribute.rs | 133 | ||||
-rw-r--r-- | vendor/anes/src/sequences/buffer.rs | 145 | ||||
-rw-r--r-- | vendor/anes/src/sequences/color.rs | 189 | ||||
-rw-r--r-- | vendor/anes/src/sequences/cursor.rs | 352 | ||||
-rw-r--r-- | vendor/anes/src/sequences/terminal.rs | 54 |
12 files changed, 2575 insertions, 0 deletions
diff --git a/vendor/anes/src/lib.rs b/vendor/anes/src/lib.rs new file mode 100644 index 000000000..99dfc8c08 --- /dev/null +++ b/vendor/anes/src/lib.rs @@ -0,0 +1,76 @@ +//! # ANSI Escape Sequences provider & parser +//! +//! ## Sequences provider +//! +//! The `anes` crate provides ANSI escape sequences you can use to control the terminal +//! cursor (show, hide, ...), colors (foreground, background), display attributes (bold, ...) +//! and many others. +//! +//! Every sequence implements the standard library [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html) +//! trait. It means that these sequences can be used in macros like +//! [`format!`](https://doc.rust-lang.org/std/macro.format.html) or +//! [`write!`](https://doc.rust-lang.org/std/macro.write.html). +//! +//! Ask if you need more sequences or use the [`sequence!`](macro.sequence.html) macro to create +//! your own sequences. +//! +//! +//! ### Terminal Support +//! +//! Not all ANSI escape sequences are supported by all terminals. You can use the +//! [interactive-test](https://github.com/zrzka/anes-rs/tree/master/interactive-test) to test them. +//! +//! ### Examples +//! +//! Retrieve the sequence as a `String`: +//! +//! ```rust +//! use anes::SaveCursorPosition; +//! +//! let string = format!("{}", SaveCursorPosition); +//! assert_eq!(&string, "\x1B7"); +//! ``` +//! +//! Execute the sequence on the standard output: +//! +//! ```rust +//! use std::io::{Result, Write}; +//! +//! use anes::execute; +//! +//! fn main() -> Result<()> { +//! let mut stdout = std::io::stdout(); +//! execute!(&mut stdout, anes::ResetAttributes) +//! } +//! ``` +//! +//! ## Sequences parser +//! +//! Parser isn't available with default features. You have to enable `parser` feature if you'd like to use it. +//! You can learn more about this feature in the [`parser`](parser/index.html) module documentation. +#![warn(rust_2018_idioms)] +#![deny(unused_imports, unused_must_use)] + +// Keep it first to load all the macros before other modules. +#[macro_use] +mod macros; + +pub use self::sequences::{ + attribute::{Attribute, ResetAttributes, SetAttribute}, + buffer::{ + ClearBuffer, ClearLine, ScrollBufferDown, ScrollBufferUp, SwitchBufferToAlternate, + SwitchBufferToNormal, + }, + color::{Color, SetBackgroundColor, SetForegroundColor}, + cursor::{ + DisableCursorBlinking, EnableCursorBlinking, HideCursor, MoveCursorDown, MoveCursorLeft, + MoveCursorRight, MoveCursorTo, MoveCursorToColumn, MoveCursorToNextLine, + MoveCursorToPreviousLine, MoveCursorUp, ReportCursorPosition, RestoreCursorPosition, + SaveCursorPosition, ShowCursor, + }, + terminal::{DisableMouseEvents, EnableMouseEvents, ResizeTextArea}, +}; + +#[cfg(feature = "parser")] +pub mod parser; +mod sequences; diff --git a/vendor/anes/src/macros.rs b/vendor/anes/src/macros.rs new file mode 100644 index 000000000..1955214fc --- /dev/null +++ b/vendor/anes/src/macros.rs @@ -0,0 +1,437 @@ +/// Creates a control sequence. +/// +/// This macro prepends provided sequence with the control sequence introducer `ESC [` (`\x1B[`). +/// +/// # Examples +/// +/// ``` +/// use anes::csi; +/// +/// assert_eq!(csi!("?1049h"), "\x1B[?1049h"); +/// ``` +#[macro_export] +macro_rules! csi { + ($($arg:expr),*) => { concat!("\x1B[", $($arg),*) }; +} + +/// Creates an escape sequence. +/// +/// This macro prepends provided sequence with the `ESC` (`\x1B`) character. +/// +/// # Examples +/// +/// ``` +/// use anes::esc; +/// +/// assert_eq!(esc!("7"), "\x1B7"); +/// ``` +#[macro_export] +macro_rules! esc { + ($($arg:expr),*) => { concat!("\x1B", $($arg),*) }; +} + +/// Creates a select graphic rendition sequence. +/// +/// This macro prepends provided sequence with the `ESC[` (`\x1B[`) character and appends `m` character. +/// +/// Also known as Set Graphics Rendition on Linux. +/// +/// # Examples +/// +/// ``` +/// use anes::sgr; +/// +/// assert_eq!(sgr!("0"), "\x1B[0m"); +/// ``` +#[macro_export] +macro_rules! sgr { + ($($arg:expr),*) => { concat!("\x1B[", $($arg),* , "m") }; +} + +/// Creates an ANSI sequence. +/// +/// You can use this macro to create your own ANSI sequence. All `anes` sequences are +/// created with this macro. +/// +/// # Examples +/// +/// An unit struct: +/// +/// ``` +/// use anes::{esc, sequence}; +/// +/// sequence!( +/// /// Saves the cursor position. +/// struct SaveCursorPosition => esc!("7") +/// ); +/// +/// assert_eq!(&format!("{}", SaveCursorPosition), "\x1B7"); +/// ``` +/// +/// An enum: +/// +/// ``` +/// use anes::{csi, sequence}; +/// +/// sequence!( +/// /// Clears part of the buffer. +/// enum ClearBuffer { +/// /// Clears from the cursor position to end of the screen. +/// Below => csi!("J"), +/// /// Clears from the cursor position to beginning of the screen. +/// Above => csi!("1J"), +/// /// Clears the entire buffer. +/// All => csi!("2J"), +/// /// Clears the entire buffer and all saved lines in the scrollback buffer. +/// SavedLines => csi!("3J"), +/// } +/// ); +/// +/// assert_eq!(&format!("{}", ClearBuffer::Below), "\x1B[J"); +/// assert_eq!(&format!("{}", ClearBuffer::Above), "\x1B[1J"); +/// assert_eq!(&format!("{}", ClearBuffer::All), "\x1B[2J"); +/// assert_eq!(&format!("{}", ClearBuffer::SavedLines), "\x1B[3J"); +/// ``` +/// +/// A struct: +/// +/// ``` +/// use anes::{csi, sequence}; +/// +/// sequence!( +/// /// Moves the cursor to the given location (column, row). +/// /// +/// /// # Notes +/// /// +/// /// Top/left cell is represented as `1, 1`. +/// struct MoveCursorTo(u16, u16) => +/// |this, f| write!(f, csi!("{};{}H"), this.0, this.1) +/// ); +/// +/// assert_eq!(&format!("{}", MoveCursorTo(10, 5)), "\x1B[10;5H"); +/// ``` +#[macro_export] +macro_rules! sequence { + // Static unit struct + ( + $(#[$meta:meta])* + struct $name:ident => $value:expr + ) => { + $(#[$meta])* + #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] + pub struct $name; + + impl ::std::fmt::Display for $name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, $value) + } + } + }; + // Static enum + ( + $(#[$meta:meta])* + enum $name:ident { + $( + $(#[$variant_meta:meta])* + $variant:ident => $variant_value:expr + ),* + $(,)? + } + ) => { + $(#[$meta])* + #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] + pub enum $name { + $( + $(#[$variant_meta])* + $variant, + )* + } + + impl ::std::fmt::Display for $name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}", match self { + $( + $name::$variant => $variant_value, + )* + }) + } + } + }; + // Dynamic struct + ( + $(#[$meta:meta])* + struct $type:ident( + $($fields:ty),* + $(,)? + ) + => + $write:expr + ) => { + $(#[$meta])* + #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] + pub struct $type($(pub $fields),*); + + impl ::std::fmt::Display for $type { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + let write: &dyn Fn(&Self, &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result = + &$write; + write(self, f) + } + } + }; +} + +/// Queues ANSI escape sequence(s). +/// +/// What does queue mean exactly? All sequences are queued with the +/// `write!($dst, "{}", $sequence)` macro without calling the +/// [`flush`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush) method. +/// +/// Check the [`execute!`](macro.execute.html) macro if you'd like execute them +/// immediately (call the `flush` method after all sequences were queued). +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{Result, Write}; +/// +/// use anes::queue; +/// +/// fn main() -> Result<()> { +/// let mut stdout = std::io::stdout(); +/// queue!( +/// &mut stdout, +/// anes::SaveCursorPosition, +/// anes::MoveCursorTo(10, 10) +/// )?; +/// +/// queue!(&mut stdout, anes::RestoreCursorPosition,)?; +/// +/// // ANSI sequences are not executed until you flush it! +/// stdout.flush() +/// } +/// ``` +#[macro_export] +macro_rules! queue { + ($dst:expr, $($sequence:expr),* $(,)?) => {{ + let mut error = None; + + $( + if let Err(e) = write!($dst, "{}", $sequence) { + error = Some(e); + } + )* + + if let Some(error) = error { + Err(error) + } else { + Ok(()) + } + }} +} + +/// Executes ANSI escape sequence(s). +/// +/// What does execute mean exactly? All sequences are queued with the +/// `write!($dst, "{}", $sequence)` macro and then the +/// [`flush`](https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush) method +/// is called. +/// +/// Check the [`queue!`](macro.queue.html) macro if you'd like queue sequences +/// and execute them later. +/// +/// ```no_run +/// use std::io::{Result, Write}; +/// +/// use anes::execute; +/// +/// fn main() -> Result<()> { +/// let mut stdout = std::io::stdout(); +/// execute!( +/// &mut stdout, +/// anes::SaveCursorPosition, +/// anes::MoveCursorTo(10, 10), +/// anes::RestoreCursorPosition +/// )?; +/// Ok(()) +/// } +/// ``` +#[macro_export] +macro_rules! execute { + ($dst:expr, $($sequence:expr),* $(,)?) => {{ + if let Err(e) = $crate::queue!($dst, $($sequence),*) { + Err(e) + } else { + $dst.flush() + } + }} +} + +#[cfg(test)] +macro_rules! test_sequences { + ( + $( + $name:ident( + $($left:expr => $right:expr),* + $(,)? + ) + ),* + $(,)? + ) => { + #[cfg(test)] + mod tests { + use super::*; + + $( + #[test] + fn $name() { + $( + assert_eq!(&format!("{}", $left), $right); + )* + } + )* + } + } +} + +#[cfg(test)] +mod tests { + use std::io::{Error, ErrorKind, Write}; + + #[test] + fn csi() { + assert_eq!(csi!("foo"), "\x1B[foo"); + } + + #[test] + fn esc() { + assert_eq!(esc!("bar"), "\x1Bbar"); + } + + #[test] + fn sgr() { + assert_eq!(sgr!("bar"), "\x1B[barm"); + } + + #[test] + fn static_struct_sequence() { + sequence!( + struct TestSeq => csi!("foo") + ); + + assert_eq!(&format!("{}", TestSeq), "\x1B[foo"); + } + + #[test] + fn static_enum_sequence() { + sequence!( + enum TestSeq { + Foo => csi!("foo"), + Bar => esc!("bar"), + } + ); + + assert_eq!(&format!("{}", TestSeq::Foo), "\x1B[foo"); + assert_eq!(&format!("{}", TestSeq::Bar), "\x1Bbar"); + } + + #[test] + fn dynamic_struct_sequence() { + sequence!( + struct TestSeq(u16) => + |this, f| write!(f, csi!("foo{}bar"), this.0) + ); + + assert_eq!(&format!("{}", TestSeq(10)), "\x1B[foo10bar"); + } + + #[test] + fn queue_allows_trailing_comma() { + let mut writer = Writer::default(); + + assert!(queue!(&mut writer, "foo",).is_ok()); + assert_eq!(&writer.buffer, "foo"); + } + + #[test] + fn queue_writes_single_sequence() { + let mut writer = Writer::default(); + + assert!(queue!(&mut writer, "foo").is_ok()); + assert_eq!(&writer.buffer, "foo"); + } + + #[test] + fn queue_writes_multiple_sequences() { + let mut writer = Writer::default(); + + assert!(queue!(&mut writer, "foo", "bar", "baz").is_ok()); + assert_eq!(&writer.buffer, "foobarbaz"); + } + + #[test] + fn queue_does_not_flush() { + let mut writer = Writer::default(); + + assert!(queue!(&mut writer, "foo").is_ok()); + assert!(!writer.flushed); + assert!(writer.flushed_buffer.is_empty()); + } + + #[test] + fn execute_allows_trailing_comma() { + let mut writer = Writer::default(); + + assert!(execute!(&mut writer, "foo",).is_ok()); + assert_eq!(&writer.flushed_buffer, "foo"); + } + + #[test] + fn execute_writes_single_sequence() { + let mut writer = Writer::default(); + + assert!(execute!(&mut writer, "foo").is_ok()); + assert_eq!(&writer.flushed_buffer, "foo"); + } + + #[test] + fn execute_writes_multiple_sequences() { + let mut writer = Writer::default(); + + assert!(execute!(&mut writer, "foo", "bar", "baz").is_ok()); + assert_eq!(&writer.flushed_buffer, "foobarbaz"); + } + + #[test] + fn execute_does_flush() { + let mut writer = Writer::default(); + + assert!(execute!(&mut writer, "foo").is_ok()); + assert!(writer.flushed); + assert_eq!(&writer.flushed_buffer, "foo"); + assert!(writer.buffer.is_empty()); + } + + #[derive(Default)] + struct Writer { + buffer: String, + flushed_buffer: String, + flushed: bool, + } + + impl Write for Writer { + fn write(&mut self, buf: &[u8]) -> Result<usize, Error> { + let s = std::str::from_utf8(buf).map_err(|_| ErrorKind::InvalidData)?; + + self.buffer.push_str(s); + Ok(s.len()) + } + + fn flush(&mut self) -> Result<(), Error> { + self.flushed_buffer = self.buffer.clone(); + self.buffer = String::new(); + self.flushed = true; + Ok(()) + } + } +} diff --git a/vendor/anes/src/parser.rs b/vendor/anes/src/parser.rs new file mode 100644 index 000000000..be87b2986 --- /dev/null +++ b/vendor/anes/src/parser.rs @@ -0,0 +1,252 @@ +//! An ANSI escape sequence parser module. +//! +//! **This module is not available with default features. You have to enable `parser` feature +//! if you'd like to use it.** +//! +//! # Parser +//! +//! The ANSI escape sequence parser parses input data in two steps: +//! +//! * transforms input data into valid characters, generic csi & escape sequences, throws away invalid data, +//! * give them meaning, throws away sequences without known meaning. +//! +//! ## First step +//! +//! State machine implementation for the first step is inspired by the +//! [A parser for DEC’s ANSI-compatible video terminals](https://vt100.net/emu/dec_ansi_parser) article +//! and the [vte](https://crates.io/crates/vte) crate. The goal of this step is to transform an input +//! data into characters, generic csi & escape sequences, validate them and throw away malformed input. +//! +//! An example of valid csi sequence: `b"\x1B[20;10R"`. Output of the first step will be: +//! +//! * valid csi sequence +//! * with two parameters `[20, 10]` +//! * and the final character `R`. +//! +//! ## Second step +//! +//! An input of this step is output of the first one. We know that the final character `R` represents +//! cursor position and two parameters should be provided. They were provided, we can give it a +//! meaning and return `Sequence::CursorPosition(10, 20)`. +//! +//! All sequences without known meaning are discarded. +//! +//! ## Implementation +//! +//! Both steps are considered as an implementation detail and there's no plan to make them +//! publicly available. +//! +//! The `parser` module provides the [`Parser`](struct.Parser.html) structure you can feed with +//! the [`advance`](struct.Parser.html#method.advance) method. It also implements the standard +//! library `Iterator<Item = Sequence>` trait which allows you to consume valid sequences with +//! known meaning via the `next()` method. Check the [`Sequence`](enum.Sequence.html) enum to learn +//! what this module can parse. +use std::collections::VecDeque; + +use engine::{Engine, Provide}; +pub use types::{KeyCode, KeyModifiers, Mouse, MouseButton, Sequence}; + +mod engine; +mod parsers; +pub(crate) mod types; + +/// An ANSI escape sequence parser. +/// +/// `Parser` implements the `Iterator<Item = Sequence>` trait, thus you can use the +/// `next()` method to consume all valid sequences with known meaning. +/// +/// # Examples +/// +/// Parse cursor position: +/// +/// ``` +/// use anes::parser::{Parser, Sequence}; +/// +/// let mut parser = Parser::default(); +/// parser.advance(b"\x1B[20;10R", false); +/// +/// assert_eq!(Some(Sequence::CursorPosition(10, 20)), parser.next()); +/// assert!(parser.next().is_none()); +/// ``` +/// +/// Parse keyboard event: +/// +/// ``` +/// use anes::parser::{KeyCode, KeyModifiers, Parser, Sequence}; +/// +/// let mut parser = Parser::default(); +/// parser.advance("𐌼a".as_bytes(), false); +/// +/// assert_eq!(Some(Sequence::Key(KeyCode::Char('𐌼'), KeyModifiers::empty())), parser.next()); +/// assert_eq!(Some(Sequence::Key(KeyCode::Char('a'), KeyModifiers::empty())), parser.next()); +/// assert!(parser.next().is_none()); +/// ``` +#[derive(Default)] +pub struct Parser { + engine: Engine, + provider: SequenceProvider, +} + +impl Parser { + /// Advances parser state machine with additional input data. + /// + /// # Arguments + /// + /// * `buffer` - input data (stdin in raw mode, etc.) + /// * `more` - more input data available right now + /// + /// It's crucial to provide correct `more` value in order to receive `KeyCode::Esc` events + /// as soon as possible. + /// + /// # Examples + /// + /// Esc key: + /// + /// ``` + /// use anes::parser::{KeyCode, KeyModifiers, Parser, Sequence}; + /// + /// let mut parser = Parser::default(); + /// // User pressed Esc key & nothing else which means that there's no additional input available + /// // aka no possible escape sequence = `KeyCode::Esc` dispatched. + /// parser.advance(&[0x1b], false); + /// + /// assert_eq!(Some(Sequence::Key(KeyCode::Esc, KeyModifiers::empty())), parser.next()); + /// assert!(parser.next().is_none()); + /// ``` + /// + /// Possible escape sequence: + /// + /// ``` + /// use anes::parser::{KeyCode, KeyModifiers, Parser, Sequence}; + /// + /// let mut parser = Parser::default(); + /// // User pressed F1 = b"\x1BOP" + /// + /// // Every escape sequence starts with Esc (0x1b). There's more input available + /// // aka possible escape sequence = `KeyCode::Esc` isn't dispatched even when the parser + /// // doesn't know rest of the sequence. + /// parser.advance(&[0x1b], true); + /// assert!(parser.next().is_none()); + /// + /// // Advance parser with rest of the sequence + /// parser.advance(&[b'O', b'P'], false); + /// assert_eq!(Some(Sequence::Key(KeyCode::F(1), KeyModifiers::empty())), parser.next()); + /// assert!(parser.next().is_none()); + /// ``` + pub fn advance(&mut self, buffer: &[u8], more: bool) { + let len = buffer.len(); + for (idx, byte) in buffer.iter().enumerate() { + self.engine + .advance(&mut self.provider, *byte, idx < len - 1 || more); + } + } +} + +impl Iterator for Parser { + type Item = Sequence; + + fn next(&mut self) -> Option<Self::Item> { + self.provider.next() + } +} + +#[derive(Default)] +struct SequenceProvider { + esc_o: bool, + seqs: VecDeque<Sequence>, +} + +impl Iterator for SequenceProvider { + type Item = Sequence; + + fn next(&mut self) -> Option<Self::Item> { + self.seqs.pop_front() + } +} + +impl Provide for SequenceProvider { + fn provide_char(&mut self, ch: char) { + // eprintln!("dispatch_char: {}", ch); + + if let Some(seq) = parsers::parse_char(ch, self.esc_o) { + self.seqs.push_back(seq); + } + self.esc_o = false; + } + + fn provide_esc_sequence(&mut self, ch: char) { + if ch == 'O' { + // Exception + // + // Esc O - dispatched as an escape sequence followed by single character (P-S) representing + // F1-F4 keys. We store Esc O flag only which is then used in the dispatch_char method. + self.esc_o = true; + } else { + self.esc_o = false; + if let Some(seq) = parsers::parse_esc_sequence(ch) { + self.seqs.push_back(seq); + } + } + } + + fn provide_csi_sequence(&mut self, parameters: &[u64], ignored_count: usize, ch: char) { + if let Some(seq) = parsers::parse_csi_sequence(parameters, ignored_count, ch) { + self.seqs.push_back(seq); + } + + self.esc_o = false; + } +} + +#[cfg(test)] +mod tests { + use super::Parser; + + #[test] + fn dispatch_char() { + let mut parser = Parser::default(); + parser.advance(&[b'a'], false); + assert!(parser.next().is_some()); + } + + #[test] + fn dispatch_esc_sequence() { + let mut parser = Parser::default(); + parser.advance(&[b'\x1B'], true); + assert!(parser.next().is_none()); + parser.advance(&[b'a'], false); + assert!(parser.next().is_some()); + } + + #[test] + fn does_not_dispatch_esc_sequence_with_upper_case_o() { + let mut parser = Parser::default(); + parser.advance(&[b'\x1B'], true); + assert!(parser.next().is_none()); + parser.advance(&[b'O'], true); + assert!(parser.next().is_none()); + } + + #[test] + fn dispatch_esc_with_upper_case_o_followed_by_char_as_single_sequence() { + let mut parser = Parser::default(); + parser.advance(&[b'\x1B'], true); + assert!(parser.next().is_none()); + parser.advance(&[b'O'], true); + assert!(parser.next().is_none()); + parser.advance(&[b'P'], false); + assert!(parser.next().is_some()); + assert!(parser.next().is_none()); + } + + #[test] + fn dispatch_csi_sequence() { + let mut parser = Parser::default(); + parser.advance(&[b'\x1B'], true); + assert!(parser.next().is_none()); + parser.advance(&[b'['], true); + assert!(parser.next().is_none()); + parser.advance(&[b'D'], false); + assert!(parser.next().is_some()); + } +} diff --git a/vendor/anes/src/parser/engine.rs b/vendor/anes/src/parser/engine.rs new file mode 100644 index 000000000..645208d37 --- /dev/null +++ b/vendor/anes/src/parser/engine.rs @@ -0,0 +1,614 @@ +// +// https://vt100.net/emu/dec_ansi_parser +// +// The parser is heavily inspired by the vte (https://crates.io/crates/vte) crate. +// Tried to use this crate, but it doesn't work for opposite way (terminal -> sequence), +// because there're couple of exceptions we have to handle and it doesn't make much +// sense to add them to the vte crate. An example is Esc key where we need to know if +// there's additional input available or not and then the decision is made if the +// Esc char is dispatched immediately (user hits just Esc key) or if it's an escape/csi/... +// sequence. +// +const MAX_PARAMETERS: usize = 30; +const DEFAULT_PARAMETER_VALUE: u64 = 0; +const MAX_UTF8_CODE_POINTS: usize = 4; + +/// A parser engine state. +/// +/// All these variant names come from the +/// [A parser for DEC’s ANSI-compatible video terminals](https://vt100.net/emu/dec_ansi_parser) +/// description. +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum State { + /// Initial state. + Ground, + /// Escape sequence started. + /// + /// `Esc` received with a flag that there's more data available. + Escape, + /// Escape sequence and we're collecting intermediates. + /// + /// # Notes + /// + /// This implementation doesn't collect intermediates. It just handles the state + /// to distinguish between (im)proper sequences. + EscapeIntermediate, + /// CSI sequence started. + /// + /// `Esc` followed by the `[` received. + CsiEntry, + /// CSI sequence should be consumed, but not dispatched. + CsiIgnore, + /// CSI sequence and we're collecting parameters. + CsiParameter, + /// CSI sequence and we're collecting intermediates. + /// + /// # Notes + /// + /// This implementation doesn't collect intermediates. It just handles the state + /// to distinguish between (im)proper sequences. + CsiIntermediate, + /// Possible UTF-8 sequence and we're collecting UTF-8 code points. + Utf8, +} + +pub(crate) trait Provide { + fn provide_char(&mut self, ch: char); + + fn provide_esc_sequence(&mut self, ch: char); + + fn provide_csi_sequence(&mut self, parameters: &[u64], ignored_count: usize, ch: char); +} + +pub(crate) struct Engine { + parameters: [u64; MAX_PARAMETERS], + parameters_count: usize, + parameter: u64, + ignored_parameters_count: usize, + state: State, + utf8_points: [u8; MAX_UTF8_CODE_POINTS], + utf8_points_count: usize, + utf8_points_expected_count: usize, +} + +impl Default for Engine { + fn default() -> Self { + Engine { + parameters: [DEFAULT_PARAMETER_VALUE; MAX_PARAMETERS], + parameters_count: 0, + parameter: DEFAULT_PARAMETER_VALUE, + ignored_parameters_count: 0, + state: State::Ground, + utf8_points: [0; MAX_UTF8_CODE_POINTS], + utf8_points_count: 0, + utf8_points_expected_count: 0, + } + } +} + +impl Engine { + fn set_state(&mut self, state: State) { + if let State::Ground = state { + self.parameters_count = 0; + self.parameter = DEFAULT_PARAMETER_VALUE; + self.ignored_parameters_count = 0; + self.utf8_points_count = 0; + self.utf8_points_expected_count = 0; + } + self.state = state; + } + + fn store_parameter(&mut self) { + if self.parameters_count < MAX_PARAMETERS { + self.parameters[self.parameters_count] = self.parameter; + self.parameters_count += 1; + } else { + self.ignored_parameters_count += 1; + } + self.parameter = DEFAULT_PARAMETER_VALUE; + } + + fn handle_possible_esc(&mut self, provider: &mut dyn Provide, byte: u8, more: bool) -> bool { + if byte != 0x1B { + return false; + } + + match (self.state, more) { + // More input means possible Esc sequence, just switch state and wait + (State::Ground, true) => self.set_state(State::Escape), + + // No more input means Esc key, dispatch it + (State::Ground, false) => provider.provide_char('\x1B'), + + // More input means possible Esc sequence, dispatch the previous Esc char + (State::Escape, true) => provider.provide_char('\x1B'), + + // No more input means Esc key, dispatch the previous & current Esc char + (State::Escape, false) => { + provider.provide_char('\x1B'); + provider.provide_char('\x1B'); + self.set_state(State::Ground); + } + + // Discard any state + // More input means possible Esc sequence + (_, true) => self.set_state(State::Escape), + + // Discard any state + // No more input means Esc key, dispatch it + (_, false) => { + provider.provide_char('\x1B'); + self.set_state(State::Ground); + } + } + + true + } + + fn handle_possible_utf8_code_points(&mut self, provider: &mut dyn Provide, byte: u8) -> bool { + if byte & 0b1000_0000 == 0b0000_0000 { + provider.provide_char(byte as char); + true + } else if byte & 0b1110_0000 == 0b1100_0000 { + self.utf8_points_count = 1; + self.utf8_points[0] = byte; + self.utf8_points_expected_count = 2; + self.set_state(State::Utf8); + true + } else if byte & 0b1111_0000 == 0b1110_0000 { + self.utf8_points_count = 1; + self.utf8_points[0] = byte; + self.utf8_points_expected_count = 3; + self.set_state(State::Utf8); + true + } else if byte & 0b1111_1000 == 0b1111_0000 { + self.utf8_points_count = 1; + self.utf8_points[0] = byte; + self.utf8_points_expected_count = 4; + self.set_state(State::Utf8); + true + } else { + false + } + } + + fn advance_ground_state(&mut self, provider: &mut dyn Provide, byte: u8) { + if self.handle_possible_utf8_code_points(provider, byte) { + return; + } + + match byte { + 0x1B => unreachable!(), + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // Print + 0x20..=0x7F => provider.provide_char(byte as char), + + _ => {} + }; + } + + fn advance_escape_state(&mut self, provider: &mut dyn Provide, byte: u8) { + match byte { + 0x1B => unreachable!(), + + // Intermediate bytes to collect + 0x20..=0x2F => { + self.set_state(State::EscapeIntermediate); + } + + // Escape followed by '[' (0x5B) + // -> CSI sequence start + 0x5B => self.set_state(State::CsiEntry), + + // Escape sequence final character + 0x30..=0x4F | 0x51..=0x57 | 0x59 | 0x5A | 0x5C | 0x60..=0x7E => { + provider.provide_esc_sequence(byte as char); + self.set_state(State::Ground); + } + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // TODO Does it mean we should ignore the whole sequence? + // Ignore + 0x7F => {} + + // Other bytes are considered as invalid -> cancel whatever we have + _ => self.set_state(State::Ground), + }; + } + + fn advance_escape_intermediate_state(&mut self, provider: &mut dyn Provide, byte: u8) { + match byte { + 0x1B => unreachable!(), + + // Intermediate bytes to collect + 0x20..=0x2F => {} + + // Escape followed by '[' (0x5B) + // -> CSI sequence start + 0x5B => self.set_state(State::CsiEntry), + + // Escape sequence final character + 0x30..=0x5A | 0x5C..=0x7E => { + provider.provide_esc_sequence(byte as char); + self.set_state(State::Ground); + } + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // TODO Does it mean we should ignore the whole sequence? + // Ignore + 0x7F => {} + + // Other bytes are considered as invalid -> cancel whatever we have + _ => self.set_state(State::Ground), + }; + } + + fn advance_csi_entry_state(&mut self, provider: &mut dyn Provide, byte: u8) { + match byte { + 0x1B => unreachable!(), + + // Semicolon = parameter delimiter + 0x3B => { + self.store_parameter(); + self.set_state(State::CsiParameter); + } + + // '0' ..= '9' = parameter value + 0x30..=0x39 => { + self.parameter = (byte as u64) - 0x30; + self.set_state(State::CsiParameter); + } + + 0x3A => self.set_state(State::CsiIgnore), + + // CSI sequence final character + // -> dispatch CSI sequence + 0x40..=0x7E => { + provider.provide_csi_sequence( + &self.parameters[..self.parameters_count], + self.ignored_parameters_count, + byte as char, + ); + + self.set_state(State::Ground); + } + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // TODO Does it mean we should ignore the whole sequence? + // Ignore + 0x7F => {} + + // Collect rest as parameters + _ => { + self.parameter = byte as u64; + self.store_parameter(); + } + }; + } + + fn advance_csi_ignore_state(&mut self, provider: &mut dyn Provide, byte: u8) { + match byte { + 0x1B => unreachable!(), + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // TODO Does it mean we should ignore the whole sequence? + // Ignore + 0x20..=0x3F | 0x7F => {} + + 0x40..=0x7E => self.set_state(State::Ground), + + // Other bytes are considered as invalid -> cancel whatever we have + _ => self.set_state(State::Ground), + }; + } + + fn advance_csi_parameter_state(&mut self, provider: &mut dyn Provide, byte: u8) { + match byte { + 0x1B => unreachable!(), + + // '0' ..= '9' = parameter value + 0x30..=0x39 => { + self.parameter = self.parameter.saturating_mul(10); + self.parameter = self.parameter.saturating_add((byte as u64) - 0x30); + } + + // Semicolon = parameter delimiter + 0x3B => self.store_parameter(), + + // CSI sequence final character + // -> dispatch CSI sequence + 0x40..=0x7E => { + self.store_parameter(); + provider.provide_csi_sequence( + &self.parameters[..self.parameters_count], + self.ignored_parameters_count, + byte as char, + ); + + self.set_state(State::Ground); + } + + // Intermediates to collect + 0x20..=0x2F => { + self.store_parameter(); + self.set_state(State::CsiIntermediate); + } + + // Ignore + 0x3A | 0x3C..=0x3F => self.set_state(State::CsiIgnore), + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // TODO Does it mean we should ignore the whole sequence? + // Ignore + 0x7F => {} + + // Other bytes are considered as invalid -> cancel whatever we have + _ => self.set_state(State::Ground), + }; + } + + fn advance_csi_intermediate_state(&mut self, provider: &mut dyn Provide, byte: u8) { + match byte { + 0x1B => unreachable!(), + + // Intermediates to collect + 0x20..=0x2F => {} + + // CSI sequence final character + // -> dispatch CSI sequence + 0x40..=0x7E => { + provider.provide_csi_sequence( + &self.parameters[..self.parameters_count], + self.ignored_parameters_count, + byte as char, + ); + + self.set_state(State::Ground); + } + + // Execute + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => provider.provide_char(byte as char), + + // TODO Does it mean we should ignore the whole sequence? + // Ignore + 0x7F => {} + + // Other bytes are considered as invalid -> cancel whatever we have + _ => self.set_state(State::Ground), + } + } + + fn advance_utf8_state(&mut self, provider: &mut dyn Provide, byte: u8) { + if byte & 0b1100_0000 != 0b1000_0000 { + self.set_state(State::Ground); + return; + } + + self.utf8_points[self.utf8_points_count] = byte; + self.utf8_points_count += 1; + + if self.utf8_points_count == self.utf8_points_expected_count { + if let Some(ch) = std::str::from_utf8(&self.utf8_points[..self.utf8_points_count]) + .ok() + .and_then(|s| s.chars().next()) + { + provider.provide_char(ch); + } + self.set_state(State::Ground); + } + } + + pub(crate) fn advance(&mut self, provider: &mut dyn Provide, byte: u8, more: bool) { + // eprintln!("advance: {:?} {} {}", self.state, byte, more); + + if self.handle_possible_esc(provider, byte, more) { + return; + } + + match self.state { + State::Ground => self.advance_ground_state(provider, byte), + State::Escape => self.advance_escape_state(provider, byte), + State::EscapeIntermediate => self.advance_escape_intermediate_state(provider, byte), + State::CsiEntry => self.advance_csi_entry_state(provider, byte), + State::CsiIgnore => self.advance_csi_ignore_state(provider, byte), + State::CsiParameter => self.advance_csi_parameter_state(provider, byte), + State::CsiIntermediate => self.advance_csi_intermediate_state(provider, byte), + State::Utf8 => self.advance_utf8_state(provider, byte), + }; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn esc_char() { + let mut engine = Engine::default(); + let mut provider = CharProvider::default(); + + // No more input means that the Esc character should be dispatched immediately + engine.advance(&mut provider, 0x1B, false); + assert_eq!(provider.chars, &['\x1B']); + + // There's more input so the machine should wait before dispatching Esc character + engine.advance(&mut provider, 0x1B, true); + assert_eq!(provider.chars, &['\x1B']); + + // Another Esc character, but no more input, machine should dispatch the postponed Esc + // character and the new one too. + engine.advance(&mut provider, 0x1B, false); + assert_eq!(provider.chars, &['\x1B', '\x1B', '\x1B']); + } + + #[test] + fn esc_without_intermediates() { + let mut engine = Engine::default(); + let mut provider = EscProvider::default(); + + let input = b"\x1B0\x1B~"; + advance(&mut engine, &mut provider, input, false); + + assert_eq!(provider.chars.len(), 2); + + assert_eq!(provider.chars[0], '0'); + + assert_eq!(provider.chars[1], '~'); + } + + #[test] + fn csi_without_parameters() { + let mut engine = Engine::default(); + let mut provider = CsiProvider::default(); + + let input = b"\x1B\x5Bm"; + advance(&mut engine, &mut provider, input, false); + + assert_eq!(provider.parameters.len(), 1); + assert_eq!(provider.parameters[0], &[]); + assert_eq!(provider.chars.len(), 1); + assert_eq!(provider.chars[0], 'm'); + } + + #[test] + fn csi_with_two_default_parameters() { + let mut engine = Engine::default(); + let mut provider = CsiProvider::default(); + + let input = b"\x1B\x5B;m"; + advance(&mut engine, &mut provider, input, false); + + assert_eq!(provider.parameters.len(), 1); + assert_eq!( + provider.parameters[0], + &[DEFAULT_PARAMETER_VALUE, DEFAULT_PARAMETER_VALUE] + ); + assert_eq!(provider.chars.len(), 1); + assert_eq!(provider.chars[0], 'm'); + } + + #[test] + fn csi_with_trailing_semicolon() { + let mut engine = Engine::default(); + let mut provider = CsiProvider::default(); + + let input = b"\x1B\x5B123;m"; + advance(&mut engine, &mut provider, input, false); + + assert_eq!(provider.parameters.len(), 1); + assert_eq!(provider.parameters[0], &[123, DEFAULT_PARAMETER_VALUE]); + assert_eq!(provider.chars.len(), 1); + assert_eq!(provider.chars[0], 'm'); + } + + #[test] + fn csi_max_parameters() { + let mut engine = Engine::default(); + let mut provider = CsiProvider::default(); + + let input = b"\x1B\x5B1;2;3;4;5;6;7;8;9;10;11;12;13;14;15;16;17;18;19;20;21;22;23;24;25;26;27;28;29;30m"; + advance(&mut engine, &mut provider, input, false); + + assert_eq!(provider.parameters.len(), 1); + assert_eq!(provider.parameters[0].len(), MAX_PARAMETERS); + assert_eq!( + provider.parameters[0], + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30 + ] + ); + assert_eq!(provider.chars.len(), 1); + assert_eq!(provider.chars[0], 'm'); + } + + #[test] + fn test_parse_utf8_character() { + let mut engine = Engine::default(); + let mut provider = CharProvider::default(); + + advance(&mut engine, &mut provider, &['a' as u8], false); + assert_eq!(provider.chars.len(), 1); + assert_eq!(provider.chars[0], 'a'); + + advance(&mut engine, &mut provider, &[0xC3, 0xB1], false); + assert_eq!(provider.chars.len(), 2); + assert_eq!(provider.chars[1], 'ñ'); + + advance(&mut engine, &mut provider, &[0xE2, 0x81, 0xA1], false); + assert_eq!(provider.chars.len(), 3); + assert_eq!(provider.chars[2], '\u{2061}'); + + advance(&mut engine, &mut provider, &[0xF0, 0x90, 0x8C, 0xBC], false); + assert_eq!(provider.chars.len(), 4); + assert_eq!(provider.chars[3], '𐌼'); + } + + fn advance(engine: &mut Engine, provider: &mut dyn Provide, bytes: &[u8], more: bool) { + let len = bytes.len(); + + for (i, byte) in bytes.iter().enumerate() { + engine.advance(provider, *byte, i < len - 1 || more); + } + } + + #[derive(Default)] + struct CharProvider { + chars: Vec<char>, + } + + impl Provide for CharProvider { + fn provide_char(&mut self, ch: char) { + self.chars.push(ch); + } + + fn provide_esc_sequence(&mut self, _ch: char) {} + + fn provide_csi_sequence(&mut self, _parameters: &[u64], _ignored_count: usize, _ch: char) {} + } + + #[derive(Default)] + struct CsiProvider { + parameters: Vec<Vec<u64>>, + chars: Vec<char>, + } + + impl Provide for CsiProvider { + fn provide_char(&mut self, _ch: char) {} + + fn provide_esc_sequence(&mut self, _ch: char) {} + + fn provide_csi_sequence(&mut self, parameters: &[u64], _ignored_count: usize, ch: char) { + self.parameters.push(parameters.to_vec()); + self.chars.push(ch); + } + } + + #[derive(Default)] + struct EscProvider { + chars: Vec<char>, + } + + impl Provide for EscProvider { + fn provide_char(&mut self, _ch: char) {} + + fn provide_esc_sequence(&mut self, ch: char) { + self.chars.push(ch); + } + + fn provide_csi_sequence(&mut self, _parameters: &[u64], _ignored_count: usize, _ch: char) {} + } +} diff --git a/vendor/anes/src/parser/parsers.rs b/vendor/anes/src/parser/parsers.rs new file mode 100644 index 000000000..9bb9acb4f --- /dev/null +++ b/vendor/anes/src/parser/parsers.rs @@ -0,0 +1,239 @@ +use super::types::{KeyCode, KeyModifiers, Mouse, MouseButton, Sequence}; + +pub(crate) fn parse_char(ch: char, esc_o: bool) -> Option<Sequence> { + if esc_o { + return match ch { + 'P'..='S' => Some(Sequence::Key( + KeyCode::F(ch as u8 - b'P' + 1), + KeyModifiers::empty(), + )), + _ => None, + }; + } + + let code = match ch { + '\r' | '\n' => KeyCode::Enter, + '\t' => KeyCode::Tab, + '\x7F' => KeyCode::BackTab, + '\x1B' => KeyCode::Esc, + '\0' => KeyCode::Null, + _ => KeyCode::Char(ch), + }; + Some(Sequence::Key(code, KeyModifiers::empty())) +} + +pub(crate) fn parse_esc_sequence(ch: char) -> Option<Sequence> { + // EscO[P-S] is handled in the Performer, see parse_char & esc_o argument + // No need to handle other cases here? It's just Alt+$char + Some(Sequence::Key(KeyCode::Char(ch), KeyModifiers::ALT)) +} + +pub(crate) fn parse_csi_sequence( + parameters: &[u64], + _ignored_count: usize, + ch: char, +) -> Option<Sequence> { + match ch { + 'A' => Some(Sequence::Key( + KeyCode::Up, + parse_csi_arrow_key_modifiers(parameters.first().cloned()), + )), + 'B' => Some(Sequence::Key( + KeyCode::Down, + parse_csi_arrow_key_modifiers(parameters.first().cloned()), + )), + 'C' => Some(Sequence::Key( + KeyCode::Right, + parse_csi_arrow_key_modifiers(parameters.first().cloned()), + )), + 'D' => Some(Sequence::Key( + KeyCode::Left, + parse_csi_arrow_key_modifiers(parameters.first().cloned()), + )), + 'H' => Some(Sequence::Key(KeyCode::Home, KeyModifiers::empty())), + 'F' => Some(Sequence::Key(KeyCode::End, KeyModifiers::empty())), + 'Z' => Some(Sequence::Key(KeyCode::BackTab, KeyModifiers::empty())), + 'R' => parse_csi_cursor_position(parameters), + 'm' => parse_csi_xterm_mouse(parameters, ch), + 'M' if parameters.first() == Some(&0x3C) => parse_csi_xterm_mouse(parameters, ch), + 'M' => parse_csi_rxvt_mouse(parameters), + '~' => parse_csi_tilde_key_code(parameters), + _ => None, + } +} + +fn parse_csi_arrow_key_modifiers(parameter: Option<u64>) -> KeyModifiers { + parse_key_modifiers(parameter.map(|x| x.saturating_sub(48))) +} + +fn parse_key_modifiers(parameter: Option<u64>) -> KeyModifiers { + if let Some(parameter) = parameter { + match parameter { + 2 => KeyModifiers::SHIFT, + 3 => KeyModifiers::ALT, + 4 => KeyModifiers::SHIFT | KeyModifiers::ALT, + 5 => KeyModifiers::CONTROL, + 6 => KeyModifiers::SHIFT | KeyModifiers::CONTROL, + 7 => KeyModifiers::ALT | KeyModifiers::CONTROL, + 8 => KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL, + 9 => KeyModifiers::META, + 10 => KeyModifiers::META | KeyModifiers::SHIFT, + 11 => KeyModifiers::META | KeyModifiers::ALT, + 12 => KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::ALT, + 13 => KeyModifiers::META | KeyModifiers::CONTROL, + 14 => KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::CONTROL, + 15 => KeyModifiers::META | KeyModifiers::ALT | KeyModifiers::CONTROL, + 16 => { + KeyModifiers::META | KeyModifiers::SHIFT | KeyModifiers::ALT | KeyModifiers::CONTROL + } + _ => KeyModifiers::empty(), + } + } else { + KeyModifiers::empty() + } +} + +fn parse_csi_tilde_key_code(parameters: &[u64]) -> Option<Sequence> { + if parameters.is_empty() { + return None; + } + + let modifiers = parse_key_modifiers(parameters.get(1).cloned()); + + let code = match parameters[0] { + 1 | 7 => KeyCode::Home, + 2 => KeyCode::Insert, + 3 => KeyCode::Delete, + 4 | 8 => KeyCode::End, + 5 => KeyCode::PageUp, + 6 => KeyCode::PageDown, + p @ 11..=15 => KeyCode::F(p as u8 - 10), + p @ 17..=21 => KeyCode::F(p as u8 - 11), + p @ 23..=24 => KeyCode::F(p as u8 - 12), + _ => return None, + }; + + Some(Sequence::Key(code, modifiers)) +} + +fn parse_csi_cursor_position(parameters: &[u64]) -> Option<Sequence> { + // ESC [ Cy ; Cx R + + if parameters.len() < 2 { + return None; + } + + let y = parameters[0] as u16; + let x = parameters[1] as u16; + + Some(Sequence::CursorPosition(x, y)) +} + +fn parse_csi_xterm_mouse(parameters: &[u64], ch: char) -> Option<Sequence> { + // ESC [ < Cb ; Cx ; Cy (;) (M or m) + + if parameters.len() < 4 { + return None; + } + + let cb = parameters[1] as u8; + let cx = parameters[2] as u16; + let cy = parameters[3] as u16; + + let up = match ch { + 'm' => true, + 'M' => false, + _ => return None, + }; + + let mut modifiers = KeyModifiers::empty(); + + if cb & 0b0000_0100 == 0b0000_0100 { + modifiers |= KeyModifiers::SHIFT; + } + + if cb & 0b0000_1000 == 0b0000_1000 { + modifiers |= KeyModifiers::ALT; + } + + if cb & 0b0001_0000 == 0b0001_0000 { + modifiers |= KeyModifiers::CONTROL; + } + + let mouse = if cb & 0b0100_0000 == 0b0100_0000 { + if cb & 0b0000_0001 == 0b0000_0001 { + Mouse::ScrollDown(cx, cy) + } else { + Mouse::ScrollUp(cx, cy) + } + } else { + let drag = cb & 0b0010_0000 == 0b0010_0000; + + match (cb & 0b0000_0011, up, drag) { + (0, true, _) => Mouse::Up(MouseButton::Left, cx, cy), + (0, false, false) => Mouse::Down(MouseButton::Left, cx, cy), + (0, false, true) => Mouse::Drag(MouseButton::Left, cx, cy), + (1, true, _) => Mouse::Up(MouseButton::Middle, cx, cy), + (1, false, false) => Mouse::Down(MouseButton::Middle, cx, cy), + (1, false, true) => Mouse::Drag(MouseButton::Middle, cx, cy), + (2, true, _) => Mouse::Up(MouseButton::Right, cx, cy), + (2, false, false) => Mouse::Down(MouseButton::Right, cx, cy), + (2, false, true) => Mouse::Drag(MouseButton::Right, cx, cy), + _ => return None, + } + }; + + Some(Sequence::Mouse(mouse, modifiers)) +} + +fn parse_csi_rxvt_mouse(parameters: &[u64]) -> Option<Sequence> { + // ESC [ Cb ; Cx ; Cy ; M + + if parameters.len() < 3 { + return None; + } + + let cb = parameters[0]; + let cx = parameters[1] as u16; + let cy = parameters[2] as u16; + + let mut modifiers = KeyModifiers::empty(); + + if cb & 0b0000_0100 == 0b0000_0100 { + modifiers |= KeyModifiers::SHIFT; + } + + if cb & 0b0000_1000 == 0b0000_1000 { + modifiers |= KeyModifiers::ALT; + } + + if cb & 0b0001_0000 == 0b0001_0000 { + modifiers |= KeyModifiers::CONTROL; + } + + let mouse = if cb & 0b0110_0000 == 0b0110_0000 { + if cb & 0b0000_0001 == 0b0000_0001 { + Mouse::ScrollDown(cx, cy) + } else { + Mouse::ScrollUp(cx, cy) + } + } else { + let drag = cb & 0b0100_0000 == 0b0100_0000; + + match (cb & 0b0000_0011, drag) { + (0b0000_0000, false) => Mouse::Down(MouseButton::Left, cx, cy), + (0b0000_0010, false) => Mouse::Down(MouseButton::Right, cx, cy), + (0b0000_0001, false) => Mouse::Down(MouseButton::Middle, cx, cy), + + (0b0000_0000, true) => Mouse::Drag(MouseButton::Left, cx, cy), + (0b0000_0010, true) => Mouse::Drag(MouseButton::Right, cx, cy), + (0b0000_0001, true) => Mouse::Drag(MouseButton::Middle, cx, cy), + + (0b0000_0011, false) => Mouse::Up(MouseButton::Any, cx, cy), + + _ => return None, + } + }; + + Some(Sequence::Mouse(mouse, modifiers)) +} diff --git a/vendor/anes/src/parser/types.rs b/vendor/anes/src/parser/types.rs new file mode 100644 index 000000000..66b6561fd --- /dev/null +++ b/vendor/anes/src/parser/types.rs @@ -0,0 +1,79 @@ +use bitflags::bitflags; + +/// A parsed ANSI escape sequence. +/// +/// Check the [`Parser`](struct.Parser.html) structure documentation for examples +/// how to retrieve these values. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum Sequence { + /// A keyboard event sequence. + Key(KeyCode, KeyModifiers), + /// A mouse event sequence. + Mouse(Mouse, KeyModifiers), + /// A cursor position (`x`, `y`). + /// + /// Top/left cell is represented as `Sequence::CursorPosition(1, 1)`. + CursorPosition(u16, u16), +} + +bitflags! { + /// A key modifiers. + pub struct KeyModifiers: u8 { + const SHIFT = 0b0000_0001; + const CONTROL = 0b0000_0010; + const ALT = 0b0000_0100; + const META = 0b0000_1000; + } +} + +/// A key code. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum KeyCode { + Backspace, + Enter, + Left, + Right, + Up, + Down, + Home, + End, + PageUp, + PageDown, + Tab, + BackTab, + Delete, + Insert, + F(u8), + Char(char), + Null, + Esc, +} + +/// A mouse event. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum Mouse { + /// A mouse button press. + Down(MouseButton, u16, u16), + /// A mouse button release. + Up(MouseButton, u16, u16), + /// A mouse movement with pressed button. + Drag(MouseButton, u16, u16), + /// A mouse wheel scrolled up. + ScrollUp(u16, u16), + /// A mouse wheel scrolled down. + ScrollDown(u16, u16), +} + +/// A mouse button. +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub enum MouseButton { + Left, + Right, + Middle, + /// This variant is provided only if [`Parser`](struct.Parser.html) doesn't know which + /// mouse button was pressed/released. + /// + /// An example is [rxvt](https://en.wikipedia.org/wiki/Rxvt) - it provides which mouse + /// button was pressed, but doesn't provide which mouse button was released. + Any, +} diff --git a/vendor/anes/src/sequences.rs b/vendor/anes/src/sequences.rs new file mode 100644 index 000000000..c66080045 --- /dev/null +++ b/vendor/anes/src/sequences.rs @@ -0,0 +1,5 @@ +pub(crate) mod attribute; +pub(crate) mod buffer; +pub(crate) mod color; +pub(crate) mod cursor; +pub(crate) mod terminal; diff --git a/vendor/anes/src/sequences/attribute.rs b/vendor/anes/src/sequences/attribute.rs new file mode 100644 index 000000000..30961ff4b --- /dev/null +++ b/vendor/anes/src/sequences/attribute.rs @@ -0,0 +1,133 @@ +use std::fmt; + +sequence!( + /// Resets all attributes. + /// + /// This sequence resets all attributes previously set by the: + /// + /// * [`SetAttribute`](struct.SetAttribute.html) + /// * [`SetForegroundColor`](struct.SetBackgroundColor.html) + /// * [`SetBackgroundColor`](struct.SetForegroundColor.html) + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ResetAttributes; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}", ResetAttributes); + /// ``` + struct ResetAttributes => sgr!("0") +); + +/// A display attribute. +/// +/// This is **NOT** a full ANSI sequence. `Attribute` must be used along with +/// the [`SetAttribute`](struct.SetAttribute.html). +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{stdout, Write}; +/// use anes::{Attribute, SetAttribute}; +/// +/// let mut stdout = stdout(); +/// write!(stdout, "{}Bold text", SetAttribute(Attribute::Bold)); +/// ``` +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum Attribute { + /// Bold (increased) intensity. + Bold = 1, + /// Faint (decreased) intensity. + Faint = 2, + /// Normal intensity (turns off `Bold` and/or `Faint`). + Normal = 22, + + /// Italic. + Italic = 3, + /// Turns off `Italic`. + ItalicOff = 23, + + /// Underlined text. + Underline = 4, + /// Turns off `Underline`. + UnderlineOff = 24, + + /// Blinking text. + Blink = 5, + /// Turns off blinking text (`Blink`). + BlinkOff = 25, + + /// Reverse foreground & background colors. + Reverse = 7, + /// Turns off `Reverse`. + ReverseOff = 27, + + /// Concealed (hidden). + Conceal = 8, + /// Turns off `Conceal`. + ConcealOff = 28, + + /// Crossed. + Crossed = 9, + /// Turns off `Crossed`. + CrossedOff = 29, +} + +impl fmt::Display for Attribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", *self as i32) + } +} + +sequence!( + /// Sets the display attribute. + /// + /// See the [`Attribute`](enum.Attribute.html) enum for a list of attributes you can (un)set. + /// + /// The [`ResetAttributes`](struct.ResetAttributes.html) sequence can be used to turn off all + /// attributes. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{Attribute, SetAttribute}; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}Blinking text", SetAttribute(Attribute::Blink)); + /// ``` + struct SetAttribute(Attribute) => + |this, f| write!(f, sgr!("{}"), this.0) +); + +#[cfg(test)] +test_sequences!( + set_attribute( + SetAttribute(Attribute::Bold) => "\x1B[1m", + SetAttribute(Attribute::Faint) => "\x1B[2m", + SetAttribute(Attribute::Normal) => "\x1B[22m", + + SetAttribute(Attribute::Italic) => "\x1B[3m", + SetAttribute(Attribute::ItalicOff) => "\x1B[23m", + + SetAttribute(Attribute::Underline) => "\x1B[4m", + SetAttribute(Attribute::UnderlineOff) => "\x1B[24m", + + SetAttribute(Attribute::Blink) => "\x1B[5m", + SetAttribute(Attribute::BlinkOff) => "\x1B[25m", + + SetAttribute(Attribute::Reverse) => "\x1B[7m", + SetAttribute(Attribute::ReverseOff) => "\x1B[27m", + + SetAttribute(Attribute::Conceal) => "\x1B[8m", + SetAttribute(Attribute::ConcealOff) => "\x1B[28m", + + SetAttribute(Attribute::Crossed) => "\x1B[9m", + SetAttribute(Attribute::CrossedOff) => "\x1B[29m", + ), + reset_attributes( + ResetAttributes => "\x1B[0m", + ) +); diff --git a/vendor/anes/src/sequences/buffer.rs b/vendor/anes/src/sequences/buffer.rs new file mode 100644 index 000000000..053195af6 --- /dev/null +++ b/vendor/anes/src/sequences/buffer.rs @@ -0,0 +1,145 @@ +sequence!( + /// Switches to the alternate buffer. + /// + /// Use the [`SwitchBufferToNormal`](struct.SwitchBufferToNormal.html) sequence to switch + /// back to the normal buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{SwitchBufferToAlternate, SwitchBufferToNormal}; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}", SwitchBufferToAlternate); + /// // Your app on alternate screen + /// write!(stdout, "{}", SwitchBufferToNormal); + /// ``` + struct SwitchBufferToAlternate => csi!("?1049h") +); + +sequence!( + /// Switches to the normal buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{SwitchBufferToAlternate, SwitchBufferToNormal}; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}", SwitchBufferToAlternate); + /// // Your app on alternate screen + /// write!(stdout, "{}", SwitchBufferToNormal); + /// ``` + struct SwitchBufferToNormal => csi!("?1049l") +); + +sequence!( + /// Scrolls up by the given number of rows. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ScrollBufferUp; + /// + /// let mut stdout = stdout(); + /// // Scroll up by 5 lines + /// write!(stdout, "{}", ScrollBufferUp(5)); + /// ``` + struct ScrollBufferUp(u16) => + |this, f| write!(f, csi!("{}S"), this.0) +); + +sequence!( + /// Scrolls down by the given number of rows. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ScrollBufferDown; + /// + /// let mut stdout = stdout(); + /// // Scroll down by 10 lines + /// write!(stdout, "{}", ScrollBufferDown(10)); + /// ``` + struct ScrollBufferDown(u16) => + |this, f| write!(f, csi!("{}T"), this.0) +); + +sequence!( + /// Clears part of the line. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ClearLine; + /// + /// let mut stdout = stdout(); + /// // Clear the whole line + /// write!(stdout, "{}", ClearLine::All); + /// ``` + enum ClearLine { + /// Clears from the cursor position to end of the line. + Right => csi!("K"), + /// Clears from the cursor position to beginning of the line. + Left => csi!("1K"), + /// Clears the whole line. + All => csi!("2K"), + } +); + +sequence!( + /// Clears part of the buffer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ClearBuffer; + /// + /// let mut stdout = stdout(); + /// // Clear the entire buffer + /// write!(stdout, "{}", ClearBuffer::All); + /// ``` + enum ClearBuffer { + /// Clears from the cursor position to end of the screen. + Below => csi!("J"), + /// Clears from the cursor position to beginning of the screen. + Above => csi!("1J"), + /// Clears the entire buffer. + All => csi!("2J"), + /// Clears the entire buffer and all saved lines in the scrollback buffer. + SavedLines => csi!("3J"), + } +); + +#[cfg(test)] +test_sequences!( + switch_buffer_to_alternate( + SwitchBufferToAlternate => "\x1B[?1049h", + ), + switch_buffer_to_main( + SwitchBufferToNormal => "\x1B[?1049l", + ), + scroll_buffer_up( + ScrollBufferUp(10) => "\x1B[10S", + ), + scroll_buffer_down( + ScrollBufferDown(10) => "\x1B[10T", + ), + clear_line( + ClearLine::Right => "\x1B[K", + ClearLine::Left => "\x1B[1K", + ClearLine::All => "\x1B[2K", + ), + clear_buffer( + ClearBuffer::Below => "\x1B[J", + ClearBuffer::Above => "\x1B[1J", + ClearBuffer::All => "\x1B[2J", + ClearBuffer::SavedLines => "\x1B[3J", + ), +); diff --git a/vendor/anes/src/sequences/color.rs b/vendor/anes/src/sequences/color.rs new file mode 100644 index 000000000..b019f0fdd --- /dev/null +++ b/vendor/anes/src/sequences/color.rs @@ -0,0 +1,189 @@ +use std::fmt; + +/// A color. +/// +/// This is **NOT** a full ANSI sequence. `Color` must be used along with +/// the: +/// +/// * [`SetBackgroundColor`](struct.SetBackgroundColor.html) +/// * [`SetForegroundColor`](struct.SetForegroundColor.html) +/// +/// # Examples +/// +/// ```no_run +/// use std::io::{stdout, Write}; +/// use anes::{Color, SetForegroundColor}; +/// +/// let mut stdout = stdout(); +/// // Set the foreground color to red +/// write!(stdout, "{}", SetForegroundColor(Color::Red)); +/// ``` +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] +pub enum Color { + /// Default color. + Default, + /// Black color. + Black, + /// Dark red color. + DarkRed, + /// Dark green color. + DarkGreen, + /// Dark yellow color. + DarkYellow, + /// Dark blue color. + DarkBlue, + /// Dark magenta color. + DarkMagenta, + /// Dark cyan color. + DarkCyan, + /// Dark gray color. + /// + /// Also knows as light (bright) black. + DarkGray, + /// Light (bright) gray color. + /// + /// Also known as dark white. + Gray, + /// Light (bright) red color. + Red, + /// Light (bright) green color. + Green, + /// Light (bright) yellow color. + Yellow, + /// Light (bright) blue color. + Blue, + /// Light (bright) magenta color. + Magenta, + /// Light (bright) cyan color. + Cyan, + /// White color. + White, + /// A color from the predefined set of ANSI colors. + /// + /// ```text + /// 0 - 7: standard colors (as in ESC [ 30–37 m) + /// 8- 15: high intensity colors (as in ESC [ 90–97 m) + /// 16-231: 6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5) + /// 232-255: grayscale from black to white in 24 steps + /// ``` + /// + /// See [8-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) for more information. + Ansi(u8), + /// An RGB color. + /// + /// See [24-bit](https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit) for more information. + Rgb(u8, u8, u8), +} + +impl fmt::Display for Color { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + // Color::Default is handled in the SetBackgroundColor & SetForegroundColor + Color::Default => Ok(()), + Color::Black => write!(f, "5;0"), + Color::DarkRed => write!(f, "5;1"), + Color::DarkGreen => write!(f, "5;2"), + Color::DarkYellow => write!(f, "5;3"), + Color::DarkBlue => write!(f, "5;4"), + Color::DarkMagenta => write!(f, "5;5"), + Color::DarkCyan => write!(f, "5;6"), + Color::Gray => write!(f, "5;7"), + Color::DarkGray => write!(f, "5;8"), + Color::Red => write!(f, "5;9"), + Color::Green => write!(f, "5;10"), + Color::Yellow => write!(f, "5;11"), + Color::Blue => write!(f, "5;12"), + Color::Magenta => write!(f, "5;13"), + Color::Cyan => write!(f, "5;14"), + Color::White => write!(f, "5;15"), + Color::Ansi(value) => write!(f, "5;{}", value), + Color::Rgb(r, g, b) => write!(f, "2;{};{};{}", r, g, b), + } + } +} + +sequence! { + /// Sets the foreground color. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{Color, SetForegroundColor}; + /// + /// let mut stdout = stdout(); + /// // Set the foreground color to blue + /// write!(stdout, "{}", SetForegroundColor(Color::Blue)); + /// ``` + struct SetForegroundColor(Color) => + |this, f| match this.0 { + Color::Default => write!(f, sgr!("39")), + _ => write!(f, sgr!("38;{}"), this.0), + } +} + +sequence! { + /// Sets the background color. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{Color, SetBackgroundColor}; + /// + /// let mut stdout = stdout(); + /// // Set the background color to yellow + /// write!(stdout, "{}", SetBackgroundColor(Color::Yellow)); + /// ``` + struct SetBackgroundColor(Color) => + |this, f| match this.0 { + Color::Default => write!(f, sgr!("49")), + _ => write!(f, sgr!("48;{}"), this.0), + } +} + +#[cfg(test)] +test_sequences!( + set_foreground_color( + SetForegroundColor(Color::Default) => "\x1B[39m", + SetForegroundColor(Color::Black) => "\x1B[38;5;0m", + SetForegroundColor(Color::DarkRed) => "\x1B[38;5;1m", + SetForegroundColor(Color::DarkGreen) => "\x1B[38;5;2m", + SetForegroundColor(Color::DarkYellow) => "\x1B[38;5;3m", + SetForegroundColor(Color::DarkBlue) => "\x1B[38;5;4m", + SetForegroundColor(Color::DarkMagenta) => "\x1B[38;5;5m", + SetForegroundColor(Color::DarkCyan) => "\x1B[38;5;6m", + SetForegroundColor(Color::DarkGray) => "\x1B[38;5;8m", + SetForegroundColor(Color::Gray) => "\x1B[38;5;7m", + SetForegroundColor(Color::Red) => "\x1B[38;5;9m", + SetForegroundColor(Color::Green) => "\x1B[38;5;10m", + SetForegroundColor(Color::Yellow) => "\x1B[38;5;11m", + SetForegroundColor(Color::Blue) => "\x1B[38;5;12m", + SetForegroundColor(Color::Magenta) => "\x1B[38;5;13m", + SetForegroundColor(Color::Cyan) => "\x1B[38;5;14m", + SetForegroundColor(Color::White) => "\x1B[38;5;15m", + SetForegroundColor(Color::Ansi(200)) => "\x1B[38;5;200m", + SetForegroundColor(Color::Rgb(1, 2, 3)) => "\x1B[38;2;1;2;3m", + ), + set_background_color( + SetBackgroundColor(Color::Default) => "\x1B[49m", + SetBackgroundColor(Color::Black) => "\x1B[48;5;0m", + SetBackgroundColor(Color::DarkRed) => "\x1B[48;5;1m", + SetBackgroundColor(Color::DarkGreen) => "\x1B[48;5;2m", + SetBackgroundColor(Color::DarkYellow) => "\x1B[48;5;3m", + SetBackgroundColor(Color::DarkBlue) => "\x1B[48;5;4m", + SetBackgroundColor(Color::DarkMagenta) => "\x1B[48;5;5m", + SetBackgroundColor(Color::DarkCyan) => "\x1B[48;5;6m", + SetBackgroundColor(Color::DarkGray) => "\x1B[48;5;8m", + SetBackgroundColor(Color::Gray) => "\x1B[48;5;7m", + SetBackgroundColor(Color::Red) => "\x1B[48;5;9m", + SetBackgroundColor(Color::Green) => "\x1B[48;5;10m", + SetBackgroundColor(Color::Yellow) => "\x1B[48;5;11m", + SetBackgroundColor(Color::Blue) => "\x1B[48;5;12m", + SetBackgroundColor(Color::Magenta) => "\x1B[48;5;13m", + SetBackgroundColor(Color::Cyan) => "\x1B[48;5;14m", + SetBackgroundColor(Color::White) => "\x1B[48;5;15m", + SetBackgroundColor(Color::Ansi(200)) => "\x1B[48;5;200m", + SetBackgroundColor(Color::Rgb(1, 2, 3)) => "\x1B[48;2;1;2;3m", + ) +); diff --git a/vendor/anes/src/sequences/cursor.rs b/vendor/anes/src/sequences/cursor.rs new file mode 100644 index 000000000..37abc3b19 --- /dev/null +++ b/vendor/anes/src/sequences/cursor.rs @@ -0,0 +1,352 @@ +//! A terminal cursor related ANSI escape sequences. + +sequence!( + /// Saves the cursor position. + /// + /// Use the [`RestoreCursorPosition`](struct.RestoreCursorPosition.html) sequence to + /// restore the cursor position. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{SaveCursorPosition, RestoreCursorPosition}; + /// + /// let mut stdout = stdout(); + /// // Save cursor position + /// write!(stdout, "{}", SaveCursorPosition); + /// + /// // Your app + /// + /// // Restore cursor position + /// write!(stdout, "{}", RestoreCursorPosition); + /// ``` + struct SaveCursorPosition => esc!("7") +); + +sequence!( + /// Restores the cursor position. + /// + /// Use the [`SaveCursorPosition`](struct.SaveCursorPosition.html) sequence to + /// save the cursor position. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{SaveCursorPosition, RestoreCursorPosition}; + /// + /// let mut stdout = stdout(); + /// // Save cursor position + /// write!(stdout, "{}", SaveCursorPosition); + /// + /// // Your app + /// + /// // Restore cursor position + /// write!(stdout, "{}", RestoreCursorPosition); + /// ``` + struct RestoreCursorPosition => esc!("8") +); + +sequence!( + /// Hides the cursor. + /// + /// Use the [`ShowCursor`](struct.ShowCursor.html) sequence to show the cursor. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::HideCursor; + /// + /// let mut stdout = stdout(); + /// // Hide cursor + /// write!(stdout, "{}", HideCursor); + /// ``` + struct HideCursor => csi!("?25l") +); + +sequence!( + /// Shows the cursor. + /// + /// Use the [`HideCursor`](struct.HideCursor.html) sequence to hide the cursor. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ShowCursor; + /// + /// let mut stdout = stdout(); + /// // Show cursor + /// write!(stdout, "{}", ShowCursor); + /// ``` + struct ShowCursor => csi!("?25h") +); + +sequence!( + /// Enables the cursor blinking. + /// + /// Use the [`DisableCursorBlinking`](struct.DisableCursorBlinking.html) sequence to disable + /// cursor blinking. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::EnableCursorBlinking; + /// + /// let mut stdout = stdout(); + /// // Enable cursor blinking + /// write!(stdout, "{}", EnableCursorBlinking); + /// ``` + struct EnableCursorBlinking => csi!("?12h") +); + +sequence!( + /// Disables the cursor blinking. + /// + /// Use the [`EnableCursorBlinking`](struct.EnableCursorBlinking.html) sequence to enable + /// cursor blinking. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::DisableCursorBlinking; + /// + /// let mut stdout = stdout(); + /// // Disable cursor blinking + /// write!(stdout, "{}", DisableCursorBlinking); + /// ``` + struct DisableCursorBlinking => csi!("?12l") +); + +sequence!( + /// Moves the cursor to the given location (column, row). + /// + /// # Notes + /// + /// Top/left cell is represented as `1, 1` (`column, row`). + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorTo; + /// + /// let mut stdout = stdout(); + /// // Move cursor to top left cell + /// write!(stdout, "{}", MoveCursorTo(1, 1)); + /// ``` + struct MoveCursorTo(u16, u16) => + |this, f| write!(f, csi!("{};{}H"), this.1, this.0) +); + +sequence!( + /// Moves the cursor up by the given number of rows. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorUp; + /// + /// let mut stdout = stdout(); + /// // Move cursor up by 5 rows + /// write!(stdout, "{}", MoveCursorUp(5)); + /// ``` + struct MoveCursorUp(u16) => + |this, f| write!(f, csi!("{}A"), this.0) +); + +sequence!( + /// Moves the cursor down by the given number of rows. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorDown; + /// + /// let mut stdout = stdout(); + /// // Move cursor down by 5 rows + /// write!(stdout, "{}", MoveCursorDown(5)); + /// ``` + struct MoveCursorDown(u16) => + |this, f| write!(f, csi!("{}B"), this.0) +); + +sequence!( + /// Moves the cursor right by the given number of columns. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorRight; + /// + /// let mut stdout = stdout(); + /// // Move cursor right by 5 columns + /// write!(stdout, "{}", MoveCursorRight(5)); + /// ``` + struct MoveCursorRight(u16) => + |this, f| write!(f, csi!("{}C"), this.0) +); + +sequence!( + /// Moves the cursor left by the given number of columns. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorLeft; + /// + /// let mut stdout = stdout(); + /// // Move cursor left by 5 columns + /// write!(stdout, "{}", MoveCursorLeft(5)); + /// ``` + struct MoveCursorLeft(u16) => + |this, f| write!(f, csi!("{}D"), this.0) +); + +sequence!( + /// Moves the cursor to beginning of line the given number of lines down. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorToNextLine; + /// + /// let mut stdout = stdout(); + /// // Move cursor down by 2 rows and the move it to the first column + /// write!(stdout, "{}", MoveCursorToNextLine(2)); + /// ``` + /// + /// The previous example does the same thing as the following one: + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{MoveCursorDown, MoveCursorToColumn}; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}{}", MoveCursorDown(2), MoveCursorToColumn(1)); + /// ``` + struct MoveCursorToNextLine(u16) => + |this, f| write!(f, csi!("{}E"), this.0) +); + +sequence!( + /// Moves the cursor to beginning of line the given number of lines up. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorToPreviousLine; + /// + /// let mut stdout = stdout(); + /// // Move cursor up by 2 rows and the move it to the first column + /// write!(stdout, "{}", MoveCursorToPreviousLine(2)); + /// ``` + /// + /// The previous example does the same thing as the following one: + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::{MoveCursorUp, MoveCursorToColumn}; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}{}", MoveCursorUp(2), MoveCursorToColumn(1)); + /// ``` + struct MoveCursorToPreviousLine(u16) => + |this, f| write!(f, csi!("{}F"), this.0) +); + +sequence!( + /// Moves the cursor to the given column. + /// + /// # Notes + /// + /// Beginning of the line (left cell) is represented as `1`. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::MoveCursorToColumn; + /// + /// let mut stdout = stdout(); + /// // Move cursor to the 10th column (same row) + /// write!(stdout, "{}", MoveCursorToColumn(10)); + /// ``` + struct MoveCursorToColumn(u16) => + |this, f| write!(f, csi!("{}G"), this.0) +); + +// TODO Enhance example with Parser to show how to retrieve it +sequence!( + /// Asks for the current cursor position. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ReportCursorPosition; + /// + /// let mut stdout = stdout(); + /// write!(stdout, "{}", ReportCursorPosition); + /// ``` + struct ReportCursorPosition => csi!("6n") +); + +#[cfg(test)] +test_sequences!( + save_cursor_position( + SaveCursorPosition => "\x1B7", + ), + restore_cursor_position( + RestoreCursorPosition => "\x1B8", + ), + hide_cursor( + HideCursor => "\x1B[?25l", + ), + show_cursor( + ShowCursor => "\x1B[?25h", + ), + disable_cursor_blinking( + DisableCursorBlinking => "\x1B[?12l", + ), + enable_cursor_blinking( + EnableCursorBlinking => "\x1B[?12h", + ), + move_cursor_up( + MoveCursorUp(10) => "\x1B[10A", + ), + move_cursor_down( + MoveCursorDown(10) => "\x1B[10B", + ), + move_cursor_right( + MoveCursorRight(10) => "\x1B[10C", + ), + move_cursor_left( + MoveCursorLeft(10) => "\x1B[10D", + ), + move_cursor_to( + MoveCursorTo(5, 10) => "\x1B[10;5H", + ), + move_cursor_to_next_line( + MoveCursorToNextLine(5) => "\x1B[5E", + ), + move_cursor_to_previous_line( + MoveCursorToPreviousLine(5) => "\x1B[5F", + ), + move_cursor_to_column( + MoveCursorToColumn(1) => "\x1B[1G", + ), + report_cursor_position( + ReportCursorPosition => "\x1B[6n", + ) +); diff --git a/vendor/anes/src/sequences/terminal.rs b/vendor/anes/src/sequences/terminal.rs new file mode 100644 index 000000000..74eada65b --- /dev/null +++ b/vendor/anes/src/sequences/terminal.rs @@ -0,0 +1,54 @@ +//! A terminal related ANSI escape sequences. + +sequence!( + /// Resizes the text area to the given width and height in characters. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::{stdout, Write}; + /// use anes::ResizeTextArea; + /// + /// let mut stdout = stdout(); + /// // Resize the terminal to 80x25 + /// write!(stdout, "{}", ResizeTextArea(80, 25)); + /// ``` + struct ResizeTextArea(u16, u16) => + |this, f| write!(f, csi!("8;{};{}t"), this.1, this.0) +); + +sequence!( + /// Tells the terminal to start reporting mouse events. + /// + /// Mouse events are not reported by default. + struct EnableMouseEvents => concat!( + csi!("?1000h"), + csi!("?1002h"), + csi!("?1015h"), + csi!("?1006h") + ) +); + +sequence!( + /// Tells the terminal to stop reporting mouse events. + struct DisableMouseEvents => concat!( + csi!("?1006l"), + csi!("?1015l"), + csi!("?1002l"), + csi!("?1000l") + ) +); + +#[cfg(test)] +test_sequences!( + resize_text_area( + ResizeTextArea(80, 25) => "\x1B[8;25;80t", + ResizeTextArea(1, 1) => "\x1B[8;1;1t", + ), + enable_mouse_events( + EnableMouseEvents => "\x1B[?1000h\x1B[?1002h\x1B[?1015h\x1B[?1006h", + ), + disable_mouse_events( + DisableMouseEvents => "\x1B[?1006l\x1B[?1015l\x1B[?1002l\x1B[?1000l", + ) +); |