forked from extern/nushell
add replace all option to str find-replace (#2569)
This commit is contained in:
parent
15f3a545f0
commit
6eb4a0e87b
@ -7,7 +7,6 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
use nu_source::{Tag, Tagged};
|
use nu_source::{Tag, Tagged};
|
||||||
use nu_value_ext::ValueExt;
|
use nu_value_ext::ValueExt;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -15,6 +14,7 @@ struct Arguments {
|
|||||||
find: Tagged<String>,
|
find: Tagged<String>,
|
||||||
replace: Tagged<String>,
|
replace: Tagged<String>,
|
||||||
rest: Vec<ColumnPath>,
|
rest: Vec<ColumnPath>,
|
||||||
|
all: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
@ -33,6 +33,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
SyntaxShape::ColumnPath,
|
SyntaxShape::ColumnPath,
|
||||||
"optionally find and replace text by column paths",
|
"optionally find and replace text by column paths",
|
||||||
)
|
)
|
||||||
|
.switch("all", "replace all occurences of find string", Some('a'))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
@ -48,11 +49,18 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
|
Example {
|
||||||
description: "Find and replace contents with capture group",
|
description: "Find and replace contents with capture group",
|
||||||
example: "echo 'my_library.rb' | str find-replace '(.+).rb' '$1.nu'",
|
example: "echo 'my_library.rb' | str find-replace '(.+).rb' '$1.nu'",
|
||||||
result: Some(vec![Value::from("my_library.nu")]),
|
result: Some(vec![Value::from("my_library.nu")]),
|
||||||
}]
|
},
|
||||||
|
Example {
|
||||||
|
description: "Find and replace all occurrences of find string",
|
||||||
|
example: "echo 'abc abc abc' | str find-replace -a 'b' 'z'",
|
||||||
|
result: Some(vec![Value::from("azc azc azc")]),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,17 +78,17 @@ async fn operate(
|
|||||||
find,
|
find,
|
||||||
replace,
|
replace,
|
||||||
rest,
|
rest,
|
||||||
|
all,
|
||||||
},
|
},
|
||||||
input,
|
input,
|
||||||
) = args.process(®istry).await?;
|
) = args.process(®istry).await?;
|
||||||
let options = FindReplace(find.item, replace.item);
|
let options = FindReplace(find.item, replace.item);
|
||||||
|
|
||||||
let column_paths: Vec<_> = rest;
|
let column_paths: Vec<_> = rest;
|
||||||
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.map(move |v| {
|
.map(move |v| {
|
||||||
if column_paths.is_empty() {
|
if column_paths.is_empty() {
|
||||||
ReturnSuccess::value(action(&v, &options, v.tag())?)
|
ReturnSuccess::value(action(&v, &options, v.tag(), all)?)
|
||||||
} else {
|
} else {
|
||||||
let mut ret = v;
|
let mut ret = v;
|
||||||
|
|
||||||
@ -89,7 +97,7 @@ async fn operate(
|
|||||||
|
|
||||||
ret = ret.swap_data_by_column_path(
|
ret = ret.swap_data_by_column_path(
|
||||||
path,
|
path,
|
||||||
Box::new(move |old| action(old, &options, old.tag())),
|
Box::new(move |old| action(old, &options, old.tag(), all)),
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +107,12 @@ async fn operate(
|
|||||||
.to_output_stream())
|
.to_output_stream())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, options: &FindReplace, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
fn action(
|
||||||
|
input: &Value,
|
||||||
|
options: &FindReplace,
|
||||||
|
tag: impl Into<Tag>,
|
||||||
|
all: bool,
|
||||||
|
) -> Result<Value, ShellError> {
|
||||||
match &input.value {
|
match &input.value {
|
||||||
UntaggedValue::Primitive(Primitive::Line(s))
|
UntaggedValue::Primitive(Primitive::Line(s))
|
||||||
| UntaggedValue::Primitive(Primitive::String(s)) => {
|
| UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||||
@ -109,7 +122,13 @@ fn action(input: &Value, options: &FindReplace, tag: impl Into<Tag>) -> Result<V
|
|||||||
let regex = Regex::new(find.as_str());
|
let regex = Regex::new(find.as_str());
|
||||||
|
|
||||||
let out = match regex {
|
let out = match regex {
|
||||||
Ok(re) => UntaggedValue::string(re.replace(s, replacement.as_str()).to_owned()),
|
Ok(re) => {
|
||||||
|
if all {
|
||||||
|
UntaggedValue::string(re.replace_all(s, replacement.as_str()).to_owned())
|
||||||
|
} else {
|
||||||
|
UntaggedValue::string(re.replace(s, replacement.as_str()).to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(_) => UntaggedValue::string(s),
|
Err(_) => UntaggedValue::string(s),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,10 +162,10 @@ mod tests {
|
|||||||
fn can_have_capture_groups() {
|
fn can_have_capture_groups() {
|
||||||
let word = string("Cargo.toml");
|
let word = string("Cargo.toml");
|
||||||
let expected = string("Carga.toml");
|
let expected = string("Carga.toml");
|
||||||
|
let all = false;
|
||||||
let find_replace_options = FindReplace("Cargo.(.+)".to_string(), "Carga.$1".to_string());
|
let find_replace_options = FindReplace("Cargo.(.+)".to_string(), "Carga.$1".to_string());
|
||||||
|
|
||||||
let actual = action(&word, &find_replace_options, Tag::unknown()).unwrap();
|
let actual = action(&word, &find_replace_options, Tag::unknown(), all).unwrap();
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user