diff --git a/crates/nu-command/src/filters/find.rs b/crates/nu-command/src/filters/find.rs index 3b4a0219f3..46b1d74b06 100644 --- a/crates/nu-command/src/filters/find.rs +++ b/crates/nu-command/src/filters/find.rs @@ -574,6 +574,46 @@ fn split_string_if_multiline(input: PipelineData, head_span: Span) -> PipelineDa } } +/// function for using find from other commands +pub fn find_internal( + input: PipelineData, + engine_state: &EngineState, + stack: &mut Stack, + search_term: &str, + columns_to_search: &[&str], + highlight: bool, +) -> Result { + let span = input.span().unwrap_or(Span::unknown()); + + let style_computer = StyleComputer::from_config(engine_state, stack); + let string_style = style_computer.compute("string", &Value::string("search result", span)); + let highlight_style = + style_computer.compute("search_result", &Value::string("search result", span)); + + let regex_str = format!("(?i){}", escape(search_term)); + + let regex = Regex::new(regex_str.as_str()).map_err(|e| ShellError::TypeMismatch { + err_message: format!("invalid regex: {e}"), + span: Span::unknown(), + })?; + + let pattern = MatchPattern { + regex, + lower_terms: vec![search_term.to_lowercase()], + highlight, + invert: false, + string_style, + highlight_style, + }; + + let columns_to_search = columns_to_search + .iter() + .map(|str| String::from(*str)) + .collect(); + + find_in_pipelinedata(pattern, columns_to_search, engine_state, stack, input) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/nu-command/src/filters/mod.rs b/crates/nu-command/src/filters/mod.rs index 6eaaaea860..8f2f161492 100644 --- a/crates/nu-command/src/filters/mod.rs +++ b/crates/nu-command/src/filters/mod.rs @@ -70,7 +70,7 @@ pub use empty::empty; pub use enumerate::Enumerate; pub use every::Every; pub use filter::Filter; -pub use find::Find; +pub use find::{Find, find_internal}; pub use first::First; pub use flatten::Flatten; pub use get::Get; diff --git a/crates/nu-command/src/help/help_.rs b/crates/nu-command/src/help/help_.rs index a1e0c81120..75f3f4151b 100644 --- a/crates/nu-command/src/help/help_.rs +++ b/crates/nu-command/src/help/help_.rs @@ -1,8 +1,5 @@ use crate::help::{help_aliases, help_commands, help_modules}; -use fancy_regex::{Regex, escape}; -use nu_ansi_term::Style; use nu_engine::command_prelude::*; -use nu_utils::IgnoreCaseExt; #[derive(Clone)] pub struct Help; @@ -125,132 +122,3 @@ You can also learn more at https://www.nushell.sh/book/"#; ] } } - -pub fn highlight_search_in_table( - table: Vec, // list of records - search_string: &str, - searched_cols: &[&str], - string_style: &Style, - highlight_style: &Style, -) -> Result, ShellError> { - let orig_search_string = search_string; - let search_string = search_string.to_folded_case(); - let mut matches = vec![]; - - for mut value in table { - let Value::Record { - val: ref mut record, - .. - } = value - else { - return Err(ShellError::NushellFailedSpanned { - msg: "Expected record".to_string(), - label: format!("got {}", value.get_type()), - span: value.span(), - }); - }; - - let has_match = record.to_mut().iter_mut().try_fold( - false, - |acc: bool, (col, val)| -> Result { - if !searched_cols.contains(&col.as_str()) { - // don't search this column - return Ok(acc); - } - let span = val.span(); - if let Value::String { val: s, .. } = val { - if s.to_folded_case().contains(&search_string) { - *val = Value::string( - highlight_search_string( - s, - orig_search_string, - string_style, - highlight_style, - )?, - span, - ); - return Ok(true); - } - } - // column does not contain the searched string - // ignore non-string values - Ok(acc) - }, - )?; - - if has_match { - matches.push(value); - } - } - - Ok(matches) -} - -// Highlight the search string using ANSI escape sequences and regular expressions. -pub fn highlight_search_string( - haystack: &str, - needle: &str, - string_style: &Style, - highlight_style: &Style, -) -> Result { - let escaped_needle = escape(needle); - let regex_string = format!("(?i){escaped_needle}"); - let regex = match Regex::new(®ex_string) { - Ok(regex) => regex, - Err(err) => { - return Err(ShellError::GenericError { - error: "Could not compile regex".into(), - msg: err.to_string(), - span: Some(Span::test_data()), - help: None, - inner: vec![], - }); - } - }; - // strip haystack to remove existing ansi style - let stripped_haystack = nu_utils::strip_ansi_string_unlikely(haystack.to_string()); - let mut last_match_end = 0; - let mut highlighted = String::new(); - - for cap in regex.captures_iter(stripped_haystack.as_ref()) { - match cap { - Ok(capture) => { - let start = match capture.get(0) { - Some(acap) => acap.start(), - None => 0, - }; - let end = match capture.get(0) { - Some(acap) => acap.end(), - None => 0, - }; - highlighted.push_str( - &string_style - .paint(&stripped_haystack[last_match_end..start]) - .to_string(), - ); - highlighted.push_str( - &highlight_style - .paint(&stripped_haystack[start..end]) - .to_string(), - ); - last_match_end = end; - } - Err(e) => { - return Err(ShellError::GenericError { - error: "Error with regular expression capture".into(), - msg: e.to_string(), - span: None, - help: None, - inner: vec![], - }); - } - } - } - - highlighted.push_str( - &string_style - .paint(&stripped_haystack[last_match_end..]) - .to_string(), - ); - Ok(highlighted) -} diff --git a/crates/nu-command/src/help/help_aliases.rs b/crates/nu-command/src/help/help_aliases.rs index 0e5241e6a9..6bbb82e0f0 100644 --- a/crates/nu-command/src/help/help_aliases.rs +++ b/crates/nu-command/src/help/help_aliases.rs @@ -1,5 +1,4 @@ -use crate::help::highlight_search_in_table; -use nu_color_config::StyleComputer; +use crate::filters::find_internal; use nu_engine::{command_prelude::*, scope::ScopeData}; #[derive(Clone)] @@ -72,31 +71,20 @@ pub fn help_aliases( let find: Option> = call.get_flag(engine_state, stack, "find")?; let rest: Vec> = call.rest(engine_state, stack, 0)?; - // 🚩The following two-lines are copied from filters/find.rs: - let style_computer = StyleComputer::from_config(engine_state, stack); - // Currently, search results all use the same style. - // Also note that this sample string is passed into user-written code (the closure that may or may not be - // defined for "string"). - let string_style = style_computer.compute("string", &Value::string("search result", head)); - let highlight_style = - style_computer.compute("search_result", &Value::string("search result", head)); - if let Some(f) = find { let all_cmds_vec = build_help_aliases(engine_state, stack, head); - let found_cmds_vec = highlight_search_in_table( + return find_internal( all_cmds_vec, + engine_state, + stack, &f.item, &["name", "description"], - &string_style, - &highlight_style, - )?; - - return Ok(Value::list(found_cmds_vec, head).into_pipeline_data()); + true, + ); } if rest.is_empty() { - let found_cmds_vec = build_help_aliases(engine_state, stack, head); - Ok(Value::list(found_cmds_vec, head).into_pipeline_data()) + Ok(build_help_aliases(engine_state, stack, head)) } else { let mut name = String::new(); @@ -152,11 +140,11 @@ pub fn help_aliases( } } -fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec { +fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> PipelineData { let mut scope_data = ScopeData::new(engine_state, stack); scope_data.populate_decls(); - scope_data.collect_aliases(span) + Value::list(scope_data.collect_aliases(span), span).into_pipeline_data() } #[cfg(test)] diff --git a/crates/nu-command/src/help/help_commands.rs b/crates/nu-command/src/help/help_commands.rs index 9d3ab9c924..7ea035fd6b 100644 --- a/crates/nu-command/src/help/help_commands.rs +++ b/crates/nu-command/src/help/help_commands.rs @@ -1,5 +1,4 @@ -use crate::help::highlight_search_in_table; -use nu_color_config::StyleComputer; +use crate::filters::find_internal; use nu_engine::{command_prelude::*, get_full_help}; #[derive(Clone)] @@ -52,31 +51,20 @@ pub fn help_commands( let find: Option> = call.get_flag(engine_state, stack, "find")?; let rest: Vec> = call.rest(engine_state, stack, 0)?; - // 🚩The following two-lines are copied from filters/find.rs: - let style_computer = StyleComputer::from_config(engine_state, stack); - // Currently, search results all use the same style. - // Also note that this sample string is passed into user-written code (the closure that may or may not be - // defined for "string"). - let string_style = style_computer.compute("string", &Value::string("search result", head)); - let highlight_style = - style_computer.compute("search_result", &Value::string("search result", head)); - if let Some(f) = find { let all_cmds_vec = build_help_commands(engine_state, head); - let found_cmds_vec = highlight_search_in_table( + return find_internal( all_cmds_vec, + engine_state, + stack, &f.item, &["name", "description", "search_terms"], - &string_style, - &highlight_style, - )?; - - return Ok(Value::list(found_cmds_vec, head).into_pipeline_data()); + true, + ); } if rest.is_empty() { - let found_cmds_vec = build_help_commands(engine_state, head); - Ok(Value::list(found_cmds_vec, head).into_pipeline_data()) + Ok(build_help_commands(engine_state, head)) } else { let mut name = String::new(); @@ -99,7 +87,7 @@ pub fn help_commands( } } -fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec { +fn build_help_commands(engine_state: &EngineState, span: Span) -> PipelineData { let commands = engine_state.get_decls_sorted(false); let mut found_cmds_vec = Vec::new(); @@ -215,7 +203,7 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec { found_cmds_vec.push(Value::record(record, span)); } - found_cmds_vec + Value::list(found_cmds_vec, span).into_pipeline_data() } #[cfg(test)] diff --git a/crates/nu-command/src/help/help_externs.rs b/crates/nu-command/src/help/help_externs.rs index 27775b416e..155fd14404 100644 --- a/crates/nu-command/src/help/help_externs.rs +++ b/crates/nu-command/src/help/help_externs.rs @@ -1,5 +1,4 @@ -use crate::help::highlight_search_in_table; -use nu_color_config::StyleComputer; +use crate::filters::find_internal; use nu_engine::{command_prelude::*, get_full_help, scope::ScopeData}; #[derive(Clone)] @@ -72,31 +71,20 @@ pub fn help_externs( let find: Option> = call.get_flag(engine_state, stack, "find")?; let rest: Vec> = call.rest(engine_state, stack, 0)?; - // 🚩The following two-lines are copied from filters/find.rs: - let style_computer = StyleComputer::from_config(engine_state, stack); - // Currently, search results all use the same style. - // Also note that this sample string is passed into user-written code (the closure that may or may not be - // defined for "string"). - let string_style = style_computer.compute("string", &Value::string("search result", head)); - let highlight_style = - style_computer.compute("search_result", &Value::string("search result", head)); - if let Some(f) = find { let all_cmds_vec = build_help_externs(engine_state, stack, head); - let found_cmds_vec = highlight_search_in_table( + return find_internal( all_cmds_vec, + engine_state, + stack, &f.item, &["name", "description"], - &string_style, - &highlight_style, - )?; - - return Ok(Value::list(found_cmds_vec, head).into_pipeline_data()); + true, + ); } if rest.is_empty() { - let found_cmds_vec = build_help_externs(engine_state, stack, head); - Ok(Value::list(found_cmds_vec, head).into_pipeline_data()) + Ok(build_help_externs(engine_state, stack, head)) } else { let mut name = String::new(); @@ -119,10 +107,10 @@ pub fn help_externs( } } -fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec { +fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> PipelineData { let mut scope = ScopeData::new(engine_state, stack); scope.populate_decls(); - scope.collect_externs(span) + Value::list(scope.collect_externs(span), span).into_pipeline_data() } #[cfg(test)] diff --git a/crates/nu-command/src/help/help_modules.rs b/crates/nu-command/src/help/help_modules.rs index 84fff36dfb..47201b4475 100644 --- a/crates/nu-command/src/help/help_modules.rs +++ b/crates/nu-command/src/help/help_modules.rs @@ -1,5 +1,4 @@ -use crate::help::highlight_search_in_table; -use nu_color_config::StyleComputer; +use crate::filters::find_internal; use nu_engine::{command_prelude::*, scope::ScopeData}; use nu_protocol::DeclId; @@ -79,31 +78,20 @@ pub fn help_modules( let find: Option> = call.get_flag(engine_state, stack, "find")?; let rest: Vec> = call.rest(engine_state, stack, 0)?; - // 🚩The following two-lines are copied from filters/find.rs: - let style_computer = StyleComputer::from_config(engine_state, stack); - // Currently, search results all use the same style. - // Also note that this sample string is passed into user-written code (the closure that may or may not be - // defined for "string"). - let string_style = style_computer.compute("string", &Value::string("search result", head)); - let highlight_style = - style_computer.compute("search_result", &Value::string("search result", head)); - if let Some(f) = find { let all_cmds_vec = build_help_modules(engine_state, stack, head); - let found_cmds_vec = highlight_search_in_table( + return find_internal( all_cmds_vec, + engine_state, + stack, &f.item, &["name", "description"], - &string_style, - &highlight_style, - )?; - - return Ok(Value::list(found_cmds_vec, head).into_pipeline_data()); + true, + ); } if rest.is_empty() { - let found_cmds_vec = build_help_modules(engine_state, stack, head); - Ok(Value::list(found_cmds_vec, head).into_pipeline_data()) + Ok(build_help_modules(engine_state, stack, head)) } else { let mut name = String::new(); @@ -239,11 +227,11 @@ pub fn help_modules( } } -fn build_help_modules(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec { +fn build_help_modules(engine_state: &EngineState, stack: &Stack, span: Span) -> PipelineData { let mut scope_data = ScopeData::new(engine_state, stack); scope_data.populate_modules(); - scope_data.collect_modules(span) + Value::list(scope_data.collect_modules(span), span).into_pipeline_data() } #[cfg(test)] diff --git a/crates/nu-command/src/help/mod.rs b/crates/nu-command/src/help/mod.rs index 6c8a9d1842..d354a089dd 100644 --- a/crates/nu-command/src/help/mod.rs +++ b/crates/nu-command/src/help/mod.rs @@ -16,7 +16,6 @@ pub use help_modules::HelpModules; pub use help_operators::HelpOperators; pub use help_pipe_and_redirect::HelpPipeAndRedirect; -pub(crate) use help_::highlight_search_in_table; pub(crate) use help_aliases::help_aliases; pub(crate) use help_commands::help_commands; pub(crate) use help_modules::help_modules;