aboutsummaryrefslogtreecommitdiff
path: root/src/ansi.rs
diff options
context:
space:
mode:
authorTheodore Dubois <tblodt@icloud.com>2019-04-28 06:24:58 -0700
committerChristian Duerr <chrisduerr@users.noreply.github.com>2019-04-28 13:24:58 +0000
commitdbd8538762ef8968a493e1bf996e8693479ca783 (patch)
tree32ac2a6a5e01238a272d4ba534551d2e42903c7a /src/ansi.rs
parent9c6d12ea2c863ba76015bdedc00db13b7307725a (diff)
downloadr-alacritty-dbd8538762ef8968a493e1bf996e8693479ca783.tar.gz
r-alacritty-dbd8538762ef8968a493e1bf996e8693479ca783.tar.bz2
r-alacritty-dbd8538762ef8968a493e1bf996e8693479ca783.zip
Split alacritty into a separate crates
The crate containing the entry point is called alacritty, and the crate containing everything else is called alacritty_terminal.
Diffstat (limited to 'src/ansi.rs')
-rw-r--r--src/ansi.rs1568
1 files changed, 0 insertions, 1568 deletions
diff --git a/src/ansi.rs b/src/ansi.rs
deleted file mode 100644
index c0ebb79c..00000000
--- a/src/ansi.rs
+++ /dev/null
@@ -1,1568 +0,0 @@
-// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-//! ANSI Terminal Stream Parsing
-use std::io;
-use std::ops::Range;
-use std::str;
-
-use crate::index::{Column, Contains, Line};
-use base64;
-use glutin::MouseCursor;
-use vte;
-
-use crate::term::color::Rgb;
-
-// Parse color arguments
-//
-// Expect that color argument looks like "rgb:xx/xx/xx" or "#xxxxxx"
-fn parse_rgb_color(color: &[u8]) -> Option<Rgb> {
- let mut iter = color.iter();
-
- macro_rules! next {
- () => {
- iter.next().map(|v| *v as char)
- };
- }
-
- macro_rules! parse_hex {
- () => {{
- let mut digit: u8 = 0;
- let next = next!().and_then(|v| v.to_digit(16));
- if let Some(value) = next {
- digit = value as u8;
- }
-
- let next = next!().and_then(|v| v.to_digit(16));
- if let Some(value) = next {
- digit <<= 4;
- digit += value as u8;
- }
- digit
- }};
- }
-
- match next!() {
- Some('r') => {
- if next!() != Some('g') {
- return None;
- }
- if next!() != Some('b') {
- return None;
- }
- if next!() != Some(':') {
- return None;
- }
-
- let r = parse_hex!();
- let val = next!();
- if val != Some('/') {
- return None;
- }
- let g = parse_hex!();
- if next!() != Some('/') {
- return None;
- }
- let b = parse_hex!();
-
- Some(Rgb { r, g, b })
- },
- Some('#') => Some(Rgb { r: parse_hex!(), g: parse_hex!(), b: parse_hex!() }),
- _ => None,
- }
-}
-
-fn parse_number(input: &[u8]) -> Option<u8> {
- if input.is_empty() {
- return None;
- }
- let mut num: u8 = 0;
- for c in input {
- let c = *c as char;
- if let Some(digit) = c.to_digit(10) {
- num = match num.checked_mul(10).and_then(|v| v.checked_add(digit as u8)) {
- Some(v) => v,
- None => return None,
- }
- } else {
- return None;
- }
- }
- Some(num)
-}
-
-/// The processor wraps a `vte::Parser` to ultimately call methods on a Handler
-pub struct Processor {
- state: ProcessorState,
- parser: vte::Parser,
-}
-
-/// Internal state for VTE processor
-struct ProcessorState {
- preceding_char: Option<char>,
-}
-
-/// Helper type that implements `vte::Perform`.
-///
-/// Processor creates a Performer when running advance and passes the Performer
-/// to `vte::Parser`.
-struct Performer<'a, H: Handler + TermInfo, W: io::Write> {
- _state: &'a mut ProcessorState,
- handler: &'a mut H,
- writer: &'a mut W,
-}
-
-impl<'a, H: Handler + TermInfo + 'a, W: io::Write> Performer<'a, H, W> {
- /// Create a performer
- #[inline]
- pub fn new<'b>(
- state: &'b mut ProcessorState,
- handler: &'b mut H,
- writer: &'b mut W,
- ) -> Performer<'b, H, W> {
- Performer { _state: state, handler, writer }
- }
-}
-
-impl Default for Processor {
- fn default() -> Processor {
- Processor { state: ProcessorState { preceding_char: None }, parser: vte::Parser::new() }
- }
-}
-
-impl Processor {
- pub fn new() -> Processor {
- Default::default()
- }
-
- #[inline]
- pub fn advance<H, W>(&mut self, handler: &mut H, byte: u8, writer: &mut W)
- where
- H: Handler + TermInfo,
- W: io::Write,
- {
- let mut performer = Performer::new(&mut self.state, handler, writer);
- self.parser.advance(&mut performer, byte);
- }
-}
-
-/// Trait that provides properties of terminal
-pub trait TermInfo {
- fn lines(&self) -> Line;
- fn cols(&self) -> Column;
-}
-
-/// Type that handles actions from the parser
-///
-/// XXX Should probably not provide default impls for everything, but it makes
-/// writing specific handler impls for tests far easier.
-pub trait Handler {
- /// OSC to set window title
- fn set_title(&mut self, _: &str) {}
-
- /// Set the window's mouse cursor
- fn set_mouse_cursor(&mut self, _: MouseCursor) {}
-
- /// Set the cursor style
- fn set_cursor_style(&mut self, _: Option<CursorStyle>) {}
-
- /// A character to be displayed
- fn input(&mut self, _c: char) {}
-
- /// Set cursor to position
- fn goto(&mut self, _: Line, _: Column) {}
-
- /// Set cursor to specific row
- fn goto_line(&mut self, _: Line) {}
-
- /// Set cursor to specific column
- fn goto_col(&mut self, _: Column) {}
-
- /// Insert blank characters in current line starting from cursor
- fn insert_blank(&mut self, _: Column) {}
-
- /// Move cursor up `rows`
- fn move_up(&mut self, _: Line) {}
-
- /// Move cursor down `rows`
- fn move_down(&mut self, _: Line) {}
-
- /// Identify the terminal (should write back to the pty stream)
- ///
- /// TODO this should probably return an io::Result
- fn identify_terminal<W: io::Write>(&mut self, _: &mut W) {}
-
- // Report device status
- fn device_status<W: io::Write>(&mut self, _: &mut W, _: usize) {}
-
- /// Move cursor forward `cols`
- fn move_forward(&mut self, _: Column) {}
-
- /// Move cursor backward `cols`
- fn move_backward(&mut self, _: Column) {}
-
- /// Move cursor down `rows` and set to column 1
- fn move_down_and_cr(&mut self, _: Line) {}
-
- /// Move cursor up `rows` and set to column 1
- fn move_up_and_cr(&mut self, _: Line) {}
-
- /// Put `count` tabs
- fn put_tab(&mut self, _count: i64) {}
-
- /// Backspace `count` characters
- fn backspace(&mut self) {}
-
- /// Carriage return
- fn carriage_return(&mut self) {}
-
- /// Linefeed
- fn linefeed(&mut self) {}
-
- /// Ring the bell
- ///
- /// Hopefully this is never implemented
- fn bell(&mut self) {}
-
- /// Substitute char under cursor
- fn substitute(&mut self) {}
-
- /// Newline
- fn newline(&mut self) {}
-
- /// Set current position as a tabstop
- fn set_horizontal_tabstop(&mut self) {}
-
- /// Scroll up `rows` rows
- fn scroll_up(&mut self, _: Line) {}
-
- /// Scroll down `rows` rows
- fn scroll_down(&mut self, _: Line) {}
-
- /// Insert `count` blank lines
- fn insert_blank_lines(&mut self, _: Line) {}
-
- /// Delete `count` lines
- fn delete_lines(&mut self, _: Line) {}
-
- /// Erase `count` chars in current line following cursor
- ///
- /// Erase means resetting to the default state (default colors, no content,
- /// no mode flags)
- fn erase_chars(&mut self, _: Column) {}
-
- /// Delete `count` chars
- ///
- /// Deleting a character is like the delete key on the keyboard - everything
- /// to the right of the deleted things is shifted left.
- fn delete_chars(&mut self, _: Column) {}
-
- /// Move backward `count` tabs
- fn move_backward_tabs(&mut self, _count: i64) {}
-
- /// Move forward `count` tabs
- fn move_forward_tabs(&mut self, _count: i64) {}
-
- /// Save current cursor position
- fn save_cursor_position(&mut self) {}
-
- /// Restore cursor position
- fn restore_cursor_position(&mut self) {}
-
- /// Clear current line
- fn clear_line(&mut self, _mode: LineClearMode) {}
-
- /// Clear screen
- fn clear_screen(&mut self, _mode: ClearMode) {}
-
- /// Clear tab stops
- fn clear_tabs(&mut self, _mode: TabulationClearMode) {}
-
- /// Reset terminal state
- fn reset_state(&mut self) {}
-
- /// Reverse Index
- ///
- /// Move the active position to the same horizontal position on the
- /// preceding line. If the active position is at the top margin, a scroll
- /// down is performed
- fn reverse_index(&mut self) {}
-
- /// set a terminal attribute
- fn terminal_attribute(&mut self, _attr: Attr) {}
-
- /// Set mode
- fn set_mode(&mut self, _mode: Mode) {}
-
- /// Unset mode
- fn unset_mode(&mut self, _: Mode) {}
-
- /// DECSTBM - Set the terminal scrolling region
- fn set_scrolling_region(&mut self, _: Range<Line>) {}
-
- /// DECKPAM - Set keypad to applications mode (ESCape instead of digits)
- fn set_keypad_application_mode(&mut self) {}
-
- /// DECKPNM - Set keypad to numeric mode (digits instead of ESCape seq)
- fn unset_keypad_application_mode(&mut self) {}
-
- /// Set one of the graphic character sets, G0 to G3, as the active charset.
- ///
- /// 'Invoke' one of G0 to G3 in the GL area. Also referred to as shift in,
- /// shift out and locking shift depending on the set being activated
- fn set_active_charset(&mut self, _: CharsetIndex) {}
-
- /// Assign a graphic character set to G0, G1, G2 or G3
- ///
- /// 'Designate' a graphic character set as one of G0 to G3, so that it can
- /// later be 'invoked' by `set_active_charset`
- fn configure_charset(&mut self, _: CharsetIndex, _: StandardCharset) {}
-
- /// Set an indexed color value
- fn set_color(&mut self, _: usize, _: Rgb) {}
-
- /// Reset an indexed color to original value
- fn reset_color(&mut self, _: usize) {}
-
- /// Set the clipboard
- fn set_clipboard(&mut self, _: &str) {}
-
- /// Run the dectest routine
- fn dectest(&mut self) {}
-}
-
-/// Describes shape of cursor
-#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash, Deserialize)]
-pub enum CursorStyle {
- /// Cursor is a block like `▒`
- Block,
-
- /// Cursor is an underscore like `_`
- Underline,
-
- /// Cursor is a vertical bar `⎸`
- Beam,
-
- /// Cursor is a box like `☐`
- HollowBlock,
-
- /// Invisible cursor
- Hidden,
-}
-
-impl Default for CursorStyle {
- fn default() -> CursorStyle {
- CursorStyle::Block
- }
-}
-
-/// Terminal modes
-#[derive(Debug, Eq, PartialEq)]
-pub enum Mode {
- /// ?1
- CursorKeys = 1,
- /// Select 80 or 132 columns per page
- ///
- /// CSI ? 3 h -> set 132 column font
- /// CSI ? 3 l -> reset 80 column font
- ///
- /// Additionally,
- ///
- /// * set margins to default positions
- /// * erases all data in page memory
- /// * resets DECLRMM to unavailable
- /// * clears data from the status line (if set to host-writable)
- DECCOLM = 3,
- /// IRM Insert Mode
- ///
- /// NB should be part of non-private mode enum
- ///
- /// * `CSI 4 h` change to insert mode
- /// * `CSI 4 l` reset to replacement mode
- Insert = 4,
- /// ?6
- Origin = 6,
- /// ?7
- LineWrap = 7,
- /// ?12
- BlinkingCursor = 12,
- /// 20
- ///
- /// NB This is actually a private mode. We should consider adding a second
- /// enumeration for public/private modesets.
- LineFeedNewLine = 20,
- /// ?25
- ShowCursor = 25,
- /// ?1000
- ReportMouseClicks = 1000,
- /// ?1002
- ReportCellMouseMotion = 1002,
- /// ?1003
- ReportAllMouseMotion = 1003,
- /// ?1004
- ReportFocusInOut = 1004,
- /// ?1006
- SgrMouse = 1006,
- /// ?1049
- SwapScreenAndSetRestoreCursor = 1049,
- /// ?2004
- BracketedPaste = 2004,
-}
-
-impl Mode {
- /// Create mode from a primitive
- ///
- /// TODO lots of unhandled values..
- pub fn from_primitive(private: bool, num: i64) -> Option<Mode> {
- if private {
- Some(match num {
- 1 => Mode::CursorKeys,
- 3 => Mode::DECCOLM,
- 6 => Mode::Origin,
- 7 => Mode::LineWrap,
- 12 => Mode::BlinkingCursor,
- 25 => Mode::ShowCursor,
- 1000 => Mode::ReportMouseClicks,
- 1002 => Mode::ReportCellMouseMotion,
- 1003 => Mode::ReportAllMouseMotion,
- 1004 => Mode::ReportFocusInOut,
- 1006 => Mode::SgrMouse,
- 1049 => Mode::SwapScreenAndSetRestoreCursor,
- 2004 => Mode::BracketedPaste,
- _ => {
- trace!("[unimplemented] primitive mode: {}", num);
- return None;
- },
- })
- } else {
- Some(match num {
- 4 => Mode::Insert,
- 20 => Mode::LineFeedNewLine,
- _ => return None,
- })
- }
- }
-}
-
-/// Mode for clearing line
-///
-/// Relative to cursor
-#[derive(Debug)]
-pub enum LineClearMode {
- /// Clear right of cursor
- Right,
- /// Clear left of cursor
- Left,
- /// Clear entire line
- All,
-}
-
-/// Mode for clearing terminal
-///
-/// Relative to cursor
-#[derive(Debug)]
-pub enum ClearMode {
- /// Clear below cursor
- Below,
- /// Clear above cursor
- Above,
- /// Clear entire terminal
- All,
- /// Clear 'saved' lines (scrollback)
- Saved,
-}
-
-/// Mode for clearing tab stops
-#[derive(Debug)]
-pub enum TabulationClearMode {
- /// Clear stop under cursor
- Current,
- /// Clear all stops
- All,
-}
-
-/// Standard colors
-///
-/// The order here matters since the enum should be castable to a `usize` for
-/// indexing a color list.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
-pub enum NamedColor {
- /// Black
- Black = 0,
- /// Red
- Red,
- /// Green
- Green,
- /// Yellow
- Yellow,
- /// Blue
- Blue,
- /// Magenta
- Magenta,
- /// Cyan
- Cyan,
- /// White
- White,
- /// Bright black
- BrightBlack,
- /// Bright red
- BrightRed,
- /// Bright green
- BrightGreen,
- /// Bright yellow
- BrightYellow,
- /// Bright blue
- BrightBlue,
- /// Bright magenta
- BrightMagenta,
- /// Bright cyan
- BrightCyan,
- /// Bright white
- BrightWhite,
- /// The foreground color
- Foreground = 256,
- /// The background color
- Background,
- /// Color for the text under the cursor
- CursorText,
- /// Color for the cursor itself
- Cursor,
- /// Dim black
- DimBlack,
- /// Dim red
- DimRed,
- /// Dim green
- DimGreen,
- /// Dim yellow
- DimYellow,
- /// Dim blue
- DimBlue,
- /// Dim magenta
- DimMagenta,
- /// Dim cyan
- DimCyan,
- /// Dim white
- DimWhite,
- /// The bright foreground color
- BrightForeground,
- /// Dim foreground
- DimForeground,
-}
-
-impl NamedColor {
- pub fn to_bright(self) -> Self {
- match self {
- NamedColor::Foreground => NamedColor::BrightForeground,
- NamedColor::Black => NamedColor::BrightBlack,
- NamedColor::Red => NamedColor::BrightRed,
- NamedColor::Green => NamedColor::BrightGreen,
- NamedColor::Yellow => NamedColor::BrightYellow,
- NamedColor::Blue => NamedColor::BrightBlue,
- NamedColor::Magenta => NamedColor::BrightMagenta,
- NamedColor::Cyan => NamedColor::BrightCyan,
- NamedColor::White => NamedColor::BrightWhite,
- NamedColor::DimForeground => NamedColor::Foreground,
- NamedColor::DimBlack => NamedColor::Black,
- NamedColor::DimRed => NamedColor::Red,
- NamedColor::DimGreen => NamedColor::Green,
- NamedColor::DimYellow => NamedColor::Yellow,
- NamedColor::DimBlue => NamedColor::Blue,
- NamedColor::DimMagenta => NamedColor::Magenta,
- NamedColor::DimCyan => NamedColor::Cyan,
- NamedColor::DimWhite => NamedColor::White,
- val => val,
- }
- }
-
- pub fn to_dim(self) -> Self {
- match self {
- NamedColor::Black => NamedColor::DimBlack,
- NamedColor::Red => NamedColor::DimRed,
- NamedColor::Green => NamedColor::DimGreen,
- NamedColor::Yellow => NamedColor::DimYellow,
- NamedColor::Blue => NamedColor::DimBlue,
- NamedColor::Magenta => NamedColor::DimMagenta,
- NamedColor::Cyan => NamedColor::DimCyan,
- NamedColor::White => NamedColor::DimWhite,
- NamedColor::Foreground => NamedColor::DimForeground,
- NamedColor::BrightBlack => NamedColor::Black,
- NamedColor::BrightRed => NamedColor::Red,
- NamedColor::BrightGreen => NamedColor::Green,
- NamedColor::BrightYellow => NamedColor::Yellow,
- NamedColor::BrightBlue => NamedColor::Blue,
- NamedColor::BrightMagenta => NamedColor::Magenta,
- NamedColor::BrightCyan => NamedColor::Cyan,
- NamedColor::BrightWhite => NamedColor::White,
- NamedColor::BrightForeground => NamedColor::Foreground,
- val => val,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
-pub enum Color {
- Named(NamedColor),
- Spec(Rgb),
- Indexed(u8),
-}
-
-/// Terminal character attributes
-#[derive(Debug, Eq, PartialEq)]
-pub enum Attr {
- /// Clear all special abilities
- Reset,
- /// Bold text
- Bold,
- /// Dim or secondary color
- Dim,
- /// Italic text
- Italic,
- /// Underscore text
- Underscore,
- /// Blink cursor slowly
- BlinkSlow,
- /// Blink cursor fast
- BlinkFast,
- /// Invert colors
- Reverse,
- /// Do not display characters
- Hidden,
- /// Strikeout text
- Strike,
- /// Cancel bold
- CancelBold,
- /// Cancel bold and dim
- CancelBoldDim,
- /// Cancel italic
- CancelItalic,
- /// Cancel underline
- CancelUnderline,
- /// Cancel blink
- CancelBlink,
- /// Cancel inversion
- CancelReverse,
- /// Cancel text hiding
- CancelHidden,
- /// Cancel strikeout
- CancelStrike,
- /// Set indexed foreground color
- Foreground(Color),
- /// Set indexed background color
- Background(Color),
-}
-
-/// Identifiers which can be assigned to a graphic character set
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum CharsetIndex {
- /// Default set, is designated as ASCII at startup
- G0,
- G1,
- G2,
- G3,
-}
-
-impl Default for CharsetIndex {
- fn default() -> Self {
- CharsetIndex::G0
- }
-}
-
-/// Standard or common character sets which can be designated as G0-G3
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
-pub enum StandardCharset {
- Ascii,
- SpecialCharacterAndLineDrawing,
-}
-
-impl Default for StandardCharset {
- fn default() -> Self {
- StandardCharset::Ascii
- }
-}
-
-impl<'a, H, W> vte::Perform for Performer<'a, H, W>
-where
- H: Handler + TermInfo + 'a,
- W: io::Write + 'a,
-{
- #[inline]
- fn print(&mut self, c: char) {
- self.handler.input(c);
- self._state.preceding_char = Some(c);
- }
-
- #[inline]
- fn execute(&mut self, byte: u8) {
- match byte {
- C0::HT => self.handler.put_tab(1),
- C0::BS => self.handler.backspace(),
- C0::CR => self.handler.carriage_return(),
- C0::LF | C0::VT | C0::FF => self.handler.linefeed(),
- C0::BEL => self.handler.bell(),
- C0::SUB => self.handler.substitute(),
- C0::SI => self.handler.set_active_charset(CharsetIndex::G0),
- C0::SO => self.handler.set_active_charset(CharsetIndex::G1),
- C1::NEL => self.handler.newline(),
- C1::HTS => self.handler.set_horizontal_tabstop(),
- C1::DECID => self.handler.identify_terminal(self.writer),
- _ => debug!("[unhandled] execute byte={:02x}", byte),
- }
- }
-
- #[inline]
- fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool) {
- debug!(
- "[unhandled hook] params={:?}, ints: {:?}, ignore: {:?}",
- params, intermediates, ignore
- );
- }
-
- #[inline]
- fn put(&mut self, byte: u8) {
- debug!("[unhandled put] byte={:?}", byte);
- }
-
- #[inline]
- fn unhook(&mut self) {
- debug!("[unhandled unhook]");
- }
-
- // TODO replace OSC parsing with parser combinators
- #[inline]
- fn osc_dispatch(&mut self, params: &[&[u8]]) {
- fn unhandled(params: &[&[u8]]) {
- let mut buf = String::new();
- for items in params {
- buf.push_str("[");
- for item in *items {
- buf.push_str(&format!("{:?},", *item as char));
- }
- buf.push_str("],");
- }
- debug!("[unhandled osc_dispatch]: [{}] at line {}", &buf, line!());
- }
-
- if params.is_empty() || params[0].is_empty() {
- return;
- }
-
- match params[0] {
- // Set window title
- b"0" | b"2" => {
- if params.len() >= 2 {
- if let Ok(utf8_title) = str::from_utf8(params[1]) {
- self.handler.set_title(utf8_title);
- return;
- }
- }
- unhandled(params);
- },
-
- // Set icon name
- // This is ignored, since alacritty has no concept of tabs
- b"1" => return,
-
- // Set color index
- b"4" => {
- if params.len() > 1 && params.len() % 2 != 0 {
- for chunk in params[1..].chunks(2) {
- let index = parse_number(chunk[0]);
- let color = parse_rgb_color(chunk[1]);
- if let (Some(i), Some(c)) = (index, color) {
- self.handler.set_color(i as usize, c);
- return;
- }
- }
- }
- unhandled(params);
- },
-
- // Set foreground color
- b"10" => {
- if params.len() >= 2 {
- if let Some(color) = parse_rgb_color(params[1]) {
- self.handler.set_color(NamedColor::Foreground as usize, color);
- return;
- }
- }
- unhandled(params);
- },
-
- // Set background color
- b"11" => {
- if params.len() >= 2 {
- if let Some(color) = parse_rgb_color(params[1]) {
- self.handler.set_color(NamedColor::Background as usize, color);
- return;
- }
- }
- unhandled(params);
- },
-
- // Set text cursor color
- b"12" => {
- if params.len() >= 2 {
- if let Some(color) = parse_rgb_color(params[1]) {
- self.handler.set_color(NamedColor::Cursor as usize, color);
- return;
- }
- }
- unhandled(params);
- },
-
- // Set cursor style
- b"50" => {
- if params.len() >= 2
- && params[1].len() >= 13
- && params[1][0..12] == *b"CursorShape="
- {
- let style = match params[1][12] as char {
- '0' => CursorStyle::Block,
- '1' => CursorStyle::Beam,
- '2' => CursorStyle::Underline,
- _ => return unhandled(params),
- };
- self.handler.set_cursor_style(Some(style));
- return;
- }
- unhandled(params);
- },
-
- // Set clipboard
- b"52" => {
- if params.len() < 3 {
- return unhandled(params);
- }
-
- match params[2] {
- b"?" => unhandled(params),
- selection => {
- if let Ok(string) = base64::decode(selection) {
- if let Ok(utf8_string) = str::from_utf8(&string) {
- self.handler.set_clipboard(utf8_string);
- }
- }
- },
- }
- },
-
- // Reset color index
- b"104" => {
- // Reset all color indexes when no parameters are given
- if params.len() == 1 {
- for i in 0..256 {
- self.handler.reset_color(i);
- }
- return;
- }
-
- // Reset color indexes given as parameters
- for param in &params[1..] {
- match parse_number(param) {
- Some(index) => self.handler.reset_color(index as usize),
- None => unhandled(params),
- }
- }
- },
-
- // Reset foreground color
- b"110" => self.handler.reset_color(NamedColor::Foreground as usize),
-
- // Reset background color
- b"111" => self.handler.reset_color(NamedColor::Background as usize),
-
- // Reset text cursor color
- b"112" => self.handler.reset_color(NamedColor::Cursor as usize),
-
- _ => unhandled(params),
- }
- }
-
- #[inline]
- fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) {
- let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
- let handler = &mut self.handler;
- let writer = &mut self.writer;
-
- macro_rules! unhandled {
- () => {{
- debug!(
- "[Unhandled CSI] action={:?}, args={:?}, intermediates={:?}",
- action, args, intermediates
- );
- return;
- }};
- }
-
- macro_rules! arg_or_default {
- (idx: $idx:expr, default: $default:expr) => {
- args.get($idx)
- .and_then(|v| if *v == 0 { None } else { Some(*v) })
- .unwrap_or($default)
- };
- }
-
- match action {
- '@' => handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'A' => {
- handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize));
- },
- 'b' => {
- if let Some(c) = self._state.preceding_char {
- for _ in 0..arg_or_default!(idx: 0, default: 1) {
- handler.input(c);
- }
- } else {
- debug!("tried to repeat with no preceding char");
- }
- },
- 'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'c' => handler.identify_terminal(writer),
- 'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'D' => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'E' => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'F' => handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'g' => {
- let mode = match arg_or_default!(idx: 0, default: 0) {
- 0 => TabulationClearMode::Current,
- 3 => TabulationClearMode::All,
- _ => unhandled!(),
- };
-
- handler.clear_tabs(mode);
- },
- 'G' | '`' => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)),
- 'H' | 'f' => {
- let y = arg_or_default!(idx: 0, default: 1) as usize;
- let x = arg_or_default!(idx: 1, default: 1) as usize;
- handler.goto(Line(y - 1), Column(x - 1));
- },
- 'I' => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)),
- 'J' => {
- let mode = match arg_or_default!(idx: 0, default: 0) {
- 0 => ClearMode::Below,
- 1 => ClearMode::Above,
- 2 => ClearMode::All,
- 3 => ClearMode::Saved,
- _ => unhandled!(),
- };
-
- handler.clear_screen(mode);
- },
- 'K' => {
- let mode = match arg_or_default!(idx: 0, default: 0) {
- 0 => LineClearMode::Right,
- 1 => LineClearMode::Left,
- 2 => LineClearMode::All,
- _ => unhandled!(),
- };
-
- handler.clear_line(mode);
- },
- 'S' => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'T' => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'L' => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'l' => {
- for arg in args {
- let mode = Mode::from_primitive(private, *arg);
- match mode {
- Some(mode) => handler.unset_mode(mode),
- None => unhandled!(),
- }
- }
- },
- 'M' => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
- 'X' => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'P' => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
- 'Z' => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)),
- 'd' => handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)),
- 'h' => {
- for arg in args {
- let mode = Mode::from_primitive(private, *arg);
- match mode {
- Some(mode) => handler.set_mode(mode),
- None => unhandled!(),
- }
- }
- },
- 'm' => {
- // Sometimes a C-style for loop is just what you need
- let mut i = 0; // C-for initializer
- if args.is_empty() {
- handler.terminal_attribute(Attr::Reset);
- return;
- }
- loop {
- if i >= args.len() {
- // C-for condition
- break;
- }
-
- let attr = match args[i] {
- 0 => Attr::Reset,
- 1 => Attr::Bold,
- 2 => Attr::Dim,
- 3 => Attr::Italic,
- 4 => Attr::Underscore,
- 5 => Attr::BlinkSlow,
- 6 => Attr::BlinkFast,
- 7 => Attr::Reverse,
- 8 => Attr::Hidden,
- 9 => Attr::Strike,
- 21 => Attr::CancelBold,
- 22 => Attr::CancelBoldDim,
- 23 => Attr::CancelItalic,
- 24 => Attr::CancelUnderline,
- 25 => Attr::CancelBlink,
- 27 => Attr::CancelReverse,
- 28 => Attr::CancelHidden,
- 29 => Attr::CancelStrike,
- 30 => Attr::Foreground(Color::Named(NamedColor::Black)),
- 31 => Attr::Foreground(Color::Named(NamedColor::Red)),
- 32 => Attr::Foreground(Color::Named(NamedColor::Green)),
- 33 => Attr::Foreground(Color::Named(NamedColor::Yellow)),
- 34 => Attr::Foreground(Color::Named(NamedColor::Blue)),
- 35 => Attr::Foreground(Color::Named(NamedColor::Magenta)),
- 36 => Attr::Foreground(Color::Named(NamedColor::Cyan)),
- 37 => Attr::Foreground(Color::Named(NamedColor::White)),
- 38 => {
- let mut start = 0;
- if let Some(color) = parse_color(&args[i..], &mut start) {
- i += start;
- Attr::Foreground(color)
- } else {
- break;
- }
- },
- 39 => Attr::Foreground(Color::Named(NamedColor::Foreground)),
- 40 => Attr::Background(Color::Named(NamedColor::Black)),
- 41 => Attr::Background(Color::Named(NamedColor::Red)),
- 42 => Attr::Background(Color::Named(NamedColor::Green)),
- 43 => Attr::Background(Color::Named(NamedColor::Yellow)),
- 44 => Attr::Background(Color::Named(NamedColor::Blue)),
- 45 => Attr::Background(Color::Named(NamedColor::Magenta)),
- 46 => Attr::Background(Color::Named(NamedColor::Cyan)),
- 47 => Attr::Background(Color::Named(NamedColor::White)),
- 48 => {
- let mut start = 0;
- if let Some(color) = parse_color(&args[i..], &mut start) {
- i += start;
- Attr::Background(color)
- } else {
- break;
- }
- },
- 49 => Attr::Background(Color::Named(NamedColor::Background)),
- 90 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)),
- 91 => Attr::Foreground(Color::Named(NamedColor::BrightRed)),
- 92 => Attr::Foreground(Color::Named(NamedColor::BrightGreen)),
- 93 => Attr::Foreground(Color::Named(NamedColor::BrightYellow)),
- 94 => Attr::Foreground(Color::Named(NamedColor::BrightBlue)),
- 95 => Attr::Foreground(Color::Named(NamedColor::BrightMagenta)),
- 96 => Attr::Foreground(Color::Named(NamedColor::BrightCyan)),
- 97 => Attr::Foreground(Color::Named(NamedColor::BrightWhite)),
- 100 => Attr::Background(Color::Named(NamedColor::BrightBlack)),
- 101 => Attr::Background(Color::Named(NamedColor::BrightRed)),
- 102 => Attr::Background(Color::Named(NamedColor::BrightGreen)),
- 103 => Attr::Background(Color::Named(NamedColor::BrightYellow)),
- 104 => Attr::Background(Color::Named(NamedColor::BrightBlue)),
- 105 => Attr::Background(Color::Named(NamedColor::BrightMagenta)),
- 106 => Attr::Background(Color::Named(NamedColor::BrightCyan)),
- 107 => Attr::Background(Color::Named(NamedColor::BrightWhite)),
- _ => unhandled!(),
- };
-
- handler.terminal_attribute(attr);
-
- i += 1; // C-for expr
- }
- },
- 'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
- 'r' => {
- if private {
- unhandled!();
- }
- let arg0 = arg_or_default!(idx: 0, default: 1) as usize;
- let top = Line(arg0 - 1);
- // Bottom should be included in the range, but range end is not
- // usually included. One option would be to use an inclusive
- // range, but instead we just let the open range end be 1
- // higher.
- let arg1 = arg_or_default!(idx: 1, default: handler.lines().0 as _) as usize;
- let bottom = Line(arg1);
-
- handler.set_scrolling_region(top..bottom);
- },
- 's' => handler.save_cursor_position(),
- 'u' => handler.restore_cursor_position(),
- 'q' => {
- let style = match arg_or_default!(idx: 0, default: 0) {
- 0 => None,
- 1 | 2 => Some(CursorStyle::Block),
- 3 | 4 => Some(CursorStyle::Underline),
- 5 | 6 => Some(CursorStyle::Beam),
- _ => unhandled!(),
- };
-
- handler.set_cursor_style(style);
- },
- _ => unhandled!(),
- }
- }
-
- #[inline]
- fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], _ignore: bool, byte: u8) {
- macro_rules! unhandled {
- () => {{
- debug!(
- "[unhandled] esc_dispatch params={:?}, ints={:?}, byte={:?} ({:02x})",
- params, intermediates, byte as char, byte
- );
- return;
- }};
- }
-
- macro_rules! configure_charset {
- ($charset:path) => {{
- let index: CharsetIndex = match intermediates.first().cloned() {
- Some(b'(') => CharsetIndex::G0,
- Some(b')') => CharsetIndex::G1,
- Some(b'*') => CharsetIndex::G2,
- Some(b'+') => CharsetIndex::G3,
- _ => unhandled!(),
- };
- self.handler.configure_charset(index, $charset)
- }};
- }
-
- match byte {
- b'B' => configure_charset!(StandardCharset::Ascii),
- b'D' => self.handler.linefeed(),
- b'E' => {
- self.handler.linefeed();
- self.handler.carriage_return();
- },
- b'H' => self.handler.set_horizontal_tabstop(),
- b'M' => self.handler.reverse_index(),
- b'Z' => self.handler.identify_terminal(self.writer),
- b'c' => self.handler.reset_state(),
- b'0' => configure_charset!(StandardCharset::SpecialCharacterAndLineDrawing),
- b'7' => self.handler.save_cursor_position(),
- b'8' => {
- if !intermediates.is_empty() && intermediates[0] == b'#' {
- self.handler.dectest();
- } else {
- self.handler.restore_cursor_position();
- }
- },
- b'=' => self.handler.set_keypad_application_mode(),
- b'>' => self.handler.unset_keypad_application_mode(),
- b'\\' => (), // String terminator, do nothing (parser handles as string terminator)
- _ => unhandled!(),
- }
- }
-}
-
-/// Parse a color specifier from list of attributes
-fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
- if attrs.len() < 2 {
- return None;
- }
-
- match attrs[*i + 1] {
- 2 => {
- // RGB color spec
- if attrs.len() < 5 {
- debug!("Expected RGB color spec; got {:?}", attrs);
- return None;
- }
-
- let r = attrs[*i + 2];
- let g = attrs[*i + 3];
- let b = attrs[*i + 4];
-
- *i += 4;
-
- let range = 0..256;
- if !range.contains_(r) || !range.contains_(g) || !range.contains_(b) {
- debug!("Invalid RGB color spec: ({}, {}, {})", r, g, b);
- return None;
- }
-
- Some(Color::Spec(Rgb { r: r as u8, g: g as u8, b: b as u8 }))
- },
- 5 => {
- if attrs.len() < 3 {
- debug!("Expected color index; got {:?}", attrs);
- None
- } else {
- *i += 2;
- let idx = attrs[*i];
- match idx {
- 0..=255 => Some(Color::Indexed(idx as u8)),
- _ => {
- debug!("Invalid color index: {}", idx);
- None
- },
- }
- }
- },
- _ => {
- debug!("Unexpected color attr: {}", attrs[*i + 1]);
- None
- },
- }
-}
-
-/// C0 set of 7-bit control characters (from ANSI X3.4-1977).
-#[allow(non_snake_case)]
-pub mod C0 {
- /// Null filler, terminal should ignore this character
- pub const NUL: u8 = 0x00;
- /// Start of Header
- pub const SOH: u8 = 0x01;
- /// Start of Text, implied end of header
- pub const STX: u8 = 0x02;
- /// End of Text, causes some terminal to respond with ACK or NAK
- pub const ETX: u8 = 0x03;
- /// End of Transmission
- pub const EOT: u8 = 0x04;
- /// Enquiry, causes terminal to send ANSWER-BACK ID
- pub const ENQ: u8 = 0x05;
- /// Acknowledge, usually sent by terminal in response to ETX
- pub const ACK: u8 = 0x06;
- /// Bell, triggers the bell, buzzer, or beeper on the terminal
- pub const BEL: u8 = 0x07;
- /// Backspace, can be used to define overstruck characters
- pub const BS: u8 = 0x08;
- /// Horizontal Tabulation, move to next predetermined position
- pub const HT: u8 = 0x09;
- /// Linefeed, move to same position on next line (see also NL)
- pub const LF: u8 = 0x0A;
- /// Vertical Tabulation, move to next predetermined line
- pub const VT: u8 = 0x0B;
- /// Form Feed, move to next form or page
- pub const FF: u8 = 0x0C;
- /// Carriage Return, move to first character of current line
- pub const CR: u8 = 0x0D;
- /// Shift Out, switch to G1 (other half of character set)
- pub const SO: u8 = 0x0E;
- /// Shift In, switch to G0 (normal half of character set)
- pub const SI: u8 = 0x0F;
- /// Data Link Escape, interpret next control character specially
- pub const DLE: u8 = 0x10;
- /// (DC1) Terminal is allowed to resume transmitting
- pub const XON: u8 = 0x11;
- /// Device Control 2, causes ASR-33 to activate paper-tape reader
- pub const DC2: u8 = 0x12;
- /// (DC2) Terminal must pause and refrain from transmitting
- pub const XOFF: u8 = 0x13;
- /// Device Control 4, causes ASR-33 to deactivate paper-tape reader
- pub const DC4: u8 = 0x14;
- /// Negative Acknowledge, used sometimes with ETX and ACK
- pub const NAK: u8 = 0x15;
- /// Synchronous Idle, used to maintain timing in Sync communication
- pub const SYN: u8 = 0x16;
- /// End of Transmission block
- pub const ETB: u8 = 0x17;
- /// Cancel (makes VT100 abort current escape sequence if any)
- pub const CAN: u8 = 0x18;
- /// End of Medium
- pub const EM: u8 = 0x19;
- /// Substitute (VT100 uses this to display parity errors)
- pub const SUB: u8 = 0x1A;
- /// Prefix to an escape sequence
- pub const ESC: u8 = 0x1B;
- /// File Separator
- pub const FS: u8 = 0x1C;
- /// Group Separator
- pub const GS: u8 = 0x1D;
- /// Record Separator (sent by VT132 in block-transfer mode)
- pub const RS: u8 = 0x1E;
- /// Unit Separator
- pub const US: u8 = 0x1F;
- /// Delete, should be ignored by terminal
- pub const DEL: u8 = 0x7f;
-}
-
-/// C1 set of 8-bit control characters (from ANSI X3.64-1979)
-///
-/// 0x80 (@), 0x81 (A), 0x82 (B), 0x83 (C) are reserved
-/// 0x98 (X), 0x99 (Y) are reserved
-/// 0x9a (Z) is 'reserved', but causes DEC terminals to respond with DA codes
-#[allow(non_snake_case)]
-pub mod C1 {
- /// Reserved
- pub const PAD: u8 = 0x80;
- /// Reserved
- pub const HOP: u8 = 0x81;
- /// Reserved
- pub const BPH: u8 = 0x82;
- /// Reserved
- pub const NBH: u8 = 0x83;
- /// Index, moves down one line same column regardless of NL
- pub const IND: u8 = 0x84;
- /// New line, moves done one line and to first column (CR+LF)
- pub const NEL: u8 = 0x85;
- /// Start of Selected Area to be sent to auxiliary output device
- pub const SSA: u8 = 0x86;
- /// End of Selected Area to be sent to auxiliary output device
- pub const ESA: u8 = 0x87;
- /// Horizontal Tabulation Set at current position
- pub const HTS: u8 = 0x88;
- /// Hor Tab Justify, moves string to next tab position
- pub const HTJ: u8 = 0x89;
- /// Vertical Tabulation Set at current line
- pub const VTS: u8 = 0x8A;
- /// Partial Line Down (subscript)
- pub const PLD: u8 = 0x8B;
- /// Partial Line Up (superscript)
- pub const PLU: u8 = 0x8C;
- /// Reverse Index, go up one line, reverse scroll if necessary
- pub const RI: u8 = 0x8D;
- /// Single Shift to G2
- pub const SS2: u8 = 0x8E;
- /// Single Shift to G3 (VT100 uses this for sending PF keys)
- pub const SS3: u8 = 0x8F;
- /// Device Control String, terminated by ST (VT125 enters graphics)
- pub const DCS: u8 = 0x90;
- /// Private Use 1
- pub const PU1: u8 = 0x91;
- /// Private Use 2
- pub const PU2: u8 = 0x92;
- /// Set Transmit State
- pub const STS: u8 = 0x93;
- /// Cancel character, ignore previous character
- pub const CCH: u8 = 0x94;
- /// Message Waiting, turns on an indicator on the terminal
- pub const MW: u8 = 0x95;
- /// Start of Protected Area
- pub const SPA: u8 = 0x96;
- /// End of Protected Area
- pub const EPA: u8 = 0x97;
- /// SOS
- pub const SOS: u8 = 0x98;
- /// SGCI
- pub const SGCI: u8 = 0x99;
- /// DECID - Identify Terminal
- pub const DECID: u8 = 0x9a;
- /// Control Sequence Introducer
- pub const CSI: u8 = 0x9B;
- /// String Terminator (VT125 exits graphics)
- pub const ST: u8 = 0x9C;
- /// Operating System Command (reprograms intelligent terminal)
- pub const OSC: u8 = 0x9D;
- /// Privacy Message (password verification), terminated by ST
- pub const PM: u8 = 0x9E;
- /// Application Program Command (to word processor), term by ST
- pub const APC: u8 = 0x9F;
-}
-
-// Tests for parsing escape sequences
-//
-// Byte sequences used in these tests are recording of pty stdout.
-#[cfg(test)]
-mod tests {
- use super::{
- parse_number, parse_rgb_color, Attr, CharsetIndex, Color, Handler, Processor,
- StandardCharset, TermInfo,
- };
- use crate::index::{Column, Line};
- use crate::term::color::Rgb;
- use std::io;
-
- /// The /dev/null of `io::Write`
- struct Void;
-
- impl io::Write for Void {
- fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
- Ok(bytes.len())
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
- }
-
- #[derive(Default)]
- struct AttrHandler {
- attr: Option<Attr>,
- }
-
- impl Handler for AttrHandler {
- fn terminal_attribute(&mut self, attr: Attr) {
- self.attr = Some(attr);
- }
- }
-
- impl TermInfo for AttrHandler {
- fn lines(&self) -> Line {
- Line(24)
- }
-
- fn cols(&self) -> Column {
- Column(80)
- }
- }
-
- #[test]
- fn parse_control_attribute() {
- static BYTES: &'static [u8] = &[0x1b, 0x5b, 0x31, 0x6d];
-
- let mut parser = Processor::new();
- let mut handler = AttrHandler::default();
-
- for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
- }
-
- assert_eq!(handler.attr, Some(Attr::Bold));
- }
-
- #[test]
- fn parse_truecolor_attr() {
- static BYTES: &'static [u8] = &[
- 0x1b, 0x5b, 0x33, 0x38, 0x3b, 0x32, 0x3b, 0x31, 0x32, 0x38, 0x3b, 0x36, 0x36, 0x3b,
- 0x32, 0x35, 0x35, 0x6d,
- ];
-
- let mut parser = Processor::new();
- let mut handler = AttrHandler::default();
-
- for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
- }
-
- let spec = Rgb { r: 128, g: 66, b: 255 };
-
- assert_eq!(handler.attr, Some(Attr::Foreground(Color::Spec(spec))));
- }
-
- /// No exactly a test; useful for debugging
- #[test]
- fn parse_zsh_startup() {
- static BYTES: &'static [u8] = &[
- 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x37, 0x6d, 0x25, 0x1b, 0x5b, 0x32, 0x37, 0x6d,
- 0x1b, 0x5b, 0x31, 0x6d, 0x1b, 0x5b, 0x30, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
- 0x20, 0x20, 0x20, 0x0d, 0x20, 0x0d, 0x0d, 0x1b, 0x5b, 0x30, 0x6d, 0x1b, 0x5b, 0x32,
- 0x37, 0x6d, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x1b, 0x5b, 0x4a, 0x6a, 0x77, 0x69, 0x6c,
- 0x6d, 0x40, 0x6a, 0x77, 0x69, 0x6c, 0x6d, 0x2d, 0x64, 0x65, 0x73, 0x6b, 0x20, 0x1b,
- 0x5b, 0x30, 0x31, 0x3b, 0x33, 0x32, 0x6d, 0xe2, 0x9e, 0x9c, 0x20, 0x1b, 0x5b, 0x30,
- 0x31, 0x3b, 0x33, 0x32, 0x6d, 0x20, 0x1b, 0x5b, 0x33, 0x36, 0x6d, 0x7e, 0x2f, 0x63,
- 0x6f, 0x64, 0x65,
- ];
-
- let mut handler = AttrHandler::default();
- let mut parser = Processor::new();
-
- for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
- }
- }
-
- struct CharsetHandler {
- index: CharsetIndex,
- charset: StandardCharset,
- }
-
- impl Default for CharsetHandler {
- fn default() -> CharsetHandler {
- CharsetHandler { index: CharsetIndex::G0, charset: StandardCharset::Ascii }
- }
- }
-
- impl Handler for CharsetHandler {
- fn configure_charset(&mut self, index: CharsetIndex, charset: StandardCharset) {
- self.index = index;
- self.charset = charset;
- }
-
- fn set_active_charset(&mut self, index: CharsetIndex) {
- self.index = index;
- }
- }
-
- impl TermInfo for CharsetHandler {
- fn lines(&self) -> Line {
- Line(200)
- }
-
- fn cols(&self) -> Column {
- Column(90)
- }
- }
-
- #[test]
- fn parse_designate_g0_as_line_drawing() {
- static BYTES: &'static [u8] = &[0x1b, b'(', b'0'];
- let mut parser = Processor::new();
- let mut handler = CharsetHandler::default();
-
- for byte in &BYTES[..] {
- parser.advance(&mut handler, *byte, &mut Void);
- }
-
- assert_eq!(handler.index, CharsetIndex::G0);
- assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing);
- }
-
- #[test]
- fn parse_designate_g1_as_line_drawing_and_invoke() {
- static BYTES: &'static [u8] = &[0x1b, 0x29, 0x30, 0x0e];
- let mut parser = Processor::new();
- let mut handler = CharsetHandler::default();
-
- for byte in &BYTES[..3] {
- parser.advance(&mut handler, *byte, &mut Void);
- }
-
- assert_eq!(handler.index, CharsetIndex::G1);
- assert_eq!(handler.charset, StandardCharset::SpecialCharacterAndLineDrawing);
-
- let mut handler = CharsetHandler::default();
- parser.advance(&mut handler, BYTES[3], &mut Void);
-
- assert_eq!(handler.index, CharsetIndex::G1);
- }
-
- #[test]
- fn parse_valid_rgb_color() {
- assert_eq!(parse_rgb_color(b"rgb:11/aa/ff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
- }
-
- #[test]
- fn parse_valid_rgb_color2() {
- assert_eq!(parse_rgb_color(b"#11aaff"), Some(Rgb { r: 0x11, g: 0xaa, b: 0xff }));
- }
-
- #[test]
- fn parse_invalid_number() {
- assert_eq!(parse_number(b"1abc"), None);
- }
-
- #[test]
- fn parse_valid_number() {
- assert_eq!(parse_number(b"123"), Some(123));
- }
-
- #[test]
- fn parse_number_too_large() {
- assert_eq!(parse_number(b"321"), None);
- }
-}