Skip to content

carry text edits until successful migration#132

Merged
georghinkel merged 3 commits intomainfrom
anytext-fixes
Mar 9, 2026
Merged

carry text edits until successful migration#132
georghinkel merged 3 commits intomainfrom
anytext-fixes

Conversation

@georghinkel
Copy link
Copy Markdown
Contributor

@georghinkel georghinkel commented Mar 9, 2026

Summary by MergeMonkey

  • New Capabilities:
    • ChangeTracker now accumulates text edits and merges overlapping/adjoining edits automatically instead of replacing them on each update, preserving user edits through migration failures.
  • Housekeeping:
    • Added debug assertions in ModelElement to detect parent hierarchy loops and self-reference errors.

@mergemonkeyhq
Copy link
Copy Markdown

mergemonkeyhq bot commented Mar 9, 2026

Risk AssessmentNEEDS-TESTING

Focus areas: ChangeTracker merge logic · Parser edit lifecycle integration · Test coverage for edge cases

Assessment: Changes edit persistence behavior; logic-heavy merge handling needs verification.

Walkthrough

User makes multiple text edits to a model. Each edit is added to ChangeTracker via AddEdit(), which merges with existing edits if adjacent or overlapping to form consolidated operations. If migration fails (e.g., grammar mismatch), previous edits remain stored. On successful migration, Reset() clears the accumulated edits. This prevents user edits from being silently lost when early edits cause migration failures.

Changes

Files Summary
Text Edit Change Tracking System
AnyText/AnyText.Core/ItemEqualityComparer.cs
AnyText/AnyText.Core/TextEdit.cs
AnyText/AnyText.Core/ChangeTracker.cs
Implements persistent edit accumulation with merge logic: new ItemEqualityComparer compares string arrays element-bywise, TextEdit gains equatable implementation, ChangeTracker converts to List-based storage with adjacent/overlap merge handling, tracks original text for each edit to enable intelligent merging.
Parser Integration
AnyText/AnyText.Core/Parser.cs
Updates parser to call AddEdit() per edit instead of SetEdits(), moves Reset() call after input capture so edits survive migration failures, applies edits incrementally then resets on successful migration.
Utility Additions
AnyText/AnyText.Core/ParsePosition.cs
Adds ToString() override for debugging convenience.
Debug Safety Checks
Models/Models/ModelElement.cs
Adds #if DEBUG guardrails detecting parent hierarchy loops and self-referencing during parent traversal.

Sequence Diagram

sequenceDiagram
 participant U as User
 participant P as Parser
 participant CT as ChangeTracker
 participant M as Matcher
 U->>P: Update(edit1)
 P->>CT: AddEdit(edit1, input)
 CT->>CT: Store edit + original text
 P->>M: Apply(edit1)
 M-->>P: migration result
 alt migration fails
 P-->>U: Return failed result
 note over CT: Edits retained for next attempt
 else migration succeeds
 P->>CT: Reset()
 end
 U->>P: Update(edit2)
 P->>CT: AddEdit(edit2, input)
 CT->>CT: Merge with edit1 if adjacent/overlap
Loading

Dig Deeper With Commands

  • /review <file-path> <function-optional>
  • /chat <file-path> "<question>"
  • /roast <file-path>

Runs only when explicitly triggered.

@georghinkel georghinkel merged commit da953ae into main Mar 9, 2026
1 check passed
@georghinkel georghinkel deleted the anytext-fixes branch March 9, 2026 14:34
var updatedText = insertion.Apply(adjacentEdit.NewText);
var overlapEnd = UpdateEndPosition(edit.End, adjacentEdit.EndAfterEdit, edit.Start);
_edits[editIndex] = new TextEdit(adjacentEdit.Start, overlapEnd, updatedText);
// TODO: update oldText
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overlapping edit merge leaves stale original text behind

In the overlap branch, the tracker rewrites _edits[editIndex] but never updates _oldTexts[editIndex]. That breaks the whole "carry edits until a successful migration" flow: a later edit can compare against the wrong pre-edit content and either drop a real change or keep an obsolete one.

Update _oldTexts[editIndex] when collapsing overlapping edits. Easiest fix is to recompute the merged old text for the new combined span, or explicitly carry forward the correct original slice before replacing _edits[editIndex].

var hash = obj.Length.GetHashCode();
for (int i = 0; i < obj.Length; i++)
{
hash ^= 23 * hash + obj[i].GetHashCode();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array comparer crashes on null entries

GetHashCode assumes every array element is non-null and calls obj[i].GetHashCode() directly. This comparer is generic and used for string arrays, so a single null element will throw during hashing instead of behaving like Equals, which already tolerates null arrays.

Use a null-safe hash, e.g. hash = (hash * 23) ^ (obj[i]?.GetHashCode() ?? 0); and mirror that in TextEdit.GetHashCode() if you want the new equality implementation to stay robust too.

@mergemonkeyhq
Copy link
Copy Markdown

mergemonkeyhq bot commented Mar 18, 2026

Actionable Comments Posted: 2

🧾 Coverage Summary
✔️ Covered (9 files)
- AnyText/AnyText.Core/ChangeTracker.cs
- AnyText/AnyText.Core/ItemEqualityComparer.cs
- AnyText/AnyText.Core/ParsePosition.cs
- AnyText/AnyText.Core/Parser.cs
- AnyText/AnyText.Core/TextEdit.cs
- AnyText/AnyText.history
- AnyText/Tests/AnyText.Tests/Diffs/ChangeTrackerTests.cs
- Models/Models.history
- Models/Models/ModelElement.cs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant