`prepare()` was setting `layoutManager.allowsNonContiguousLayout = false`
before calling `scrollRangeToVisible`. With it off, TextKit has to build
layout contiguously from the start of the document to the target range,
so jumping into a match deep inside a large response body (multi-MB
JSON) stalls noticeably.
The code already carried a `// Remove this workaround` comment, but I
don't know the original reason it was added — so this is a best-effort
cleanup. On large responses scrolling into a match feels visibly faster
with no glitches observed (jumping to the first / middle / last match,
opening with a pre-filled search term via TextViewSearchContext). If
there's a specific scenario where the workaround was needed, happy to
restore it or scope it differently.
Searching inside a large response (megabytes of JSON, tens of thousands of
matches) made the search field unresponsive — typing froze the UI for
seconds at a time, and stepping between matches stalled.
The hot paths were:
- Every keystroke triggered a full search pass. `isSearchingInBackground`
serialized them but didn't drop intermediate inputs.
- After ranges came back, `didUpdateMatches` filtered and decorated each
one on the main thread by calling `textStorage.attributes(at:)` twice
per match to snapshot foreground / background colors. For thousands of
matches this alone blocked the run loop.
- The "extend scroll range by 8 newlines" loop walked the text one UTF-16
unit at a time with Character boxing, which is much slower than a
native substring search.
This change:
- Debounces searchTerm / searchOptions updates by 200 ms so typing
doesn't kick off a search per keystroke.
- Builds SearchMatch (technical-attribute filter + original color
snapshots) on the background queue against the immutable originalText
instead of touching textStorage on the main thread.
- Replaces the per-character newline scan in
didUpdateCurrentSelectedMatch with NSString.range(of: "\n", ...).
No public API change. The "extremely slow on iOS 16" TextKit 1 path in
WrappedTextView is untouched.