use std::{
io::Write,
mem,
};
use alacritty_terminal::ansi::{
Attr,
Color,
Handler,
NamedColor,
Processor,
};
use tui::{
style,
style::Style,
text::Span,
};
use crate::text::{
Line,
Text,
};
pub struct AnsiHandler {
lines: Vec<Line>,
current: Text,
prev: char,
prevprev: char,
}
pub struct AnsiProcessor {
handler: AnsiHandler,
processor: Processor,
}
impl AnsiProcessor {
pub fn new(style: Style) -> Self {
AnsiProcessor {
processor: Processor::new(),
handler: AnsiHandler::new(style),
}
}
pub fn append(&mut self, bytes: &[u8]) {
let handler = &mut self.handler;
let processor = &mut self.processor;
for b in bytes {
processor.advance(handler, *b)
}
}
pub fn take(&mut self) -> (Vec<Line>, Text) {
let cap = self.handler.lines.capacity();
let lines = mem::replace(&mut self.handler.lines, Vec::with_capacity(cap));
let current = mem::replace(&mut self.handler.current, Text::new());
return (lines, current);
}
}
struct NopWriter;
impl Write for NopWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
impl AnsiHandler {
fn new(style: Style) -> AnsiHandler {
let mut handler = AnsiHandler {
lines: Vec::with_capacity(32),
current: Text::with_style(style),
prev: 0 as char,
prevprev: 0 as char,
};
handler.new_line();
handler
}
fn new_line(&mut self) {
let line = self.current.swap_complete(Line::new());
self.lines.push(line);
}
fn apply_style(&mut self, style: Style) {
self.current.set_style(style);
}
fn current_span_mut(&mut self) -> &mut Span<'static> {
self.current.current_span_mut()
}
fn shift_prev(&mut self, c: char) {
self.prevprev = self.prev;
self.prev = c;
}
}
impl Handler for AnsiHandler {
fn input(&mut self, c: char) {
if self.prev == '\r' && self.prevprev != '\n' {
self.new_line();
}
self.shift_prev(c);
self.current_span_mut().content.to_mut().push(c)
}
fn newline(&mut self) {
self.linefeed();
}
fn linefeed(&mut self) {
self.shift_prev('\n');
self.new_line();
}
fn carriage_return(&mut self) {
self.shift_prev('\r');
}
fn terminal_attribute(&mut self, attr: Attr) {
let mut style = self.current.style();
match attr {
Attr::Reset => style = Style::reset(),
Attr::Foreground(color) => {
to_tui_color(color).map(|color| style.fg = color.into());
}
Attr::Background(color) => {
to_tui_color(color).map(|color| style.bg = color.into());
}
Attr::Bold => style = style.add_modifier(tui::style::Modifier::BOLD),
Attr::Italic => style = style.add_modifier(tui::style::Modifier::ITALIC),
Attr::Dim => style = style.add_modifier(tui::style::Modifier::DIM),
Attr::Underline => style = style.add_modifier(tui::style::Modifier::UNDERLINED),
Attr::BlinkSlow => style = style.add_modifier(tui::style::Modifier::SLOW_BLINK),
Attr::BlinkFast => style = style.add_modifier(tui::style::Modifier::RAPID_BLINK),
Attr::Reverse => style = style.add_modifier(tui::style::Modifier::REVERSED),
Attr::Hidden => style = style.add_modifier(tui::style::Modifier::HIDDEN),
Attr::Strike => style = style.add_modifier(tui::style::Modifier::CROSSED_OUT),
Attr::CancelBold => style = style.remove_modifier(tui::style::Modifier::BOLD),
Attr::CancelItalic => style = style.remove_modifier(tui::style::Modifier::ITALIC),
Attr::CancelBoldDim => {
style = style
.remove_modifier(tui::style::Modifier::DIM)
.remove_modifier(tui::style::Modifier::BOLD)
}
Attr::CancelUnderline => {
style = style.remove_modifier(tui::style::Modifier::UNDERLINED)
}
Attr::CancelBlink => {
style = style
.remove_modifier(tui::style::Modifier::SLOW_BLINK)
.remove_modifier(tui::style::Modifier::RAPID_BLINK);
}
Attr::CancelReverse => style = style.remove_modifier(tui::style::Modifier::REVERSED),
Attr::CancelHidden => style = style.remove_modifier(tui::style::Modifier::HIDDEN),
Attr::CancelStrike => style = style.remove_modifier(tui::style::Modifier::CROSSED_OUT),
_ => {}
}
self.apply_style(style)
}
}
fn to_tui_color(color: Color) -> Option<style::Color> {
let named: NamedColor = match color {
Color::Named(named) => named,
Color::Indexed(id) => return style::Color::Indexed(id).into(),
Color::Spec(rgb) => return style::Color::Rgb(rgb.r, rgb.g, rgb.b).into(),
};
Some(match named {
NamedColor::BrightBlack => style::Color::DarkGray,
NamedColor::Black => style::Color::Black,
NamedColor::BrightRed => style::Color::LightRed,
NamedColor::BrightWhite => style::Color::White,
NamedColor::BrightGreen => style::Color::LightGreen,
NamedColor::BrightYellow => style::Color::LightYellow,
NamedColor::BrightBlue => style::Color::LightBlue,
NamedColor::BrightMagenta => style::Color::LightMagenta,
NamedColor::BrightCyan => style::Color::LightCyan,
NamedColor::Red => style::Color::Red,
NamedColor::White => style::Color::Gray,
NamedColor::Green => style::Color::Green,
NamedColor::Yellow => style::Color::Yellow,
NamedColor::Blue => style::Color::Blue,
NamedColor::Magenta => style::Color::Magenta,
NamedColor::Cyan => style::Color::Cyan,
_ => return None,
})
}