Reject on an optimistically-applied proposal now restores the retained original even after interior tweaks: appliedSpans tracked-range fallback (pure shiftTracked, boundary-straddle distrust, close-clears, rebuild-only resync), honest hard failure with a Discard action, INV-16 read-only guards, rejectAll {reverted,skipped} reporting on all batch surfaces, CodeLens reachability at the tracked span. 312 unit + 94/5 host E2E.
Closes #70.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit was merged in pull request #73.
This commit is contained in:
+52
-1
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { buildFingerprint, resolve, shift, type OffsetRange } from "../src/anchorer";
|
||||
import { buildFingerprint, resolve, shift, shiftTracked, type OffsetRange } from "../src/anchorer";
|
||||
|
||||
const DOC = "line0\nline1 needle here\nline2\nneedle again on line3\n";
|
||||
|
||||
@@ -67,3 +67,54 @@ describe("shift", () => {
|
||||
expect(shift(r, { start: 3, end: 7, newLength: 1 })).toEqual({ start: 3, end: 7 });
|
||||
});
|
||||
});
|
||||
|
||||
describe("shiftTracked (#70 — tracked applied spans)", () => {
|
||||
const r: OffsetRange = { start: 10, end: 20 };
|
||||
|
||||
it("edit entirely before the span shifts both endpoints by the delta", () => {
|
||||
expect(shiftTracked(r, { start: 0, end: 0, newLength: 3 })).toEqual({ start: 13, end: 23 });
|
||||
});
|
||||
|
||||
it("edit entirely after the span leaves it unchanged", () => {
|
||||
expect(shiftTracked(r, { start: 25, end: 28, newLength: 0 })).toEqual({ start: 10, end: 20 });
|
||||
});
|
||||
|
||||
it("edit fully inside the span keeps the start and grows/shrinks the end", () => {
|
||||
// replace [12,15) with 5 chars (delta = +2)
|
||||
expect(shiftTracked(r, { start: 12, end: 15, newLength: 5 })).toEqual({ start: 10, end: 22 });
|
||||
});
|
||||
|
||||
it("replacing the span's exact full contents is an interior edit", () => {
|
||||
expect(shiftTracked(r, { start: 10, end: 20, newLength: 4 })).toEqual({ start: 10, end: 14 });
|
||||
});
|
||||
|
||||
it("an insertion exactly at the span start lands BEFORE the span (never absorbed)", () => {
|
||||
expect(shiftTracked(r, { start: 10, end: 10, newLength: 6 })).toEqual({ start: 16, end: 26 });
|
||||
});
|
||||
|
||||
it("an insertion exactly at the span end lands AFTER the span (never absorbed)", () => {
|
||||
expect(shiftTracked(r, { start: 20, end: 20, newLength: 6 })).toEqual({ start: 10, end: 20 });
|
||||
});
|
||||
|
||||
it("an insertion at an EMPTY span's point lands before it (deletion-proposal span)", () => {
|
||||
const empty: OffsetRange = { start: 10, end: 10 };
|
||||
expect(shiftTracked(empty, { start: 10, end: 10, newLength: 3 })).toEqual({ start: 13, end: 13 });
|
||||
});
|
||||
|
||||
it("an edit straddling the span's start boundary is distrusted (INV-11)", () => {
|
||||
expect(shiftTracked(r, { start: 8, end: 12, newLength: 0 })).toBe("distrusted");
|
||||
});
|
||||
|
||||
it("an edit straddling the span's end boundary is distrusted (INV-11)", () => {
|
||||
expect(shiftTracked(r, { start: 18, end: 22, newLength: 1 })).toBe("distrusted");
|
||||
});
|
||||
|
||||
it("an edit fully containing the span (e.g. a wholesale replace) is distrusted (INV-11)", () => {
|
||||
expect(shiftTracked(r, { start: 0, end: 30, newLength: 12 })).toBe("distrusted");
|
||||
});
|
||||
|
||||
it("a deletion consuming an EMPTY span's neighborhood is distrusted (INV-11)", () => {
|
||||
const empty: OffsetRange = { start: 10, end: 10 };
|
||||
expect(shiftTracked(empty, { start: 8, end: 12, newLength: 0 })).toBe("distrusted");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user