diff --git a/crates/nu-cli/src/commands/ansi.rs b/crates/nu-cli/src/commands/ansi.rs index e24d14c31f..ea1209f790 100644 --- a/crates/nu-cli/src/commands/ansi.rs +++ b/crates/nu-cli/src/commands/ansi.rs @@ -3,12 +3,15 @@ use crate::prelude::*; use ansi_term::Color; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; +use nu_source::Tagged; pub struct Ansi; #[derive(Deserialize)] struct AnsiArgs { color: Value, + escape: Option>, + osc: Option>, } #[async_trait] @@ -18,15 +21,70 @@ impl WholeStreamCommand for Ansi { } fn signature(&self) -> Signature { - Signature::build("ansi").required( - "color", - SyntaxShape::Any, - "the name of the color to use or 'reset' to reset the color", - ) + Signature::build("ansi") + .optional( + "color", + SyntaxShape::Any, + "the name of the color to use or 'reset' to reset the color", + ) + .named( + "escape", // \x1b + SyntaxShape::Any, + "escape sequence without the escape character(s)", + Some('e'), + ) + .named( + "osc", + SyntaxShape::Any, + "operating system command (ocs) escape sequence without the escape character(s)", + Some('o'), + ) } fn usage(&self) -> &str { - "Output ANSI codes to change color" + r#"Output ANSI codes to change color + +For escape sequences: +Escape: '\x1b[' is not required for --escape parameter +Format: #(;#)m +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 + + foreground/bright colors background/bright colors + 30/90 black 40/100 black + 31/91 red 41/101 red + 32/92 green 42/102 green + 33/93 yellow 43/103 yellow + 34/94 blue 44/104 blue + 35/95 magenta 45/105 magenta + 36/96 cyan 46/106 cyan + 37/97 white 47/107 white + https://en.wikipedia.org/wiki/ANSI_escape_code + +OSC: '\x1b]' is not required for --osc parameter +Example: echo [$(ansi -o '0') 'some title' $(char bel)] | str collect +Format: # + 0 Set window title and icon name + 1 Set icon name + 2 Set window title + 4 Set/read color palette + 9 iTerm2 Grown notifications + 10 Set foreground color (x11 color spec) + 11 Set background color (x11 color spec) + ... others"# } fn examples(&self) -> Vec { @@ -49,6 +107,14 @@ impl WholeStreamCommand for Ansi { "\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld", )]), }, + 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"#, + result: Some(vec![Value::from( + "\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld", + )]), + }, ] } @@ -57,10 +123,41 @@ impl WholeStreamCommand for Ansi { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - let (AnsiArgs { color }, _) = args.process(®istry).await?; + let (AnsiArgs { color, escape, osc }, _) = args.process(®istry).await?; + + if let Some(e) = escape { + let esc_vec: Vec = e.item.chars().collect(); + if esc_vec[0] == '\\' { + return Err(ShellError::labeled_error( + "no need for escape characters", + "no need for escape characters", + e.tag(), + )); + } + let output = format!("\x1b[{}", e.item); + return Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::string(output).into_value(e.tag()), + ))); + } + + if let Some(o) = osc { + let osc_vec: Vec = o.item.chars().collect(); + if osc_vec[0] == '\\' { + return Err(ShellError::labeled_error( + "no need for escape characters", + "no need for escape characters", + o.tag(), + )); + } + //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 + let output = format!("\x1b]{};", o.item); + return Ok(OutputStream::one(ReturnSuccess::value( + UntaggedValue::string(output).into_value(o.tag()), + ))); + } let color_string = color.as_string()?; - let ansi_code = str_to_ansi_color(color_string); if let Some(output) = ansi_code { @@ -74,6 +171,7 @@ impl WholeStreamCommand for Ansi { color.tag(), )) } + // } } }