fix(editor): sync external text binding changes into the source editor#1592
Conversation
|
Follow-up to the self-review notes, in 337b379:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 337b379806
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| guard newValue != lastSyncedText else { return } | ||
| textBindingTask?.cancel() | ||
| isUpdatingFromRepresentable = true | ||
| controller.textView.setText(newValue) |
There was a problem hiding this comment.
Refresh highlighting after binding text replacement
When a loaded editor receives an external binding change, this swaps the TextView storage directly, but it bypasses TextViewController.setText(_:), which is the path that calls setUpHighlighter() after replacing the text. TextView.setTextStorage reuses the existing storage delegate without emitting a full-character edit, so the current highlighter/style container remains sized and initialized for the old document; externally loaded text can render with stale or missing syntax highlighting until some later highlighter reset/edit happens.
Useful? React with 👍 / 👎.
Fixes #1576
Problem
Selecting rows and switching the results view to JSON showed a blank editor. The data only appeared after clicking Tree and then Text, which forces SwiftUI to recreate the editor.
Root cause
SourceEditorset the text binding's value once inmakeNSViewControllerand never again.updateNSViewControlleronly handled cursor, scroll, find panel, and configuration, so a binding that changed after creation never reached the text view.ResultsJsonViewcomputes its JSON asynchronously, so the editor was always created with an empty string and stayed empty.The same gap made
DDLTextView's.onChange(of: ddl)writeback a no-op, and stale JSON stayed on screen when the row selection changed while in text mode.Fix
lastSyncedText, the last value synced in either direction.updateNSViewControllernow pushes the binding value into the text view when it differs. The comparison is O(1) when nothing changed because the unchanged binding returns the same string instance.textViewDidChangeTextrecordslastSyncedTexton both the immediate and the debounced (>500k chars) writeback paths, and a pending debounce is cancelled before an external value is applied. ProgrammaticsetTextruns underisUpdatingFromRepresentable, which the change notification handler now respects.ResultsJsonViewshows a small spinner until the first JSON formatting pass finishes, instead of an empty editor in text mode or an "Invalid JSON" flash in tree mode. Recomputes triggered by selection changes keep the previous content visible until the new result is ready.Tests
SourceEditorBindingSyncTests(5 tests, all passing): external change syncs down, in-flight user edits are not clobbered, typing writes back and records the synced value, notifications during programmatic sync do not write back, and same-value syncs leave the text storage untouched.TreeSitterClientTests,HighlighterTests, andHighlightProviderStateTestreferencedCodeLanguage.swiftand.c, which the vendoredCodeEditLanguagesno longer ships. They now use.sqland.json.TableProUITests.Verification
swiftlint lint --strictpasses on the changed app file; the package's SwiftLint build plugin passes. Manual check: select rows, switch to JSON, data renders immediately; changing the selection updates the text view in place.