Skip to content

Commit

Permalink
add regex and fixed-string modes (#20)
Browse files Browse the repository at this point in the history
  • Loading branch information
untitaker committed Jun 25, 2023
1 parent 476b5db commit 160be91
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ vec![String::from(String::from(String::from("foo"), "bar"), "baz")]
Let's try that again. `spacemod` lets you write:

```bash
spacemod '" (.*) " \.to_string ( )' 'String::from("$1")'
spacemod -S '" (.*) " \.to_string ( )' 'String::from("$1")'
```

The correct end result looks like:
Expand Down
12 changes: 12 additions & 0 deletions src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ impl Expr {
Ok(rv)
}

pub fn parse_fixed_string(input: &str) -> Result<Self, ParseError> {
Ok(Expr {
tokens: vec![Token::Text(regex::escape(input))],
})
}

pub fn parse_regex(input: &str) -> Result<Self, ParseError> {
Ok(Expr {
tokens: vec![Token::Text(input.to_owned())],
})
}

pub fn parse_expr(input: &str, user_defined_pairs: Pairs) -> Result<Self, ParseError> {
let pairs = {
let mut rv = default_pairs();
Expand Down
33 changes: 31 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ struct Cli {
#[structopt(short = "u", long = "hidden")]
hidden: bool,

/// Enable parenthesis-matching and implicit whitespace matching.
///
/// Any (unescaped) space in SEARCH is implicitly replaced with '\s*', and parenthesis
/// surrounded by spaces such as in '( .* )' will match literally.
#[structopt(short = "S", long = "spacemode")]
spacemode: bool,

/// Interpret SEARCH as literal string.
///
/// Disables all pattern-matching.
#[structopt(short = "F", long = "fixed-strings")]
fixed_strings: bool,

/// Have regex work over multiple lines.
///
/// When using parenthesis-matching, multiline mode is already enabled.
Expand Down Expand Up @@ -89,6 +102,8 @@ fn main() -> Result<(), Error> {
file_or_dir,
multiline,
pairs,
spacemode,
fixed_strings,
} = Cli::from_args();

// most of the work we do is kind of I/O bound. rayon assumes CPU-heavy workload. we could
Expand All @@ -101,8 +116,22 @@ fn main() -> Result<(), Error> {
.unwrap();

let user_defined_pairs = parse_pairs(&pairs)?;
let expr = Expr::parse_expr(&search, user_defined_pairs.clone())
.context("failed to parse search string")?;

let expr = if spacemode && fixed_strings {
anyhow::bail!("-S conflicts with -F");
} else if fixed_strings {
Expr::parse_fixed_string(&search).context("failed to parse search string")?
} else if spacemode {
Expr::parse_expr(&search, user_defined_pairs.clone())
.context("failed to parse search string")?
} else {
if !user_defined_pairs.is_empty() {
anyhow::bail!("-p will do nothing because -s was not provided.");
}

Expr::parse_regex(&search).context("failed to parse search string")?
};

let replacer = expr.get_replacer(multiline, user_defined_pairs)?;

let mut walk_builder = if file_or_dir.is_empty() {
Expand Down
59 changes: 59 additions & 0 deletions tests/acceptance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,62 @@ fn test_basic() -> Result<(), Error> {
assert_snapshot!(read_to_string(harness.join("content"))?, @"hello lard");
Ok(())
}

#[test]
fn test_parenthesis() -> Result<(), Error> {
let harness = setup()?;

write(harness.join("content"), "hello ( world ) lard )")?;

assert_cmd!(harness, spacemod "--accept-all" "-S" "hello ( (.*) )" "goodbye", @r###"
success: true
exit_code: 0
----- stdout -----
Automatically changed ./content
----- stderr -----
"###);

assert_snapshot!(read_to_string(harness.join("content"))?, @"goodbye lard )");

Ok(())
}

#[test]
fn test_no_parenthesis() -> Result<(), Error> {
let harness = setup()?;

write(harness.join("content"), "hello ( world ) lard )")?;

assert_cmd!(harness, spacemod "--accept-all" "hello ( (.*) )" "goodbye", @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
"###);

assert_snapshot!(read_to_string(harness.join("content"))?, @"hello ( world ) lard )");

Ok(())
}

#[test]
fn test_fixed_string() -> Result<(), Error> {
let harness = setup()?;

write(harness.join("content"), "hello ( (.*) ) lard )")?;

assert_cmd!(harness, spacemod "--accept-all" "-F" "hello ( (.*) )" "goodbye", @r###"
success: true
exit_code: 0
----- stdout -----
Automatically changed ./content
----- stderr -----
"###);

assert_snapshot!(read_to_string(harness.join("content"))?, @"goodbye lard )");

Ok(())
}

0 comments on commit 160be91

Please sign in to comment.