use super::*;
fn inner_substitute(
history: &mut crate::History<crate::Buffer>,
clipboard: &mut Clipboard,
command: &str, selection: (usize, usize),
pattern: &str,
substitute: &str,
global: bool,
) -> Result<usize> {
use regex::RegexBuilder;
let regex = RegexBuilder::new(pattern)
.multi_line(true)
.build()
.map_err(|e| EdError::regex_error(e, pattern))
?;
let buffer_view = history.current();
buffer_view.verify_selection(selection)?;
let mut agg = String::new();
for line in &buffer_view[selection.0 - 1 .. selection.1] {
agg.push_str(&line.text);
}
if !regex.is_match(&agg) {
return Err(EdError::RegexNoMatch(pattern.to_owned()));
}
let buffer = history.current_mut(command.into());
let mut tail = buffer.split_off(selection.1);
let before = buffer.split_off(selection.0 - 1);
*clipboard = (&*before).into();
let replace = substitute_escape(substitute);
let after = if global {
regex.replace_all(&agg, replace).to_string()
} else {
regex.replace(&agg, replace).to_string()
};
for line in after.lines() {
buffer.push(
Line::new( format!("{}\n", line) )
.map_err(InternalError::InvalidLineText)?
)
}
let end = buffer.len();
buffer.append(&mut tail);
Ok(end)
}
pub fn substitute(
state: &mut Ed<'_>,
pflags: &mut PrintingFlags,
command: &str,
selection: Option<Sel<'_>>,
tail: &str,
) -> Result<()> {
let selection = interpret_selection(&state, selection, state.selection)?;
let tail = tail.trim_end_matches('\n');
if tail.is_empty() {
match &state.prev_s {
None => return Err(EdError::DefaultSArgsUnset),
Some(s) => {
pflags.p = s.p;
pflags.n = s.n;
pflags.l = s.l;
let end = inner_substitute(
&mut state.history,
&mut state.clipboard,
command,
selection,
&s.pattern,
&s.substitute,
s.global,
)?;
state.selection = (selection.0.min(end).max(1), end);
}
}
}
else {
let expressions = parse_expressions(tail)?;
if expressions.len() != 3 {
return Err( EdError::ArgumentsWrongNr{expected: "none or 3".into(), received: expressions.len()} );
}
let mut flags = parse_flags(&(expressions[2]), "gpnl")?;
let g = flags.remove(&'g').unwrap();
pflags.p = flags.remove(&'p').unwrap();
pflags.n = flags.remove(&'n').unwrap();
pflags.l = flags.remove(&'l').unwrap();
let end = inner_substitute(
&mut state.history,
&mut state.clipboard,
command,
selection,
&expressions[0],
&expressions[1],
g,
)?;
state.selection = (selection.0.min(end).max(1), end);
state.prev_s = Some(Substitution{
pattern: expressions[0].to_string(),
substitute: expressions[1].to_string(),
global: g,
p: pflags.p,
n: pflags.n,
l: pflags.l,
});
}
Ok(())
}