diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index 947edeba5a..4d3e3a7e9b 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -1,3 +1,5 @@ +use std::borrow::Cow; + // use super::icons::{icon_for_file, iconify_style_ansi_to_nu}; use super::icons::icon_for_file; use lscolors::{LsColors, Style}; @@ -131,12 +133,24 @@ prints out the list properly."# } } -fn strip_ansi(astring: &str) -> String { - if let Ok(bytes) = strip_ansi_escapes::strip(astring) { - String::from_utf8_lossy(&bytes).to_string() - } else { - astring.to_string() +/// Removes ANSI escape codes and some ASCII control characters +/// +/// Keeps `\n` removes `\r`, `\t` etc. +/// +/// If parsing fails silently returns the input string +fn strip_ansi(string: &str) -> Cow { + // Check if any ascii control character except LF(0x0A = 10) is present, + // which will be stripped. Includes the primary start of ANSI sequences ESC + // (0x1B = decimal 27) + if string.bytes().any(|x| matches!(x, 0..=9 | 11..=31)) { + if let Ok(stripped) = strip_ansi_escapes::strip(string) { + if let Ok(new_string) = String::from_utf8(stripped) { + return Cow::Owned(new_string); + } + } } + // Else case includes failures to parse! + Cow::Borrowed(string) } fn create_grid_output( @@ -177,7 +191,7 @@ fn create_grid_output( if color_param { if use_grid_icons { let no_ansi = strip_ansi(&value); - let path = std::path::Path::new(&no_ansi); + let path = std::path::Path::new(no_ansi.as_ref()); let icon = icon_for_file(path, call.head)?; let ls_colors_style = ls_colors.style_for_path(path); // eprintln!("ls_colors_style: {:?}", &ls_colors_style); diff --git a/crates/nu-term-grid/src/grid.rs b/crates/nu-term-grid/src/grid.rs index 11c50c3b85..2847d3a244 100644 --- a/crates/nu-term-grid/src/grid.rs +++ b/crates/nu-term-grid/src/grid.rs @@ -91,22 +91,35 @@ //! [`fit_into_width`]: ./struct.Grid.html#method.fit_into_width //! [`GridOptions`]: ./struct.GridOptions.html +use std::borrow::Cow; use std::cmp::max; use std::fmt; use std::iter::repeat; -use strip_ansi_escapes::strip; +use strip_ansi_escapes; use unicode_width::UnicodeWidthStr; -fn unicode_width_strip_ansi(astring: &str) -> usize { - let stripped_string: String = { - if let Ok(bytes) = strip(astring) { - String::from_utf8_lossy(&bytes).to_string() - } else { - astring.to_string() +/// Removes ANSI escape codes and some ASCII control characters +/// +/// Keeps `\n` removes `\r`, `\t` etc. +/// +/// If parsing fails silently returns the input string +fn strip_ansi(string: &str) -> Cow { + // Check if any ascii control character except LF(0x0A = 10) is present, + // which will be stripped. Includes the primary start of ANSI sequences ESC + // (0x1B = decimal 27) + if string.bytes().any(|x| matches!(x, 0..=9 | 11..=31)) { + if let Ok(stripped) = strip_ansi_escapes::strip(string) { + if let Ok(new_string) = String::from_utf8(stripped) { + return Cow::Owned(new_string); + } } - }; + } + // Else case includes failures to parse! + Cow::Borrowed(string) +} - UnicodeWidthStr::width(&stripped_string[..]) +fn unicode_width_strip_ansi(astring: &str) -> usize { + strip_ansi(astring).width() } /// Alignment indicate on which side the content should stick if some filling