forked from extern/nushell
added ansi gradient in the spirit of fun (#3570)
This commit is contained in:
parent
aa1cd7eba6
commit
94fc8a1334
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -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"
|
||||
|
@ -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"
|
||||
|
37
crates/nu-ansi-term/examples/gradient_colors.rs
Normal file
37
crates/nu-ansi-term/examples/gradient_colors.rs
Normal 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)
|
||||
);
|
||||
}
|
105
crates/nu-ansi-term/src/gradient.rs
Normal file
105
crates/nu-ansi-term/src/gradient.rs
Normal 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;
|
||||
}
|
@ -121,13 +121,13 @@
|
||||
//! `Fixed` colors instead, but there’s 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::*;
|
||||
|
173
crates/nu-ansi-term/src/rgb.rs
Normal file
173
crates/nu-ansi-term/src/rgb.rs
Normal 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)
|
||||
}
|
||||
);
|
@ -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),
|
||||
];
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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()),
|
||||
|
326
crates/nu-command/src/commands/ansi/gradient.rs
Normal file
326
crates/nu-command/src/commands/ansi/gradient.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user