Skip to content

feat: Add programmatic diff access API with Redline dataclass#63

Merged
houfu merged 2 commits into
mainfrom
feat/programmatic-diff-api
Oct 11, 2025
Merged

feat: Add programmatic diff access API with Redline dataclass#63
houfu merged 2 commits into
mainfrom
feat/programmatic-diff-api

Conversation

@houfu
Copy link
Copy Markdown
Owner

@houfu houfu commented Oct 10, 2025

Summary

Adds a user-friendly programmatic API for accessing and filtering diff results without parsing output strings.

Resolves #58

New Features

🎯 Redline Dataclass

  • Structured representation of changes (delete, insert, replace)
  • Direct access to changed text via source_text and test_text
  • Token position information via source_position and test_position
  • Excludes "equal" operations (only actual changes)

📊 Properties

  • r.changes - Returns list of Redline objects
  • r.redlines - Alias for changes (more intuitive naming)
  • Both exclude "equal" operations automatically

🔍 Filtering API

r.get_changes(operation="delete")    # Filter deletions
r.get_changes(operation="insert")    # Filter insertions
r.get_changes(operation="replace")   # Filter replacements
r.get_changes()                       # Get all changes

📈 Statistics API

stats = r.stats()
print(f"Total changes: {stats.total_changes}")
print(f"Deletions: {stats.deletions}")
print(f"Insertions: {stats.insertions}")
print(f"Replacements: {stats.replacements}")

⚠️ BREAKING CHANGES

The r.redlines property return type has changed:

  • Before: Returned internal objects (included "equal" operations)
  • After: Returns public Redline dataclass objects (only actual changes)

Impact: Minimal - r.redlines was primarily used internally. All rendering methods (output_markdown, output_rich) continue to work unchanged.

API Example

from redlines import Redlines, Redline, Stats

test = Redlines(
    "The quick brown fox jumps over the lazy dog.",
    "The quick brown fox walks past the lazy dog."
)

# Access changes
for redline in test.redlines:
    print(f"{redline.operation}: {redline.source_text} -> {redline.test_text}")
# Output: replace: jumps over  -> walks past 

# Filter changes
replacements = test.get_changes(operation="replace")

# Get statistics
stats = test.stats()
print(f"Total changes: {stats.total_changes}")  # 1

Testing

  • ✅ Added 17 comprehensive tests in test_changes_api.py
  • ✅ All 30 tests pass
  • ✅ Covers replacements, insertions, deletions, filtering, stats, edge cases

Implementation Details

Internal Refactoring

  • Renamed internal Redline class to DiffOperation (for rendering)
  • Created public Redline class (user-facing API)
  • Added r._diff_ops internal property for rendering code
  • Maintains backward compatibility for all rendering methods

Improvements Over Original Proposal

  1. ✅ Better naming: "Redline" instead of "Change" (matches library domain)
  2. ✅ Separate source_text & test_text fields (more useful than single 'text')
  3. ✅ Excludes "equal" operations (only actual changes)
  4. ✅ Added r.redlines as intuitive primary property
  5. ✅ Comprehensive documentation with code examples

🤖 Generated with Claude Code

Resolves #58

Adds a user-friendly programmatic API for accessing and filtering diff results without parsing output strings.

- Structured representation of changes (delete, insert, replace)
- Direct access to changed text via `source_text` and `test_text`
- Token position information via `source_position` and `test_position`
- No "equal" operations included (only actual changes)

- `r.changes` - Returns list of Redline objects
- `r.redlines` - Alias for changes (more intuitive naming)
- Excludes "equal" operations automatically

- `r.get_changes(operation="delete")` - Filter deletions
- `r.get_changes(operation="insert")` - Filter insertions
- `r.get_changes(operation="replace")` - Filter replacements
- `r.get_changes()` - Get all changes

- `r.stats()` - Returns Stats dataclass
- Provides: total_changes, deletions, insertions, replacements

⚠️ **The `r.redlines` property return type has changed:**
- **Before**: Returned internal `Redline` objects (included "equal" operations)
- **After**: Returns public `Redline` dataclass objects (only actual changes)

If you were using `r.redlines` directly in your code:
- The property now returns user-friendly `Redline` objects
- "equal" operations are no longer included
- Internal rendering code now uses `r._diff_ops` instead

**Migration**: Most users should not be affected as `r.redlines` was primarily used internally for rendering. If you need the old behavior, the rendering methods (`output_markdown`, `output_rich`) continue to work unchanged.

- Renamed internal `Redline` class to `DiffOperation` (for rendering)
- Created public `Redline` class (user-facing API)
- `r._diff_ops` - Internal property for rendering code
- Maintains backward compatibility for existing rendering methods

```python
from redlines import Redlines, Redline, Stats

test = Redlines(
    "The quick brown fox jumps over the lazy dog.",
    "The quick brown fox walks past the lazy dog."
)

for redline in test.redlines:
    print(f"{redline.operation}: {redline.source_text} -> {redline.test_text}")

replacements = test.get_changes(operation="replace")

stats = test.stats()
print(f"Total changes: {stats.total_changes}")
```

- Added 17 comprehensive tests in test_changes_api.py
- All 30 tests pass
- Covers: replacements, insertions, deletions, filtering, stats, edge cases

1. Better naming: "Redline" instead of "Change" (matches domain)
2. Separate source_text & test_text fields (more useful than single 'text')
3. Excludes "equal" operations (only actual changes)
4. Added r.redlines as intuitive primary property
5. Comprehensive documentation with examples

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@houfu houfu force-pushed the feat/programmatic-diff-api branch from fe18fd6 to 249b90b Compare October 11, 2025 02:52
- Add `from __future__ import annotations` to processor.py for consistency
- Replace `import typing as t` with direct Literal import
- Change `t.Literal` to `Literal` for cleaner code
- Add `-> None` return type annotations to all test functions

All changes ensure compatibility with Python 3.10-3.14 and pass mypy checks.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@houfu houfu merged commit bee1a55 into main Oct 11, 2025
10 checks passed
@houfu houfu deleted the feat/programmatic-diff-api branch October 11, 2025 03:13
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.

Add programmatic diff access API

1 participant