diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:24 +0000 |
commit | 023939b627b7dc93b01471f7d41fb8553ddb4ffa (patch) | |
tree | 60fc59477c605c72b0a1051409062ddecc43f877 /vendor/anes/src/parser.rs | |
parent | Adding debian version 1.72.1+dfsg1-1. (diff) | |
download | rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.tar.xz rustc-023939b627b7dc93b01471f7d41fb8553ddb4ffa.zip |
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/anes/src/parser.rs')
-rw-r--r-- | vendor/anes/src/parser.rs | 252 |
1 files changed, 252 insertions, 0 deletions
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()); + } +} |