mirror of
https://github.com/nushell/nushell.git
synced 2025-06-20 09:58:15 +02:00
Use internal find.rs code for help --find
(#15982)
# Description Currently, `help --find` uses it's own code for looking for the keyword in a string and highlighting it. This code duplicates a lot of the logic found in the code of `find`. This commit re-uses the code of `find` in `help` commands instead. # User-Facing Changes This should not affect the behavior of `help`.
This commit is contained in:
parent
52604f8b00
commit
2e484156e0
@ -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<PipelineData, ShellError> {
|
||||
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::*;
|
||||
|
@ -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;
|
||||
|
@ -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<Value>, // list of records
|
||||
search_string: &str,
|
||||
searched_cols: &[&str],
|
||||
string_style: &Style,
|
||||
highlight_style: &Style,
|
||||
) -> Result<Vec<Value>, 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<bool, ShellError> {
|
||||
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<String, ShellError> {
|
||||
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)
|
||||
}
|
||||
|
@ -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<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||
let rest: Vec<Spanned<String>> = 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<Value> {
|
||||
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)]
|
||||
|
@ -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<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||
let rest: Vec<Spanned<String>> = 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<Value> {
|
||||
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<Value> {
|
||||
found_cmds_vec.push(Value::record(record, span));
|
||||
}
|
||||
|
||||
found_cmds_vec
|
||||
Value::list(found_cmds_vec, span).into_pipeline_data()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -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<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||
let rest: Vec<Spanned<String>> = 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<Value> {
|
||||
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)]
|
||||
|
@ -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<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||
let rest: Vec<Spanned<String>> = 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<Value> {
|
||||
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)]
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user