in src/main.rs [331:398]
fn present_and_apply_patches(
&mut self,
regex: &Regex,
subst: &str,
path: &Path,
mut contents: String,
) -> Result<()> {
// Overall flow:
// 0) offset = 0.
// 1) Find next patch from *current* contents of the file at
// given offset. If none, we are done.
// 2) Set the offset to the start of the previous patch + 1.
// 3) Ask the user to make a modification to the file.
// 4) Re-read the file. (User may have made arbitrary edits!)
let mut offset = 0;
while offset < contents.len() {
{
let mat = regex.find(&contents[offset..]);
match mat {
None => break,
Some(mat) => {
let mut new_contents = contents[..offset].to_string();
let new_trailing_contents = regex.replace(&contents[offset..], subst);
new_contents.push_str(&new_trailing_contents);
// Zero-length matches can happen with any
// regex that matches the empty string,
// such as `a?` or the empty regex.
let is_zero_length_match = mat.end() == mat.start();
let (start_line, _) = index_to_row_col(&contents, mat.start() + offset);
let (end_line, _) = index_to_row_col(
&contents,
// Avoid generating index of -1 when start
// == end == offset = 0 for a zero-length
// match.
backward_to_char_boundary(
&contents,
mat.end() + offset - if is_zero_length_match { 0 } else { 1 },
),
);
let accepted = self.ask_about_patch(
path,
&contents,
start_line + 1,
end_line + 1,
&new_contents,
)?;
if accepted {
offset = to_char_boundary(
&contents,
offset
+ mat.start()
+ subst.len()
// Ensure forward progress when there
// is a zero-length match.
+ if is_zero_length_match { 1 } else { 0 },
);
} else {
// Advance to the next character after the match.
offset = to_char_boundary(&contents, offset + mat.end() + 1);
}
}
}
}
// re-open file in case contents changed.
contents = read_to_string(path)?;
}
Ok(())
}