mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
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:
parent
c8f54476c9
commit
393f424f1c
1 changed files with 90 additions and 32 deletions
|
@ -58,6 +58,12 @@ impl Command for Find {
|
||||||
"dotall regex mode: allow a dot . to match newlines \\n; equivalent to (?s)",
|
"dotall regex mode: allow a dot . to match newlines \\n; equivalent to (?s)",
|
||||||
Some('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'))
|
.switch("invert", "invert the match", Some('v'))
|
||||||
.rest("rest", SyntaxShape::Any, "terms to search")
|
.rest("rest", SyntaxShape::Any, "terms to search")
|
||||||
.category(Category::Filters)
|
.category(Category::Filters)
|
||||||
|
@ -134,8 +140,33 @@ impl Command for Find {
|
||||||
Example {
|
Example {
|
||||||
description: "Remove ANSI sequences from result",
|
description: "Remove ANSI sequences from result",
|
||||||
example: "[[foo bar]; [abc 123] [def 456]] | find 123 | get bar | ansi strip",
|
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) {
|
let flags = match (insensitive, multiline, dotall) {
|
||||||
(false, false, false) => "",
|
(false, false, false) => "",
|
||||||
(true, false, false) => "(?i)",
|
(true, false, false) => "(?i)", // case insensitive
|
||||||
(false, true, false) => "(?m)",
|
(false, true, false) => "(?m)", // multi-line mode
|
||||||
(false, false, true) => "(?s)",
|
(false, false, true) => "(?s)", // allow . to match \n
|
||||||
(true, true, false) => "(?im)",
|
(true, true, false) => "(?im)", // case insensitive and multi-line mode
|
||||||
(true, false, true) => "(?is)",
|
(true, false, true) => "(?is)", // case insensitive and allow . to match \n
|
||||||
(false, true, true) => "(?ms)",
|
(false, true, true) => "(?ms)", // multi-line mode and allow . to match \n
|
||||||
(true, true, true) => "(?ims)",
|
(true, true, true) => "(?ims)", // case insensitive, multi-line mode and allow . to match \n
|
||||||
};
|
};
|
||||||
|
|
||||||
let regex = flags.to_string() + regex.as_str();
|
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],
|
cols: &mut [String],
|
||||||
vals: &mut Vec<Value>,
|
vals: &mut Vec<Value>,
|
||||||
span: &mut Span,
|
span: &mut Span,
|
||||||
|
@ -235,15 +268,24 @@ fn highlight_terms_in_record(
|
||||||
string_style: Style,
|
string_style: Style,
|
||||||
ls_colors: &LsColors,
|
ls_colors: &LsColors,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
|
let cols_to_search = if search_cols.is_empty() {
|
||||||
|
cols.to_vec()
|
||||||
|
} else {
|
||||||
|
search_cols.to_vec()
|
||||||
|
};
|
||||||
let mut output = 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 val_str = val.into_string("", config);
|
||||||
let lower_val = val.into_string("", config).to_lowercase();
|
let lower_val = val.into_string("", config).to_lowercase();
|
||||||
let mut term_added_to_output = false;
|
let mut term_added_to_output = false;
|
||||||
for term in terms {
|
for term in terms {
|
||||||
let term_str = term.into_string("", config);
|
let term_str = term.into_string("", config);
|
||||||
let lower_term = term.into_string("", config).to_lowercase();
|
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 {
|
if config.use_ls_colors {
|
||||||
// Get the original LS_COLORS color
|
// Get the original LS_COLORS color
|
||||||
let style = ls_colors.style_for_path(val_str.clone());
|
let style = ls_colors.style_for_path(val_str.clone());
|
||||||
|
@ -263,11 +305,10 @@ fn highlight_terms_in_record(
|
||||||
Ok(hi) => hi,
|
Ok(hi) => hi,
|
||||||
Err(_) => string_style.paint(term_str.to_string()).to_string(),
|
Err(_) => string_style.paint(term_str.to_string()).to_string(),
|
||||||
};
|
};
|
||||||
output.push(Value::String {
|
potential_output.push(Value::String {
|
||||||
val: hi,
|
val: hi,
|
||||||
span: *span,
|
span: *span,
|
||||||
});
|
});
|
||||||
term_added_to_output = true;
|
|
||||||
} else {
|
} else {
|
||||||
// No LS_COLORS support, so just use the original value
|
// No LS_COLORS support, so just use the original value
|
||||||
let hi = match highlight_search_string(&val_str, &term_str, &string_style) {
|
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 {
|
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 {
|
Value::Record {
|
||||||
cols: cols.to_vec(),
|
cols: cols.to_vec(),
|
||||||
vals: output,
|
vals: output,
|
||||||
|
@ -315,6 +361,7 @@ fn find_with_rest_and_highlight(
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<Value>>();
|
.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);
|
let style_computer = StyleComputer::from_config(&engine_state, stack);
|
||||||
// Currently, search results all use the same style.
|
// Currently, search results all use the same style.
|
||||||
|
@ -328,12 +375,19 @@ fn find_with_rest_and_highlight(
|
||||||
};
|
};
|
||||||
let ls_colors = get_ls_colors(ls_colors_env_str);
|
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 {
|
match input {
|
||||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||||
PipelineData::Value(_, _) => input
|
PipelineData::Value(_, _) => input
|
||||||
.map(
|
.map(
|
||||||
move |mut x| match &mut x {
|
move |mut x| match &mut x {
|
||||||
Value::Record { cols, vals, span } => highlight_terms_in_record(
|
Value::Record { cols, vals, span } => {
|
||||||
|
highlight_terms_in_record_with_search_columns(
|
||||||
|
&cols_to_search,
|
||||||
cols,
|
cols,
|
||||||
vals,
|
vals,
|
||||||
span,
|
span,
|
||||||
|
@ -341,7 +395,8 @@ fn find_with_rest_and_highlight(
|
||||||
&terms,
|
&terms,
|
||||||
string_style,
|
string_style,
|
||||||
&ls_colors,
|
&ls_colors,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
_ => x,
|
_ => x,
|
||||||
},
|
},
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
|
@ -417,7 +472,9 @@ fn find_with_rest_and_highlight(
|
||||||
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
|
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
|
||||||
stream
|
stream
|
||||||
.map(move |mut x| match &mut x {
|
.map(move |mut x| match &mut x {
|
||||||
Value::Record { cols, vals, span } => highlight_terms_in_record(
|
Value::Record { cols, vals, span } => {
|
||||||
|
highlight_terms_in_record_with_search_columns(
|
||||||
|
&cols_to_search,
|
||||||
cols,
|
cols,
|
||||||
vals,
|
vals,
|
||||||
span,
|
span,
|
||||||
|
@ -425,7 +482,8 @@ fn find_with_rest_and_highlight(
|
||||||
&terms,
|
&terms,
|
||||||
string_style,
|
string_style,
|
||||||
&ls_colors,
|
&ls_colors,
|
||||||
),
|
)
|
||||||
|
}
|
||||||
_ => x,
|
_ => x,
|
||||||
})
|
})
|
||||||
.filter(move |value| {
|
.filter(move |value| {
|
||||||
|
|
Loading…
Reference in a new issue