mirror of
https://github.com/nushell/nushell.git
synced 2025-05-31 07:08:22 +02:00
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:
parent
1dbf351425
commit
1837acfc70
@ -2,13 +2,13 @@ use nu_ansi_term::{Color, Style};
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize, PartialEq, Debug)]
|
#[derive(Deserialize, PartialEq, Debug)]
|
||||||
struct NuStyle {
|
pub struct NuStyle {
|
||||||
fg: Option<String>,
|
pub fg: Option<String>,
|
||||||
bg: Option<String>,
|
pub bg: Option<String>,
|
||||||
attr: 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
|
// get the nu_ansi_term::Color foreground color
|
||||||
let fg_color = match nu_style.fg {
|
let fg_color = match nu_style.fg {
|
||||||
Some(fg) => color_from_hex(&fg).expect("error with foreground color"),
|
Some(fg) => color_from_hex(&fg).expect("error with foreground color"),
|
||||||
|
@ -193,7 +193,7 @@ impl Command for AnsiCommand {
|
|||||||
Signature::build("ansi")
|
Signature::build("ansi")
|
||||||
.optional(
|
.optional(
|
||||||
"code",
|
"code",
|
||||||
SyntaxShape::String,
|
SyntaxShape::Any,
|
||||||
"the name of the code to use like 'green' or 'reset' to reset the color",
|
"the name of the code to use like 'green' or 'reset' to reset the color",
|
||||||
)
|
)
|
||||||
.switch(
|
.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
|
There can be multiple text formatting sequence numbers
|
||||||
separated by a ; and ending with an m where the # is of the
|
separated by a ; and ending with an m where the # is of the
|
||||||
following values:
|
following values:
|
||||||
attributes
|
attribute_number, abbreviation, description
|
||||||
0 reset / normal display
|
0 reset / normal display
|
||||||
1 bold or increased intensity
|
1 b bold or increased intensity
|
||||||
2 faint or decreased intensity
|
2 d faint or decreased intensity
|
||||||
3 italic on (non-mono font)
|
3 i italic on (non-mono font)
|
||||||
4 underline on
|
4 u underline on
|
||||||
5 slow blink on
|
5 l slow blink on
|
||||||
6 fast blink on
|
6 fast blink on
|
||||||
7 reverse video on
|
7 r reverse video on
|
||||||
8 nondisplayed (invisible) on
|
8 h nondisplayed (invisible) on
|
||||||
9 strike-through on
|
9 s strike-through on
|
||||||
|
|
||||||
foreground/bright colors background/bright colors
|
foreground/bright colors background/bright colors
|
||||||
30/90 black 40/100 black
|
30/90 black 40/100 black
|
||||||
@ -273,17 +273,23 @@ Format: #
|
|||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
"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(
|
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 {
|
Example {
|
||||||
description:
|
description: "Use ansi to color text (italic bright yellow on red 'Hello' with green bold 'Nu' and purble bold 'World')",
|
||||||
"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 (ansi reset)] | str collect"#,
|
||||||
example: r#"echo [(ansi -e '3;93;41m') Hello (ansi reset) " " (ansi gb) Nu " " (ansi pb) World] | str collect"#,
|
|
||||||
result: Some(Value::test_string(
|
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 list: bool = call.has_flag("list");
|
||||||
let escape: bool = call.has_flag("escape");
|
let escape: bool = call.has_flag("escape");
|
||||||
let osc: bool = call.has_flag("osc");
|
let osc: bool = call.has_flag("osc");
|
||||||
|
|
||||||
if list {
|
if list {
|
||||||
return generate_ansi_code_list(engine_state, call.head);
|
return generate_ansi_code_list(engine_state, call.head);
|
||||||
}
|
}
|
||||||
let code: String = match call.opt::<String>(engine_state, stack, 0)? {
|
|
||||||
Some(x) => x,
|
// The code can now be one of the ansi abbreviations like green_bold
|
||||||
None => {
|
// or it can be a record like this: { fg: "#ff0000" bg: "#00ff00" attr: bli }
|
||||||
return Err(ShellError::MissingParameter("code".into(), call.head));
|
// 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 {
|
if escape && osc {
|
||||||
return Err(ShellError::IncompatibleParameters {
|
return Err(ShellError::IncompatibleParameters {
|
||||||
left_message: "escape".into(),
|
left_message: "escape".into(),
|
||||||
@ -322,8 +334,17 @@ Format: #
|
|||||||
.span,
|
.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] == '\\' {
|
if code_vec[0] == '\\' {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
String::from("no need for escape characters"),
|
String::from("no need for escape characters"),
|
||||||
@ -333,14 +354,15 @@ Format: #
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let output = if escape {
|
|
||||||
format!("\x1b[{}", code)
|
let output = if escape && param_is_valid_string {
|
||||||
} else if osc {
|
format!("\x1b[{}", code_string)
|
||||||
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
|
} 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
|
// OCS's need to end with a bell '\x07' char
|
||||||
format!("\x1b]{};", code)
|
format!("\x1b]{};", code_string)
|
||||||
} else {
|
} else if param_is_valid_string {
|
||||||
match str_to_ansi(&code) {
|
match str_to_ansi(&code_string) {
|
||||||
Some(c) => c,
|
Some(c) => c,
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::UnsupportedInput(
|
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())
|
Ok(Value::string(output, call.head).into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user