From 659da3c4a43dfba7854e4ff7b5b8f7f54e4d9400 Mon Sep 17 00:00:00 2001 From: Stefan Holderbach Date: Wed, 9 Feb 2022 01:25:31 +0100 Subject: [PATCH] Make ANSI stripping lazy in more places (#4380) Same rationale as in #4378 Also accelerate `grid` before: ``` Command being timed: "./eager_nu -c for i in 0..100000 { echo whatever } | grid" User time (seconds): 0.21 System time (seconds): 0.05 Percent of CPU this job got: 36% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.71 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 48112 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 10580 Voluntary context switches: 266 Involuntary context switches: 2595 Swaps: 0 File system inputs: 0 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 ``` after: ``` Command being timed: "./lazy_nu -c for i in 0..100000 { echo whatever } | grid" User time (seconds): 0.14 System time (seconds): 0.05 Percent of CPU this job got: 33% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.60 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 48272 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 1 Minor (reclaiming a frame) page faults: 10582 Voluntary context switches: 286 Involuntary context switches: 831 Swaps: 0 File system inputs: 56 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 ``` --- crates/nu-command/src/viewers/griddle.rs | 26 +++++++++++++++----- crates/nu-term-grid/src/grid.rs | 31 +++++++++++++++++------- 2 files changed, 42 insertions(+), 15 deletions(-) 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