diff --git a/Cargo.lock b/Cargo.lock index 0d14654..8114e3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "ansi-parser" -version = "0.6.4" +version = "0.6.5" dependencies = [ "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/src/enums.rs b/src/enums.rs index ac641b5..bae47d0 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -4,6 +4,7 @@ mod tests; ///The following are the implemented ANSI escape sequences. More to be added. #[derive(Debug, PartialEq)] pub enum AnsiSequence { + Escape, CursorPos(u32, u32), CursorUp(u32), CursorDown(u32), @@ -61,6 +62,7 @@ impl Display for AnsiSequence { use AnsiSequence::*; match self { + Escape => write!(formatter, "\u{1b}"), CursorPos(line, col) => write!(formatter, "[{};{}H", line, col), CursorUp(amt) diff --git a/src/parsers.rs b/src/parsers.rs index 6b54b8d..8073ce3 100644 --- a/src/parsers.rs +++ b/src/parsers.rs @@ -26,27 +26,45 @@ named!( ) ); +// TODO kind of ugly, would prefer to pass in the default so we could use it for +// all escapes with defaults (not just those that default to 1). +named!( + parse_def_cursor_int<&str, u32>, + map!( + nom::digit0, + |s: &str| s.parse::().unwrap_or(1) + ) +); + named!( cursor_pos<&str, AnsiSequence>, do_parse!( - tag!("[") >> - x: parse_int >> - tag!(";") >> - y: parse_int >> + tag!("[") >> + x: parse_def_cursor_int >> + opt!(tag!(";")) >> + y: parse_def_cursor_int >> alt!( - tag!("H") | + tag!("H") | tag!("f") ) >> (AnsiSequence::CursorPos(x, y)) ) ); +named!( + escape<&str, AnsiSequence>, + do_parse!( + tag!("\u{1b}") >> + (AnsiSequence::Escape) + ) +); + named!( cursor_up<&str, AnsiSequence>, do_parse!( - tag!("[") >> - am: parse_int >> - tag!("A") >> + tag!("[") >> + am: parse_def_cursor_int >> + tag!("A") >> (AnsiSequence::CursorUp(am)) ) ); @@ -54,9 +72,9 @@ named!( named!( cursor_down<&str, AnsiSequence>, do_parse!( - tag!("[") >> - am: parse_int >> - tag!("B") >> + tag!("[") >> + am: parse_def_cursor_int >> + tag!("B") >> (AnsiSequence::CursorDown(am)) ) ); @@ -64,9 +82,9 @@ named!( named!( cursor_forward<&str, AnsiSequence>, do_parse!( - tag!("[") >> - am: parse_int >> - tag!("C") >> + tag!("[") >> + am: parse_def_cursor_int >> + tag!("C") >> (AnsiSequence::CursorForward(am)) ) ); @@ -74,9 +92,9 @@ named!( named!( cursor_backward<&str, AnsiSequence>, do_parse!( - tag!("[") >> - am: parse_int >> - tag!("D") >> + tag!("[") >> + am: parse_def_cursor_int >> + tag!("D") >> (AnsiSequence::CursorBackward(am)) ) ); @@ -125,6 +143,26 @@ named!( ) ); + +named!( + graphics_mode5<&str, AnsiSequence>, + do_parse!( + tag!("[") >> + val1: parse_int >> + tag!(";") >> + val2: parse_int >> + tag!(";") >> + val3: parse_int >> + tag!(";") >> + val4: parse_int >> + tag!(";") >> + val5: parse_int >> + tag!("m") >> + (AnsiSequence::SetGraphicsMode(vec![val1, val2, val3, val4, val5])) + ) +); + + named!( graphics_mode<&str, AnsiSequence>, alt!( @@ -132,6 +170,7 @@ named!( | graphics_mode2 | graphics_mode3 | graphics_mode4 + | graphics_mode5 ) ); @@ -213,11 +252,12 @@ tag_parser!(set_single_shift3, "O", AnsiSequence::SetSingleShift3); named!( combined<&str, AnsiSequence>, alt!( - cursor_pos + escape + | cursor_pos | cursor_up | cursor_down | cursor_forward - | cursor_backward + | cursor_backward | cursor_save | cursor_restore | erase_display @@ -272,4 +312,3 @@ named!( (Output::Escape(seq)) ) ); - diff --git a/src/parsers/tests.rs b/src/parsers/tests.rs index 0a384ad..399ab4a 100644 --- a/src/parsers/tests.rs +++ b/src/parsers/tests.rs @@ -21,13 +21,37 @@ macro_rules! test_parser { } } -test_parser!(cursor_pos, "\u{1b}[10;5H"); -test_parser!(cursor_up, "\u{1b}[5A"); -test_parser!(cursor_down, "\u{1b}[5B"); -test_parser!(cursor_forward, "\u{1b}[5C"); -test_parser!(cursor_backward,"\u{1b}[5D"); -test_parser!(cursor_save, "\u{1b}[s"); -test_parser!(cursor_restore, "\u{1b}[u"); +macro_rules! test_def_val_parser { + ($name:ident, $string:expr) => { + #[test] + fn $name() { + let mut buff = String::new(); + let ret = parse_escape($string); + + assert!(ret.is_ok()); + let ret = ret.unwrap().1; + + write!(&mut buff, "{}", ret) + .unwrap(); + + let ret2 = parse_escape(&buff); + assert!(ret2.is_ok()); + + let ret2 = ret2.unwrap().1; + assert_eq!(ret, ret2); + } + } +} + +test_def_val_parser!(cursor_pos_default, "\u{1b}[H"); +test_def_val_parser!(cursor_pos, "\u{1b}[10;5H"); +test_def_val_parser!(cursor_up_default, "\u{1b}[A"); +test_def_val_parser!(cursor_up, "\u{1b}[5A"); +test_def_val_parser!(cursor_down, "\u{1b}[5B"); +test_def_val_parser!(cursor_forward, "\u{1b}[5C"); +test_def_val_parser!(cursor_backward, "\u{1b}[5D"); +test_parser!(cursor_save, "\u{1b}[s"); +test_parser!(cursor_restore, "\u{1b}[u"); test_parser!(erase_display, "\u{1b}[2J"); test_parser!(erase_line, "\u{1b}[K"); @@ -96,3 +120,28 @@ fn test_parser_iterator_failure() { assert_eq!(strings.len(), 6); } + +#[test] +fn test_default_value() { + let strings: Vec<_> = "\x1b[H\x1b[123456H\x1b[;123456H\x1b[7asd;1234H\x1b[a;sd7H" + .ansi_parse() + .collect(); + assert_eq!(strings.len(), 5); + assert_eq!(strings[0], Output::Escape(AnsiSequence::CursorPos(1,1))); + assert_eq!(strings[1], Output::Escape(AnsiSequence::CursorPos(123456,1))); + assert_eq!(strings[2], Output::Escape(AnsiSequence::CursorPos(1,123456))); + assert_eq!(strings[3], Output::TextBlock("\x1b[7asd;1234H")); + assert_eq!(strings[4], Output::TextBlock("\x1b[a;sd7H")); +} + +#[test] +fn test_escape() { + let parts: Vec<_> = "\x1b\x1b[33mFoobar".ansi_parse().collect(); + assert_eq!( + parts, + vec![ + Output::Escape(AnsiSequence::Escape), + Output::TextBlock("[33mFoobar") + ] + ); +}