diff options
author | Joe Wilm <joe@jwilm.com> | 2017-01-10 20:50:21 -0800 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2017-01-10 20:51:15 -0800 |
commit | 3149ed008ad0694a85ca50231e3d6833998d53cf (patch) | |
tree | 8bc87bfebd2fecd814b19aea1c7a4cd71e82b1ef | |
parent | 7cec8a569a718e64e579f32f83d0c66cd6df5e89 (diff) | |
download | r-alacritty-vte-3149ed008ad0694a85ca50231e3d6833998d53cf.tar.gz r-alacritty-vte-3149ed008ad0694a85ca50231e3d6833998d53cf.tar.bz2 r-alacritty-vte-3149ed008ad0694a85ca50231e3d6833998d53cf.zip |
Version 0.2
Changelog has details, but this basically made OSC handling way easier
for dependents.
-rw-r--r-- | CHANGELOG.md | 9 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | examples/parselog.rs | 22 | ||||
-rw-r--r-- | src/lib.rs | 150 |
4 files changed, 150 insertions, 33 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..af2ed8d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,9 @@ +CHANGELOG +========= + +## 0.2.0 + +- Removes `osc_start`, `osc_put`, and `osc_end` +- Adds `osc_dispatch` which simply receives a list of parameters +- Removes `byte: u8` parameter from `hook` and `unhook` because it's always + zero. @@ -1,6 +1,6 @@ [package] name = "vte" -version = "0.1.2" +version = "0.2.0" authors = ["Joe Wilm <joe@jwilm.com>"] license = "Apache-2.0 OR MIT" description = "Parser for implementing terminal emulators" diff --git a/examples/parselog.rs b/examples/parselog.rs index e04fd1e..86751c9 100644 --- a/examples/parselog.rs +++ b/examples/parselog.rs @@ -15,29 +15,21 @@ impl vte::Perform for Log { println!("[execute] {:02x}", byte); } - fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, byte: u8) { - println!("[hook] params={:?}, intermediates={:?}, ignore={:?}, byte={:02x}", - params, intermediates, ignore, byte); + fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) { + println!("[hook] params={:?}, intermediates={:?}, ignore={:?}", + params, intermediates, ignore); } fn put(&mut self, byte: u8) { println!("[put] {:02x}", byte); } - fn unhook(&mut self, byte: u8) { - println!("[unhook] {:02x}", byte); - } - - fn osc_start(&mut self) { - println!("[osc_start]"); - } - - fn osc_put(&mut self, byte: u8) { - println!("[osc_put] {:02x}", byte); + fn unhook(&mut self) { + println!("[unhook]"); } - fn osc_end(&mut self, byte: u8) { - println!("[osc_end] {:02x}", byte); + fn osc_dispatch(&mut self, params: &[&[u8]]) { + println!("[csi_dispatch] params={:?}", params); } fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, c: char) { @@ -30,6 +30,8 @@ //! [Paul Williams' ANSI parser state machine]: http://vt100.net/emu/dec_ansi_parser extern crate utf8parse as utf8; +use std::mem; + mod table; mod definitions; @@ -57,6 +59,7 @@ impl State { const MAX_INTERMEDIATES: usize = 2; +const MAX_OSC_RAW: usize = 1024; const MAX_PARAMS: usize = 16; struct VtUtf8Receiver<'a, P: Perform + 'a>(&'a mut P, &'a mut State); @@ -82,6 +85,10 @@ pub struct Parser { intermediate_idx: usize, params: [i64; MAX_PARAMS], num_params: usize, + osc_raw: [u8; MAX_OSC_RAW], + osc_params: [(usize, usize); MAX_PARAMS], + osc_idx: usize, + osc_num_params: usize, ignoring: bool, utf8_parser: utf8::Parser, } @@ -95,6 +102,10 @@ impl Parser { intermediate_idx: 0, params: [0i64; MAX_PARAMS], num_params: 0, + osc_raw: [0; MAX_OSC_RAW], + osc_params: [(0, 0); MAX_PARAMS], + osc_idx: 0, + osc_num_params: 0, ignoring: false, utf8_parser: utf8::Parser::new(), } @@ -183,6 +194,23 @@ impl Parser { } } + /// Separate method for osc_dispatch that borrows self as read-only + /// + /// The aliasing is needed here for multiple slices into self.osc_raw + #[inline] + fn osc_dispatch<P: Perform>(&self, performer: &mut P) { + let mut slices: [&[u8]; MAX_PARAMS] = unsafe { mem::uninitialized() }; + + for i in 0..self.osc_num_params { + let indices = self.osc_params[i]; + slices[i] = &self.osc_raw[indices.0..indices.1]; + } + + performer.osc_dispatch( + &slices[..self.osc_num_params], + ); + } + #[inline] fn perform_action<P: Perform>(&mut self, performer: &mut P, action: Action, byte: u8) { match action { @@ -193,14 +221,59 @@ impl Parser { self.params(), self.intermediates(), self.ignoring, - byte ); }, Action::Put => performer.put(byte), - Action::OscStart => performer.osc_start(), - Action::OscPut => performer.osc_put(byte), - Action::OscEnd => performer.osc_end(byte), - Action::Unhook => performer.unhook(byte), + Action::OscStart => { + self.osc_idx = 0; + self.osc_num_params = 0; + }, + Action::OscPut => { + let idx = self.osc_idx; + if idx == self.osc_raw.len() { + return; + } + + // Param separator + if byte == b';' { + let param_idx = self.osc_num_params; + match param_idx { + // Only process up to MAX_PARAMS + MAX_PARAMS => return, + + // First param is special - 0 to current byte index + 0 => { + self.osc_params[param_idx] = (0, idx); + }, + + // All other params depend on previous indexing + _ => { + let prev = self.osc_params[param_idx - 1]; + let begin = prev.1; + self.osc_params[param_idx] = (begin, idx); + } + } + + self.osc_num_params += 1; + } else { + self.osc_raw[idx] = byte; + self.osc_idx += 1; + } + }, + Action::OscEnd => { + let param_idx = self.osc_num_params; + let idx = self.osc_idx; + // Finish last parameter if not already maxed + if param_idx != MAX_PARAMS { + let prev = self.osc_params[param_idx - 1]; + let begin = prev.1; + self.osc_params[param_idx] = (begin, idx); + } + self.osc_num_params += 1; + + self.osc_dispatch(performer); + }, + Action::Unhook => performer.unhook(), Action::CsiDispatch => { performer.csi_dispatch( self.params(), @@ -281,7 +354,7 @@ pub trait Perform { /// /// The `ignore` flag indicates that more than two intermediates arrived and /// subsequent characters were ignored. - fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, byte: u8); + fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool); /// Pass bytes as part of a device control string to the handle chosen in `hook`. C0 controls /// will also be passed to the handler. @@ -291,18 +364,10 @@ pub trait Perform { /// /// The previously selected handler should be notified that the DCS has /// terminated. - fn unhook(&mut self, byte: u8); + fn unhook(&mut self); - /// Notifies the start of an Operating System Command - fn osc_start(&mut self); - - /// Receives characters for the OSC control string - /// - /// Apparently characters don't need buffering here. - fn osc_put(&mut self, byte: u8); - - /// Called when the OSC has terminated - fn osc_end(&mut self, byte: u8); + /// Dispatch an operating system command + fn osc_dispatch(&mut self, params: &[&[u8]]); /// A final character has arrived for a CSI sequence /// @@ -316,3 +381,54 @@ pub trait Perform { /// subsequent characters were ignored. fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, byte: u8); } + +#[cfg(test)] +mod tests { + use super::{Parser, Perform}; + + static OSC_BYTES: &'static [u8] = &[0x9d, // Begin OSC + b'2', b';', b'j', b'w', b'i', b'l', b'm', b'@', b'j', b'w', b'i', b'l', + b'm', b'-', b'd', b'e', b's', b'k', b':', b' ', b'~', b'/', b'c', b'o', + b'd', b'e', b'/', b'a', b'l', b'a', b'c', b'r', b'i', b't', b't', b'y', + 0x07 // End OSC + ]; + + #[test] + fn parse_osc() { + #[derive(Default)] + struct OscDispatcher { + dispatched_osc: bool + } + + // All empty bodies except osc_dispatch + impl Perform for OscDispatcher { + fn print(&mut self, _: char) {} + fn execute(&mut self, _byte: u8) {} + fn hook(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool) {} + fn put(&mut self, _byte: u8) {} + fn unhook(&mut self) {} + fn osc_dispatch(&mut self, params: &[&[u8]]) { + // Set a flag so we know these assertions all run + self.dispatched_osc = true; + assert_eq!(params.len(), 2); + assert_eq!(params[0], &OSC_BYTES[1..2]); + assert_eq!(params[1], &OSC_BYTES[3..(OSC_BYTES.len() - 1)]); + } + fn csi_dispatch(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _c: char) {} + fn esc_dispatch(&mut self, _params: &[i64], _intermediates: &[u8], _ignore: bool, _byte: u8) {} + } + + // Create dispatcher and check state + let mut dispatcher = OscDispatcher::default(); + assert_eq!(dispatcher.dispatched_osc, false); + + // Run parser using OSC_BYTES + let mut parser = Parser::new(); + for byte in OSC_BYTES { + parser.advance(&mut dispatcher, *byte); + } + + // Check that flag is set and thus osc_dispatch assertions ran. + assert!(dispatcher.dispatched_osc); + } +} |