uttrflow / uttrflow/uttrflow-swift
Formatter diff treats CRLF text as one line and bypasses the 20,000-line guard
- Dominant language
- Swift
- Stars
- 4
- Forks
- 17
- Avg merge
- 3h 32m
- Merged PRs (30d)
- 277
Description
## Problem
`TextDiff.split` splits Swift Characters on `"\n"`. A CRLF pair is one Character, so Windows-style line endings do not split. The formatter preview consequently represents a multiline CRLF clip as one removed block and one added block, losing unchanged context. The same split supplies the 20,000-line guard, so that guard also miscounts CRLF input.
## Verified reproduction
On unmodified main `8d93f5b8574f3c7b8c30086d9c817bf7f5d4827d`, call the production API:
```swift
TextDiff.compare(from: "one\r\ntwo\r\nthree", to: "one\r\nchanged\r\nthree")
```
Actual: two rows containing the complete before and after strings, with zero unchanged rows. Expected: unchanged first and third lines, one removal for `two`, and one addition for `changed`. The equivalent LF input correctly retains two unchanged rows.
A second probe joins 20,001 copies of `x` with CRLF and compares the result with `x`. This is below the byte limit, but above the documented line limit. Actual: `.lines` with two rows. Expected: `.tooLarge(before: 20001, after: 1)`.
The 14 existing diff tests passed, including the randomized table oracle and work-limit checks. The new CRLF probes failed four assertions; the LF control passed. These were isolated Swift tests against the production module, not a UI test or a measurement of rendering cost.
## Code path and impact
- `Sources/UttrflowClipboard/TextDiff.swift`: `split`, `compare`, and the separate byte-based `lineCount` use inconsistent newline handling.
- `Sources/UttrflowUX/PanelSheetPresentation.swift`: the formatting case passes original and formatted clip text to `TextDiff.compare`; `formattingSheet` derives its displayed change count and preview from those rows.
- `Tests/UttrflowClipboardTests/TextDiffTests.swift`: the randomized oracle uses the same LF-only splitting and LF-only generated input, so it cannot expose this boundary.
Users reviewing formatted CRLF code cannot see a focused line diff, and the line limit does not behave as advertised. The byte limit remains effective; no unbounded memory claim is made.
## Suggested fix and acceptance checks
Use a shared newline-aware splitting/counting policy that recognizes CRLF while preserving intended trailing-empty-line behavior. Add direct expected-output fixtures for LF, CRLF, mixed endings, and trailing newlines, plus a CRLF input just above the line limit. Keep the existing shortest-edit and resource-bound tests passing.
This differs from #484 (repeated computation), #513 (eager view construction), and #406 (the previous quadratic diff algorithm): the problem here is newline segmentation and the resulting incorrect preview/guard.
Contributor guide
Research direction
Start in Sources/UttrflowClipboard/TextDiff.swift by reading split, compare, and lineCount to understand their newline handling. Then inspect Tests/UttrflowClipboardTests/TextDiffTests.swift and add direct fixtures for LF, CRLF, mixed endings, trailing newlines, and a CRLF input above the line limit. Done means the expected rows and .tooLarge counts are produced while the existing shortest-edit and resource-bound tests still pass.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100