uttrflow / uttrflow/uttrflow-swift

Formatter diff treats CRLF text as one line and bypasses the 20,000-line guard

Open
#803 0 comments 0 reactions 0 assignees View on GitHub
bug P2
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.