summaryrefslogtreecommitdiffstats
path: root/src/terminal
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2020-02-04 15:52:12 +0200
committerManos Pitsidianakis <el13635@mail.ntua.gr>2020-02-04 17:29:55 +0200
commit8b6ea8de9a89b3ad42276eb7835992f7b875128b (patch)
tree864a7136b4c23d76761ee3e7d034307f8c302838 /src/terminal
parent6fcc792b83f715644c041f728049de65f7da2e38 (diff)
Remove ui crate
Merge ui crate with root crate. In preparation for uploading `meli` as a separate crate on crates.io. Workspace crates will need to be published as well and having a separate `ui` crate and binary perhaps doesn't make sense anymore.
Diffstat (limited to 'src/terminal')
-rw-r--r--src/terminal/cells.rs2067
-rw-r--r--src/terminal/embed.rs391
-rw-r--r--src/terminal/embed/grid.rs1126
-rw-r--r--src/terminal/keys.rs427
-rw-r--r--src/terminal/position.rs162
-rw-r--r--src/terminal/text_editing.rs175
6 files changed, 4348 insertions, 0 deletions
diff --git a/src/terminal/cells.rs b/src/terminal/cells.rs
new file mode 100644
index 00000000..df76faf6
--- /dev/null
+++ b/src/terminal/cells.rs
@@ -0,0 +1,2067 @@
+/*
+ * meli
+ *
+ * Copyright 2017-2018 Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*!
+ Define a (x, y) point in the terminal display as a holder of a character, foreground/background
+ colors and attributes.
+*/
+
+use super::position::*;
+use crate::state::Context;
+use text_processing::wcwidth;
+
+use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
+use std::convert::From;
+use std::fmt;
+use std::ops::{Deref, DerefMut, Index, IndexMut};
+use termion::color::{AnsiValue, Rgb as TermionRgb};
+
+/// In a scroll region up and down cursor movements shift the region vertically. The new lines are
+/// empty.
+///
+/// See `CellBuffer::scroll_up` and `CellBuffer::scroll_down` for an explanation of how `xterm`
+/// scrolling works.
+#[derive(Debug, Clone, PartialEq, Eq, Default)]
+pub struct ScrollRegion {
+ pub top: usize,
+ pub bottom: usize,
+ pub left: usize,
+ pub right: usize,
+}
+
+/// An array of `Cell`s that represents a terminal display.
+///
+/// A `CellBuffer` is a two-dimensional array of `Cell`s, each pair of indices correspond to a
+/// single point on the underlying terminal.
+///
+/// The first index, `Cellbuffer[y]`, corresponds to a row, and thus the y-axis. The second
+/// index, `Cellbuffer[y][x]`, corresponds to a column within a row and thus the x-axis.
+#[derive(Clone, PartialEq, Eq)]
+pub struct CellBuffer {
+ cols: usize,
+ rows: usize,
+ buf: Vec<Cell>,
+ /// ASCII-only flag.
+ pub ascii_drawing: bool,
+ /// If printing to this buffer and we run out of space, expand it.
+ growable: bool,
+}
+
+impl fmt::Debug for CellBuffer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "CellBuffer {{ cols: {}, rows: {}, buf: {} cells",
+ self.cols,
+ self.rows,
+ self.buf.len()
+ )
+ }
+}
+
+impl CellBuffer {
+ pub fn area(&self) -> Area {
+ (
+ (0, 0),
+ (self.cols.saturating_sub(1), self.rows.saturating_sub(1)),
+ )
+ }
+ pub fn set_cols(&mut self, new_cols: usize) {
+ self.cols = new_cols;
+ }
+
+ /// Constructs a new `CellBuffer` with the given number of columns and rows, using the given
+ /// `cell` as a blank.
+ pub fn new(cols: usize, rows: usize, cell: Cell) -> CellBuffer {
+ CellBuffer {
+ cols,
+ rows,
+ buf: vec![cell; cols * rows],
+ growable: false,
+ ascii_drawing: false,
+ }
+ }
+
+ pub fn new_with_context(cols: usize, rows: usize, cell: Cell, context: &Context) -> CellBuffer {
+ CellBuffer {
+ cols,
+ rows,
+ buf: vec![cell; cols * rows],
+ growable: false,
+ ascii_drawing: context.settings.terminal.ascii_drawing,
+ }
+ }
+
+ pub fn set_ascii_drawing(&mut self, new_val: bool) {
+ self.ascii_drawing = new_val;
+ }
+
+ pub fn set_growable(&mut self, new_val: bool) {
+ self.growable = new_val;
+ }
+
+ /// Resizes `CellBuffer` to the given number of rows and columns, using the given `Cell` as
+ /// a blank.
+ pub fn resize(&mut self, newcols: usize, newrows: usize, blank: Cell) {
+ let newlen = newcols * newrows;
+ if self.buf.len() == newlen {
+ self.cols = newcols;
+ self.rows = newrows;
+ return;
+ }
+
+ if newlen >= 200_000 {
+ return;
+ }
+
+ let mut newbuf: Vec<Cell> = Vec::with_capacity(newlen);
+ for y in 0..newrows {
+ for x in 0..newcols {
+ let cell = self.get(x, y).unwrap_or(&blank);
+ newbuf.push(*cell);
+ }
+ }
+ self.buf = newbuf;
+ self.cols = newcols;
+ self.rows = newrows;
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.buf.is_empty()
+ }
+
+ pub fn empty(&mut self) {
+ self.buf.clear();
+ self.cols = 0;
+ self.rows = 0;
+ }
+
+ /// Clears `self`, using the given `Cell` as a blank.
+ pub fn clear(&mut self, blank: Cell) {
+ for cell in self.cellvec_mut().iter_mut() {
+ *cell = blank;
+ }
+ }
+
+ pub fn pos_to_index(&self, x: usize, y: usize) -> Option<usize> {
+ let (cols, rows) = self.size();
+ if x < cols && y < rows {
+ Some((cols * y) + x)
+ } else {
+ None
+ }
+ }
+
+ /// Returns a reference to the `Cell` at the given coordinates, or `None` if the index is out of
+ /// bounds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ ///
+ /// let mut term = Terminal::new().unwrap();
+ ///
+ /// let a_cell = term.get(5, 5);
+ /// ```
+ pub fn get(&self, x: usize, y: usize) -> Option<&Cell> {
+ match self.pos_to_index(x, y) {
+ Some(i) => self.cellvec().get(i),
+ None => None,
+ }
+ }
+
+ /// Returns a mutable reference to the `Cell` at the given coordinates, or `None` if the index
+ /// is out of bounds.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ ///
+ /// let mut term = Terminal::new().unwrap();
+ ///
+ /// let a_mut_cell = term.get_mut(5, 5);
+ /// ```
+ pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut Cell> {
+ match self.pos_to_index(x, y) {
+ Some(i) => self.cellvec_mut().get_mut(i),
+ None => None,
+ }
+ }
+
+ pub fn size(&self) -> (usize, usize) {
+ (self.cols, self.rows)
+ }
+
+ pub fn cellvec(&self) -> &Vec<Cell> {
+ &self.buf
+ }
+
+ pub fn cellvec_mut(&mut self) -> &mut Vec<Cell> {
+ &mut self.buf
+ }
+
+ pub fn cols(&self) -> usize {
+ self.size().0
+ }
+
+ pub fn rows(&self) -> usize {
+ self.size().1
+ }
+
+ #[inline(always)]
+ /// Performs the normal scroll up motion:
+ ///
+ /// First clear offset number of lines:
+ ///
+ /// For offset = 1, top = 1:
+ ///
+ /// ```text
+ /// | 111111111111 | | |
+ /// | 222222222222 | | 222222222222 |
+ /// | 333333333333 | | 333333333333 |
+ /// | 444444444444 | --> | 444444444444 |
+ /// | 555555555555 | | 555555555555 |
+ /// | 666666666666 | | 666666666666 |
+ /// ```
+ ///
+ /// In each step, swap the current line with the next by offset:
+ ///
+ /// ```text
+ /// | | | 222222222222 |
+ /// | 222222222222 | | |
+ /// | 333333333333 | | 333333333333 |
+ /// | 444444444444 | --> | 444444444444 |
+ /// | 555555555555 | | 555555555555 |
+ /// | 666666666666 | | 666666666666 |
+ /// ```
+ ///
+ /// Result:
+ /// ```text
+ /// Before After
+ /// | 111111111111 | | 222222222222 |
+ /// | 222222222222 | | 333333333333 |
+ /// | 333333333333 | | 444444444444 |
+ /// | 444444444444 | | 555555555555 |
+ /// | 555555555555 | | 666666666666 |
+ /// | 666666666666 | | |
+ /// ```
+ ///
+ pub fn scroll_up(&mut self, scroll_region: &ScrollRegion, top: usize, offset: usize) {
+ //debug!(
+ // "scroll_up scroll_region {:?}, top: {} offset {}",
+ // scroll_region, top, offset
+ //);
+ let l = scroll_region.left;
+ let r = if scroll_region.right == 0 {
+ self.size().0
+ } else {
+ scroll_region.right
+ };
+ for y in top..=(top + offset - 1) {
+ for x in l..r {
+ self[(x, y)] = Cell::default();
+ }
+ }
+ for y in top..=(scroll_region.bottom - offset) {
+ for x in l..r {
+ let temp = self[(x, y)];
+ self[(x, y)] = self[(x, y + offset)];
+ self[(x, y + offset)] = temp;
+ }
+ }
+ }
+
+ #[inline(always)]
+ /// Performs the normal scroll down motion:
+ ///
+ /// First clear offset number of lines:
+ ///
+ /// For offset = 1, top = 1:
+ ///
+ /// ```text
+ /// | 111111111111 | | 111111111111 |
+ /// | 222222222222 | | 222222222222 |
+ /// | 333333333333 | | 333333333333 |
+ /// | 444444444444 | --> | 444444444444 |
+ /// | 555555555555 | | 555555555555 |
+ /// | 666666666666 | | |
+ /// ```
+ ///
+ /// In each step, swap the current line with the prev by offset:
+ ///
+ /// ```text
+ /// | 111111111111 | | 111111111111 |
+ /// | 222222222222 | | 222222222222 |
+ /// | 333333333333 | | 333333333333 |
+ /// | 444444444444 | --> | 444444444444 |
+ /// | 555555555555 | | |
+ /// | | | 555555555555 |
+ /// ```
+ ///
+ /// Result:
+ /// ```text
+ /// Before After
+ /// | 111111111111 | | |
+ /// | 222222222222 | | 111111111111 |
+ /// | 333333333333 | | 222222222222 |
+ /// | 444444444444 | | 333333333333 |
+ /// | 555555555555 | | 444444444444 |
+ /// | 666666666666 | | 555555555555 |
+ /// ```
+ ///
+ pub fn scroll_down(&mut self, scroll_region: &ScrollRegion, top: usize, offset: usize) {
+ //debug!(
+ // "scroll_down scroll_region {:?}, top: {} offset {}",
+ // scroll_region, top, offset
+ //);
+ for y in (scroll_region.bottom - offset + 1)..=scroll_region.bottom {
+ for x in 0..self.size().0 {
+ self[(x, y)] = Cell::default();
+ }
+ }
+
+ for y in ((top + offset)..=scroll_region.bottom).rev() {
+ for x in 0..self.size().0 {
+ let temp = self[(x, y)];
+ self[(x, y)] = self[(x, y - offset)];
+ self[(x, y - offset)] = temp;
+ }
+ }
+ }
+
+ /// See `BoundsIterator` documentation.
+ pub fn bounds_iter(&self, area: Area) -> BoundsIterator {
+ BoundsIterator {
+ rows: std::cmp::min(self.rows.saturating_sub(1), get_y(upper_left!(area)))
+ ..(std::cmp::min(self.rows, get_y(bottom_right!(area)) + 1)),
+ cols: (
+ std::cmp::min(self.cols.saturating_sub(1), get_x(upper_left!(area))),
+ std::cmp::min(self.cols, get_x(bottom_right!(area)) + 1),
+ ),
+ }
+ }
+
+ /// See `RowIterator` documentation.
+ pub fn row_iter(&self, bounds: std::ops::Range<usize>, row: usize) -> RowIterator {
+ if row < self.rows {
+ RowIterator {
+ row,
+ col: std::cmp::min(self.cols.saturating_sub(1), bounds.start)
+ ..(std::cmp::min(self.cols, bounds.end)),
+ }
+ } else {
+ RowIterator { row, col: 0..0 }
+ }
+ }
+}
+
+impl Deref for CellBuffer {
+ type Target = [Cell];
+
+ fn deref(&self) -> &[Cell] {
+ &self.buf
+ }
+}
+
+impl DerefMut for CellBuffer {
+ fn deref_mut(&mut self) -> &mut [Cell] {
+ &mut self.buf
+ }
+}
+
+impl Index<Pos> for CellBuffer {
+ type Output = Cell;
+
+ fn index(&self, index: Pos) -> &Cell {
+ let (x, y) = index;
+ self.get(x, y).expect("index out of bounds")
+ }
+}
+
+impl IndexMut<Pos> for CellBuffer {
+ fn index_mut(&mut self, index: Pos) -> &mut Cell {
+ let (x, y) = index;
+ self.get_mut(x, y).expect("index out of bounds")
+ }
+}
+
+impl Default for CellBuffer {
+ /// Constructs a new `CellBuffer` with a size of `(0, 0)`, using the default `Cell` as a blank.
+ fn default() -> CellBuffer {
+ CellBuffer::new(0, 0, Cell::default())
+ }
+}
+
+impl fmt::Display for CellBuffer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ '_y: for y in 0..self.rows {
+ for x in 0..self.cols {
+ let c: &char = &self[(x, y)].ch();
+ write!(f, "{}", *c).unwrap();
+ if *c == '\n' {
+ continue '_y;
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+/// A single point on a terminal display.
+///
+/// A `Cell` contains a character and style.
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub struct Cell {
+ ch: char,
+
+ /// Set a `Cell` as empty when a previous cell spans multiple columns and it would
+ /// "overflow" to this cell.
+ empty: bool,
+ fg: Color,
+ bg: Color,
+ attrs: Attr,
+ keep_fg: bool,
+ keep_bg: bool,
+}
+
+impl Cell {
+ /// Creates a new `Cell` with the given `char`, `Color`s and `Attr`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let cell = Cell::new('x', Color::Default, Color::Green, Attr::Default);
+ /// assert_eq!(cell.ch(), 'x');
+ /// assert_eq!(cell.fg(), Color::Default);
+ /// assert_eq!(cell.bg(), Color::Green);
+ /// assert_eq!(cell.attrs(), Attr::Default);
+ /// ```
+ pub fn new(ch: char, fg: Color, bg: Color, attrs: Attr) -> Cell {
+ Cell {
+ ch,
+ fg,
+ bg,
+ attrs,
+ empty: false,
+ keep_fg: false,
+ keep_bg: false,
+ }
+ }
+
+ /// Creates a new `Cell` with the given `char` and default style.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::with_char('x');
+ /// assert_eq!(cell.ch(), 'x');
+ /// assert_eq!(cell.fg(), Color::Default);
+ /// assert_eq!(cell.bg(), Color::Default);
+ /// assert_eq!(cell.attrs(), Attr::Default);
+ /// ```
+ pub fn with_char(ch: char) -> Cell {
+ Cell::new(ch, Color::Default, Color::Default, Attr::Default)
+ }
+
+ /// Creates a new `Cell` with the given style and a blank `char`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::with_style(Color::Default, Color::Red, Attr::Bold);
+ /// assert_eq!(cell.fg(), Color::Default);
+ /// assert_eq!(cell.bg(), Color::Red);
+ /// assert_eq!(cell.attrs(), Attr::Bold);
+ /// assert_eq!(cell.ch(), ' ');
+ /// ```
+ pub fn with_style(fg: Color, bg: Color, attr: Attr) -> Cell {
+ Cell::new(' ', fg, bg, attr)
+ }
+
+ /// Returns the `Cell`'s character.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::with_char('x');
+ /// assert_eq!(cell.ch(), 'x');
+ /// ```
+ pub fn ch(&self) -> char {
+ self.ch
+ }
+
+ /// Sets the `Cell`'s character to the given `char`
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::with_char('x');
+ /// assert_eq!(cell.ch(), 'x');
+ ///
+ /// cell.set_ch('y');
+ /// assert_eq!(cell.ch(), 'y');
+ /// ```
+ pub fn set_ch(&mut self, newch: char) -> &mut Cell {
+ self.ch = newch;
+ self.keep_fg = false;
+ self.keep_bg = false;
+ self
+ }
+
+ /// Returns the `Cell`'s foreground `Color`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::with_style(Color::Blue, Color::Default, Attr::Default);
+ /// assert_eq!(cell.fg(), Color::Blue);
+ /// ```
+ pub fn fg(&self) -> Color {
+ self.fg
+ }
+
+ /// Sets the `Cell`'s foreground `Color` to the given `Color`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::default();
+ /// assert_eq!(cell.fg(), Color::Default);
+ ///
+ /// cell.set_fg(Color::White);
+ /// assert_eq!(cell.fg(), Color::White);
+ /// ```
+ pub fn set_fg(&mut self, newfg: Color) -> &mut Cell {
+ if !self.keep_fg {
+ self.fg = newfg;
+ }
+ self
+ }
+
+ /// Returns the `Cell`'s background `Color`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::with_style(Color::Default, Color::Green, Attr::Default);
+ /// assert_eq!(cell.bg(), Color::Green);
+ /// ```
+ pub fn bg(&self) -> Color {
+ self.bg
+ }
+
+ /// Sets the `Cell`'s background `Color` to the given `Color`.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::default();
+ /// assert_eq!(cell.bg(), Color::Default);
+ ///
+ /// cell.set_bg(Color::Black);
+ /// assert_eq!(cell.bg(), Color::Black);
+ /// ```
+ pub fn set_bg(&mut self, newbg: Color) -> &mut Cell {
+ if !self.keep_bg {
+ self.bg = newbg;
+ }
+ self
+ }
+
+ pub fn attrs(&self) -> Attr {
+ self.attrs
+ }
+
+ pub fn set_attrs(&mut self, newattrs: Attr) -> &mut Cell {
+ self.attrs = newattrs;
+ self
+ }
+
+ /// Set a `Cell` as empty when a previous cell spans multiple columns and it would
+ /// "overflow" to this cell.
+ pub fn empty(&self) -> bool {
+ self.empty
+ }
+
+ pub fn set_empty(&mut self, new_val: bool) -> &mut Cell {
+ self.empty = new_val;
+ self
+ }
+
+ /// Sets `keep_fg` field. If true, the foreground color will not be altered if attempted so
+ /// until the character content of the cell is changed.
+ pub fn set_keep_fg(&mut self, new_val: bool) -> &mut Cell {
+ self.keep_fg = new_val;
+ self
+ }
+
+ /// Sets `keep_bg` field. If true, the background color will not be altered if attempted so
+ /// until the character content of the cell is changed.
+ pub fn set_keep_bg(&mut self, new_val: bool) -> &mut Cell {
+ self.keep_bg = new_val;
+ self
+ }
+}
+
+impl Default for Cell {
+ /// Constructs a new `Cell` with a blank `char` and default `Color`s.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// let mut cell = Cell::default();
+ /// assert_eq!(cell.ch(), ' ');
+ /// assert_eq!(cell.fg(), Color::Default);
+ /// assert_eq!(cell.bg(), Color::Default);
+ /// ```
+ fn default() -> Cell {
+ Cell::new(' ', Color::Default, Color::Default, Attr::Default)
+ }
+}
+
+/// The color of a `Cell`.
+///
+/// `Color::Default` represents the default color of the underlying terminal.
+///
+/// The eight basic colors may be used directly and correspond to 0x00..0x07 in the 8-bit (256)
+/// color range; in addition, the eight basic colors coupled with `Attr::Bold` correspond to
+/// 0x08..0x0f in the 8-bit color range.
+///
+/// `Color::Byte(..)` may be used to specify a color in the 8-bit range.
+///
+/// # Examples
+///
+/// ```no_run
+/// // The default color.
+/// let default = Color::Default;
+///
+/// // A basic color.
+/// let red = Color::Red;
+///
+/// // An 8-bit color.
+/// let fancy = Color::Byte(0x01);
+///
+/// // Basic colors are also 8-bit colors (but not vice-versa).
+/// assert_eq!(red.as_byte(), fancy.as_byte())
+/// ```
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum Color {
+ Black,
+ Red,
+ Green,
+ Yellow,
+ Blue,
+ Magenta,
+ Cyan,
+ White,
+ Byte(u8),
+ Rgb(u8, u8, u8),
+ /// Terminal default.
+ Default,
+}
+
+impl Color {
+ /// Returns the `u8` representation of the `Color`.
+ pub fn as_byte(self) -> u8 {
+ match self {
+ Color::Black => 0x00,
+ Color::Red => 0x01,
+ Color::Green => 0x02,
+ Color::Yellow => 0x03,
+ Color::Blue => 0x04,
+ Color::Magenta => 0x05,
+ Color::Cyan => 0x06,
+ Color::White => 0x07,
+ Color::Byte(b) => b,
+ Color::Rgb(_, _, _) => unreachable!(),
+ Color::Default => 0x00,
+ }
+ }
+
+ pub fn from_byte(val: u8) -> Self {
+ match val {
+ 0x00 => Color::Black,
+ 0x01 => Color::Red,
+ 0x02 => Color::Green,
+ 0x03 => Color::Yellow,
+ 0x04 => Color::Blue,
+ 0x05 => Color::Magenta,
+ 0x06 => Color::Cyan,
+ 0x07 => Color::White,
+ _ => Color::Default,
+ }
+ }
+
+ pub fn write_fg(self, stdout: &mut crate::StateStdout) -> std::io::Result<()> {
+ use std::io::Write;
+ match self {
+ Color::Default => write!(stdout, "{}", termion::color::Fg(termion::color::Reset)),
+ Color::Rgb(r, g, b) => write!(stdout, "{}", termion::color::Fg(TermionRgb(r, g, b))),
+ _ => write!(stdout, "{}", termion::color::Fg(self.as_termion())),
+ }
+ }
+
+ pub fn write_bg(self, stdout: &mut crate::StateStdout) -> std::io::Result<()> {
+ use std::io::Write;
+ match self {
+ Color::Default => write!(stdout, "{}", termion::color::Bg(termion::color::Reset)),
+ Color::Rgb(r, g, b) => write!(stdout, "{}", termion::color::Bg(TermionRgb(r, g, b))),
+ _ => write!(stdout, "{}", termion::color::Bg(self.as_termion())),
+ }
+ }
+
+ pub fn as_termion(self) -> AnsiValue {
+ match self {
+ b @ Color::Black
+ | b @ Color::Red
+ | b @ Color::Green
+ | b @ Color::Yellow
+ | b @ Color::Blue
+ | b @ Color::Magenta
+ | b @ Color::Cyan
+ | b @ Color::White
+ | b @ Color::Default => AnsiValue(b.as_byte()),
+ Color::Byte(b) => AnsiValue(b),
+ Color::Rgb(_, _, _) => AnsiValue(0),
+ }
+ }
+
+ pub fn from_string_de<'de, D>(s: String) -> std::result::Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let byte = match s.as_str() {
+ "Aqua" => 14,
+ "Aquamarine1" => 122,
+ "Aquamarine2" => 86,
+ "Aquamarine3" => 79,
+ "Black" => 0,
+ "Blue" => 12,
+ "Blue1" => 21,
+ "Blue2" => 19,
+ "Blue3" => 20,
+ "BlueViolet" => 57,
+ "CadetBlue" => 72,
+ "CadetBlue1" => 73,
+ "Chartreuse1" => 118,
+ "Chartreuse2" => 112,
+ "Chartreuse3" => 82,
+ "Chartreuse4" => 70,
+ "Chartreuse5" => 76,
+ "Chartreuse6" => 64,
+ "CornflowerBlue" => 69,
+ "Cornsilk1" => 230,
+ "Cyan1" => 51,
+ "Cyan2" => 50,
+ "Cyan3" => 43,
+ "DarkBlue" => 18,
+ "DarkCyan" => 36,
+ "DarkGoldenrod" => 136,
+ "DarkGreen" => 22,
+ "DarkKhaki" => 143,
+ "DarkMagenta" => 90,
+ "DarkMagenta1" => 91,
+ "DarkOliveGreen1" => 192,
+ "DarkOliveGreen2" => 155,
+ "DarkOliveGreen3" => 191,
+ "DarkOliveGreen4" => 107,
+ "DarkOliveGreen5" => 113,
+ "DarkOliveGreen6" => 149,
+ "DarkOrange" => 208,
+ "DarkOrange2" => 130,
+ "DarkOrange3" => 166,
+ "DarkRed" => 52,
+ "DarkRed2" => 88,
+ "DarkSeaGreen" => 108,
+ "DarkSeaGreen1" => 158,
+ "DarkSeaGreen2" => 193,
+ "DarkSeaGreen3" => 151,
+ "DarkSeaGreen4" => 157,
+ "DarkSeaGreen5" => 115,
+ "DarkSeaGreen6" => 150,
+ "DarkSeaGreen7" => 65,
+ "DarkSeaGreen8" => 71,
+ "DarkSlateGray1" => 123,
+ "DarkSlateGray2" => 87,
+ "DarkSlateGray3" => 116,
+ "DarkTurquoise" => 44,
+ "DarkViolet" => 128,
+ "DarkViolet1" => 92,
+ "DeepPink1" => 199,
+ "DeepPink2" => 197,
+ "DeepPink3" => 198,
+ "DeepPink4" => 125,
+ "DeepPink6" => 162,
+ "DeepPink7" => 89,
+ "DeepPink8" => 53,
+ "DeepPink9" => 161,
+ "DeepSkyBlue1" => 39,
+ "DeepSkyBlue2" => 38,
+ "DeepSkyBlue3" => 31,
+ "DeepSkyBlue4" => 32,
+ "DeepSkyBlue5" => 23,
+ "DeepSkyBlue6" => 24,
+ "DeepSkyBlue7" => 25,
+ "DodgerBlue1" => 33,
+ "DodgerBlue2" => 27,
+ "DodgerBlue3" => 26,
+ "Fuchsia" => 13,
+ "Gold1" => 220,
+ "Gold2" => 142,
+ "Gold3" => 178,
+ "Green" => 2,
+ "Green1" => 46,
+ "Green2" => 34,
+ "Green3" => 40,
+ "Green4" => 28,
+ "GreenYellow" => 154,
+ "Grey" => 8,
+ "Grey0" => 16,
+ "Grey100" => 231,
+ "Grey11" => 234,
+ "Grey15" => 235,
+ "Grey19" => 236,
+ "Grey23" => 237,
+ "Grey27" => 238,
+ "Grey3" => 232,
+ "Grey30" => 239,
+ "Grey35" => 240,
+ "Grey37" => 59,
+ "Grey39" => 241,
+ "Grey42" => 242,
+ "Grey46" => 243,
+ "Grey50" => 244,
+ "Grey53" => 102,
+ "Grey54" => 245,
+ "Grey58" => 246,
+ "Grey62" => 247,
+ "Grey63" => 139,
+ "Grey66" => 248,
+ "Grey69" => 145,
+ "Grey7" => 233,
+ "Grey70" => 249,
+ "Grey74" => 250,
+ "Grey78" => 251,
+ "Grey82" => 252,
+ "Grey84" => 188,
+ "Grey85" => 253,
+ "Grey89" => 254,
+ "Grey93" => 255,
+ "Honeydew2" => 194,
+ "HotPink" => 205,
+ "HotPink1" => 206,
+ "HotPink2" => 169,
+ "HotPink3" => 132,
+ "HotPink4" => 168,
+ "IndianRed" => 131,
+ "IndianRed1" => 167,
+ "IndianRed2" => 204,
+ "IndianRed3" => 203,
+ "Khaki1" => 228,
+ "Khaki3" => 185,
+ "LightCoral" => 210,
+ "LightCyan2" => 195,
+ "LightCyan3" => 152,
+ "LightGoldenrod1" => 227,
+ "LightGoldenrod2" => 222,
+ "LightGoldenrod3" => 179,
+ "LightGoldenrod4" => 221,
+ "LightGoldenrod5" => 186,
+ "LightGreen" => 119,
+ "LightGreen1" => 120,
+ "LightPink1" => 217,
+ "LightPink2" => 174,
+ "LightPink3" => 95,
+ "LightSalmon1" => 216,
+ "LightSalmon2" => 137,
+ "LightSalmon3" => 173,
+ "LightSeaGreen" => 37,
+ "LightSkyBlue1" => 153,
+ "LightSkyBlue2" => 109,
+ "LightSkyBlue3" => 110,
+ "LightSlateBlue" => 105,
+ "LightSlateGrey" => 103,
+ "LightSteelBlue" => 147,
+ "LightSteelBlue1" => 189,
+ "LightSteelBlue3" => 146,
+ "LightYellow3" => 187,
+ "Lime" => 10,
+ "Magenta1" => 201,
+ "Magenta2" => 165,
+ "Magenta3" => 200,
+ "Magenta4" => 127,
+ "Magenta5" => 163,
+ "Magenta6" => 164,
+ "Maroon" => 1,
+ "MediumOrchid" => 134,
+ "MediumOrchid1" => 171,
+ "MediumOrchid2" => 207,
+ "MediumOrchid3" => 133,
+ "MediumPurple" => 104,
+ "MediumPurple1" => 141,
+ "MediumPurple2" => 135,
+ "MediumPurple3" => 140,
+ "MediumPurple4" => 97,
+ "MediumPurple5" => 98,
+ "MediumPurple6" => 60,
+ "MediumSpringGreen" => 49,
+ "MediumTurquoise" => 80,
+ "MediumVioletRed" => 126,
+ "MistyRose1" => 224,
+ "MistyRose3" => 181,
+ "NavajoWhite1" => 223,
+ "NavajoWhite3" => 144,
+ "Navy" => 4,
+ "NavyBlue" => 17,
+ "Olive" => 3,
+ "Orange1" => 214,
+ "Orange2" => 172,
+ "Orange3" => 58,
+ "Orange4" => 94,
+ "OrangeRed1" => 202,
+ "Orchid" => 170,
+ "Orchid1" => 213,
+ "Orchid2" => 212,
+ "PaleGreen1" => 121,
+ "PaleGreen2" => 156,
+ "PaleGreen3" => 114,
+ "PaleGreen4" => 77,
+ "PaleTurquoise1" => 159,
+ "PaleTurquoise4" => 66,
+ "PaleVioletRed1" => 211,
+ "Pink1" => 218,
+ "Pink3" => 175,
+ "Plum1" => 219,
+ "Plum2" => 183,
+ "Plum3" => 176,
+ "Plum4" => 96,
+ "Purple" => 129,
+ "Purple1" => 5,
+ "Purple2" => 93,
+ "Purple3" => 56,
+ "Purple4" => 54,
+ "Purple5" => 55,
+ "Red" => 9,
+ "Red1" => 196,
+ "Red2" => 124,
+ "Red3" => 160,
+ "RosyBrown" => 138,
+ "RoyalBlue1" => 63,
+ "Salmon1" => 209,
+ "SandyBrown" => 215,
+ "SeaGreen1" => 84,
+ "SeaGreen2" => 85,
+ "SeaGreen3" => 83,
+ "SeaGreen4" => 78,
+ "Silver" => 7,
+ "SkyBlue1" => 117,
+ "SkyBlue2" => 111,
+ "SkyBlue3" => 74,
+ "SlateBlue1" => 99,
+ "SlateBlue2" => 61,
+ "SlateBlue3" => 62,
+ "SpringGreen1" => 48,
+ "SpringGreen2" => 42,
+ "SpringGreen3" => 47,
+ "SpringGreen4" => 35,
+ "SpringGreen5" => 41,
+ "SpringGreen6" => 29,
+ "SteelBlue" => 67,
+ "SteelBlue1" => 75,
+ "SteelBlue2" => 81,
+ "SteelBlue3" => 68,
+ "Tan" => 180,
+ "Teal" => 6,
+ "Thistle1" => 225,
+ "Thistle3" => 182,
+ "Turquoise2" => 45,
+ "Turquoise4" => 30,
+ "Violet" => 177,
+ "Wheat1" => 229,
+ "Wheat4" => 101,
+ "White" => 15,
+ "Yellow" => 11,
+ "Yellow1" => 226,
+ "Yellow2" => 190,
+ "Yellow3" => 184,
+ "Yellow4" => 100,
+ "Yellow5" => 106,
+ "Yellow6" => 148,
+ "Default" => return Ok(Color::Default),
+ s if s.starts_with("#")
+ && s.len() == 7
+ && s[1..].as_bytes().iter().all(|&b| {
+ (b >= b'0' && b <= b'9') || (b >= b'a' && b <= b'f') || (b >= b'A' && b <= b'F')
+ }) =>
+ {
+ return Ok(Color::Rgb(
+ u8::from_str_radix(&s[1..3], 16)
+ .map_err(|_| de::Error::custom("invalid `color` value"))?,
+ u8::from_str_radix(&s[3..5], 16)
+ .map_err(|_| de::Error::custom("invalid `color` value"))?,
+ u8::from_str_radix(&s[5..7], 16)
+ .map_err(|_| de::Error::custom("invalid `color` value"))?,
+ ))
+ }
+ s if s.starts_with("#")
+ && s.len() == 4
+ && s[1..].as_bytes().iter().all(|&b| {
+ (b >= b'0' && b <= b'9') || (b >= b'a' && b <= b'f') || (b >= b'A' && b <= b'F')
+ }) =>
+ {
+ return Ok(Color::Rgb(
+ 17 * u8::from_str_radix(&s[1..2], 16)
+ .map_err(|_| de::Error::custom("invalid `color` value"))?,
+ 17 * u8::from_str_radix(&s[2..3], 16)
+ .map_err(|_| de::Error::c