added ansi gradient in the spirit of fun (#3570)

This commit is contained in:
Darren Schroeder 2021-06-07 13:20:05 -05:00 committed by GitHub
parent aa1cd7eba6
commit 94fc8a1334
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 678 additions and 14 deletions

8
Cargo.lock generated
View File

@ -3272,6 +3272,8 @@ name = "nu-ansi-term"
version = "0.32.1"
dependencies = [
"doc-comment",
"itertools",
"overload",
"regex 1.5.4",
"serde 1.0.126",
"serde_json",
@ -4294,6 +4296,12 @@ dependencies = [
"num-traits 0.2.14",
]
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "parking"
version = "2.0.0"

View File

@ -18,10 +18,15 @@ doctest = false
[features]
derive_serde_style = ["serde"]
[dependencies.serde]
version = "1.0.90"
features = ["derive"]
optional = true
[dependencies]
overload = "0.1.1"
serde = { version = "1.0.90", features = ["derive"], optional = true }
itertools = "0.10.0"
# [dependencies.serde]
# version = "1.0.90"
# features = ["derive"]
# optional = true
[target.'cfg(target_os="windows")'.dependencies.winapi]
version = "0.3.4"

View File

@ -0,0 +1,37 @@
use nu_ansi_term::{build_all_gradient_text, Color, Gradient, Rgb, TargetGround};
fn main() {
let text = "lorem ipsum quia dolor sit amet, consectetur, adipisci velit";
// a gradient from hex colors
let start = Rgb::from_hex(0x40c9ff);
let end = Rgb::from_hex(0xe81cff);
let grad0 = Gradient::new(start, end);
// a gradient from color::rgb()
let start = Color::Rgb(64, 201, 255);
let end = Color::Rgb(232, 28, 255);
let gradient = Gradient::from_color_rgb(start, end);
// a slightly different gradient
let start2 = Color::Rgb(128, 64, 255);
let end2 = Color::Rgb(0, 28, 255);
let gradient2 = Gradient::from_color_rgb(start2, end2);
// reverse the gradient
let gradient3 = gradient.reverse();
let build_fg = gradient.build(text, TargetGround::Foreground);
println!("{}", build_fg);
let build_bg = gradient.build(text, TargetGround::Background);
println!("{}", build_bg);
let bgt = build_all_gradient_text(text, gradient, gradient2);
println!("{}", bgt);
let bgt2 = build_all_gradient_text(text, gradient, gradient3);
println!("{}", bgt2);
println!(
"{}",
grad0.build("nushell is awesome", TargetGround::Foreground)
);
}

View File

@ -0,0 +1,105 @@
use crate::{rgb::Rgb, Color};
/// Linear color gradient between two color stops
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Gradient {
/// Start Color of Gradient
pub start: Rgb,
/// End Color of Gradient
pub end: Rgb,
}
impl Gradient {
/// Creates a new [Gradient] with two [Rgb] colors, `start` and `end`
#[inline]
pub const fn new(start: Rgb, end: Rgb) -> Self {
Self { start, end }
}
pub const fn from_color_rgb(start: Color, end: Color) -> Self {
let start_grad = match start {
Color::Rgb(r, g, b) => Rgb { r, g, b },
_ => Rgb { r: 0, g: 0, b: 0 },
};
let end_grad = match end {
Color::Rgb(r, g, b) => Rgb { r, g, b },
_ => Rgb { r: 0, g: 0, b: 0 },
};
Self {
start: start_grad,
end: end_grad,
}
}
/// Computes the [Rgb] color between `start` and `end` for `t`
pub fn at(&self, t: f32) -> Rgb {
self.start.lerp(self.end, t)
}
/// Returns the reverse of `self`
#[inline]
pub const fn reverse(&self) -> Self {
Self::new(self.end, self.start)
}
#[allow(dead_code)]
pub fn build(&self, text: &str, target: TargetGround) -> String {
let delta = 1.0 / text.len() as f32;
let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| {
let temp = format!(
"\x1B[{}m{}",
self.at(i as f32 * delta).ansi_color_code(target),
c
);
acc.push_str(&temp);
acc
});
result.push_str("\x1B[0m");
result
}
}
#[allow(dead_code)]
pub fn build_all_gradient_text(text: &str, foreground: Gradient, background: Gradient) -> String {
let delta = 1.0 / text.len() as f32;
let mut result = text.char_indices().fold(String::new(), |mut acc, (i, c)| {
let step = i as f32 * delta;
let temp = format!(
"\x1B[{};{}m{}",
foreground
.at(step)
.ansi_color_code(TargetGround::Foreground),
background
.at(step)
.ansi_color_code(TargetGround::Background),
c
);
acc.push_str(&temp);
acc
});
result.push_str("\x1B[0m");
result
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TargetGround {
Foreground,
Background,
}
impl TargetGround {
#[inline]
pub const fn code(&self) -> u8 {
match self {
Self::Foreground => 30,
Self::Background => 40,
}
}
}
pub trait ANSIColorCode {
fn ansi_color_code(&self, target: TargetGround) -> String;
}

View File

@ -121,13 +121,13 @@
//! `Fixed` colors instead, but theres nothing to be gained by doing so
//! either.
//!
//! You can also access full 24-bit color by using the `Color::RGB` variant,
//! You can also access full 24-bit color by using the `Color::Rgb` variant,
//! which takes separate `u8` arguments for red, green, and blue:
//!
//! ```
//! use nu_ansi_term::Color::RGB;
//! use nu_ansi_term::Color::Rgb;
//!
//! RGB(70, 130, 180).paint("Steel blue");
//! Rgb(70, 130, 180).paint("Steel blue");
//! ```
//!
//! ## Combining successive colored strings
@ -233,7 +233,7 @@
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![warn(missing_copy_implementations)]
#![warn(missing_docs)]
// #![warn(missing_docs)]
#![warn(trivial_casts, trivial_numeric_casts)]
// #![warn(unused_extern_crates, unused_qualifications)]
@ -265,3 +265,9 @@ mod util;
pub use util::*;
mod debug;
pub mod gradient;
pub use gradient::*;
mod rgb;
pub use rgb::*;

View File

@ -0,0 +1,173 @@
// Code liberally borrowed from here
// https://github.com/navierr/coloriz
use std::ops;
use std::u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rgb {
/// Red
pub r: u8,
/// Green
pub g: u8,
/// Blue
pub b: u8,
}
impl Rgb {
/// Creates a new [Rgb] color
#[inline]
pub const fn new(r: u8, g: u8, b: u8) -> Self {
Self { r, g, b }
}
/// Creates a new [Rgb] color with a hex code
#[inline]
pub const fn from_hex(hex: u32) -> Self {
Self::new((hex >> 16) as u8, (hex >> 8) as u8, hex as u8)
}
pub fn from_hex_string(hex: String) -> Self {
if hex.chars().count() == 8 && hex.starts_with("0x") {
// eprintln!("hex:{:?}", hex);
let (_, value_string) = hex.split_at(2);
// eprintln!("value_string:{:?}", value_string);
let int_val = u64::from_str_radix(value_string, 16);
match int_val {
Ok(num) => Self::new(
((num & 0xff0000) >> 16) as u8,
((num & 0xff00) >> 8) as u8,
(num & 0xff) as u8,
),
// Don't fail, just make the color black
// Should we fail?
_ => Self::new(0, 0, 0),
}
} else {
// Don't fail, just make the color black.
// Should we fail?
Self::new(0, 0, 0)
}
}
/// Creates a new [Rgb] color with three [f32] values
pub fn from_f32(r: f32, g: f32, b: f32) -> Self {
Self::new(
(r.clamp(0.0, 1.0) * 255.0) as u8,
(g.clamp(0.0, 1.0) * 255.0) as u8,
(b.clamp(0.0, 1.0) * 255.0) as u8,
)
}
/// Creates a grayscale [Rgb] color
#[inline]
pub const fn gray(x: u8) -> Self {
Self::new(x, x, x)
}
/// Creates a grayscale [Rgb] color with a [f32] value
pub fn gray_f32(x: f32) -> Self {
Self::from_f32(x, x, x)
}
/// Creates a new [Rgb] color from a [HSL] color
// pub fn from_hsl(hsl: HSL) -> Self {
// if hsl.s == 0.0 {
// return Self::gray_f32(hsl.l);
// }
// let q = if hsl.l < 0.5 {
// hsl.l * (1.0 + hsl.s)
// } else {
// hsl.l + hsl.s - hsl.l * hsl.s
// };
// let p = 2.0 * hsl.l - q;
// let h2c = |t: f32| {
// let t = t.clamp(0.0, 1.0);
// if 6.0 * t < 1.0 {
// p + 6.0 * (q - p) * t
// } else if t < 0.5 {
// q
// } else if 1.0 < 1.5 * t {
// p + 6.0 * (q - p) * (1.0 / 1.5 - t)
// } else {
// p
// }
// };
// Self::from_f32(h2c(hsl.h + 1.0 / 3.0), h2c(hsl.h), h2c(hsl.h - 1.0 / 3.0))
// }
/// Computes the linear interpolation between `self` and `other` for `t`
pub fn lerp(&self, other: Self, t: f32) -> Self {
let t = t.clamp(0.0, 1.0);
self * (1.0 - t) + other * t
}
}
impl From<(u8, u8, u8)> for Rgb {
fn from((r, g, b): (u8, u8, u8)) -> Self {
Self::new(r, g, b)
}
}
impl From<(f32, f32, f32)> for Rgb {
fn from((r, g, b): (f32, f32, f32)) -> Self {
Self::from_f32(r, g, b)
}
}
use crate::ANSIColorCode;
use crate::TargetGround;
impl ANSIColorCode for Rgb {
fn ansi_color_code(&self, target: TargetGround) -> String {
format!("{};2;{};{};{}", target.code() + 8, self.r, self.g, self.b)
}
}
overload::overload!(
(lhs: ?Rgb) + (rhs: ?Rgb) -> Rgb {
Rgb::new(
lhs.r.saturating_add(rhs.r),
lhs.g.saturating_add(rhs.g),
lhs.b.saturating_add(rhs.b)
)
}
);
overload::overload!(
(lhs: ?Rgb) - (rhs: ?Rgb) -> Rgb {
Rgb::new(
lhs.r.saturating_sub(rhs.r),
lhs.g.saturating_sub(rhs.g),
lhs.b.saturating_sub(rhs.b)
)
}
);
overload::overload!(
(lhs: ?Rgb) * (rhs: ?f32) -> Rgb {
Rgb::new(
(lhs.r as f32 * rhs.clamp(0.0, 1.0)) as u8,
(lhs.g as f32 * rhs.clamp(0.0, 1.0)) as u8,
(lhs.b as f32 * rhs.clamp(0.0, 1.0)) as u8
)
}
);
overload::overload!(
(lhs: ?f32) * (rhs: ?Rgb) -> Rgb {
Rgb::new(
(rhs.r as f32 * lhs.clamp(0.0, 1.0)) as u8,
(rhs.g as f32 * lhs.clamp(0.0, 1.0)) as u8,
(rhs.b as f32 * lhs.clamp(0.0, 1.0)) as u8
)
}
);
overload::overload!(
-(rgb: ?Rgb) -> Rgb {
Rgb::new(
255 - rgb.r,
255 - rgb.g,
255 - rgb.b)
}
);

View File

@ -364,7 +364,7 @@ pub enum Color {
/// [cc]: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
Fixed(u8),
/// A 24-bit RGB color, as specified by ISO-8613-3.
/// A 24-bit Rgb color, as specified by ISO-8613-3.
Rgb(u8, u8, u8),
}
@ -546,7 +546,7 @@ impl Color {
/// ```
/// use nu_ansi_term::Color;
///
/// let style = Color::RGB(31, 31, 31).on(Color::White);
/// let style = Color::Rgb(31, 31, 31).on(Color::White);
/// println!("{}", style.paint("eyyyy"));
/// ```
pub fn on(self, background: Color) -> Style {
@ -584,13 +584,13 @@ mod serde_json_tests {
let colors = &[
Color::Red,
Color::Blue,
Color::RGB(123, 123, 123),
Color::Rgb(123, 123, 123),
Color::Fixed(255),
];
assert_eq!(
serde_json::to_string(&colors).unwrap(),
String::from("[\"Red\",\"Blue\",{\"RGB\":[123,123,123]},{\"Fixed\":255}]")
String::from("[\"Red\",\"Blue\",{\"Rgb\":[123,123,123]},{\"Fixed\":255}]")
);
}
@ -599,7 +599,7 @@ mod serde_json_tests {
let colors = &[
Color::Red,
Color::Blue,
Color::RGB(123, 123, 123),
Color::Rgb(123, 123, 123),
Color::Fixed(255),
];

View File

@ -146,6 +146,7 @@ pub(crate) use autoview::Autoview;
pub(crate) use cd::Cd;
pub(crate) use ansi::Ansi;
pub(crate) use ansi::AnsiGradient;
pub(crate) use ansi::AnsiStrip;
pub(crate) use append::Command as Append;
pub(crate) use autoenv::Autoenv;

View File

@ -331,7 +331,7 @@ pub fn str_to_ansi(s: &str) -> Option<String> {
// OSC escape (Operating system command)
"osc" | "escape_right" => Some("\x1b]".to_string()),
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
// Ansi Rgb - Needs to be 32;2;r;g;b or 48;2;r;g;b
// assuming the rgb will be passed via command and no here
"rgb_fg" => Some("\x1b[38;2;".to_string()),
"rgb_bg" => Some("\x1b[48;2;".to_string()),

View File

@ -0,0 +1,326 @@
use crate::prelude::*;
use nu_ansi_term::{build_all_gradient_text, gradient::TargetGround, Gradient, Rgb};
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{Primitive, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tag;
pub struct SubCommand;
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"ansi gradient"
}
fn signature(&self) -> Signature {
Signature::build("ansi gradient")
.named(
"fgstart",
SyntaxShape::String,
"foreground gradient start color in hex (0x123456)",
Some('a'),
)
.named(
"fgend",
SyntaxShape::String,
"foreground gradient end color in hex",
Some('b'),
)
.named(
"bgstart",
SyntaxShape::String,
"background gradient start color in hex",
Some('c'),
)
.named(
"bgend",
SyntaxShape::String,
"background gradient end color in hex",
Some('d'),
)
.rest(
SyntaxShape::ColumnPath,
"optionally, draw gradients using text from column paths",
)
}
fn usage(&self) -> &str {
"draw text with a provided start and end code making a gradient"
}
fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
operate(args)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "draw text in a gradient with foreground start and end colors",
example:
"echo 'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart 0x40c9ff --fgend 0xe81cff",
result: None,
},
Example {
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
example:
"echo 'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart 0x40c9ff --fgend 0xe81cff --bgstart 0xe81cff --bgend 0x40c9ff",
result: None,
},
Example {
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
example:
"echo 'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart 0x40c9ff",
result: None,
},
Example {
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
example:
"echo 'Hello, Nushell! This is a gradient.' | ansi gradient --fgend 0xe81cff",
result: None,
},
]
}
}
fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once()?;
let fgstart: Option<Value> = args.get_flag("fgstart")?;
let fgend: Option<Value> = args.get_flag("fgend")?;
let bgstart: Option<Value> = args.get_flag("bgstart")?;
let bgend: Option<Value> = args.get_flag("bgend")?;
let column_paths: Vec<_> = args.rest(0)?;
let fgs_hex = fgstart.map(|color| Rgb::from_hex_string(color.convert_to_string()));
let fge_hex = fgend.map(|color| Rgb::from_hex_string(color.convert_to_string()));
let bgs_hex = bgstart.map(|color| Rgb::from_hex_string(color.convert_to_string()));
let bge_hex = bgend.map(|color| Rgb::from_hex_string(color.convert_to_string()));
let result: Vec<Value> = args
.input
.map(move |v| {
if column_paths.is_empty() {
action(&v, v.tag(), fgs_hex, fge_hex, bgs_hex, bge_hex)
} else {
let mut ret = v;
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| {
action(old, old.tag(), fgs_hex, fge_hex, bgs_hex, bge_hex)
}),
)?;
}
Ok(ret)
}
})
.collect::<Result<Vec<Value>, _>>()?;
Ok(OutputStream::from_stream(result.into_iter()))
}
fn action(
input: &Value,
tag: impl Into<Tag>,
fg_start: Option<Rgb>,
fg_end: Option<Rgb>,
bg_start: Option<Rgb>,
bg_end: Option<Rgb>,
) -> Result<Value, ShellError> {
let tag = tag.into();
match &input.value {
UntaggedValue::Primitive(Primitive::String(astring)) => {
match (fg_start, fg_end, bg_start, bg_end) {
(None, None, None, None) => {
// Error - no colors
Err(ShellError::labeled_error(
"please supply color parameters",
"please supply foreground and/or background color parameters",
Tag::unknown(),
))
}
(None, None, None, Some(bg_end)) => {
// Error - missing bg_start, so assume black
let bg_start = Rgb::new(0, 0, 0);
let gradient = Gradient::new(bg_start, bg_end);
let gradient_string = gradient.build(astring, TargetGround::Background);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(None, None, Some(bg_start), None) => {
// Error - missing bg_end, so assume black
let bg_end = Rgb::new(0, 0, 0);
let gradient = Gradient::new(bg_start, bg_end);
let gradient_string = gradient.build(astring, TargetGround::Background);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(None, None, Some(bg_start), Some(bg_end)) => {
// Background Only
let gradient = Gradient::new(bg_start, bg_end);
let gradient_string = gradient.build(astring, TargetGround::Background);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(None, Some(fg_end), None, None) => {
// Error - missing fg_start, so assume black
let fg_start = Rgb::new(0, 0, 0);
let gradient = Gradient::new(fg_start, fg_end);
let gradient_string = gradient.build(astring, TargetGround::Foreground);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(None, Some(fg_end), None, Some(bg_end)) => {
// missin fg_start and bg_start, so assume black
let fg_start = Rgb::new(0, 0, 0);
let bg_start = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(None, Some(fg_end), Some(bg_start), None) => {
// Error - missing fg_start and bg_end
let fg_start = Rgb::new(0, 0, 0);
let bg_end = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(None, Some(fg_end), Some(bg_start), Some(bg_end)) => {
// Error - missing fg_start, so assume black
let fg_start = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), None, None, None) => {
// Error - missing fg_end, so assume black
let fg_end = Rgb::new(0, 0, 0);
let gradient = Gradient::new(fg_start, fg_end);
let gradient_string = gradient.build(astring, TargetGround::Foreground);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), None, None, Some(bg_end)) => {
// Error - missing fg_end, bg_start, so assume black
let fg_end = Rgb::new(0, 0, 0);
let bg_start = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), None, Some(bg_start), None) => {
// Error - missing fg_end, bg_end, so assume black
let fg_end = Rgb::new(0, 0, 0);
let bg_end = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), None, Some(bg_start), Some(bg_end)) => {
// Error - missing fg_end, so assume black
let fg_end = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), Some(fg_end), None, None) => {
// Foreground Only
let gradient = Gradient::new(fg_start, fg_end);
let gradient_string = gradient.build(astring, TargetGround::Foreground);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), Some(fg_end), None, Some(bg_end)) => {
// Error - missing bg_start, so assume black
let bg_start = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), Some(fg_end), Some(bg_start), None) => {
// Error - missing bg_end, so assume black
let bg_end = Rgb::new(0, 0, 0);
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
(Some(fg_start), Some(fg_end), Some(bg_start), Some(bg_end)) => {
// Foreground and Background Gradient
let fg_gradient = Gradient::new(fg_start, fg_end);
let bg_gradient = Gradient::new(bg_start, bg_end);
let gradient_string =
build_all_gradient_text(astring, fg_gradient, bg_gradient);
Ok(UntaggedValue::string(gradient_string).into_value(tag))
}
}
}
other => {
let got = format!("got {}", other.type_name());
Err(ShellError::labeled_error(
"value is not string",
got,
tag.span,
))
}
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, SubCommand};
use nu_ansi_term::Rgb;
use nu_protocol::UntaggedValue;
use nu_source::Tag;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(SubCommand {})
}
// #[test]
// fn test_stripping() {
// let input_string =
// UntaggedValue::string("\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld")
// .into_untagged_value();
// let expected = UntaggedValue::string("Hello Nu World").into_untagged_value();
// let actual = action(&input_string, Tag::unknown()).unwrap();
// assert_eq!(actual, expected);
// }
#[test]
fn test_fg_gradient() {
let input_string = UntaggedValue::string("Hello, World!").into_untagged_value();
let expected = UntaggedValue::string("\u{1b}[38;2;64;201;255mH\u{1b}[38;2;76;187;254me\u{1b}[38;2;89;174;254ml\u{1b}[38;2;102;160;254ml\u{1b}[38;2;115;147;254mo\u{1b}[38;2;128;133;254m,\u{1b}[38;2;141;120;254m \u{1b}[38;2;153;107;254mW\u{1b}[38;2;166;94;254mo\u{1b}[38;2;179;80;254mr\u{1b}[38;2;192;67;254ml\u{1b}[38;2;205;53;254md\u{1b}[38;2;218;40;254m!\u{1b}[0m").into_untagged_value();
let fg_start = Rgb::from_hex_string("0x40c9ff".to_string());
let fg_end = Rgb::from_hex_string("0xe81cff".to_string());
let actual = action(
&input_string,
Tag::unknown(),
Some(fg_start),
Some(fg_end),
None,
None,
)
.unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -1,5 +1,7 @@
mod command;
mod gradient;
mod strip;
pub use command::Command as Ansi;
pub use gradient::SubCommand as AnsiGradient;
pub use strip::SubCommand as AnsiStrip;

View File

@ -111,6 +111,7 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(BuildString),
whole_stream_command(Ansi),
whole_stream_command(AnsiStrip),
whole_stream_command(AnsiGradient),
whole_stream_command(Char),
// Column manipulation
whole_stream_command(DropColumn),