weavatrix-edit 0.1.4

Deterministic, Unicode-safe edit-plan validation and application for source refactoring
Documentation
use weavatrix_edit::{ByteEdit, Provenance, prepare_byte_edits};

#[test]
fn normalized_changes_expose_exact_source_and_output_ranges() {
    let prepared = prepare_byte_edits(
        "abcd",
        &[
            ByteEdit::insert(1, "<", Provenance::EXACT_LSP),
            ByteEdit::replace(1..3, "bc", "X", Provenance::RESOLVED),
            ByteEdit::delete(3..4, "d", Provenance::EXTRACTED),
        ],
    )
    .unwrap();

    let changes = prepared.changes().collect::<Vec<_>>();
    assert_eq!(prepared.apply().text, "a<X");
    assert_eq!(changes.len(), 3);
    assert_eq!(changes[0].source_range, 1..1);
    assert_eq!(changes[0].output_range, 1..2);
    assert_eq!(changes[0].before, "");
    assert_eq!(changes[0].after, "<");
    assert_eq!(changes[0].input_order, 0);
    assert_eq!(changes[0].provenance().as_str(), Provenance::EXACT_LSP);
    assert_eq!(changes[0].provenance_count(), 1);
    assert_eq!(changes[1].source_range, 1..3);
    assert_eq!(changes[1].output_range, 2..3);
    assert_eq!(changes[1].before, "bc");
    assert_eq!(changes[1].after, "X");
    assert_eq!(changes[2].source_range, 3..4);
    assert_eq!(changes[2].output_range, 3..3);

    let summary = prepared.change_summary();
    assert_eq!(summary.edits, 3);
    assert_eq!(summary.bytes_before, 4);
    assert_eq!(summary.bytes_after, 3);
    assert_eq!(summary.removed_bytes, 3);
    assert_eq!(summary.inserted_bytes, 2);
}

#[test]
fn union_preserves_distinct_provenance_for_an_identical_replacement() {
    let exact = prepare_byte_edits(
        "abc",
        &[ByteEdit::replace(0..1, "a", "A", Provenance::EXACT_LSP)],
    )
    .unwrap();
    let resolved = prepare_byte_edits(
        "abc",
        &[ByteEdit::replace(0..1, "a", "A", Provenance::RESOLVED)],
    )
    .unwrap();

    let merged = exact.union(resolved).unwrap();
    let changes = merged.changes().collect::<Vec<_>>();
    assert_eq!(changes.len(), 1);
    let provenances = changes[0]
        .provenances()
        .map(Provenance::as_str)
        .collect::<Vec<_>>();
    assert_eq!(provenances, [Provenance::EXACT_LSP, Provenance::RESOLVED]);
    assert_eq!(changes[0].provenance_count(), 2);
}

#[test]
fn union_rebases_but_preserves_each_plan_input_order() {
    let left = prepare_byte_edits(
        "abcd",
        &[ByteEdit::replace(1..2, "b", "B", Provenance::EXACT_LSP)],
    )
    .unwrap();
    let right = prepare_byte_edits(
        "abcd",
        &[
            ByteEdit::replace(3..4, "d", "D", Provenance::RESOLVED),
            ByteEdit::replace(0..1, "a", "A", Provenance::RESOLVED),
        ],
    )
    .unwrap();

    let merged = left.union(right).unwrap();
    let changes = merged.changes().collect::<Vec<_>>();
    assert_eq!(
        changes
            .iter()
            .map(|change| (change.source_range.clone(), change.input_order))
            .collect::<Vec<_>>(),
        [(0..1, 2), (1..2, 0), (3..4, 1)]
    );
}