fix(editor): rebuild text selection around an anchor and the rect being drawn - #2542
Merged
Merged
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Selecting a long stretch of text downward past the viewport and then scrolling back up left a band
near the top with no highlight. Executing still ran the whole selection, so the model was right and
only the drawing was wrong.
Tracing that turned up a subsystem with three separate structural problems, so this rebuilds it
rather than patching the symptom.
Root cause
Selection was modelled as a bare
NSRangewith no anchor and no granularity, its geometry wasproduced as a side effect of drawing, and its drawing read the scroll position. So the model could
not say which end was live, the view could not scroll to it, and the paint went stale.
The reported bug.
getFillRectsclipped the selection to the current viewport instead of to therect
draw(_:)was handed. The text view opts into responsive scrolling and is layer-backed, andAppKit documents that
draw(_:)'sdirtyRect"can also represent a nonvisible portion of the viewthat AppKit wants to cache". Forcing that exact case with
cacheDisplay(in:to:):dirtyRectvisibleRectSame rect, same selection, same layout; only the scroll position differs. The blank band is then
cached, and nothing on the scroll path repainted the text view, because
layoutLinesmarked onlythe
LineFragmentViewsubviews. The text came back on the way up and the highlight could not.That line is verbatim upstream CodeEditTextView, not a TablePro modification, and upstream has not
fixed it.
What else this fixes
Measured in a real key window, the editor never scrolled to follow a ranged selection, only a
caret, because it scrolled to the selection's whole bounding rect and
scrollToVisiblestops oncethat rect is already on screen:
rectsFor(range:)rebased a document-absolute range against each line, so the offset went negativeon every line after the first and the fragment lookup matched nothing. A three-line range returned
one rect. That truncated multi-line find highlights, the statement run band and VoiceOver bounds.
setSelectedRangewas the only range setter that never invalidated. Five call sites patched aroundit;
Option+Shift+Downstatement extension and accessibility-set selections did not, and changedthe selection without repainting.
Shift+arrow after a drag guessed the live end fromthe arrow direction and moved the wrong one.
sweep started the selection several characters from where the pointer went down.
NSPressGestureRecognizerturns ondelaysPrimaryMouseButtonEventsfor itself, which withheldevery primary mouse-down inside a selection for the press duration. Measured on the live
recogniser: 167ms of dead pointer. A press inside a selection now defers its caret to mouse up the
way
NSTextViewdoes, so the delay is no longer needed.mouseDraggedand the timer, plus once per real drag event, so itsspeed tracked the input device's event rate. It was also scheduled in
.default, which stopsfiring inside the tracking loop a drag runs in.
=,<,>,+,|,~,^or$selected nothing: they are Unicode symbols,not punctuation, and fell through to the zero-length return. In a SQL editor.
keyDownbailed onisEditable, so the read-only JSON, DDL and preview editors had no arrow keysand no
Shift+arrow at all.colour rather than
unemphasizedSelectedTextBackgroundColor.selectedTextChanged, so assistive clients never heard the selection move.NSRangeto length -1.Shape of the change
TextLayoutManager.textRange(covering:)answers "what text is in this rect";getFillRectsclipsto that, and the two hand-copied
visibleTextRangecomputations collapse onto it. Drawing no longerpublishes
boundingRect.announces a change to both the internal notification and accessibility.
so both ends round out to the current granularity, and drives autoscroll from one source at one call
per tick.
Verification
swift test --package-path LocalPackages/CodeEditTextView: 188 passed, 17 of them new. The newguards were checked against the unfixed source and fail there.
TableProTests: 76 cases across the Vim and editor-coordinator suites, all passing.verify.sh docspasses.0.203ms and 0.209ms at 20,000, flat in document size either way.
UI automation is not included: the behaviours here are pointer gestures, autoscroll timing and
painted pixels, none of which XCUITest drives deterministically. They are covered by unit tests
against the layout and selection managers instead.