patchfile 0.2.2

Render a set of diffs into diff -u format, which is compatible with patch.
Documentation
use std::cmp::max;
use crate::{DiffDelta, DiffU, LineNumber, Range};

const SELECT_CLEARANCE: usize = 3;
const LOOKBACK_RANGE: usize = SELECT_CLEARANCE * 2 - 1;

pub(crate) fn render_diff<'a>(diff: &Vec<DiffDelta<&'a str>>) -> Vec<DiffU<'a>> {
    let mut buf = vec![];

    let mut zipped_lines = vec![];
    {
        let mut left = 0;
        let mut right = 0;
        for d in diff {
            let content = match d {
                DiffDelta::Left(s) => {
                    left += 1;
                    DiffU::Deletion(*s)
                }
                DiffDelta::Both(_, s) => {
                    left += 1;
                    right += 1;
                    DiffU::Display(*s)
                }
                DiffDelta::Right(s) => {
                    right += 1;
                    DiffU::Addition(*s)
                }
            };

            let ln = LineNumber { left: max(left, 1), right: max(right, 1) };
            zipped_lines.push((ln, content));
        }
    }

    let mut sync_pos = vec![];
    {
        let mut delta_start = None;
        let mut consecutive_sames = 0;
        for (i, (_, d)) in zipped_lines.iter().enumerate().skip(1) {
            match d {
                DiffU::CaretPos { .. } | DiffU::Display(_) => {
                    consecutive_sames += 1;

                    if consecutive_sames >= LOOKBACK_RANGE {
                        if let Some(mut start) = delta_start {
                            delta_start = None;

                            if start > SELECT_CLEARANCE {
                                start -= SELECT_CLEARANCE;
                            } else {
                                start = 0;
                            }
                            sync_pos.push((start, i + 1 - SELECT_CLEARANCE));
                        }
                    }
                    continue;
                }
                DiffU::Addition(_) | DiffU::Deletion(_) => {
                    consecutive_sames = 0;

                    if delta_start.is_none() {
                        delta_start = Some(i);
                    }
                }
            }
        }

        if let Some(mut start) = delta_start {
            if start > SELECT_CLEARANCE {
                start -= SELECT_CLEARANCE;
            } else {
                start = 0;
            }
            sync_pos.push((start, zipped_lines.len() - 1));
        }
    }

    if sync_pos.is_empty() {
        sync_pos.push((0, diff.len() - 1));
    }

    for (lower, upper) in sync_pos {
        let left_start = zipped_lines[lower].0.left;
        let left_count = zipped_lines[upper].0.left - left_start + 1;
        let right_start = zipped_lines[lower].0.right;
        let right_count = zipped_lines[upper].0.right - right_start + 1;
        buf.push(DiffU::CaretPos {
            left: Range { start: left_start, count: left_count },
            right: Range { start: right_start, count: right_count },
        });

        for i in lower..=upper {
            buf.push(zipped_lines[i].1.clone());
        }
    }

    return buf;
}