───────┬──────────────────────────────────────────────────────────────────────── │ File: src/parser.rs ───────┼────────────────────────────────────────────────────────────────────────  1 │ use crate::code::AnsiCode;  2 │ use nom::{  3 │  branch::alt,  4 │  bytes::complete::*,  5 │  character::complete::*,  6 │  character::is_alphabetic,  7 │  combinator::{map_res, opt, recognize, value},  8 │  error,  9 │  error::FromExternalError,  10 │  multi::*,  11 │  sequence::{delimited, preceded, terminated, tuple},  12 │  IResult, Parser,  13 │ };  14 │ use std::str::FromStr;  15 │ use tui::{  16 │  style::{Color, Modifier, Style},  17 │  text::{Line, Span, Text},  18 │ };  19 │  20 │ #[derive(Debug, Clone, Copy, Eq, PartialEq)]  21 │ enum ColorType {  22 │  /// Eight Bit color  23 │  EightBit,  24 │  /// 24-bit color or true color  25 │  TrueColor,  26 │ }  27 │  28 │ #[derive(Debug, Clone, PartialEq)]  29 │ struct AnsiItem {  30 │  code: AnsiCode,  31 │  color: Option<Color>,  32 │ }  33 │  34 │ #[derive(Debug, Clone, PartialEq)]  35 │ struct AnsiStates {  36 │  pub items: smallvec::SmallVec<[AnsiItem; 2]>,  37 │  pub style: Style,  38 │ }  39 │  40 │ impl From<AnsiStates> for tui::style::Style {  41 │  fn from(states: AnsiStates) -> Self {  42 │  let mut style = states.style;  43 │  for item in states.items {  44 │  match item.code {  45 │  AnsiCode::Reset => style = Style::default(),  46 │  AnsiCode::Bold => style = style.add_modifier(Modifier::BOLD),  47 │  AnsiCode::Faint => style = style.add_modifier(Modifier::DIM),  48 │  AnsiCode::Italic => style = style.add_modifier(Modifier::ITALIC),  49 │  AnsiCode::Underline => style = style.add_modifier(Modifier::UNDERLINED),  50 │  AnsiCode::SlowBlink => style = style.add_modifier(Modifier::SLOW_BLINK),  51 │  AnsiCode::RapidBlink => style = style.add_modifier(Modifier::RAPID_BLINK),  52 │  AnsiCode::Reverse => style = style.add_modifier(Modifier::REVERSED),  53 │  AnsiCode::Conceal => style = style.add_modifier(Modifier::HIDDEN),  54 │  AnsiCode::CrossedOut => style = style.add_modifier(Modifier::CROSSED_OUT),  55 │  AnsiCode::DefaultForegroundColor => style = style.fg(Color::Reset),  56 │  AnsiCode::DefaultBackgroundColor => style = style.bg(Color::Reset),  57 │  AnsiCode::SetForegroundColor => {  58 │  if let Some(color) = item.color {  59 │  style = style.fg(color)  60 │  }  61 │  }  62 │  AnsiCode::SetBackgroundColor => {  63 │  if let Some(color) = item.color {  64 │  style = style.bg(color)  65 │  }  66 │  }  67 │  AnsiCode::ForegroundColor(color) => style = style.fg(color),  68 │  AnsiCode::BackgroundColor(color) => style = style.bg(color),  69 │  AnsiCode::AlternateFonts(_) => (),  70 │  _ => (),  71 │  }  72 │  }  73 │  style  74 │  }  75 │ }  76 │  77 │ pub(crate) fn text(mut s: &[u8]) -> IResult<&[u8], Text<'static>> {  78 │  let mut lines = Vec::new();  79 │  let mut last = Default::default();  80 │  while let Ok((_s, (line, style))) = line(last)(s) {  81 │  lines.push(line);  82 │  last = style;  83 │  s = _s;  84 │  if s.is_empty() {  85 │  break;  86 │  }  87 │  }  88 │  Ok((s, Text::from(lines)))  89 │ }  90 │  91 │ #[cfg(feature = "zero-copy")]  92 │ pub(crate) fn text_fast(mut s: &[u8]) -> IResult<&[u8], Text<'_>> {  93 │  let mut lines = Vec::new();  94 │  let mut last = Default::default();  95 │  while let Ok((_s, (line, style))) = line_fast(last)(s) {  96 │  lines.push(line);  97 │  last = style;  98 │  s = _s;  99 │  if s.is_empty() {  100 │  break;  101 │  }  102 │  }  103 │  Ok((s, Text::from(lines)))  104 │ }  105 │  106 │ fn line(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], (Line<'static>, Style)> {  107 │  // let style_: Style = Default::default();  108 │  move |s: &[u8]| -> IResult<&[u8], (Line<'static>, Style)> {  109 │  let (s, mut text) = take_while(|c| c != b'\n')(s)?;  110 │  let (s, _) = opt(tag("\n"))(s)?;  111 │  let mut spans = Vec::new();  112 │  let mut last = style;  113 │  while let Ok((s, span)) = span(last)(text) {  114 │  if span.style == Style::default() && span.content.is_empty() {  115 │  // Reset styles  116 │  last = Style::default();  117 │  } else {  118 │  last = last.patch(span.style);  119 │  }  120 │  // Don't include empty spans but keep changing the style  121 │  if spans.is_empty() || span.content != "" {  122 │  spans.push(span);  123 │  }  124 │  text = s;  125 │  if text.is_empty() {  126 │  break;  127 │  }  128 │  }  129 │  130 │  Ok((s, (Line::from(spans), last)))  131 │  }  132 │ }  133 │  134 │ #[cfg(feature = "zero-copy")]  135 │ fn line_fast(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], (Line<'_>, Style)> {  136 │  // let style_: Style = Default::default();  137 │  move |s: &[u8]| -> IResult<&[u8], (Line<'_>, Style)> {  138 │  let (s, mut text) = take_while(|c| c != b'\n')(s)?;  139 │  let (s, _) = opt(tag("\n"))(s)?;  140 │  let mut spans = Vec::new();  141 │  let mut last = style;  142 │  while let Ok((s, span)) = span_fast(last)(text) {  143 │  if span.style == Style::default() && span.content.is_empty() {  144 │  // Reset styles  145 │  last = Style::default();  146 │  } else {  147 │  last = last.patch(span.style);  148 │  }  149 │  // Don't include empty spans but keep changing the style  150 │  if spans.is_empty() || !span.content.is_empty() {  151 │  spans.push(span);  152 │  }  153 │  text = s;  154 │  if text.is_empty() {  155 │  break;  156 │  }  157 │  }  158 │  159 │  Ok((s, (Line::from(spans), last)))  160 │  }  161 │ }  162 │  163 │ // fn span(s: &[u8]) -> IResult<&[u8], tui::text::Span> {  164 │ fn span(last: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Span<'static>, nom::error::Error<&[u8]>> {  165 │  move |s: &[u8]| -> IResult<&[u8], Span<'static>> {  166 │  let mut last = last;  167 │  let (s, style) = opt(style(last))(s)?;  168 │  169 │  #[cfg(feature = "simd")]  170 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  171 │  simdutf8::basic::from_utf8(t)  172 │  })(s)?;  173 │  174 │  #[cfg(not(feature = "simd"))]  175 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  176 │  std::str::from_utf8(t)  177 │  })(s)?;  178 │  179 │  if let Some(style) = style {  180 │  if style == Default::default() {  181 │  last = Default::default();  182 │  } else {  183 │  last = last.patch(style);  184 │  }  185 │  }  186 │  187 │  Ok((s, Span::styled(text.to_owned(), last)))  188 │  }  189 │ }  190 │  191 │ #[cfg(feature = "zero-copy")]  192 │ fn span_fast(last: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Span<'_>, nom::error::Error<&[u8]>> {  193 │  move |s: &[u8]| -> IResult<&[u8], Span<'_>> {  194 │  let mut last = last;  195 │  let (s, style) = opt(style_fast(last))(s)?;  196 │  197 │  #[cfg(feature = "simd")]  198 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  199 │  simdutf8::basic::from_utf8(t)  200 │  })(s)?;  201 │  202 │  #[cfg(not(feature = "simd"))]  203 │  let (s, text) = map_res(take_while(|c| c != b'\x1b' | b'\n'), |t| {  204 │  std::str::from_utf8(t)  205 │  })(s)?;  206 │  207 │  if let Some(style) = style {  208 │  if style == Default::default() {  209 │  last = Default::default();  210 │  } else {  211 │  last = last.patch(style);  212 │  }  213 │  }  214 │  215 │  Ok((s, Span::styled(text.to_owned(), last)))  216 │  }  217 │ }  218 │  219 │ fn style(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Style, nom::error::Error<&[u8]>> {  220 │  move |s: &[u8]| -> IResult<&[u8], Style> {  221 │  let (s, r) = match opt(ansi_sgr_code)(s)? {  222 │  (s, Some(r)) => (s, r),  223 │  (s, None) => {  224 │  let (s, _) = any_escape_sequence(s)?;  225 │  (s, Vec::new())  226 │  }  227 │  };  228 │  Ok((  229 │  s,  230 │  Style::from(AnsiStates {  231 │  style,  232 │  items: r.into(),  233 │  }),  234 │  ))  235 │  }  236 │ }  237 │  238 │ #[cfg(feature = "zero-copy")]  239 │ fn style_fast(style: Style) -> impl Fn(&[u8]) -> IResult<&[u8], Style, nom::error::Error<&[u8]>> {  240 │  move |s: &[u8]| -> IResult<&[u8], Style> {  241 │  let (s, r) = match opt(ansi_sgr_code_fast)(s)? {  242 │  (s, Some(r)) => (s, r),  243 │  (s, None) => {  244 │  let (s, _) = any_escape_sequence(s)?;  245 │  (s, Default::default())  246 │  }  247 │  };  248 │  Ok((s, Style::from(AnsiStates { style, items: r })))  249 │  }  250 │ }  251 │  252 │ /// A complete ANSI SGR code  253 │ fn ansi_sgr_code(s: &[u8]) -> IResult<&[u8], Vec<AnsiItem>, nom::error::Error<&[u8]>> {  254 │  delimited(  255 │  tag("\x1b["),  256 │  separated_list1(tag(";"), ansi_sgr_item),  257 │  char('m'),  258 │  )(s)  259 │ }  260 │  261 │ #[cfg(feature = "zero-copy")]  262 │ fn ansi_sgr_code_fast(  263 │  s: &[u8],  264 │ ) -> IResult<&[u8], smallvec::SmallVec<[AnsiItem; 2]>, nom::error::Error<&[u8]>> {  265 │  delimited(  266 │  tag("\x1b["),  267 │  fold_many1(ansi_sgr_item, smallvec::SmallVec::new, |mut items, item| {  268 │  items.push(item);  269 │  items  270 │  }),  271 │  char('m'),  272 │  )(s)  273 │ }  274 │  275 │ fn any_escape_sequence(s: &[u8]) -> IResult<&[u8], Option<&[u8]>> {  276 │  // Attempt to consume most escape codes, including a single escape char.  277 │  //  278 │  // Most escape codes begin with ESC[ and are terminated by an alphabetic character,  279 │  // but OSC codes begin with ESC] and are terminated by an ascii bell (\x07)  280 │  // and a truncated/invalid code may just be a standalone ESC or not be terminated.  281 │  //  282 │  // We should try to consume as much of it as possible to match behavior of most terminals;  283 │  // where we fail at that we should at least consume the escape char to avoid infinitely looping  284 │  285 │  preceded(  286 │  char('\x1b'),  287 │  opt(alt((  288 │  delimited(char('['), take_till(is_alphabetic), opt(take(1u8))),  289 │  delimited(char(']'), take_till(|c| c == b'\x07'), opt(take(1u8))),  290 │  ))),  291 │  )(s)  292 │ }  293 │  294 │ /// An ANSI SGR attribute  295 │ fn ansi_sgr_item(s: &[u8]) -> IResult<&[u8], AnsiItem> {  296 │  let (s, c) = u8(s)?;  297 │  let code = AnsiCode::from(c);  298 │  let (s, color) = match code {  299 │  AnsiCode::SetForegroundColor | AnsiCode::SetBackgroundColor => {  300 │  let (s, _) = opt(tag(";"))(s)?;  301 │  let (s, color) = color(s)?;  302 │  (s, Some(color))  303 │  }  304 │  _ => (s, None),  305 │  };  306 │  let (s, _) = opt(tag(";"))(s)?;  307 │  Ok((s, AnsiItem { code, color }))  308 │ }  309 │  310 │ fn color(s: &[u8]) -> IResult<&[u8], Color> {  311 │  let (s, c_type) = color_type(s)?;  312 │  let (s, _) = opt(tag(";"))(s)?;  313 │  match c_type {  314 │  ColorType::TrueColor => {  315 │  let (s, (r, _, g, _, b)) = tuple((u8, tag(";"), u8, tag(";"), u8))(s)?;  316 │  Ok((s, Color::Rgb(r, g, b)))  317 │  }  318 │  ColorType::EightBit => {  319 │  let (s, index) = u8(s)?;  320 │  Ok((s, Color::Indexed(index)))  321 │  }  322 │  }  323 │ }  324 │  325 │ fn color_type(s: &[u8]) -> IResult<&[u8], ColorType> {  326 │  let (s, t) = i64(s)?;  327 │  // NOTE: This isn't opt because a color type must always be followed by a color  328 │  // let (s, _) = opt(tag(";"))(s)?;  329 │  let (s, _) = tag(";")(s)?;  330 │  match t {  331 │  2 => Ok((s, ColorType::TrueColor)),  332 │  5 => Ok((s, ColorType::EightBit)),  333 │  _ => Err(nom::Err::Error(nom::error::Error::new(  334 │  s,  335 │  nom::error::ErrorKind::Alt,  336 │  ))),  337 │  }  338 │ }  339 │  340 │ #[test]  341 │ fn color_test() {  342 │  let c = color(b"2;255;255;255").unwrap();  343 │  assert_eq!(c.1, Color::Rgb(255, 255, 255));  344 │  let c = color(b"5;255").unwrap();  345 │  assert_eq!(c.1, Color::Indexed(255));  346 │ }  347 │  348 │ #[test]  349 │ fn ansi_items_test() {  350 │  let sc = Default::default();  351 │  let t = style(sc)(b"\x1b[38;2;3;3;3m").unwrap();  352 │  assert_eq!(  353 │  t.1,  354 │  Style::from(AnsiStates {  355 │  style: sc,  356 │  items: vec![AnsiItem {  357 │  code: AnsiCode::SetForegroundColor,  358 │  color: Some(Color::Rgb(3, 3, 3))  359 │  }]  360 │  .into()  361 │  })  362 │  );  363 │ } ───────┴────────────────────────────────────────────────────────────────────────