diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:59:35 +0000 |
commit | d1b2d29528b7794b41e66fc2136e395a02f8529b (patch) | |
tree | a4a17504b260206dec3cf55b2dca82929a348ac2 /vendor/anes/src/parser | |
parent | Releasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.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')
-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 |
3 files changed, 932 insertions, 0 deletions
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, +} |