mirror of
https://github.com/chmln/sd
synced 2024-11-13 23:17:07 +00:00
Unescape replacement expression
This commit is contained in:
parent
9a8e8a5c2f
commit
20228b6fe3
5 changed files with 42 additions and 19 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -247,6 +247,7 @@ dependencies = [
|
|||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unescape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -315,6 +316,11 @@ name = "ucd-util"
|
|||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unescape"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.2.1"
|
||||
|
@ -408,6 +414,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
"checksum textwrap 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "307686869c93e71f94da64286f9a9524c0f308a9e1c87a583de8e9c9039ad3f6"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
|
||||
"checksum unescape 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccb97dac3243214f8d8507998906ca3e2e0b900bf9bf4870477f125b82e68f6e"
|
||||
"checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1"
|
||||
"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526"
|
||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||
|
|
|
@ -17,6 +17,7 @@ structopt = "0.2.14"
|
|||
atomic-write = "0.2.0"
|
||||
rayon = "1.0.3"
|
||||
regex-syntax = "0.6.4"
|
||||
unescape = "0.1.0"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
|
|
@ -48,7 +48,7 @@ pub(crate) fn run() -> Result<()> {
|
|||
let source = Source::from(args.files);
|
||||
let replacer = Replacer::new(
|
||||
args.find,
|
||||
&args.replace_with,
|
||||
args.replace_with,
|
||||
args.literal_mode,
|
||||
args.flags
|
||||
)?;
|
||||
|
|
47
src/input.rs
47
src/input.rs
|
@ -23,16 +23,16 @@ impl Source {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Replacer<'a> {
|
||||
pub(crate) struct Replacer {
|
||||
regex: Regex,
|
||||
replace_with: &'a str,
|
||||
replace_with: String,
|
||||
is_literal: bool,
|
||||
}
|
||||
|
||||
impl<'a> Replacer<'a> {
|
||||
impl Replacer {
|
||||
pub(crate) fn new(
|
||||
look_for: String,
|
||||
replace_with: &'a str,
|
||||
replace_with: String,
|
||||
is_literal: bool,
|
||||
flags: Option<String>,
|
||||
) -> Result<Self> {
|
||||
|
@ -45,15 +45,21 @@ impl<'a> Replacer<'a> {
|
|||
|
||||
let mut regex = regex::RegexBuilder::new(&look_for);
|
||||
regex.case_insensitive(
|
||||
!is_literal && !utils::regex_case_sensitive(&look_for)
|
||||
!is_literal && !utils::regex_case_sensitive(&look_for),
|
||||
);
|
||||
|
||||
if let (Some(flags), false) = (flags, is_literal) {
|
||||
for c in flags.chars() {
|
||||
match c {
|
||||
'c' => { regex.case_insensitive(false); },
|
||||
'i' => { regex.case_insensitive(true); },
|
||||
'm' => { regex.multi_line(true); },
|
||||
'c' => {
|
||||
regex.case_insensitive(false);
|
||||
},
|
||||
'i' => {
|
||||
regex.case_insensitive(true);
|
||||
},
|
||||
'm' => {
|
||||
regex.multi_line(true);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
}
|
||||
|
@ -61,7 +67,8 @@ impl<'a> Replacer<'a> {
|
|||
|
||||
return Ok(Replacer {
|
||||
regex: regex.build()?,
|
||||
replace_with,
|
||||
replace_with: utils::unescape(&replace_with)
|
||||
.unwrap_or_else(|| replace_with),
|
||||
is_literal,
|
||||
});
|
||||
}
|
||||
|
@ -74,13 +81,13 @@ impl<'a> Replacer<'a> {
|
|||
|
||||
if self.is_literal {
|
||||
self.regex
|
||||
.replace_all(&content, regex::NoExpand(self.replace_with))
|
||||
.replace_all(&content, regex::NoExpand(&self.replace_with))
|
||||
.to_string()
|
||||
.into()
|
||||
}
|
||||
else {
|
||||
self.regex
|
||||
.replace_all(&content, self.replace_with)
|
||||
.replace_all(&content, &*self.replace_with)
|
||||
.to_string()
|
||||
.into()
|
||||
}
|
||||
|
@ -143,44 +150,48 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn default_global() -> Result<()> {
|
||||
let r = Replacer::new("a".to_string(), "b", false, None)?;
|
||||
let r = Replacer::new("a".into(), "b".into(), false, None)?;
|
||||
assert_eq!(r.replace("aaa"), "bbb");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escaped_char_preservation() -> Result<()> {
|
||||
let r = Replacer::new("a".to_string(), "b", false, None)?;
|
||||
let r = Replacer::new("a".into(), "b".into(), false, None)?;
|
||||
assert_eq!(r.replace("a\\n"), "b\\n");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smart_case_insensitive() -> Result<()> {
|
||||
let r = Replacer::new("abc".to_string(), "x", false, None)?;
|
||||
let r = Replacer::new("abc".into(), "x".into(), false, None)?;
|
||||
assert_eq!(r.replace("abcABCAbcabC"), "xxxx");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn smart_case_sensitive() -> Result<()> {
|
||||
let r = Replacer::new("Abc".to_string(), "x", false, None)?;
|
||||
let r = Replacer::new("Abc".into(), "x".into(), false, None)?;
|
||||
assert_eq!(r.replace("abcABCAbcabC"), "abcABCxabC");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_smart_case_literals() -> Result<()> {
|
||||
let r = Replacer::new("abc".to_string(), "x", true, None)?;
|
||||
let r = Replacer::new("abc".into(), "x".into(), true, None)?;
|
||||
assert_eq!(r.replace("abcABCAbcabC"), "xABCAbcabC");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_check_literal_replacements() -> Result<()> {
|
||||
let r = Replacer::new("((special[]))".to_string(), "x", true, None)?;
|
||||
let r = Replacer::new(
|
||||
"((special[]))".to_string(),
|
||||
"x".to_string(),
|
||||
true,
|
||||
None,
|
||||
)?;
|
||||
assert_eq!(r.replace("((special[]))y"), "xy");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
pub(crate) type Result<T> = std::result::Result<T, crate::Error>;
|
||||
|
||||
pub(crate) fn unescape(s: &str) -> Option<String> {
|
||||
unescape::unescape(s)
|
||||
}
|
||||
|
||||
pub(crate) fn regex_case_sensitive(pattern: &str) -> bool {
|
||||
use regex_syntax::Parser;
|
||||
Parser::new()
|
||||
|
|
Loading…
Reference in a new issue