Skip to content

Decouple TextDiff engine and add reusable diff results#7

Merged
iSapozhnik merged 3 commits intomainfrom
feature/decouple-text-engine
Mar 26, 2026
Merged

Decouple TextDiff engine and add reusable diff results#7
iSapozhnik merged 3 commits intomainfrom
feature/decouple-text-engine

Conversation

@iSapozhnik
Copy link
Owner

@iSapozhnik iSapozhnik commented Mar 25, 2026

Summary

  • add a reusable TextDiffResult API with persistable change records and summary stats
  • share segment indexing between engine result generation and revert handling
  • support precomputed, display-only rendering in TextDiffView and NSTextDiffView
  • document the persistence and precomputed rendering workflows and add test coverage, including a snapshot baseline

Testing

  • swift test 2>&1 | xcsift --quiet

Closes #6

Summary by CodeRabbit

  • New Features

    • Added a public engine API that returns a reusable precomputed diff payload with structured change records and a summary.
    • Views can render precomputed diffs in a read-only/result-driven mode (revert actions disabled).
  • Documentation

    • README updated with engine-only workflow and examples for rendering precomputed results.
  • Tests

    • New tests and snapshot helpers to validate result generation, rendering, and result-driven behavior.

@coderabbitai
Copy link

coderabbitai bot commented Mar 25, 2026

Warning

Rate limit exceeded

@iSapozhnik has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fb6e4509-ae2b-4bf1-bbc3-68624fa3c262

📥 Commits

Reviewing files that changed from the base of the PR and between 6eefbae and 0ae38e4.

📒 Files selected for processing (1)
  • Sources/TextDiff/DiffSegmentIndexer.swift
📝 Walkthrough

Walkthrough

Introduces a persistable TextDiffResult and engine API TextDiffEngine.result(...). Views can render precomputed results (disabling revert interactions) and reuse indexed segment data via a new DiffSegmentIndexer, decoupling computation from view rendering. Tests and README updated.

Changes

Cohort / File(s) Summary
Result data model & engine
Sources/TextDiff/TextDiffResult.swift, Sources/TextDiff/TextDiffEngine.swift
Add TextDiffResult, TextDiffChange, TextDiffSummary, and TextDiffChangeKind. Add TextDiffEngine.result(original:updated:mode:); refactor segment computation into computeSegments.
Segment indexing utility
Sources/TextDiff/DiffSegmentIndexer.swift, Sources/TextDiff/AppKit/DiffRevertActionResolver.swift
Add DiffSegmentIndexer and IndexedDiffSegment. Remove local indexing from DiffRevertActionResolver, which now delegates to the indexer and adapts to indexed range fields.
AppKit: content source & view APIs
Sources/TextDiff/AppKit/NSTextDiffContentSource.swift, Sources/TextDiff/AppKit/NSTextDiffView.swift
Introduce NSTextDiffContentSource enum. Add init(result:style:) and setContent(result:style:) to NSTextDiffView. View tracks content source and disables/suppresses revert/layout updates when result-driven.
Representable & SwiftUI view updates
Sources/TextDiff/AppKit/DiffTextViewRepresentable.swift, Sources/TextDiff/TextDiffView.swift
DiffTextViewRepresentable accepts optional result and constructs/updates NSTextDiffView using result-driven APIs; revert actions disabled when rendering a result. Add TextDiffView(result:style:) initializer and pass result through to representable; disable revert bindings for result-driven views.
Docs
README.md
Document engine-only workflow, precomputed rendering examples for SwiftUI and AppKit, and clarify mode-specific outputs and result-driven display-only behavior.
Tests & snapshot support
Tests/TextDiffTests/TextDiffEngineTests.swift, Tests/TextDiffTests/NSTextDiffViewTests.swift, Tests/TextDiffTests/TextDiffSnapshotTests.swift, Tests/TextDiffTests/SnapshotTestSupport.swift, Tests/TextDiffTests/DiffRevertActionResolverTests.swift
Add tests for TextDiffEngine.result (segments/changes/summary), result-driven view behavior and snapshots. Add snapshot helper overload for TextDiffResult. Update one test to use DiffSegmentIndexer.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Engine as TextDiffEngine
    participant Result as TextDiffResult
    participant View as TextDiffView / NSTextDiffView
    participant Revert as RevertHandler

    rect rgba(100, 150, 200, 0.5)
    Note over User,Revert: Text-driven flow
    User->>View: init(original, updated, mode)
    View->>Engine: diff(original, updated, mode)
    Engine->>Engine: compute segments
    View->>View: render & enable revert
    View->>Revert: revert actions enabled
    end

    rect rgba(200, 150, 100, 0.5)
    Note over User,Revert: Result-driven flow
    User->>Engine: result(original, updated, mode)
    Engine->>Engine: compute segments, changes, summary
    Engine->>Result: return TextDiffResult
    User->>View: init(result)
    View->>View: render from result
    View->>Revert: revert actions disabled
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

Engine computes, view just shows,
Segments stored where memory grows,
Revert is quiet when result arrives,
Snapshots prove the rendering lives,
A tidy split — the diff survives.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main changes: decoupling the TextDiff engine and introducing reusable diff results as shown by new TextDiffResult, TextDiffChange, and related APIs.
Linked Issues check ✅ Passed All coding requirements from issue #6 are met: TextDiffEngine is decoupled (result API added), enables independent use and statistics collection (TextDiffResult with summary), and view coupling is removed (separate init paths for result-driven rendering).
Out of Scope Changes check ✅ Passed All changes align with decoupling the engine and adding reusable results. README documentation, view updates, and test additions all support the stated objectives; no unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/decouple-text-engine

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/TextDiff/DiffSegmentIndexer.swift`:
- Around line 33-44: The asserts around the textMatches validation are behind a
wrong compile guard (`#if` !TESTING) so they never run when TESTING is set; remove
the surrounding conditional or invert it so the asserts execute in debug/testing
builds. Specifically, in DiffSegmentIndexer.swift keep the textMatches calls and
the assert checks for originalMatches and updatedMatches (referencing
originalMatches, updatedMatches, textMatches, originalCursor, updatedCursor,
index, and segment.text) but delete the `#if` !TESTING / `#endif` wrapper (or change
it to `#if` TESTING) so the assertions run during testing/debugging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3159370-8e9e-4e3b-9d2c-e294a86a2c93

📥 Commits

Reviewing files that changed from the base of the PR and between 167064a and c51ffb0.

⛔ Files ignored due to path filters (1)
  • Tests/TextDiffTests/__Snapshots__/TextDiffSnapshotTests/precomputed_result_rendering.1.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • README.md
  • Sources/TextDiff/AppKit/DiffRevertActionResolver.swift
  • Sources/TextDiff/AppKit/DiffTextViewRepresentable.swift
  • Sources/TextDiff/AppKit/NSTextDiffContentSource.swift
  • Sources/TextDiff/AppKit/NSTextDiffView.swift
  • Sources/TextDiff/DiffSegmentIndexer.swift
  • Sources/TextDiff/TextDiffEngine.swift
  • Sources/TextDiff/TextDiffResult.swift
  • Sources/TextDiff/TextDiffView.swift
  • Tests/TextDiffTests/DiffRevertActionResolverTests.swift
  • Tests/TextDiffTests/NSTextDiffViewTests.swift
  • Tests/TextDiffTests/SnapshotTestSupport.swift
  • Tests/TextDiffTests/TextDiffEngineTests.swift
  • Tests/TextDiffTests/TextDiffSnapshotTests.swift

@iSapozhnik iSapozhnik merged commit b5815ad into main Mar 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decouple TextDiffEngine

1 participant