applyPatch function

ApplyPatchUtils applyPatch(
  1. String baseText,
  2. List<DiffOp> ops
)

Applies an edit script ops to baseText (line-based).

Reconstructs text by taking lines from baseText for equal/delete and from ops for insert. Validates that equal segments match baseText; returns ApplyPatchConflict if they do not.

Implementation

ApplyPatchUtils applyPatch(String baseText, List<DiffOp> ops) {
  final List<String> lines = _splitLines(baseText);
  int lineIndex = 0;
  final StringBuffer out = StringBuffer();
  // lineIndex is the cursor into baseText. equal/delete consume base lines and
  // must match what the patch expected (the patch is rejected otherwise, so a
  // stale patch can't corrupt mismatched text); insert emits without consuming.
  for (final DiffOp op in ops) {
    final List<String> opLines = _opToLines(op.text);
    switch (op.kind) {
      case DiffOpKind.equal:
        for (final String ln in opLines) {
          if (lineIndex >= lines.length) {
            return ApplyPatchConflict(
              'Expected line "${ln.trimRight()}" but base has no more lines',
            );
          }
          final String baseLine = lines[lineIndex];
          if (baseLine != ln) {
            return ApplyPatchConflict(
              'Conflict at line ${lineIndex + 1}: expected "${ln.trimRight()}", got "${baseLine.trimRight()}"',
            );
          }
          out.write(baseLine);
          lineIndex++;
        }
        break;
      case DiffOpKind.delete:
        for (final String ln in opLines) {
          if (lineIndex >= lines.length) {
            return ApplyPatchConflict(
              'Delete expected "${ln.trimRight()}" but base has no more lines',
            );
          }
          final String baseLine = lines[lineIndex];
          if (baseLine != ln) {
            return ApplyPatchConflict(
              'Conflict at line ${lineIndex + 1}: delete expected "${ln.trimRight()}", got "${baseLine.trimRight()}"',
            );
          }
          lineIndex++;
        }
        break;
      case DiffOpKind.insert:
        for (final String ln in opLines) {
          out.write(ln);
        }
        break;
    }
  }
  // The patch must account for every base line; leftover base lines mean the
  // patch was built against a shorter/different base, so reject rather than
  // silently truncate them.
  if (lineIndex < lines.length) {
    return ApplyPatchConflict('Base has ${lines.length - lineIndex} extra lines after patch');
  }
  return ApplyPatchSuccess(out.toString());
}