add ability to specify an ansi style (#595)

* add ability to specify an ansi style

* remove comments

* remove more debug code

* some cleanup and refactoring
This commit is contained in:
Darren Schroeder 2021-12-27 08:59:55 -06:00 committed by GitHub
parent 1dbf351425
commit 1837acfc70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 37 deletions

View File

@ -2,13 +2,13 @@ use nu_ansi_term::{Color, Style};
use serde::Deserialize;
#[derive(Deserialize, PartialEq, Debug)]
struct NuStyle {
fg: Option<String>,
bg: Option<String>,
attr: Option<String>,
pub struct NuStyle {
pub fg: Option<String>,
pub bg: Option<String>,
pub attr: Option<String>,
}
fn parse_nustyle(nu_style: NuStyle) -> Style {
pub fn parse_nustyle(nu_style: NuStyle) -> Style {
// get the nu_ansi_term::Color foreground color
let fg_color = match nu_style.fg {
Some(fg) => color_from_hex(&fg).expect("error with foreground color"),

View File

@ -193,7 +193,7 @@ impl Command for AnsiCommand {
Signature::build("ansi")
.optional(
"code",
SyntaxShape::String,
SyntaxShape::Any,
"the name of the code to use like 'green' or 'reset' to reset the color",
)
.switch(
@ -222,17 +222,17 @@ Example: 1;31m for bold red or 2;37;41m for dimmed white fg with red bg
There can be multiple text formatting sequence numbers
separated by a ; and ending with an m where the # is of the
following values:
attributes
0 reset / normal display
1 bold or increased intensity
2 faint or decreased intensity
3 italic on (non-mono font)
4 underline on
5 slow blink on
6 fast blink on
7 reverse video on
8 nondisplayed (invisible) on
9 strike-through on
attribute_number, abbreviation, description
0 reset / normal display
1 b bold or increased intensity
2 d faint or decreased intensity
3 i italic on (non-mono font)
4 u underline on
5 l slow blink on
6 fast blink on
7 r reverse video on
8 h nondisplayed (invisible) on
9 s strike-through on
foreground/bright colors background/bright colors
30/90 black 40/100 black
@ -273,17 +273,23 @@ Format: #
Example {
description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [(ansi rb) Hello " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
example: r#"echo [(ansi rb) Hello " " (ansi gb) Nu " " (ansi pb) World (ansi reset)] | str collect"#,
result: Some(Value::test_string(
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld\u{1b}[0m",
)),
},
Example {
description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
description: "Use ansi to color text (italic bright yellow on red 'Hello' with green bold 'Nu' and purble bold 'World')",
example: r#"echo [(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World (ansi reset)] | str collect"#,
result: Some(Value::test_string(
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld",
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld\u{1b}[0m",
)),
},
Example {
description: "Use ansi to color text with a style (blue on red in bold)",
example: r#"$"(ansi -e { fg: '#0000ff' bg: '#ff0000' attr: b })Hello Nu World(ansi reset)""#,
result: Some(Value::test_string(
"\u{1b}[1;48;2;255;0;0;38;2;0;0;255mHello Nu World\u{1b}[0m",
)),
},
]
@ -299,15 +305,21 @@ Format: #
let list: bool = call.has_flag("list");
let escape: bool = call.has_flag("escape");
let osc: bool = call.has_flag("osc");
if list {
return generate_ansi_code_list(engine_state, call.head);
}
let code: String = match call.opt::<String>(engine_state, stack, 0)? {
Some(x) => x,
None => {
return Err(ShellError::MissingParameter("code".into(), call.head));
}
// The code can now be one of the ansi abbreviations like green_bold
// or it can be a record like this: { fg: "#ff0000" bg: "#00ff00" attr: bli }
// this record is defined in nu-color-config crate
let code: Value = match call.opt(engine_state, stack, 0)? {
Some(c) => c,
None => return Err(ShellError::MissingParameter("code".into(), call.head)),
};
let param_is_string = matches!(code, Value::String { val: _, span: _ });
if escape && osc {
return Err(ShellError::IncompatibleParameters {
left_message: "escape".into(),
@ -322,8 +334,17 @@ Format: #
.span,
});
}
if escape || osc {
let code_vec: Vec<char> = code.chars().collect();
let code_string = if param_is_string {
code.as_string().expect("error getting code as string")
} else {
"".to_string()
};
let param_is_valid_string = param_is_string && !code_string.is_empty();
if (escape || osc) && (param_is_valid_string) {
let code_vec: Vec<char> = code_string.chars().collect();
if code_vec[0] == '\\' {
return Err(ShellError::UnsupportedInput(
String::from("no need for escape characters"),
@ -333,14 +354,15 @@ Format: #
));
}
}
let output = if escape {
format!("\x1b[{}", code)
} else if osc {
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
let output = if escape && param_is_valid_string {
format!("\x1b[{}", code_string)
} else if osc && param_is_valid_string {
// Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
// OCS's need to end with a bell '\x07' char
format!("\x1b]{};", code)
} else {
match str_to_ansi(&code) {
format!("\x1b]{};", code_string)
} else if param_is_valid_string {
match str_to_ansi(&code_string) {
Some(c) => c,
None => {
return Err(ShellError::UnsupportedInput(
@ -349,7 +371,36 @@ Format: #
))
}
}
} else {
// This is a record that should look like
// { fg: "#ff0000" bg: "#00ff00" attr: bli }
let record = code.as_record()?;
// create a NuStyle to parse the information into
let mut nu_style = nu_color_config::NuStyle {
fg: None,
bg: None,
attr: None,
};
// Iterate and populate NuStyle with real values
for (k, v) in record.0.iter().zip(record.1) {
match k.as_str() {
"fg" => nu_style.fg = Some(v.as_string()?),
"bg" => nu_style.bg = Some(v.as_string()?),
"attr" => nu_style.attr = Some(v.as_string()?),
_ => {
return Err(ShellError::IncompatibleParametersSingle(
format!("problem with key: {}", k.to_string()),
code.span().expect("error with span"),
))
}
}
}
// Now create a nu_ansi_term::Style from the NuStyle
let style = nu_color_config::parse_nustyle(nu_style);
// Return the prefix string. The prefix is the Ansi String. The suffix would be 0m, reset/stop coloring.
style.prefix().to_string()
};
Ok(Value::string(output, call.head).into_pipeline_data())
}
}