Skip to content

Rewrite indoc! macro Has Fix

Description

This example, created from a Tweet, shows a refactoring operation being performed on Rust source code. The changes involve removing indoc! macro declarations while preserving the CSS-like content within them.

Previously, the same refactor is implemented by a unreadable monster regex in vim syntax.

Click to see the original regex (neovim, btw)
vimscript
:%s/\v(indoc!|)(| )([|\{)r#"(([^#]+|\n+)+)"#/`\4`

I have to confess that I don't understand this regex even if I use neovim, btw.

Let Claude break it down piece by piece:

  • :%s/ - Vim substitution command for all lines
  • \v - Very magic mode in vim for simpler regex syntax
  • (indoc!|) - First capture group: matches either "indoc!" or nothing
  • (| ) - Second capture group: matches either empty string or a space
  • ([|\{) - Third capture group: matches either [ or {
  • r#" - Matches literal r#" (Rust raw string delimiter)
  • (([^#]+|\n+)+) - Fourth capture group (nested):
    • [^#]+ - One or more non-# characters
    • |\n+ - OR one or more newlines
    • Outer ()+ makes this repeat one or more times
  • "# - Matches the closing raw string delimiter
  • `\4` - Replaces with the fourth capture group wrapped in backticks

This regex is designed to find Rust raw string literals (possibly wrapped in indoc! macro), capture their content, and replace the entire match with just the content wrapped in backticks. It's more precise than my previous explanation and matches the pattern you're showing.

Pattern

shell
ast-grep --pattern 'indoc! { r#"$$$A"# }' --rewrite '`$$$A`' sgtest.rs

Example

rs
fn main() {
    indoc! {r#"
        .foo {
            order: 1;
        }
    "#};
}

Diff

rs
fn main() {
    indoc! {r#" // [!code --]
    `.foo {    // [!code ++]
        order: 1;
    }
    "#}; 
        `; 
}

Contributed by

Zack in SF

Made with ❤️ with Rust