A long line you can reach, and ⌥Z to fold it away - #20
Conversation
**A line that ran off the right edge could not be reached.** With wrapping off
the editor drew the whole line and then refused to scroll sideways to any of it,
while scrolling down worked perfectly — which is the shape of the bug, because
the height is measured somewhere else entirely.
The width is measured in CodeEditTextView, and the measurement is thrown away:
private func layoutLine(…, maxFoundLineWidth: inout CGFloat) … {
…
var maxFoundLineWidth = maxFoundLineWidth // shadows the parameter
…
if maxFoundLineWidth < lineSize.width { maxFoundLineWidth = lineSize.width }
}
That `var` shadows the `inout` parameter with a local copy, so every line's width
is written into the copy and dropped when the method returns. `maxLineWidth`
never leaves zero, `estimatedWidth()` is therefore the gutter's inset alone, and
`updateFrameIfNeeded` sizes the document to `max(inset, pane width)` — never
wider than the pane, so the scroll view has nothing to scroll and the rest of the
line is simply cut off.
So the measurement is made here instead, from the same numbers:
`WidenDocumentForLongLines` takes the same route `ClipFloatingSubviews` already
takes for the same kind of reason — a coordinator, and the controller it is
handed. The widest line fragment actually laid out, plus the same insets, written
to the text view's frame. Only the widest *laid-out* line, because that is all
the package itself would ever have known: the width grows as the reader scrolls
into longer lines and never shrinks, which is the behaviour `maxLineWidth` was
written to have.
**It has to be re-applied rather than set once**, because the package puts its
own too-narrow number back on every scroll — so the two notifications are exactly
the two moments it does that: the clip view's bounds moving, and the frame being
reset underneath.
**Both observers run synchronously, and that is not a detail.** Handed `.main`
the block is an operation on the main queue and lands a turn later, so the narrow
frame is live when the scroll view next tiles — and a clip view whose document
has just shrunk under it clamps its origin back to zero. The reader would be
thrown back to column one every time they scrolled down. Widening from inside the
setter means the narrow width never survives long enough to be acted on.
Wrapping on is left alone entirely: there the pane's width is the answer and a
wide document would be the bug. And the whole thing goes quiet by itself if this
is ever fixed upstream, because the frame is only widened when the package has
left it narrower than the text needs.
**⌥Z folds a long line into the pane, and the same key lets it run off the edge
again.** VS Code's key for it, which is the one a reader coming from there tries
first. It is a `ShortcutAction` rather than a key literal, so the Editor menu's
Wrap Lines item wears it and Settings → Shortcuts can move it or take it away —
there is a new **Editor** group holding it.
Checked: the defect and the fix were both run against the real pinned package in
a headless harness. Before, with a 6149pt line laid out: `estimatedWidth=40`,
document 600 wide, and the clip view refusing any horizontal origin at all
(`constrainBoundsRect` clamps x=400 to 0). After the coordinator's measurement:
document 6189, x=400 reachable, up to 5589. Then the package's own
`updateFrameIfNeeded` puts 600 back, and re-applying restores it — which is the
loop the notifications close. With wrapping on the frame is untouched.
`Scripts/run.sh` — Debug build succeeded, no warnings, app launches. Not checked
by me: the scrolling as felt under a trackpad, which is the user's screen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QZeMBBySiGJmqM81eXurS3
|
Warning Review limit reached
Next review available in: 50 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. 📝 WalkthroughWalkthroughThe editor adds an ⌥Z shortcut for toggling long-line wrapping, registers it under the Editor shortcut group, and widens the text view when visible lines exceed its frame width. README documentation describes the new behavior. ChangesEditor wrapping behavior
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant WorkspaceApp
participant EditorPane
participant WidenDocumentForLongLines
participant TextView
WorkspaceApp->>EditorPane: Toggle Wrap Lines with toggleWrap
EditorPane->>WidenDocumentForLongLines: Apply editor sizing
WidenDocumentForLongLines->>TextView: Measure visible line fragments
WidenDocumentForLongLines->>TextView: Widen frame for overflow lines
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@Sources/Workspace/Editor/CodeEditorView.swift`:
- Around line 621-632: Update Package.swift to declare CodeEditTextView as a
direct dependency with an exact version pin at 0.12.1, matching the internals
used by the coordinator around lineStorage, lineFragments, fragment.data.width,
and edgeInsets. Keep the existing CodeEditSourceEditor dependency intact, and
ensure the explicit product/package reference resolves to the pinned version.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 82e77cdb-310d-4657-92a4-4f263991f2e5
📒 Files selected for processing (4)
README.mdSources/Workspace/Editor/CodeEditorView.swiftSources/Workspace/Models/KeyboardShortcuts.swiftSources/Workspace/WorkspaceApp.swift
`WidenDocumentForLongLines` walks CodeEditTextView's layout internals — `lineStorage`, a line's `lineFragments`, a fragment's `width`, the layout manager's `edgeInsets` — and that package was reaching us transitively through CodeEditSourceEditor, which is to say nothing in the manifest promised any version of it at all. Three of our files import it directly (the coordinator, the gutter markers and the language server join), so it is named here now and pinned to the version already resolved. `exact: "0.12.1"` narrows the `from: "0.12.1"` CodeEditSourceEditor 0.15.2 asks for rather than contradicting it, so `Package.resolved` does not move — verified byte-identical. The pin is the point: internal layout can be rearranged in a patch release, the compiler only notices if a name changes, and raising it is an audit of that coordinator rather than a routine update. Raised by CodeRabbit on #20. Checked: `Scripts/bundle.sh` — build succeeded, `Package.resolved` unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QZeMBBySiGJmqM81eXurS3
A line that ran off the right edge could not be reached. With wrapping off the editor drew the whole line and then refused to scroll sideways to any of it, while scrolling down worked perfectly — which is the shape of the bug, because the height is measured somewhere else entirely.
The width is measured in CodeEditTextView, and the measurement is thrown away:
That
varshadows theinoutparameter with a local copy, so every line's width is written into the copy and dropped when the method returns.maxLineWidthnever leaves zero,estimatedWidth()is therefore the gutter's inset alone, andupdateFrameIfNeededsizes the document tomax(inset, pane width)— never wider than the pane, so the scroll view has nothing to scroll and the rest of the line is simply cut off.So the measurement is made here instead, from the same numbers:
WidenDocumentForLongLinestakes the same routeClipFloatingSubviewsalready takes for the same kind of reason — a coordinator, and the controller it is handed. The widest line fragment actually laid out, plus the same insets, written to the text view's frame. Only the widest laid-out line, because that is all the package itself would ever have known: the width grows as the reader scrolls into longer lines and never shrinks, which is the behaviourmaxLineWidthwas written to have.It has to be re-applied rather than set once, because the package puts its own too-narrow number back on every scroll — so the two notifications are exactly the two moments it does that: the clip view's bounds moving, and the frame being reset underneath.
Both observers run synchronously, and that is not a detail. Handed
.mainthe block is an operation on the main queue and lands a turn later, so the narrow frame is live when the scroll view next tiles — and a clip view whose document has just shrunk under it clamps its origin back to zero. The reader would be thrown back to column one every time they scrolled down. Widening from inside the setter means the narrow width never survives long enough to be acted on.Wrapping on is left alone entirely: there the pane's width is the answer and a wide document would be the bug. And the whole thing goes quiet by itself if this is ever fixed upstream, because the frame is only widened when the package has left it narrower than the text needs.
⌥Z folds a long line into the pane, and the same key lets it run off the edge again. VS Code's key for it, which is the one a reader coming from there tries first. It is a
ShortcutActionrather than a key literal, so the Editor menu's Wrap Lines item wears it and Settings → Shortcuts can move it or take it away — there is a new Editor group holding it.Checked
The defect and the fix were both run against the real pinned package in a headless harness, with a 6149pt line laid out in a 600pt pane:
(
reachableXis whatNSClipView.constrainBoundsRectwill actually allow.)Scripts/run.sh— Debug build succeeded, no warnings, app launches.Not checked by me: the scrolling as felt under a trackpad, and ⌥Z as pressed, both of which are the user's screen.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QZeMBBySiGJmqM81eXurS3
Summary by CodeRabbit
New Features
Documentation