added ability to opt in to normal string replacement in replace cmd (#5133)

* added ability to opt in to normal string replacement in `replace` cmd

* type-o
This commit is contained in:
Darren Schroeder 2022-04-08 12:23:16 -05:00 committed by GitHub
parent 7ce570e52c
commit 74d0f19291
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -13,6 +13,7 @@ struct Arguments {
replace: String, replace: String,
column_paths: Vec<CellPath>, column_paths: Vec<CellPath>,
literal_replace: bool, literal_replace: bool,
no_regex: bool,
} }
#[derive(Clone)] #[derive(Clone)]
@ -38,6 +39,11 @@ impl Command for SubCommand {
"do not expand the replace parameter as a regular expression", "do not expand the replace parameter as a regular expression",
Some('n'), Some('n'),
) )
.switch(
"string",
"do not use regular expressions for string find and replace",
Some('s'),
)
.category(Category::Strings) .category(Category::Strings)
} }
@ -107,7 +113,22 @@ impl Command for SubCommand {
span: Span::test_data(), span: Span::test_data(),
}), }),
}, },
Example {
description: "Find and replace the first occurence using string replacement *not* regular expressions",
example: r#"'c:\some\cool\path' | str replace 'c:\some\cool' '~' -s"#,
result: Some(Value::String {
val: "~\\path".to_string(),
span: Span::test_data(),
}),
},
Example {
description: "Find and replace all occurences using string replacement *not* regular expressions",
example: r#"'abc abc abc' | str replace -a 'b' 'z' -s"#,
result: Some(Value::String {
val: "azc azc azc".to_string(),
span: Span::test_data(),
}),
},
] ]
} }
} }
@ -118,9 +139,11 @@ fn operate(
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let find: Spanned<String> = call.req(engine_state, stack, 0)?; let find: Spanned<String> = call.req(engine_state, stack, 0)?;
let replace: Spanned<String> = call.req(engine_state, stack, 1)?; let replace: Spanned<String> = call.req(engine_state, stack, 1)?;
let literal_replace = call.has_flag("no-expand"); let literal_replace = call.has_flag("no-expand");
let no_regex = call.has_flag("string");
let options = Arc::new(Arguments { let options = Arc::new(Arguments {
all: call.has_flag("all"), all: call.has_flag("all"),
@ -128,8 +151,9 @@ fn operate(
replace: replace.item, replace: replace.item,
column_paths: call.rest(engine_state, stack, 2)?, column_paths: call.rest(engine_state, stack, 2)?,
literal_replace, literal_replace,
no_regex,
}); });
let head = call.head;
input.map( input.map(
move |v| { move |v| {
if options.column_paths.is_empty() { if options.column_paths.is_empty() {
@ -162,6 +186,7 @@ fn action(
replace, replace,
all, all,
literal_replace, literal_replace,
no_regex,
.. ..
}: &Arguments, }: &Arguments,
head: Span, head: Span,
@ -169,38 +194,54 @@ fn action(
match input { match input {
Value::String { val, .. } => { Value::String { val, .. } => {
let FindReplace(find, replacement) = FindReplace(find, replace); let FindReplace(find, replacement) = FindReplace(find, replace);
let regex = Regex::new(find); if *no_regex {
// just use regular string replacement vs regular expressions
match regex { if *all {
Ok(re) => { Value::String {
if *all { val: val.replace(find, replacement),
Value::String { span: head,
val: { }
if *literal_replace { } else {
re.replace_all(val, NoExpand(replacement)).to_string() Value::String {
} else { val: val.replacen(find, replacement, 1),
re.replace_all(val, replacement).to_string() span: head,
}
},
span: head,
}
} else {
Value::String {
val: {
if *literal_replace {
re.replace(val, NoExpand(replacement)).to_string()
} else {
re.replace(val, replacement).to_string()
}
},
span: head,
}
} }
} }
Err(_) => Value::String { } else {
val: val.to_string(), // use regular expressions to replace strings
span: head, let regex = Regex::new(find);
},
match regex {
Ok(re) => {
if *all {
Value::String {
val: {
if *literal_replace {
re.replace_all(val, NoExpand(replacement)).to_string()
} else {
re.replace_all(val, replacement).to_string()
}
},
span: head,
}
} else {
Value::String {
val: {
if *literal_replace {
re.replace(val, NoExpand(replacement)).to_string()
} else {
re.replace(val, replacement).to_string()
}
},
span: head,
}
}
}
Err(_) => Value::String {
val: val.to_string(),
span: head,
},
}
} }
} }
other => Value::Error { other => Value::Error {
@ -240,6 +281,7 @@ mod tests {
column_paths: vec![], column_paths: vec![],
literal_replace: false, literal_replace: false,
all: false, all: false,
no_regex: false,
}; };
let actual = action(&word, &options, Span::test_data()); let actual = action(&word, &options, Span::test_data());