///|
/// A scalar-range patch against an exact source snapshot. Expected text prevents
/// stale editor state from silently changing a different token.
pub(all) struct SourceEdit {
span : Span
expected : String
replacement : String
}
///|
pub suberror EditError {
InvalidEdit(Int32)
UnorderedOrOverlappingEdit(Int32)
StaleEdit(Int32)
}
///|
/// Apply caller-ordered, nonoverlapping patches atomically. No partial result is
/// returned on failure. Two edits at the same start are deliberately ambiguous.
pub fn apply_edits(
source : String,
edits : Array[SourceEdit],
) -> String raise EditError {
let map = SourceMap::new(source)
let mut previous_end = 0
let mut previous_start = -1
for i, edit in edits {
let { start, end, } = edit.span
if start < 0 || end < start || end > map.length() {
raise InvalidEdit(i)
}
if start < previous_end || start <= previous_start {
raise UnorderedOrOverlappingEdit(i)
}
let actual = map.slice(edit.span) catch { _ => raise InvalidEdit(i) }
if actual != edit.expected {
raise StaleEdit(i)
}
previous_start = start
previous_end = end
}
let out = StringBuilder()
let mut cursor = 0
for i, edit in edits {
let prefix = map.slice({ start: cursor, end: edit.span.start, }) catch {
_ => raise InvalidEdit(i)
}
out.write_string(prefix)
out.write_string(edit.replacement)
cursor = edit.span.end
}
let tail = map.slice({ start: cursor, end: map.length(), }) catch {
_ => raise InvalidEdit(edits.length())
}
out.write_string(tail)
out.to_string()
}