allow find command to look in specified columns only (#8937)

# Description
This PR allows the `find` command to search in specific columns using
`--columns [col1 col2 col3]`. This is really meant to help with the
`help` command in the std.nu.

There are a few more things I want to look at so this is a draft for
now.
- [x] add example
- [x] look at regex part

# User-Facing Changes
<!-- List of all changes that impact the user experience here. This
helps us keep track of breaking changes. -->

# Tests + Formatting
<!--
Don't forget to add tests that cover your changes.

Make sure you've run and fixed any issues with these commands:

- `cargo fmt --all -- --check` to check standard code formatting (`cargo
fmt --all` applies these changes)
- `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A
clippy::needless_collect` to check that you're using the standard code
style
- `cargo test --workspace` to check that all tests pass
- `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the
standard library

> **Note**
> from `nushell` you can also use the `toolkit` as follows
> ```bash
> use toolkit.nu # or use an `env_change` hook to activate it
automatically
> toolkit check pr
> ```
-->

# After Submitting
<!-- If your PR had any user-facing changes, update [the
documentation](https://github.com/nushell/nushell.github.io) after the
PR is merged, if necessary. This will help us keep the docs up to date.
-->
This commit is contained in:
Darren Schroeder 2023-04-20 08:13:12 -05:00 committed by GitHub
parent c8f54476c9
commit 393f424f1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -58,6 +58,12 @@ impl Command for Find {
"dotall regex mode: allow a dot . to match newlines \\n; equivalent to (?s)",
Some('s'),
)
.named(
"columns",
SyntaxShape::List(Box::new(SyntaxShape::String)),
"column names to be searched (with rest parameter, not regex yet)",
Some('c'),
)
.switch("invert", "invert the match", Some('v'))
.rest("rest", SyntaxShape::Any, "terms to search")
.category(Category::Filters)
@ -134,8 +140,33 @@ impl Command for Find {
Example {
description: "Remove ANSI sequences from result",
example: "[[foo bar]; [abc 123] [def 456]] | find 123 | get bar | ansi strip",
result: None,
result: None, // This is None because ansi strip is not available in tests
},
Example {
description: "Find and highlight text in specific columns",
example: "[[col1 col2 col3]; [moe larry curly] [larry curly moe]] | find moe -c [col1 col3]",
result: Some(Value::List {
vals: vec![
Value::test_record(
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
vec![
Value::test_string("\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m".to_string()),
Value::test_string("larry".to_string()),
Value::test_string("curly".to_string()),
]
),
Value::test_record(
vec!["col1".to_string(), "col2".to_string(), "col3".to_string()],
vec![
Value::test_string("larry".to_string()),
Value::test_string("curly".to_string()),
Value::test_string("\u{1b}[37m\u{1b}[0m\u{1b}[41;37mmoe\u{1b}[0m\u{1b}[37m\u{1b}[0m".to_string()),
]
),
],
span: Span::test_data(),
}),
}
]
}
@ -179,13 +210,13 @@ fn find_with_regex(
let flags = match (insensitive, multiline, dotall) {
(false, false, false) => "",
(true, false, false) => "(?i)",
(false, true, false) => "(?m)",
(false, false, true) => "(?s)",
(true, true, false) => "(?im)",
(true, false, true) => "(?is)",
(false, true, true) => "(?ms)",
(true, true, true) => "(?ims)",
(true, false, false) => "(?i)", // case insensitive
(false, true, false) => "(?m)", // multi-line mode
(false, false, true) => "(?s)", // allow . to match \n
(true, true, false) => "(?im)", // case insensitive and multi-line mode
(true, false, true) => "(?is)", // case insensitive and allow . to match \n
(false, true, true) => "(?ms)", // multi-line mode and allow . to match \n
(true, true, true) => "(?ims)", // case insensitive, multi-line mode and allow . to match \n
};
let regex = flags.to_string() + regex.as_str();
@ -226,7 +257,9 @@ fn find_with_regex(
)
}
fn highlight_terms_in_record(
#[allow(clippy::too_many_arguments)]
fn highlight_terms_in_record_with_search_columns(
search_cols: &Vec<String>,
cols: &mut [String],
vals: &mut Vec<Value>,
span: &mut Span,
@ -235,15 +268,24 @@ fn highlight_terms_in_record(
string_style: Style,
ls_colors: &LsColors,
) -> Value {
let cols_to_search = if search_cols.is_empty() {
cols.to_vec()
} else {
search_cols.to_vec()
};
let mut output = vec![];
for val in vals {
let mut potential_output = vec![];
let mut found_a_hit = false;
for (cur_col, val) in cols.iter().zip(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 lower_val.contains(&lower_term) && cols_to_search.contains(cur_col) {
found_a_hit = true;
term_added_to_output = true;
if config.use_ls_colors {
// Get the original LS_COLORS color
let style = ls_colors.style_for_path(val_str.clone());
@ -263,11 +305,10 @@ fn highlight_terms_in_record(
Ok(hi) => hi,
Err(_) => string_style.paint(term_str.to_string()).to_string(),
};
output.push(Value::String {
potential_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) {
@ -282,9 +323,14 @@ fn highlight_terms_in_record(
}
}
if !term_added_to_output {
output.push(val.clone());
potential_output.push(val.clone());
}
}
if found_a_hit {
output.append(&mut potential_output);
}
Value::Record {
cols: cols.to_vec(),
vals: output,
@ -315,6 +361,7 @@ fn find_with_rest_and_highlight(
}
})
.collect::<Vec<Value>>();
let columns_to_search: Option<Vec<String>> = call.get_flag(&engine_state, stack, "columns")?;
let style_computer = StyleComputer::from_config(&engine_state, stack);
// Currently, search results all use the same style.
@ -328,20 +375,28 @@ fn find_with_rest_and_highlight(
};
let ls_colors = get_ls_colors(ls_colors_env_str);
let cols_to_search = match columns_to_search {
Some(cols) => cols,
None => vec![],
};
match input {
PipelineData::Empty => Ok(PipelineData::Empty),
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,
),
Value::Record { cols, vals, span } => {
highlight_terms_in_record_with_search_columns(
&cols_to_search,
cols,
vals,
span,
&config,
&terms,
string_style,
&ls_colors,
)
}
_ => x,
},
ctrlc.clone(),
@ -417,15 +472,18 @@ fn find_with_rest_and_highlight(
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,
),
Value::Record { cols, vals, span } => {
highlight_terms_in_record_with_search_columns(
&cols_to_search,
cols,
vals,
span,
&config,
&terms,
string_style,
&ls_colors,
)
}
_ => x,
})
.filter(move |value| {