terminal/cells: remove ansi module
parent
22fb2ed46c
commit
25579d8807
|
@ -1229,380 +1229,6 @@ pub fn clear_area(grid: &mut CellBuffer, area: Area, attributes: crate::conf::Th
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod ansi {
|
|
||||||
//! Create a `CellBuffer` from a string slice containing ANSI escape codes.
|
|
||||||
use super::{Attr, Cell, CellBuffer, Color};
|
|
||||||
/// Create a `CellBuffer` from a string slice containing ANSI escape codes.
|
|
||||||
pub fn ansi_to_cellbuffer(s: &str) -> Option<CellBuffer> {
|
|
||||||
let mut bufs: Vec<Vec<Cell>> = Vec::with_capacity(2048);
|
|
||||||
let mut row: Vec<Cell> = Vec::with_capacity(2048);
|
|
||||||
|
|
||||||
enum State {
|
|
||||||
Start,
|
|
||||||
Csi,
|
|
||||||
SetFg,
|
|
||||||
SetBg,
|
|
||||||
}
|
|
||||||
use State::*;
|
|
||||||
|
|
||||||
let mut rows = 0;
|
|
||||||
let mut max_cols = 0;
|
|
||||||
let mut current_fg = Color::Default;
|
|
||||||
let mut current_bg = Color::Default;
|
|
||||||
let mut current_attrs = Attr::DEFAULT;
|
|
||||||
let mut cur_cell;
|
|
||||||
let mut state: State;
|
|
||||||
for l in s.lines() {
|
|
||||||
cur_cell = Cell::default();
|
|
||||||
state = State::Start;
|
|
||||||
let mut chars = l.chars().peekable();
|
|
||||||
if rows > 0 {
|
|
||||||
max_cols = std::cmp::max(row.len(), max_cols);
|
|
||||||
bufs.push(row);
|
|
||||||
row = Vec::with_capacity(2048);
|
|
||||||
}
|
|
||||||
rows += 1;
|
|
||||||
'line_loop: loop {
|
|
||||||
let c = chars.next();
|
|
||||||
if c.is_none() {
|
|
||||||
break 'line_loop;
|
|
||||||
}
|
|
||||||
match (&state, c.unwrap()) {
|
|
||||||
(Start, '\x1b') => {
|
|
||||||
if chars.next() != Some('[') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
state = Csi;
|
|
||||||
}
|
|
||||||
(Start, c) => {
|
|
||||||
cur_cell.set_ch(c);
|
|
||||||
cur_cell.set_fg(current_fg);
|
|
||||||
cur_cell.set_bg(current_bg);
|
|
||||||
cur_cell.set_attrs(current_attrs);
|
|
||||||
row.push(cur_cell);
|
|
||||||
cur_cell = Cell::default();
|
|
||||||
}
|
|
||||||
(Csi, 'm') => {
|
|
||||||
/* Reset styles */
|
|
||||||
current_fg = Color::Default;
|
|
||||||
current_bg = Color::Default;
|
|
||||||
current_attrs = Attr::DEFAULT;
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
(Csi, '0') if chars.peek() == Some(&'0') => {
|
|
||||||
current_attrs = Attr::DEFAULT;
|
|
||||||
chars.next();
|
|
||||||
let next = chars.next();
|
|
||||||
if next == Some('m') {
|
|
||||||
state = Start;
|
|
||||||
} else if next != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Csi, c @ '0'..='8') if chars.peek() == Some(&'m') => {
|
|
||||||
chars.next();
|
|
||||||
state = Start;
|
|
||||||
match c {
|
|
||||||
'0' => {
|
|
||||||
//Reset all attributes
|
|
||||||
current_fg = Color::Default;
|
|
||||||
current_bg = Color::Default;
|
|
||||||
current_attrs = Attr::DEFAULT;
|
|
||||||
}
|
|
||||||
'1' => {
|
|
||||||
current_attrs.set(Attr::BOLD, true);
|
|
||||||
}
|
|
||||||
'2' => {
|
|
||||||
current_attrs.set(Attr::DIM, true);
|
|
||||||
}
|
|
||||||
'3' => {
|
|
||||||
current_attrs.set(Attr::ITALICS, true);
|
|
||||||
}
|
|
||||||
'4' => {
|
|
||||||
current_attrs.set(Attr::UNDERLINE, true);
|
|
||||||
}
|
|
||||||
'5' => {
|
|
||||||
current_attrs.set(Attr::BLINK, true);
|
|
||||||
}
|
|
||||||
'7' => {
|
|
||||||
current_attrs.set(Attr::REVERSE, true);
|
|
||||||
}
|
|
||||||
'8' => {
|
|
||||||
current_attrs.set(Attr::HIDDEN, true);
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Csi, '0') => {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
(Csi, '2') => {
|
|
||||||
match (chars.next(), chars.next()) {
|
|
||||||
(Some('2'), Some('m')) => {
|
|
||||||
current_attrs.set(Attr::BOLD, false);
|
|
||||||
current_attrs.set(Attr::DIM, false);
|
|
||||||
}
|
|
||||||
(Some('3'), Some('m')) => {
|
|
||||||
current_attrs.set(Attr::ITALICS, false);
|
|
||||||
}
|
|
||||||
(Some('4'), Some('m')) => {
|
|
||||||
current_attrs.set(Attr::UNDERLINE, false);
|
|
||||||
}
|
|
||||||
(Some('5'), Some('m')) => {
|
|
||||||
current_attrs.set(Attr::BLINK, false);
|
|
||||||
}
|
|
||||||
(Some('7'), Some('m')) => {
|
|
||||||
current_attrs.set(Attr::REVERSE, false);
|
|
||||||
}
|
|
||||||
(Some('8'), Some('m')) => {
|
|
||||||
current_attrs.set(Attr::HIDDEN, false);
|
|
||||||
}
|
|
||||||
(Some('9'), Some('m')) => { /* Not crossed out */ }
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Csi, '3') => {
|
|
||||||
match chars.next() {
|
|
||||||
Some('8') => {
|
|
||||||
/* Set foreground color */
|
|
||||||
if chars.next() == Some(';') {
|
|
||||||
state = SetFg;
|
|
||||||
/* Next arguments are 5;n or 2;r;g;b */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
chars.next();
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some('9') => {
|
|
||||||
current_fg = Color::Default;
|
|
||||||
/* default foreground color */
|
|
||||||
let next = chars.next();
|
|
||||||
if next == Some('m') {
|
|
||||||
state = Start;
|
|
||||||
} else if next != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Some(c) if c >= '0' && c < '8' => {
|
|
||||||
current_fg = Color::from_byte(c as u8 - 0x30);
|
|
||||||
if chars.next() != Some('m') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Csi, '4') => {
|
|
||||||
match chars.next() {
|
|
||||||
Some('8') => {
|
|
||||||
/* Set background color */
|
|
||||||
if chars.next() == Some(';') {
|
|
||||||
state = SetBg;
|
|
||||||
/* Next arguments are 5;n or 2;r;g;b */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some('9') => {
|
|
||||||
/* default background color */
|
|
||||||
current_bg = Color::Default;
|
|
||||||
let next = chars.next();
|
|
||||||
if next == Some('m') {
|
|
||||||
state = Start;
|
|
||||||
} else if next != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Some(c) if c >= '0' && c < '8' => {
|
|
||||||
current_bg = Color::from_byte(c as u8 - 0x30);
|
|
||||||
if chars.next() != Some('m') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(Csi, '9') => {
|
|
||||||
match chars.next() {
|
|
||||||
Some('0') => current_fg = Color::Black,
|
|
||||||
Some('1') => current_fg = Color::Red,
|
|
||||||
Some('2') => current_fg = Color::Green,
|
|
||||||
Some('3') => current_fg = Color::Yellow,
|
|
||||||
Some('4') => current_fg = Color::Blue,
|
|
||||||
Some('5') => current_fg = Color::Magenta,
|
|
||||||
Some('6') => current_fg = Color::Cyan,
|
|
||||||
Some('7') => current_fg = Color::White,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
let next = chars.next();
|
|
||||||
if next != Some('m') {
|
|
||||||
//debug!(next);
|
|
||||||
}
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
(Csi, '1') if chars.peek() == Some(&'0') => {
|
|
||||||
chars.next();
|
|
||||||
match chars.next() {
|
|
||||||
Some('0') => current_bg = Color::Black,
|
|
||||||
Some('1') => current_bg = Color::Red,
|
|
||||||
Some('2') => current_bg = Color::Green,
|
|
||||||
Some('3') => current_bg = Color::Yellow,
|
|
||||||
Some('4') => current_bg = Color::Blue,
|
|
||||||
Some('5') => current_bg = Color::Magenta,
|
|
||||||
Some('6') => current_bg = Color::Cyan,
|
|
||||||
Some('7') => current_bg = Color::White,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
let next = chars.next();
|
|
||||||
if next != Some('m') {
|
|
||||||
//debug!(next);
|
|
||||||
}
|
|
||||||
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
(SetFg, '5') => {
|
|
||||||
if chars.next() != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut accum = 0;
|
|
||||||
while chars.peek().is_some() && chars.peek() != Some(&'m') {
|
|
||||||
let c = chars.next().unwrap();
|
|
||||||
accum *= 10;
|
|
||||||
accum += c as u8 - 0x30;
|
|
||||||
}
|
|
||||||
if chars.next() != Some('m') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
current_fg = Color::from_byte(accum);
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
(SetFg, '2') => {
|
|
||||||
if chars.next() != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut rgb_color = Color::Rgb(0, 0, 0);
|
|
||||||
if let Color::Rgb(ref mut r, ref mut g, ref mut b) = rgb_color {
|
|
||||||
'rgb_fg: for val in &mut [r, g, b] {
|
|
||||||
let mut accum = 0;
|
|
||||||
while chars.peek().is_some()
|
|
||||||
&& chars.peek() != Some(&';')
|
|
||||||
&& chars.peek() != Some(&'m')
|
|
||||||
{
|
|
||||||
let c = chars.next().unwrap();
|
|
||||||
accum *= 10;
|
|
||||||
accum += c as u8 - 0x30;
|
|
||||||
}
|
|
||||||
**val = accum;
|
|
||||||
match chars.peek() {
|
|
||||||
Some(&'m') => {
|
|
||||||
break 'rgb_fg;
|
|
||||||
}
|
|
||||||
Some(&';') => {
|
|
||||||
chars.next();
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if chars.next() != Some('m') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
current_fg = rgb_color;
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
(SetBg, '5') => {
|
|
||||||
if chars.next() != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut accum = 0;
|
|
||||||
while chars.peek().is_some() && chars.peek() != Some(&'m') {
|
|
||||||
let c = chars.next().unwrap();
|
|
||||||
accum *= 10;
|
|
||||||
accum += c as u8 - 0x30;
|
|
||||||
}
|
|
||||||
if chars.next() != Some('m') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
current_bg = Color::from_byte(accum);
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
(SetBg, '2') => {
|
|
||||||
if chars.next() != Some(';') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut rgb_color = Color::Rgb(0, 0, 0);
|
|
||||||
if let Color::Rgb(ref mut r, ref mut g, ref mut b) = rgb_color {
|
|
||||||
'rgb_bg: for val in &mut [r, g, b] {
|
|
||||||
let mut accum = 0;
|
|
||||||
while chars.peek().is_some()
|
|
||||||
&& chars.peek() != Some(&';')
|
|
||||||
&& chars.peek() != Some(&'m')
|
|
||||||
{
|
|
||||||
let c = chars.next().unwrap();
|
|
||||||
accum *= 10;
|
|
||||||
accum += c as u8 - 0x30;
|
|
||||||
}
|
|
||||||
**val = accum;
|
|
||||||
match chars.peek() {
|
|
||||||
Some(&'m') => {
|
|
||||||
break 'rgb_bg;
|
|
||||||
}
|
|
||||||
Some(&';') => {
|
|
||||||
chars.next();
|
|
||||||
}
|
|
||||||
_ => return None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if chars.next() != Some('m') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
current_bg = rgb_color;
|
|
||||||
state = Start;
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
max_cols = std::cmp::max(row.len(), max_cols);
|
|
||||||
bufs.push(row);
|
|
||||||
let mut buf: Vec<Cell> = Vec::with_capacity(max_cols * bufs.len());
|
|
||||||
for l in bufs {
|
|
||||||
let row_len = l.len();
|
|
||||||
buf.extend(l.into_iter());
|
|
||||||
if row_len < max_cols {
|
|
||||||
for _ in row_len..max_cols {
|
|
||||||
buf.push(Cell::default());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf.len() != rows * max_cols {
|
|
||||||
debug!(
|
|
||||||
"BUG: rows: {} cols: {} = {}, but buf.len() = {}",
|
|
||||||
rows,
|
|
||||||
max_cols,
|
|
||||||
rows * max_cols,
|
|
||||||
buf.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Some(CellBuffer {
|
|
||||||
buf,
|
|
||||||
rows,
|
|
||||||
cols: max_cols,
|
|
||||||
default_cell: Cell::default(),
|
|
||||||
growable: false,
|
|
||||||
ascii_drawing: false,
|
|
||||||
tag_table: Default::default(),
|
|
||||||
tag_associations: smallvec::SmallVec::new(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Use `RowIterator` to iterate the cells of a row without the need to do any bounds checking;
|
/// Use `RowIterator` to iterate the cells of a row without the need to do any bounds checking;
|
||||||
/// the iterator will simply return `None` when it reaches the end of the row.
|
/// the iterator will simply return `None` when it reaches the end of the row.
|
||||||
/// `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by
|
/// `RowIterator` can be created via the `CellBuffer::row_iter` method and can be returned by
|
||||||
|
|
Loading…
Reference in New Issue