diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index 0f576ba59f..a2385b5782 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -1,13 +1,14 @@ use crate::help::highlight_search_string; + use fancy_regex::Regex; -use lscolors::{Color as LsColors_Color, Style as LsColors_Style}; +use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style}; use nu_ansi_term::{Color, Color::Default, Style}; use nu_color_config::get_color_config; use nu_engine::{env_to_string, eval_block, CallExt}; use nu_protocol::{ ast::Call, engine::{CaptureBlock, Command, EngineState, Stack}, - Category, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, ShellError, + Category, Config, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, }; use nu_utils::get_ls_colors; @@ -278,6 +279,72 @@ fn find_with_predicate( ) } +fn highlight_terms_in_record( + cols: &mut [String], + vals: &mut Vec, + span: &mut Span, + config: &Config, + terms: &[Value], + string_style: Style, + ls_colors: &LsColors, +) -> Value { + let mut output = vec![]; + for val in vals { + let val_str = val.into_string("", config); + let lower_val = val.into_string("", config).to_lowercase(); + let mut term_added_to_output = false; + for term in terms { + let term_str = term.into_string("", config); + let lower_term = term.into_string("", config).to_lowercase(); + if lower_val.contains(&lower_term) { + if config.use_ls_colors { + // Get the original LS_COLORS color + let style = ls_colors.style_for_path(val_str.clone()); + let ansi_style = style + .map(LsColors_Style::to_crossterm_style) + .unwrap_or_default(); + + let ls_colored_val = ansi_style.apply(&val_str).to_string(); + + let ansi_term_style = style + .map(to_nu_ansi_term_style) + .unwrap_or_else(|| string_style); + + let hi = + match highlight_search_string(&ls_colored_val, &term_str, &ansi_term_style) + { + Ok(hi) => hi, + Err(_) => string_style.paint(term_str.to_string()).to_string(), + }; + output.push(Value::String { + val: hi, + span: *span, + }); + term_added_to_output = true; + } else { + // No LS_COLORS support, so just use the original value + let hi = match highlight_search_string(&val_str, &term_str, &string_style) { + Ok(hi) => hi, + Err(_) => string_style.paint(term_str.to_string()).to_string(), + }; + output.push(Value::String { + val: hi, + span: *span, + }); + } + } + } + if !term_added_to_output { + output.push(val.clone()); + } + } + Value::Record { + cols: cols.to_vec(), + vals: output, + span: *span, + } +} + fn find_with_rest_and_highlight( engine_state: &EngineState, stack: &mut Stack, @@ -315,177 +382,129 @@ fn find_with_rest_and_highlight( let ls_colors = get_ls_colors(ls_colors_env_str); match input { - PipelineData::Value(_, _) => input.filter( - move |value| { - let lower_value = if let Ok(span) = value.span() { - Value::string(value.into_string("", &config).to_lowercase(), span) - } else { - value.clone() - }; - - lower_terms.iter().any(|term| match value { - Value::Bool { .. } - | Value::Int { .. } - | Value::Filesize { .. } - | Value::Duration { .. } - | Value::Date { .. } - | Value::Range { .. } - | Value::Float { .. } - | Value::Block { .. } - | Value::Nothing { .. } - | Value::Error { .. } => lower_value - .eq(span, term, span) - .map_or(false, |val| val.is_true()), - Value::String { .. } - | Value::List { .. } - | Value::CellPath { .. } - | Value::CustomValue { .. } => term - .r#in(span, &lower_value, span) - .map_or(false, |val| val.is_true()), - Value::Record { vals, .. } => vals.iter().any(|val| { - if let Ok(span) = val.span() { - let lower_val = Value::string( - val.into_string("", &config).to_lowercase(), - Span::test_data(), - ); - - term.r#in(span, &lower_val, span) - .map_or(false, |aval| aval.is_true()) - } else { - term.r#in(span, val, span) - .map_or(false, |aval| aval.is_true()) - } - }), - Value::Binary { .. } => false, - }) != invert - }, - ctrlc, - ), - PipelineData::ListStream(stream, meta) => { - Ok(ListStream::from_stream( - stream - .map(move |mut x| match &mut x { - Value::Record { cols, vals, span } => { - let mut output = vec![]; - for val in vals { - let val_str = val.into_string("", &config); - let lower_val = val.into_string("", &config).to_lowercase(); - let mut term_added_to_output = false; - for term in terms.clone() { - let term_str = term.into_string("", &config); - let lower_term = term.into_string("", &config).to_lowercase(); - if lower_val.contains(&lower_term) { - if config.use_ls_colors { - // Get the original LS_COLORS color - let style = ls_colors.style_for_path(val_str.clone()); - let ansi_style = style - .map(LsColors_Style::to_crossterm_style) - .unwrap_or_default(); - - let ls_colored_val = - ansi_style.apply(&val_str).to_string(); - - let ansi_term_style = style - .map(to_nu_ansi_term_style) - .unwrap_or_else(|| string_style); - - let hi = match highlight_search_string( - &ls_colored_val, - &term_str, - &ansi_term_style, - ) { - Ok(hi) => hi, - Err(_) => string_style - .paint(term_str.to_string()) - .to_string(), - }; - output.push(Value::String { - val: hi, - span: *span, - }); - term_added_to_output = true; - } else { - // No LS_COLORS support, so just use the original value - let hi = match highlight_search_string( - &val_str, - &term_str, - &string_style, - ) { - Ok(hi) => hi, - Err(_) => string_style - .paint(term_str.to_string()) - .to_string(), - }; - output.push(Value::String { - val: hi, - span: *span, - }); - } - } - } - if !term_added_to_output { - output.push(val.clone()); - } - } - Value::Record { - cols: cols.to_vec(), - vals: output, - span: *span, - } - } - _ => x, - }) - .filter(move |value| { - let lower_value = if let Ok(span) = value.span() { - Value::string( - value.into_string("", &filter_config).to_lowercase(), - span, - ) - } else { - value.clone() - }; - - lower_terms.iter().any(|term| match value { - Value::Bool { .. } - | Value::Int { .. } - | Value::Filesize { .. } - | Value::Duration { .. } - | Value::Date { .. } - | Value::Range { .. } - | Value::Float { .. } - | Value::Block { .. } - | Value::Nothing { .. } - | Value::Error { .. } => lower_value - .eq(span, term, span) - .map_or(false, |value| value.is_true()), - Value::String { .. } - | Value::List { .. } - | Value::CellPath { .. } - | Value::CustomValue { .. } => term - .r#in(span, &lower_value, span) - .map_or(false, |value| value.is_true()), - Value::Record { vals, .. } => vals.iter().any(|val| { - if let Ok(span) = val.span() { - let lower_val = Value::string( - val.into_string("", &filter_config).to_lowercase(), - Span::test_data(), - ); - - term.r#in(span, &lower_val, span) - .map_or(false, |value| value.is_true()) - } else { - term.r#in(span, val, span) - .map_or(false, |value| value.is_true()) - } - }), - Value::Binary { .. } => false, - }) != invert - }), + PipelineData::Value(_, _) => input + .map( + move |mut x| match &mut x { + Value::Record { cols, vals, span } => highlight_terms_in_record( + cols, + vals, + span, + &config, + &terms, + string_style, + &ls_colors, + ), + _ => x, + }, ctrlc.clone(), - ) - .into_pipeline_data(ctrlc) - .set_metadata(meta)) - } + )? + .filter( + move |value| { + let lower_value = if let Ok(span) = value.span() { + Value::string(value.into_string("", &filter_config).to_lowercase(), span) + } else { + value.clone() + }; + + lower_terms.iter().any(|term| match value { + Value::Bool { .. } + | Value::Int { .. } + | Value::Filesize { .. } + | Value::Duration { .. } + | Value::Date { .. } + | Value::Range { .. } + | Value::Float { .. } + | Value::Block { .. } + | Value::Nothing { .. } + | Value::Error { .. } => lower_value + .eq(span, term, span) + .map_or(false, |val| val.is_true()), + Value::String { .. } + | Value::List { .. } + | Value::CellPath { .. } + | Value::CustomValue { .. } => term + .r#in(span, &lower_value, span) + .map_or(false, |val| val.is_true()), + Value::Record { vals, .. } => vals.iter().any(|val| { + if let Ok(span) = val.span() { + let lower_val = Value::string( + val.into_string("", &filter_config).to_lowercase(), + Span::test_data(), + ); + + term.r#in(span, &lower_val, span) + .map_or(false, |aval| aval.is_true()) + } else { + term.r#in(span, val, span) + .map_or(false, |aval| aval.is_true()) + } + }), + Value::Binary { .. } => false, + }) != invert + }, + ctrlc, + ), + PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream( + stream + .map(move |mut x| match &mut x { + Value::Record { cols, vals, span } => highlight_terms_in_record( + cols, + vals, + span, + &config, + &terms, + string_style, + &ls_colors, + ), + _ => x, + }) + .filter(move |value| { + let lower_value = if let Ok(span) = value.span() { + Value::string(value.into_string("", &filter_config).to_lowercase(), span) + } else { + value.clone() + }; + + lower_terms.iter().any(|term| match value { + Value::Bool { .. } + | Value::Int { .. } + | Value::Filesize { .. } + | Value::Duration { .. } + | Value::Date { .. } + | Value::Range { .. } + | Value::Float { .. } + | Value::Block { .. } + | Value::Nothing { .. } + | Value::Error { .. } => lower_value + .eq(span, term, span) + .map_or(false, |value| value.is_true()), + Value::String { .. } + | Value::List { .. } + | Value::CellPath { .. } + | Value::CustomValue { .. } => term + .r#in(span, &lower_value, span) + .map_or(false, |value| value.is_true()), + Value::Record { vals, .. } => vals.iter().any(|val| { + if let Ok(span) = val.span() { + let lower_val = Value::string( + val.into_string("", &filter_config).to_lowercase(), + Span::test_data(), + ); + + term.r#in(span, &lower_val, span) + .map_or(false, |value| value.is_true()) + } else { + term.r#in(span, val, span) + .map_or(false, |value| value.is_true()) + } + }), + Value::Binary { .. } => false, + }) != invert + }), + ctrlc.clone(), + ) + .into_pipeline_data(ctrlc) + .set_metadata(meta)), PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(span)), PipelineData::ExternalStream { stdout: Some(stream),